Merge branch 'main' into HEAD
diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644
index 853d3f4..0000000
--- a/.appveyor.yml
+++ /dev/null
@@ -1,72 +0,0 @@
-environment:
-  matrix:
-    - JOB: "2.7.13 32-bit"
-      PYTHON_HOME: "C:\\Python27"
-      TOXENV: "py27-cov"
-      TOXPYTHON: "C:\\Python27\\python.exe"
-
-    - JOB: "3.5.2 32-bit"
-      PYTHON_HOME: "C:\\Python35"
-      TOXENV: "py35-cov"
-      TOXPYTHON: "C:\\Python35\\python.exe"
-
-    - JOB: "3.6.0 32-bit"
-      PYTHON_HOME: "C:\\Python36"
-      TOXENV: "py36-cov"
-      TOXPYTHON: "C:\\Python36\\python.exe"
-
-    - JOB: "2.7.13 64-bit"
-      PYTHON_HOME: "C:\\Python27-x64"
-      TOXENV: "py27-cov"
-      TOXPYTHON: "C:\\Python27-x64\\python.exe"
-
-    - JOB: "3.5.2 64-bit"
-      PYTHON_HOME: "C:\\Python35-x64"
-      TOXENV: "py35-cov"
-      TOXPYTHON: "C:\\Python35-x64\\python.exe"
-
-    - JOB: "3.6.0 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/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..b3b2b25
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,11 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
+
+# Explicitly declare text files you want to always be normalized and converted
+# to native line endings on checkout.
+*.py text
+
+# Font files are binary (so that autocrlf doesn't muck with them)
+*.lwfn binary
+*.pfa binary
+*.pfb binary
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..ec6abe2
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,31 @@
+# This workflows will upload a Python Package using Twine when a tag is created
+# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
+
+name: Upload Python Package
+
+on:
+  push:
+    # Sequence of patterns matched against refs/tags
+    tags:
+      - '*.*.*' # e.g. 1.0.0 or 20.15.10
+
+jobs:
+  deploy:
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python
+      uses: actions/setup-python@v2
+      with:
+        python-version: '3.x'
+    - name: Install dependencies
+      run: |
+        pip install setuptools wheel twine
+    - name: Build and publish
+      env:
+        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+      run: |
+        python setup.py sdist bdist_wheel
+        twine upload dist/*
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..89d668d
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,91 @@
+name: Test
+
+on:
+  push:
+    branches: [main]
+  pull_request:
+    branches: [main]
+
+jobs:
+  lint:
+    runs-on: ubuntu-latest
+    # https://github.community/t/github-actions-does-not-respect-skip-ci/17325/8
+    if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python 3.x
+      uses: actions/setup-python@v2
+      with:
+        python-version: "3.x"
+    - name: Install packages
+      run: pip install tox
+    - name: Run Tox
+      run: tox -e mypy,package_readme
+
+  test:
+    runs-on: ${{ matrix.platform }}
+    if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
+    strategy:
+      matrix:
+        python-version: [3.6, 3.7, 3.8, 3.9]
+        platform: [ubuntu-latest, macos-latest, windows-latest]
+        exclude: # Only test on the oldest and latest supported stable Python on macOS and Windows.
+          - platform: macos-latest
+            python-version: 3.7
+          - platform: macos-latest
+            python-version: 3.8
+          - platform: windows-latest
+            python-version: 3.7
+          - platform: windows-latest
+            python-version: 3.8
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v2
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install packages
+      run: pip install tox coverage
+    - name: Run Tox
+      run: tox -e py-cov
+    - name: Run Tox without lxml
+      run: tox -e py-cov-nolxml
+    - name: Produce coverage files
+      run: |
+        coverage combine
+        coverage xml
+    - name: Upload coverage to Codecov
+      uses: codecov/codecov-action@v1
+      with:
+        file: coverage.xml
+        flags: unittests
+        name: codecov-umbrella
+        fail_ci_if_error: true
+
+  test-cython:
+    runs-on: ubuntu-latest
+    if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python 3.x
+      uses: actions/setup-python@v2
+      with:
+        python-version: "3.x"
+    - name: Install packages
+      run: pip install tox
+    - name: Run Tox
+      run: tox -e py-cy-nolxml
+
+  test-pypy3:
+    runs-on: ubuntu-latest
+    if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]')"
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python pypy3
+      uses: actions/setup-python@v2
+      with:
+        python-version: "pypy3"
+    - name: Install packages
+      run: pip install tox
+    - name: Run Tox
+      run: tox -e pypy3-nolxml
diff --git a/.gitignore b/.gitignore
index 73b2713..eba633e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,25 +1,59 @@
-# Byte-compiled / optimized files
+# Byte-compiled / optimized / DLL files
 __pycache__/
-*.py[co]
+*.py[cod]
 *$py.class
 
-# Distribution / Packaging
-*.egg
-*.egg-info
-*.eggs
-MANIFEST
-build
-dist
+# C extensions
+*.so
 
-# Unit test / coverage files
-.tox/*
-.cache/
+# Distribution / packaging
+.Python
+build/
+dist/
+.eggs/
+*.egg-info/
+*.egg
+MANIFEST
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
 .coverage
 .coverage.*
-htmlcov/
+.cache
+coverage.xml
+*.cover
+.pytest_cache/
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
 
 # emacs backup files
 *~
 
 # OSX Finder
 .DS_Store
+
+# VSCode
+.vscode
+
+# Cython sources (e.g. cu2qu)
+Lib/**/*.c
+
+# Ctags
+tags
diff --git a/.pyup.yml b/.pyup.yml
new file mode 100644
index 0000000..6ea2065
--- /dev/null
+++ b/.pyup.yml
@@ -0,0 +1,4 @@
+# autogenerated pyup.io config file 
+# see https://pyup.io/docs/configuration/ for all available options
+
+schedule: every week
diff --git a/.readthedocs.yml b/.readthedocs.yml
new file mode 100644
index 0000000..928d658
--- /dev/null
+++ b/.readthedocs.yml
@@ -0,0 +1,29 @@
+# .readthedocs.yml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+build:
+  image: latest
+
+# Build documentation in the docs/ directory with Sphinx
+sphinx:
+  configuration: Doc/source/conf.py
+  fail_on_warning: false
+
+# Optionally build your docs in additional formats such as PDF and ePub
+formats:
+  - htmlzip
+  - epub
+
+# Optionally set the version of Python and requirements required to build your docs
+python:
+  version: 3.8
+  install:
+    - requirements: Doc/docs-requirements.txt
+    - method: pip
+      path: .
+      extra_requirements:
+        - all
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 2598ccd..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-sudo: false
-
-language: python
-
-matrix:
-  fast_finish: true
-  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'
-
-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
deleted file mode 100755
index d113fe7..0000000
--- a/.travis/after_success.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/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
deleted file mode 100755
index 1ded8f0..0000000
--- a/.travis/before_deploy.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/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
deleted file mode 100755
index 8cc4edb..0000000
--- a/.travis/before_install.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/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
deleted file mode 100755
index 03cc0b3..0000000
--- a/.travis/install.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/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
deleted file mode 100755
index 6804f7d..0000000
--- a/.travis/run.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-set -e
-set -x
-
-if [ "$TRAVIS_OS_NAME" == "osx" ]; then
-    source .venv/bin/activate
-fi
-
-tox
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..30f4976
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at <cosimo@anthrotype.com>. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ea116c4
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,26 @@
+## How to Contribute
+
+FontTools development is on-going in an active community of developers, that includes professional developers employed at major software corporations and at type foundries, as well as hobbyists.
+
+The project is run on Github, in the typical free/libre/open-source software way. 
+If you are unfamiliar with that, check out [opensource.guide](https://opensource.guide) and [producingoss.com](http://producingoss.com).
+
+We use Github's Issue Tracker to report, discuss and track bugs, map out future improvements, set priorities, and self-assign issues.
+If you find a bug, have an idea for a new feature, then please [create a new issue](https://github.com/fonttools/fonttools/issues) and we'll be happy to work with you on it!
+
+If you have a question or want to discuss usage from an end-user perspective, there is a mailing list at [groups.google.com/d/forum/fonttools](https://groups.google.com/d/forum/fonttools) mailing list.
+
+If you would like to speak to someone directly, you can also email the project lead, Behdad Esfahbod, privately at <behdad@behdad.org>
+
+If you make a pull request, you (or the organization that owns your copyrights) should be listed in the [README](https://github.com/fonttools/fonttools#copyrights).
+
+(There is also a development [groups.google.com/d/forum/fonttools-dev](https://groups.google.com/d/forum/fonttools-dev) mailing list for Continuous Integration notifications.)
+
+## Code reviews
+
+All submissions, including submissions by project members, go through a review process using GitHub Pull Requests.
+Consult [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on making Pull Requests.
+
+## Code of Conduct
+
+This project has a [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
diff --git a/Doc/README.md b/Doc/README.md
new file mode 100644
index 0000000..26641b7
--- /dev/null
+++ b/Doc/README.md
@@ -0,0 +1,121 @@
+# fontTools Documentation
+
+The fontTools project documentation updates continuously on Read the Docs as the project source changes.  
+
+The documentation is hosted at https://fonttools.readthedocs.io/.
+
+## Contents
+
+- [How to Build Local Documentation](#how-to-build-local-documentation)
+- [Contributing to the fontTools Documentation](#contributing-to-the-documentation)
+- [Documentation License](#documentation-license)
+
+## How to Build Local Documentation
+
+### Install Dependencies
+
+You must have a Python 3 interpreter and the `pip` Python package manager installed on your system to build the fontTools documentation.
+
+Pull the fontTools project source files, create a Python virtual environment, and then install fontTools and the documentation build dependencies by executing the following commands in the root of the fontTools source repository:
+
+```
+$ pip install -e . [all]
+$ pip install -r Doc/docs-requirements.txt
+```
+
+### Build Documentation
+
+**With `make`**: execute the following command in the root of the repository:
+
+```
+$ make docs
+```
+
+**Without `make`**: execute the following command in the **`Doc` directory**:
+
+```
+$ sphinx-build -b html source build
+```
+
+Open the `Doc/build/html/index.html` file in your browser to view the documentation home page.
+
+## Contributing to the Documentation
+
+We highly encourage contributions!  Please follow the instructions below to improve the documentation.
+
+### Python Docstring Style
+
+We recommend the use of Python docstrings that follow [the Google Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md#381-docstrings).  Our documentation build approach parses appropriately formatted docstrings into formatted documentation files.
+
+#### Function Documentation Example
+
+```python
+def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
+    """Fetches rows from a Bigtable.
+
+    Retrieves rows pertaining to the given keys from the Table instance
+    represented by big_table.  Silly things may happen if
+    other_silly_variable is not None.
+
+    Args:
+        big_table: An open Bigtable Table instance.
+        keys: A sequence of strings representing the key of each table row
+            to fetch.
+        other_silly_variable: Another optional variable, that has a much
+            longer name than the other args, and which does nothing.
+
+    Returns:
+        A dict mapping keys to the corresponding table row data
+        fetched. Each row is represented as a tuple of strings. For
+        example:
+
+        {'Serak': ('Rigel VII', 'Preparer'),
+         'Zim': ('Irk', 'Invader'),
+         'Lrrr': ('Omicron Persei 8', 'Emperor')}
+
+        If a key from the keys argument is missing from the dictionary,
+        then that row was not found in the table.
+
+    Raises:
+        IOError: An error occurred accessing the bigtable.Table object.
+    """
+```
+*Source: [Google Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md) (CC BY-SA 3.0)*
+
+#### Class Documentation Example
+
+```python
+class SampleClass(object):
+    """Summary of class here.
+
+    Longer class information....
+    Longer class information....
+
+    Attributes:
+        likes_spam: A boolean indicating if we like SPAM or not.
+        eggs: An integer count of the eggs we have laid.
+    """
+
+    def __init__(self, likes_spam=False):
+        """Inits SampleClass with blah."""
+        self.likes_spam = likes_spam
+        self.eggs = 0
+
+    def public_method(self):
+        """Performs operation blah."""
+```
+*Source: [Google Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md) (CC BY-SA 3.0)*
+
+### Build Local Documentation and Review Your Changes
+
+Build a local set of HTML documentation files with the instructions above and review your changes.
+
+### Submit a Pull Request
+
+Submit a Github pull request with your proposed improvements to the documentation.  
+
+Thanks for your contribution!
+
+## Documentation License
+
+The fontTools documentation is released under a [CC BY-SA 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/).
diff --git a/Doc/docs-requirements.txt b/Doc/docs-requirements.txt
new file mode 100644
index 0000000..c62c8d1
--- /dev/null
+++ b/Doc/docs-requirements.txt
@@ -0,0 +1,3 @@
+sphinx==3.3.1
+sphinx_rtd_theme==0.5.0
+reportlab==3.5.55
diff --git a/Doc/source/afmLib.rst b/Doc/source/afmLib.rst
index f56d3c1..ab9f356 100644
--- a/Doc/source/afmLib.rst
+++ b/Doc/source/afmLib.rst
@@ -1,7 +1,8 @@
-######
-afmLib
-######
+###########################################
+afmLib: Read/write Adobe Font Metrics files
+###########################################
 
 .. automodule:: fontTools.afmLib
+
+.. autoclass:: fontTools.afmLib.AFM
    :members:
-   :undoc-members:
diff --git a/Doc/source/agl.rst b/Doc/source/agl.rst
index 0ecf14d..6e89857 100644
--- a/Doc/source/agl.rst
+++ b/Doc/source/agl.rst
@@ -1,7 +1,6 @@
-###
-agl
-###
+######################################
+agl: Interface to the Adobe Glyph List
+######################################
 
 .. automodule:: fontTools.agl
-   :members:
-   :undoc-members:
+   :members: toUnicode, UV2AGL, AGL2UV
diff --git a/Doc/source/assets/img/favicon.ico b/Doc/source/assets/img/favicon.ico
new file mode 100755
index 0000000..f55a275
--- /dev/null
+++ b/Doc/source/assets/img/favicon.ico
Binary files differ
diff --git a/Doc/source/cffLib.rst b/Doc/source/cffLib.rst
deleted file mode 100644
index 364824f..0000000
--- a/Doc/source/cffLib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-######
-cffLib
-######
-
-.. automodule:: fontTools.cffLib
-   :members:
-   :undoc-members:
diff --git a/Doc/source/cffLib/index.rst b/Doc/source/cffLib/index.rst
new file mode 100644
index 0000000..281a0b1
--- /dev/null
+++ b/Doc/source/cffLib/index.rst
@@ -0,0 +1,53 @@
+##################################
+cffLib: read/write Adobe CFF fonts
+##################################
+
+.. automodule:: fontTools.cffLib
+
+This package also contains two modules for manipulating CFF format glyphs:
+
+.. toctree::
+   :maxdepth: 1
+
+   specializer
+   width
+
+.. autoclass:: fontTools.cffLib.CFFFontSet
+   :inherited-members:
+   :members:
+
+.. autoclass:: fontTools.cffLib.TopDict
+   :members:
+
+.. autoclass:: fontTools.cffLib.CharStrings
+   :members:
+
+.. autoclass:: fontTools.cffLib.Index
+   :members:
+
+.. autoclass:: fontTools.cffLib.GlobalSubrsIndex
+   :members:
+
+.. autoclass:: fontTools.cffLib.TopDictIndex
+   :members:
+
+.. autoclass:: fontTools.cffLib.CFFWriter
+   :members:
+
+.. autoclass:: fontTools.cffLib.IndexCompiler
+   :members:
+
+.. autoclass:: fontTools.cffLib.TopDictIndexCompiler
+   :members:
+
+.. autoclass:: fontTools.cffLib.FDArrayIndexCompiler
+   :members:
+
+.. autoclass:: fontTools.cffLib.GlobalSubrsCompiler
+   :members:
+
+.. autoclass:: fontTools.cffLib.SubrsCompiler
+   :members:
+
+.. autoclass:: fontTools.cffLib.CharStringsCompiler
+   :members:
diff --git a/Doc/source/cffLib/specializer.rst b/Doc/source/cffLib/specializer.rst
new file mode 100644
index 0000000..016a896
--- /dev/null
+++ b/Doc/source/cffLib/specializer.rst
@@ -0,0 +1,8 @@
+##############################################################
+specializer: T2CharString operator specializer and generalizer
+##############################################################
+
+.. automodule:: fontTools.cffLib.specializer
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/cffLib/width.rst b/Doc/source/cffLib/width.rst
new file mode 100644
index 0000000..68944da
--- /dev/null
+++ b/Doc/source/cffLib/width.rst
@@ -0,0 +1,6 @@
+#########################################
+width: T2CharString glyph width optimizer
+#########################################
+
+.. automodule:: fontTools.cffLib.width
+   :members: optimizeWidths, optimizeWidthsBruteforce
diff --git a/Doc/source/colorLib/index.rst b/Doc/source/colorLib/index.rst
new file mode 100644
index 0000000..d4eb9f8
--- /dev/null
+++ b/Doc/source/colorLib/index.rst
@@ -0,0 +1,11 @@
+#####################################################
+colorLib.builder: Build COLR/CPAL tables from scratch
+#####################################################
+
+.. automodule:: fontTools.colorLib.builder
+   :members: buildCPAL, buildCOLR, populateCOLRv0
+
+.. autoclass:: fontTools.colorLib.builder.ColorPaletteType
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/conf.py b/Doc/source/conf.py
index a3b2be2..82a5d57 100644
--- a/Doc/source/conf.py
+++ b/Doc/source/conf.py
@@ -25,40 +25,43 @@
 
 # If your documentation needs a minimal Sphinx version, state it here.
 #
-needs_sphinx = '1.3'
+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']
+extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode", "sphinx.ext.napoleon", "sphinx.ext.coverage"]
 
-autodoc_mock_imports = ['gtk']
+autodoc_mock_imports = ["gtk"]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+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'
+source_suffix = ".rst"
 
 # The master toctree document.
-master_doc = 'index'
+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.'
+project = u"fontTools"
+copyright = u"2020, Just van Rossum, Behdad Esfahbod, and the fontTools Authors. CC BY-SA 4.0"
+author = u"Just van Rossum, Behdad Esfahbod, and the fontTools Authors"
+
+# HTML page title
+html_title = "fontTools Documentation"
 
 # 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'
+version = u"4.0"
 # The full version, including alpha/beta/rc tags.
-release = u'3.10'
+release = u"4.0"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
@@ -73,10 +76,11 @@
 exclude_patterns = []
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+# pygments_style = "sphinx" (the default sphinx docs style on RTD)
+pygments_style = "default"
 
 # If true, `todo` and `todoList` produce output, else they produce nothing.
-todo_include_todos = False
+todo_include_todos = True
 
 
 # -- Options for HTML output ----------------------------------------------
@@ -84,24 +88,29 @@
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 #
-html_theme = 'classic'
+html_theme = "sphinx_rtd_theme"
 
 # 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 = {}
+html_theme_options = {"display_version": False}
 
 # 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']
+html_static_path = ["_static"]
+
+html_favicon = "assets/img/favicon.ico"
+
+# display the Sphinx attribution in the footer
+html_show_sphinx = False
 
 
 # -- Options for HTMLHelp output ------------------------------------------
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = 'fontToolsDoc'
+htmlhelp_basename = "fontToolsDoc"
 
 
 # -- Options for LaTeX output ---------------------------------------------
@@ -110,15 +119,12 @@
     # 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',
@@ -128,8 +134,13 @@
 # (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'),
+    (
+        master_doc,
+        "fontTools.tex",
+        u"fontTools Documentation",
+        u"Just van Rossum, Behdad Esfahbod et al.",
+        "manual",
+    )
 ]
 
 
@@ -137,10 +148,7 @@
 
 # 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)
-]
+man_pages = [(master_doc, "fonttools", u"fontTools Documentation", [author], 1)]
 
 
 # -- Options for Texinfo output -------------------------------------------
@@ -149,8 +157,13 @@
 # (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'),
+    (
+        master_doc,
+        "fontTools",
+        u"fontTools Documentation",
+        author,
+        "fontTools",
+        "A library for manipulating fonts, written in Python.",
+        "Typography",
+    )
 ]
-
diff --git a/Doc/source/cu2qu/index.rst b/Doc/source/cu2qu/index.rst
new file mode 100644
index 0000000..41730e5
--- /dev/null
+++ b/Doc/source/cu2qu/index.rst
@@ -0,0 +1,38 @@
+##########################################
+cu2qu: Cubic to quadratic curve conversion
+##########################################
+
+Routines for converting cubic curves to quadratic splines, suitable for use
+in OpenType to TrueType outline conversion.
+
+Conversion is carried out to a degree of tolerance provided by the user. While
+it is relatively easy to find the best *single* quadratic curve to represent a
+given cubic (see for example `this method from CAGD <https://www.sirver.net/blog/2011/08/23/degree-reduction-of-bezier-curves/>`_),
+the best-fit method may not be sufficiently accurate for type design.
+
+Instead, this method chops the cubic curve into multiple segments before
+converting each cubic segment to a quadratic, in order to ensure that the
+resulting spline fits within the given tolerance.
+
+The basic curve conversion routines are implemented in the
+:mod:`fontTools.cu2qu.cu2qu` module; the :mod:`fontTools.cu2qu.ufo` module
+applies these routines to all of the curves in a UFO file or files; while the
+:mod:`fontTools.cu2qu.cli` module implements the ``fonttools cu2qu`` command
+for converting a UFO format font with cubic curves into one with quadratic
+curves.
+
+fontTools.cu2qu.cu2qu
+---------------------
+
+.. automodule:: fontTools.cu2qu.cu2qu
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+fontTools.cu2qu.ufo
+-------------------
+
+.. automodule:: fontTools.cu2qu.ufo
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/designspaceLib/index.rst b/Doc/source/designspaceLib/index.rst
new file mode 100644
index 0000000..2c33df7
--- /dev/null
+++ b/Doc/source/designspaceLib/index.rst
@@ -0,0 +1,18 @@
+##############
+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
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/designspaceLib/readme.rst b/Doc/source/designspaceLib/readme.rst
new file mode 100644
index 0000000..c5757a6
--- /dev/null
+++ b/Doc/source/designspaceLib/readme.rst
@@ -0,0 +1,1150 @@
+#################################
+DesignSpaceDocument Specification
+#################################
+
+An object to read, write and edit interpolation systems for typefaces. Define sources, axes, rules and instances.
+
+-  `The Python API of the objects <#python-api>`_
+-  `The document XML structure <#document-xml-structure>`_
+
+
+**********
+Python API
+**********
+
+
+
+.. _designspacedocument-object:
+
+DesignSpaceDocument object
+==========================
+
+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. Reader and
+Writer objects can be subclassed as well.
+
+**Note:** Python attribute names are usually camelCased, the
+corresponding `XML <#document-xml-structure>`_ attributes are usually
+all lowercase.
+
+.. example-1:
+
+.. code:: python
+
+    from fontTools.designspaceLib import DesignSpaceDocument
+    doc = DesignSpaceDocument()
+    doc.read("some/path/to/my.designspace")
+    doc.axes
+    doc.sources
+    doc.instances
+
+Attributes
+----------
+
+-  ``axes``: list of axisDescriptors
+-  ``sources``: list of sourceDescriptors
+-  ``instances``: list of instanceDescriptors
+-  ``rules``: list if ruleDescriptors
+-  ``readerClass``: class of the reader object
+-  ``writerClass``: class of the writer object
+-  ``lib``: dict for user defined, custom data that needs to be stored
+   in the designspace. Use reverse-DNS notation to identify your own data.
+   Respect the data stored by others.
+-  ``rulesProcessingLast``: This flag indicates whether the substitution rules should be applied before or after other glyph substitution features. False: before, True: after.
+
+Methods
+-------
+
+-  ``read(path)``: read a designspace file from ``path``
+-  ``write(path)``: write this designspace to ``path``
+-  ``addSource(aSourceDescriptor)``: add this sourceDescriptor to 
+   ``doc.sources``.
+-  ``addInstance(anInstanceDescriptor)``: add this instanceDescriptor
+   to ``doc.instances``.
+-  ``addAxis(anAxisDescriptor)``: add this instanceDescriptor to ``doc.axes``.
+-  ``newDefaultLocation()``: returns a dict with the default location
+   in designspace coordinates.
+-  ``updateFilenameFromPath(masters=True, instances=True, force=False)``:
+   set a descriptor filename attr from the path and this document.
+-  ``newAxisDescriptor()``: return a new axisDescriptor object.
+-  ``newSourceDescriptor()``: return a new sourceDescriptor object.
+-  ``newInstanceDescriptor()``: return a new instanceDescriptor object.
+-  ``getAxisOrder()``: return a list of axisnames
+-  ``findDefault()``: return the sourceDescriptor that is on the default
+   location. Returns None if there isn't one.
+-  ``normalizeLocation(aLocation)``: return a dict with normalized axis values.
+-  ``normalize()``: normalize the geometry of this designspace: scale all the
+   locations of all masters and instances to the ``-1 - 0 - 1`` value.
+-  ``loadSourceFonts()``: Ensure SourceDescriptor.font attributes are loaded,
+   and return list of fonts.
+-  ``tostring(encoding=None)``: Returns the designspace as a string. Default 
+   encoding `utf-8`.
+
+Class Methods
+-------------
+- ``fromfile(path)``
+- ``fromstring(string)``
+
+
+
+
+
+
+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. MutatorMath
+-  ``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.
+-  ``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. Values are required to be ``unicode`` strings, even if
+   they only contain ASCII characters.
+-  ``minimum``: number. The minimum value for this axis in user space.
+   MutatorMath + Varlib.
+-  ``maximum``: number. The maximum value for this axis in user space.
+   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 in user
+   space. MutatorMath + Varlib.
+-  ``map``: list of input / output values that can describe a warp
+   of user space to design space coordinates. If no map values are present, it is assumed user space is the same as design space, as
+   in [(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").
+-  Note: By default, rules are applied first, before other text shaping/OpenType layout, as they are part of the `Required Variation Alternates OpenType feature <https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#-tag-rvrn>`_. See `5.0 rules element`_ § Attributes.
+
+Evaluating rules
+----------------
+    
+-  ``evaluateRule(rule, location)``: Return True if any of the rule's conditionsets 
+   matches the given location.
+-  ``evaluateConditions(conditions, location)``: Return True if all the conditions
+   matches the given location. 
+-  ``processRules(rules, location, glyphNames)``: Apply all the rules to the list
+   of glyphNames. Return a new list of glyphNames with substitutions applied.
+
+.. code:: python
+
+    r1 = RuleDescriptor()
+    r1.name = "unique.rule.name"
+    r1.conditionSets.append([dict(name="weight", minimum=-10, maximum=10), dict(...)])
+    r1.conditionSets.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, in user space coordinates.
+-  ``maximum``: required, number. The maximum value for this axis, in user space coordinates.
+-  ``default``: required, number. The default value for this axis, in user space coordinates.
+-  ``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-2:
+
+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 (user space coordinate)
+   to output value (designspace coordinate) pairs.
+-  Together these values transform the designspace.
+-  Child of ``axis`` element.
+
+.. example-3:
+
+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-4:
+
+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 or layer that contributes to the designspace.
+-  Child element of ``sources``
+-  Location in designspace coordinates.
+
+.. 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
+
+.. 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-5:
+
+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.
+-  Location in designspace coordinates.
+
+.. 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-6:
+
+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-7:
+
+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.
+
+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``.
+
+.. attributes-11:
+
+Attributes
+----------
+
+-  ``processing``: flag, optional. Valid values are [``first``, ``last``]. This flag indicates whether the substitution rules should be applied before or after other glyph substitution features.
+-  If no ``processing`` attribute is given, interpret as ``first``, and put the substitution rule in the `rvrn` feature.
+-  If ``processing`` is ``last``, put it in `rclt`.
+
+.. 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 condition is ``True``.
+-  ``minimum`` and ``maximum`` are in designspace coordinates.
+-  If ``minimum`` is not available, assume it is ``axis.minimum``, mapped to designspace coordinates.
+-  If ``maximum`` is not available, assume it is ``axis.maximum``, mapped to designspace coordinates.
+-  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, in designspace coordinates.
+-  ``maximum``: number, required*. The high value, in designspace coordinates.
+
+.. 513-sub-element:
+
+5.1.3 sub element
+=================
+
+-  Child element of ``rule``.
+-  Defines which glyph to replace when the rule evaluates to **True**.
+-  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**.
+
+Axis values in Conditions are in designspace coordinates.
+
+.. 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-8:
+
+Example
+-------
+
+Example with an implied ``conditionset``. Here the conditions are not
+contained in a conditionset. 
+
+.. code:: xml
+
+    <rules processing="last">
+        <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-common-lib-key-registry:
+
+7 Common Lib Key Registry
+=========================
+
+public.skipExportGlyphs
+-----------------------
+
+This lib key works the same as the UFO lib key with the same name. The
+difference is that applications using a Designspace as the corner stone of the
+font compilation process should use the lib key in that Designspace instead of
+any of the UFOs. If the lib key is empty or not present in the Designspace, all
+glyphs should be exported, regardless of what the same lib key in any of the
+UFOs says.
+
+.. 8-implementation-and-differences:
+
+
+8 Implementation and differences
+================================
+
+The designspace format has gone through considerable development. 
+
+ -  the format was originally written for MutatorMath.
+ -  the format is now also used in fontTools.varlib.
+ -  not all values are be required by all implementations.
+
+8.1 Varlib vs. MutatorMath
+--------------------------
+
+There are some differences between the way MutatorMath and fontTools.varlib handle designspaces.
+
+ -  Varlib does not support anisotropic interpolations.
+ -  MutatorMath 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.
+
+8.2 Older versions
+------------------
+
+-  In some implementations that preceed Variable Fonts, the `copyInfo`
+   flag in a source indicated the source was to be treated as the default.
+   This is no longer compatible with the assumption that the default font
+   is located on the default value of each axis.
+-  Older implementations did not require axis records to be present in
+   the designspace file. The axis extremes for instance were generated 
+   from the locations used in the sources. This is no longer possible.
+
+8.3 Rules and generating static UFO instances
+---------------------------------------------
+
+When making instances as UFOs from a designspace with rules, it can
+be useful to evaluate the rules so that the characterset of the ufo 
+reflects, as much as possible, the state of a variable font when seen
+at the same location. This can be done by some swapping and renaming of
+glyphs.
+
+While useful for proofing or development work, it should be noted that
+swapping and renaming leaves the UFOs with glyphnames that are no longer
+descriptive. For instance, after a swap `dollar.bar` could contain a shape
+without a bar. Also, when the swapped glyphs are part of other GSUB variations
+it can become complex very quickly. So proceed with caution.
+
+ -  Assuming `rulesProcessingLast = True`:
+ -  We need to swap the glyphs so that the original shape is still available. 
+    For instance, if a rule swaps ``a`` for ``a.alt``, a glyph
+    that references ``a`` in a component would then show the new ``a.alt``.
+ -  But that can lead to unexpected results, the two glyphs may have different
+    widths or height. So, glyphs that are not specifically referenced in a rule
+    **should not change appearance**. That means that the implementation that swaps
+    ``a`` and ``a.alt`` also swap all components that reference these
+    glyphs in order to preserve their appearance.
+ -  The swap function also needs to take care of swapping the names in
+    kerning data and any GPOS code.
+
+
+.. 9-this-document
+
+9 This document
+===============
+
+-  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..5a17816
--- /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 ``unicode`` 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/developer.rst b/Doc/source/developer.rst
new file mode 100644
index 0000000..3e259f0
--- /dev/null
+++ b/Doc/source/developer.rst
@@ -0,0 +1,115 @@
+.. _developerinfo:
+.. image:: ../../Icons/FontToolsIconGreenCircle.png
+   :width: 200px
+   :height: 200px
+   :alt: Font Tools
+   :align: center
+
+
+fontTools Developer Information
+===============================
+
+If you would like to contribute to the development of fontTools, 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 the Python 3 `venv <https://docs.python.org/3/library/venv.html>`_ module::
+
+    # 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 venv fonttools-venv
+
+    # source the `activate` shell script to enter the environment (Un*x)
+    . 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 .
+
+
+.. note::
+
+    To exit a Python virtual environment, enter the command ``deactivate``.
+
+Testing
+-------
+
+To run the test suite, you need to install `pytest <http://docs.pytest.org/en/latest/>`__.
+When you run the ``pytest`` command, the tests will run against the
+installed fontTools package, or the first one found in the
+``PYTHONPATH``.
+
+You can also use `tox <https://tox.readthedocs.io/en/latest/>`__ to
+automatically run tests on different Python versions in isolated virtual
+environments::
+
+    pip install tox
+    tox
+
+
+.. note::
+
+    When you run ``tox`` without arguments, the tests are executed for all the environments listed in the ``tox.ini`` ``envlist``. The Python versions that are not available on your system ``PATH`` will be skipped.
+
+You can specify a particular testing environment list via the ``-e`` option, or the ``TOXENV`` environment variable::
+
+    tox -e py36
+    TOXENV="py36-cov,htmlcov" tox
+
+
+Development Community
+---------------------
+
+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 end-user and developer discussion about the fontTools project is the `fontTools gitter channel <https://gitter.im/fonttools-dev/Lobby>`_. There is also a development https://groups.google.com/d/forum/fonttools-dev mailing list for continuous integration notifications.
+
+
+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.
+
+
+Acknowledgments
+---------------
+
+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, Peter Dekkers, 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, Chris Simpkins, Miguel Sousa, Adam Twardoch, Adrien Tétar, Vitaly Volkov,
+Paul Wise.
+
+License
+-------
+
+`MIT license <https://github.com/fonttools/fonttools/blob/master/LICENSE>`_.  See the full text of the license for details.
+
+.. |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
+.. |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/Doc/source/encodings.rst b/Doc/source/encodings.rst
deleted file mode 100644
index 8bcd38a..0000000
--- a/Doc/source/encodings.rst
+++ /dev/null
@@ -1,14 +0,0 @@
-#########
-encodings
-#########
-
-.. automodule:: fontTools.encodings
-   :members:
-   :undoc-members:
-
-codecs
-------
-
-.. automodule:: fontTools.encodings.codecs
-   :members:
-   :undoc-members:
diff --git a/Doc/source/encodings/index.rst b/Doc/source/encodings/index.rst
new file mode 100644
index 0000000..32d13c7
--- /dev/null
+++ b/Doc/source/encodings/index.rst
@@ -0,0 +1,21 @@
+##################################################
+encodings: Support for OpenType-specific encodings
+##################################################
+
+fontTools includes support for some character encodings found in legacy Mac
+TrueType fonts. Many of these legacy encodings have found their way into the
+standard Python ``encodings`` library, but others still remain unimplemented.
+Importing ``fontTools.encodings.codecs`` will therefore add string ``encode``
+and ``decode`` support for the following encodings:
+
+* ``x_mac_japanese_ttx``
+* ``x_mac_trad_chinese_ttx``
+* ``x_mac_korean_ttx``
+* ``x_mac_simp_chinese_ttx``
+
+fontTools also includes a package (``fontTools.encodings.MacRoman``) which
+contains a mapping of glyph IDs to glyph names in the MacRoman character set::
+
+		>>> from fontTools.encodings.MacRoman import MacRoman
+		>>> MacRoman[26]
+		'twosuperior'
diff --git a/Doc/source/feaLib.rst b/Doc/source/feaLib.rst
deleted file mode 100644
index ad69217..0000000
--- a/Doc/source/feaLib.rst
+++ /dev/null
@@ -1,43 +0,0 @@
-######
-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/feaLib/index.rst b/Doc/source/feaLib/index.rst
new file mode 100644
index 0000000..61ac31f
--- /dev/null
+++ b/Doc/source/feaLib/index.rst
@@ -0,0 +1,40 @@
+#########################################
+feaLib: Read/write OpenType feature files
+#########################################
+
+fontTools' ``feaLib`` allows for the creation and parsing of Adobe
+Font Development Kit for OpenType feature (``.fea``) files. The syntax
+of these files is described `here <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html>`_.
+
+The :class:`fontTools.feaLib.parser.Parser` class can be used to parse files
+into an abstract syntax tree, and from there the
+:class:`fontTools.feaLib.builder.Builder` class can add features to an existing
+font file. You can inspect the parsed syntax tree, walk the tree and do clever
+things with it, and also generate your own feature files programmatically, by
+using the classes in the :mod:`fontTools.feaLib.ast` module.
+
+Parsing
+-------
+
+.. autoclass:: fontTools.feaLib.parser.Parser
+   :members: parse
+   :member-order: bysource
+
+Building
+---------
+
+.. automodule:: fontTools.feaLib.builder
+   :members: addOpenTypeFeatures, addOpenTypeFeaturesFromString
+
+Generation/Interrogation
+------------------------
+
+.. _`glyph-containing object`:
+.. _`glyph-containing objects`:
+
+In the below, a **glyph-containing object** is an object of one of the following
+classes: :class:`GlyphName`, :class:`GlyphClass`, :class:`GlyphClassName`.
+
+.. automodule:: fontTools.feaLib.ast
+   :member-order: bysource
+   :members:
diff --git a/Doc/source/index.rst b/Doc/source/index.rst
index a64dbe8..83bb850 100644
--- a/Doc/source/index.rst
+++ b/Doc/source/index.rst
@@ -1,28 +1,154 @@
+.. image:: ../../Icons/FontToolsIconGreenCircle.png
+   :width: 200px
+   :height: 200px
+   :alt: Font Tools
+   :align: center
+
+
 fontTools Docs
 ==============
 
+About
+-----
+
+fontTools is a family of libraries and utilities for manipulating fonts in Python.
+
+The project has an `MIT open-source license <https://github.com/fonttools/fonttools/blob/master/LICENSE>`_. Among other things this means you can use it free of charge.
+
+Installation
+------------
+
+.. note::
+
+    fontTools requires `Python <http://www.python.org/download/>`_ 3.6 or later.
+
+The package is listed in the Python Package Index (PyPI), so you can install it with `pip <https://pip.pypa.io/>`_::
+
+    pip install fonttools
+
+See the Optional Requirements section below for details about module-specific dependencies that must be installed in select cases.
+
+Utilities
+---------
+
+fontTools installs four command-line utilities:
+
+- ``pyftmerge``, a tool for merging fonts; see :py:mod:`fontTools.merge`
+- ``pyftsubset``, a tool for subsetting fonts; see :py:mod:`fontTools.subset`
+- ``ttx``, a tool for converting between OpenType binary fonts (OTF) and an XML representation (TTX); see :py:mod:`fontTools.ttx`
+- ``fonttools``, a "meta-tool" for accessing other components of the fontTools family.
+
+This last utility takes a subcommand, which could be one of:
+
+- ``cffLib.width``: Calculate optimum defaultWidthX/nominalWidthX values
+- ``cu2qu``: Convert a UFO font from cubic to quadratic curves
+- ``feaLib``: Add features from a feature file (.fea) into a OTF font
+- ``help``: Show this help
+- ``merge``: Merge multiple fonts into one
+- ``mtiLib``: Convert a FontDame OTL file to TTX XML
+- ``subset``: OpenType font subsetter and optimizer
+- ``ttLib.woff2``: Compress and decompress WOFF2 fonts
+- ``ttx``: Convert OpenType fonts to XML and back
+- ``varLib``: Build a variable font from a designspace file and masters
+- ``varLib.instancer``: Partially instantiate a variable font.
+- ``varLib.interpolatable``: Test for interpolatability issues between fonts
+- ``varLib.interpolate_layout``: Interpolate GDEF/GPOS/GSUB tables for a point on a designspace
+- ``varLib.models``: Normalize locations on a given designspace
+- ``varLib.mutator``: Instantiate a variation font
+- ``varLib.varStore``: Optimize a font's GDEF variation store
+
+Libraries
+---------
+
+The main library you will want to access when using fontTools for font
+engineering is likely to be :py:mod:`fontTools.ttLib`, which is the package
+for handling TrueType/OpenType fonts. However, there are many other
+libraries in the fontTools suite:
+
+- :py:mod:`fontTools.afmLib`: Module for reading and writing AFM files
+- :py:mod:`fontTools.agl`: Access to the Adobe Glyph List
+- :py:mod:`fontTools.cffLib`: Read/write tools for Adobe CFF fonts
+- :py:mod:`fontTools.colorLib`: Module for handling colors in CPAL/COLR fonts
+- :py:mod:`fontTools.cu2qu`: Module for cubic to quadratic conversion
+- :py:mod:`fontTools.designspaceLib`: Read and write designspace files
+- :py:mod:`fontTools.encodings`: Support for font-related character encodings
+- :py:mod:`fontTools.feaLib`: Read and read AFDKO feature files
+- :py:mod:`fontTools.fontBuilder`: Construct TTF/OTF fonts from scratch
+- :py:mod:`fontTools.merge`: Tools for merging font files
+- :py:mod:`fontTools.pens`: Various classes for manipulating glyph outlines
+- :py:mod:`fontTools.subset`: OpenType font subsetting and optimization
+- :py:mod:`fontTools.svgLib.path`: Library for drawing SVG paths onto glyphs
+- :py:mod:`fontTools.t1Lib`: Tools for PostScript Type 1 fonts (Python2 only)
+- :py:mod:`fontTools.tfmLib`: Module for reading TFM files
+- :py:mod:`fontTools.ttx`: Module for converting between OTF and XML representation
+- :py:mod:`fontTools.ufoLib`: Module for reading and writing UFO files
+- :py:mod:`fontTools.unicodedata`: Convert between Unicode and OpenType script information
+- :py:mod:`fontTools.varLib`: Module for dealing with 'gvar'-style font variations
+- :py:mod:`fontTools.voltLib`: Module for dealing with Visual OpenType Layout Tool (VOLT) files
+
+A selection of sample Python programs using these libaries can be found in the `Snippets directory <https://github.com/fonttools/fonttools/blob/master/Snippets/>`_ of the fontTools repository.
+
+Optional Dependencies
+---------------------
+
+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 to unlock optional features
+in some of the library modules. See the :doc:`optional requirements <./optional>`
+page for more information.
+
+Developer information
+---------------------
+
+Information for developers can be found :doc:`here <./developer>`.
+
+License
+-------
+
+`MIT license <https://github.com/fonttools/fonttools/blob/master/LICENSE>`_.  See the full text of the license for details.
+
+
+Table of Contents
+-----------------
+
 .. toctree::
-   :maxdepth: 1
+   :maxdepth: 2
+   :caption: Library
 
    afmLib
    agl
-   cffLib
-   inspect
-   encodings
-   feaLib
+   cffLib/index
+   colorLib/index
+   cu2qu/index
+   designspaceLib/index
+   encodings/index
+   feaLib/index
    merge
    misc/index
+   mtiLib
+   otlLib/index
    pens/index
-   subset
+   subset/index
+   svgLib/index
    t1Lib
+   tfmLib
    ttLib/index
    ttx
+   ufoLib/index
+   unicode
+   unicodedata/index
    varLib/index
    voltLib
 
-Indices and tables
-==================
 
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
+.. |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
+.. |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
\ No newline at end of file
diff --git a/Doc/source/inspect.rst b/Doc/source/inspect.rst
deleted file mode 100644
index e0f65c2..0000000
--- a/Doc/source/inspect.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-#######
-inspect
-#######
-
-.. automodule:: fontTools.inspect
-   :members:
-   :undoc-members:
diff --git a/Doc/source/merge.rst b/Doc/source/merge.rst
index 74cf9ad..3114615 100644
--- a/Doc/source/merge.rst
+++ b/Doc/source/merge.rst
@@ -1,7 +1,10 @@
-#####
-merge
-#####
+####################################
+merge: Merge multiple fonts into one
+####################################
 
-.. automodule:: fontTools.merge
+``fontTools.merge`` provides both a library and a command line interface
+(``fonttools merge``) for merging multiple fonts together.
+
+.. autoclass:: fontTools.merge.Merger
+   :inherited-members:
    :members:
-   :undoc-members:
diff --git a/Doc/source/misc/arrayTools.rst b/Doc/source/misc/arrayTools.rst
index acb2a51..d996cc2 100644
--- a/Doc/source/misc/arrayTools.rst
+++ b/Doc/source/misc/arrayTools.rst
@@ -1,7 +1,9 @@
-##########
-arrayTools
-##########
+#############################################
+arrayTools: Various array and rectangle tools
+#############################################
 
 .. automodule:: fontTools.misc.arrayTools
+   :member-order: bysource
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/bezierTools.rst b/Doc/source/misc/bezierTools.rst
index c5b4d2a..10ddc3a 100644
--- a/Doc/source/misc/bezierTools.rst
+++ b/Doc/source/misc/bezierTools.rst
@@ -1,7 +1,8 @@
-###########
-bezierTools
-###########
+####################################################
+bezierTools: Routines for working with Bezier curves
+####################################################
 
 .. automodule:: fontTools.misc.bezierTools
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/classifyTools.rst b/Doc/source/misc/classifyTools.rst
index b02b350..38c35d4 100644
--- a/Doc/source/misc/classifyTools.rst
+++ b/Doc/source/misc/classifyTools.rst
@@ -3,5 +3,6 @@
 #############
 
 .. automodule:: fontTools.misc.classifyTools
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/cliTools.rst b/Doc/source/misc/cliTools.rst
new file mode 100644
index 0000000..36b2aeb
--- /dev/null
+++ b/Doc/source/misc/cliTools.rst
@@ -0,0 +1,8 @@
+###################################################################
+cliTools: Utilities for command-line interfaces and console scripts
+###################################################################
+
+.. automodule:: fontTools.misc.cliTools
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/eexec.rst b/Doc/source/misc/eexec.rst
index 8506f86..b229d58 100644
--- a/Doc/source/misc/eexec.rst
+++ b/Doc/source/misc/eexec.rst
@@ -1,7 +1,8 @@
-#####
-eexec
-#####
+###############################################################
+eexec: PostScript charstring encryption and decryption routines
+###############################################################
 
 .. automodule:: fontTools.misc.eexec
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/encodingTools.rst b/Doc/source/misc/encodingTools.rst
index ff29f66..4e4b719 100644
--- a/Doc/source/misc/encodingTools.rst
+++ b/Doc/source/misc/encodingTools.rst
@@ -3,5 +3,6 @@
 #############
 
 .. automodule:: fontTools.misc.encodingTools
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/etree.rst b/Doc/source/misc/etree.rst
new file mode 100644
index 0000000..4679a1d
--- /dev/null
+++ b/Doc/source/misc/etree.rst
@@ -0,0 +1,8 @@
+#####
+etree
+#####
+
+.. automodule:: fontTools.misc.etree
+   :inherited-members:
+   :members:
+   :undoc-members:
\ No newline at end of file
diff --git a/Doc/source/misc/filenames.rst b/Doc/source/misc/filenames.rst
new file mode 100644
index 0000000..2ebef35
--- /dev/null
+++ b/Doc/source/misc/filenames.rst
@@ -0,0 +1,7 @@
+##########################################################
+filenames: Implements UFO User Name to File Name Algorithm
+##########################################################
+
+.. automodule:: fontTools.misc.filenames
+   :members: userNameToFileName
+   :undoc-members:
diff --git a/Doc/source/misc/fixedTools.rst b/Doc/source/misc/fixedTools.rst
index 30a1f02..d3785f4 100644
--- a/Doc/source/misc/fixedTools.rst
+++ b/Doc/source/misc/fixedTools.rst
@@ -1,7 +1,8 @@
-##########
-fixedTools
-##########
+######################################################
+fixedTools: Tools for working with fixed-point numbers
+######################################################
 
 .. automodule:: fontTools.misc.fixedTools
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/index.rst b/Doc/source/misc/index.rst
index 29a7245..bd7db09 100644
--- a/Doc/source/misc/index.rst
+++ b/Doc/source/misc/index.rst
@@ -1,6 +1,9 @@
-####
-misc
-####
+##########################################################
+misc: Miscellaneous libraries helpful for font engineering
+##########################################################
+
+This is a collection of packages, most of which are used as internal support
+utilities by fontTools, but some of which may be more generally useful.
 
 .. toctree::
    :maxdepth: 2
@@ -8,16 +11,26 @@
    arrayTools
    bezierTools
    classifyTools
+   cliTools
    eexec
    encodingTools
+   etree
+   filenames
    fixedTools
+   intTools
    loggingTools
-   sstruct
+   macCreatorType
+   macRes
+   plistlib
    psCharStrings
+   psLib
+   psOperators
+   py23
+   sstruct
+   symfont
    testTools
    textTools
    timeTools
    transform
    xmlReader
    xmlWriter
-
diff --git a/Doc/source/misc/intTools.rst b/Doc/source/misc/intTools.rst
new file mode 100644
index 0000000..24ea231
--- /dev/null
+++ b/Doc/source/misc/intTools.rst
@@ -0,0 +1,6 @@
+###############################################
+intTools: Tools for working with integer values
+###############################################
+
+.. automodule:: fontTools.misc.intTools
+   :members:
diff --git a/Doc/source/misc/loggingTools.rst b/Doc/source/misc/loggingTools.rst
index fb8eab5..157e020 100644
--- a/Doc/source/misc/loggingTools.rst
+++ b/Doc/source/misc/loggingTools.rst
@@ -1,6 +1,6 @@
-############
-loggingTools
-############
+###################################################################
+loggingTools: tools for interfacing with the Python logging package
+###################################################################
 
 .. automodule:: fontTools.misc.loggingTools
    :members:
diff --git a/Doc/source/misc/macCreatorType.rst b/Doc/source/misc/macCreatorType.rst
new file mode 100644
index 0000000..809098d
--- /dev/null
+++ b/Doc/source/misc/macCreatorType.rst
@@ -0,0 +1,9 @@
+##############################################################
+macCreatorType: Functions for working with Mac file attributes
+##############################################################
+
+This module requires the `xattr <https://pypi.org/project/xattr/>`_ module
+to be installed in order to function correctly.
+
+.. automodule:: fontTools.misc.macCreatorType
+   :members:
diff --git a/Doc/source/misc/macRes.rst b/Doc/source/misc/macRes.rst
new file mode 100644
index 0000000..6fce8e2
--- /dev/null
+++ b/Doc/source/misc/macRes.rst
@@ -0,0 +1,11 @@
+############################################
+macRes: Tools for reading Mac resource forks
+############################################
+
+Classic Mac OS files are made up of two parts - the "data fork" which contains the file contents proper, and the "resource fork" which contains a number of structured data items called "resources". Some fonts, such as Mac "font suitcases" and Type 1 LWFN fonts, still use the resource fork for this kind of structured data, and so to read them, fontTools needs to have access to resource forks.
+
+The Inside Macintosh volume `More Macintosh Toolbox <https://developer.apple.com/library/archive/documentation/mac/pdf/MoreMacintoshToolbox.pdf#page=34>`_ explains the structure of resource and data forks.
+
+.. automodule:: fontTools.misc.macRes
+   :members: ResourceReader, Resource
+   :member-order: bysource
\ No newline at end of file
diff --git a/Doc/source/misc/plistlib.rst b/Doc/source/misc/plistlib.rst
new file mode 100644
index 0000000..6857096
--- /dev/null
+++ b/Doc/source/misc/plistlib.rst
@@ -0,0 +1,9 @@
+#########################################
+plistlib: Tools for handling .plist files
+#########################################
+
+.. automodule:: fontTools.misc.plistlib
+   :members: totree, fromtree, load, loads, dump, dumps
+
+.. autoclass:: fontTools.misc.plistlib.Data
+   :members:
diff --git a/Doc/source/misc/psCharStrings.rst b/Doc/source/misc/psCharStrings.rst
index 3dc0dce..58497f6 100644
--- a/Doc/source/misc/psCharStrings.rst
+++ b/Doc/source/misc/psCharStrings.rst
@@ -3,5 +3,6 @@
 #############
 
 .. automodule:: fontTools.misc.psCharStrings
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/psLib.rst b/Doc/source/misc/psLib.rst
new file mode 100644
index 0000000..f3afa8b
--- /dev/null
+++ b/Doc/source/misc/psLib.rst
@@ -0,0 +1,8 @@
+#####
+psLib
+#####
+
+.. automodule:: fontTools.misc.psLib
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/psOperators.rst b/Doc/source/misc/psOperators.rst
new file mode 100644
index 0000000..432274e
--- /dev/null
+++ b/Doc/source/misc/psOperators.rst
@@ -0,0 +1,8 @@
+###########
+psOperators
+###########
+
+.. automodule:: fontTools.misc.psOperators
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/py23.rst b/Doc/source/misc/py23.rst
new file mode 100644
index 0000000..49a76bf
--- /dev/null
+++ b/Doc/source/misc/py23.rst
@@ -0,0 +1,8 @@
+####
+py23
+####
+
+.. automodule:: fontTools.misc.py23
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/sstruct.rst b/Doc/source/misc/sstruct.rst
index 482aa23..0544795 100644
--- a/Doc/source/misc/sstruct.rst
+++ b/Doc/source/misc/sstruct.rst
@@ -3,5 +3,6 @@
 #######
 
 .. automodule:: fontTools.misc.sstruct
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/symfont.rst b/Doc/source/misc/symfont.rst
new file mode 100644
index 0000000..c189f3d
--- /dev/null
+++ b/Doc/source/misc/symfont.rst
@@ -0,0 +1,8 @@
+#######
+symfont
+#######
+
+.. automodule:: fontTools.misc.symfont
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/misc/testTools.rst b/Doc/source/misc/testTools.rst
index b3215ac..0019765 100644
--- a/Doc/source/misc/testTools.rst
+++ b/Doc/source/misc/testTools.rst
@@ -3,5 +3,6 @@
 #########
 
 .. automodule:: fontTools.misc.testTools
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/textTools.rst b/Doc/source/misc/textTools.rst
index 754eb67..0044c08 100644
--- a/Doc/source/misc/textTools.rst
+++ b/Doc/source/misc/textTools.rst
@@ -3,5 +3,6 @@
 #########
 
 .. automodule:: fontTools.misc.textTools
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/timeTools.rst b/Doc/source/misc/timeTools.rst
index f8d1508..c0a7199 100644
--- a/Doc/source/misc/timeTools.rst
+++ b/Doc/source/misc/timeTools.rst
@@ -3,5 +3,6 @@
 #########
 
 .. automodule:: fontTools.misc.timeTools
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/transform.rst b/Doc/source/misc/transform.rst
index 9517fe0..44a3dbd 100644
--- a/Doc/source/misc/transform.rst
+++ b/Doc/source/misc/transform.rst
@@ -3,5 +3,6 @@
 #########
 
 .. automodule:: fontTools.misc.transform
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/xmlReader.rst b/Doc/source/misc/xmlReader.rst
index 6e09354..d0b80f5 100644
--- a/Doc/source/misc/xmlReader.rst
+++ b/Doc/source/misc/xmlReader.rst
@@ -3,5 +3,6 @@
 #########
 
 .. automodule:: fontTools.misc.xmlReader
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/misc/xmlWriter.rst b/Doc/source/misc/xmlWriter.rst
index f488183..5f7aaef 100644
--- a/Doc/source/misc/xmlWriter.rst
+++ b/Doc/source/misc/xmlWriter.rst
@@ -3,5 +3,6 @@
 #########
 
 .. automodule:: fontTools.misc.xmlWriter
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/mtiLib.rst b/Doc/source/mtiLib.rst
new file mode 100644
index 0000000..1bf74e1
--- /dev/null
+++ b/Doc/source/mtiLib.rst
@@ -0,0 +1,14 @@
+###########################################
+mtiLib: Read Monotype FontDame source files
+###########################################
+
+FontTools provides support for reading the OpenType layout tables produced by
+Monotype's FontDame and Font Chef font editors. These tables are written in a
+simple textual format. The ``mtiLib`` library parses these text files and creates
+table objects representing their contents.
+
+Additionally, ``fonttools mtiLib`` will convert a text file to TTX XML.
+
+
+.. automodule:: fontTools.mtiLib
+   :members: build, main
diff --git a/Doc/source/optional.rst b/Doc/source/optional.rst
new file mode 100644
index 0000000..09376a2
--- /dev/null
+++ b/Doc/source/optional.rst
@@ -0,0 +1,140 @@
+Optional Dependencies
+=====================
+
+The fonttools PyPI distribution also supports so-called "extras", i.e. a
+set of keywords that describe a group of additional dependencies, which can be
+used when installing via pip, or when specifying a requirement.
+For example:
+
+.. code:: sh
+
+    pip install fonttools[ufo,lxml,woff,unicode]
+
+This command will install fonttools, as well as the optional dependencies that
+are required to unlock the extra features named "ufo", etc.
+
+.. note::
+
+    Optional dependencies are detailed by module in the list below with the ``Extra`` setting that automates ``pip`` dependency installation when this is supported.
+
+
+
+:py:mod:`fontTools.misc.etree`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The module exports a ElementTree-like API for reading/writing XML files, and allows to use as the backend either the built-in ``xml.etree`` module or `lxml <https://lxml.de>`__. The latter is preferred whenever present, as it is generally faster and more secure.
+
+*Extra:* ``lxml``
+
+
+:py:mod:`fontTools.ufoLib`
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Package for reading and writing UFO source files; it requires:
+
+* `fs <https://pypi.org/pypi/fs>`__: (aka ``pyfilesystem2``) filesystem abstraction layer.
+
+* `enum34 <https://pypi.org/pypi/enum34>`__: backport for the built-in ``enum`` module (only required on Python < 3.4).
+
+*Extra:* ``ufo``
+
+
+:py:mod:`fontTools.ttLib.woff2`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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.
+
+*Extra:* ``woff``
+
+
+:py:mod:`fontTools.unicode`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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.x updated to the latest Unicode version 12.0. Note this is not necessary if you use Python 3.8
+  as the latter already comes with an up-to-date ``unicodedata``.
+
+*Extra:* ``unicode``
+
+
+:py:mod:`fontTools.varLib.interpolatable`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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.
+
+*Extra:* ``interpolatable``
+
+
+:py:mod:`fontTools.varLib.plot`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Module for visualizing DesignSpaceDocument and resulting VariationModel.
+
+* `matplotlib <https://pypi.org/pypi/matplotlib>`__: 2D plotting library.
+
+*Extra:* ``plot``
+
+
+:py:mod:`fontTools.misc.symfont`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Advanced module for symbolic font statistics analysis; it requires:
+
+* `sympy <https://pypi.python.org/pypi/sympy>`__: the Python library for symbolic mathematics.
+
+*Extra:* ``symfont``
+
+
+:py:mod:`fontTools.t1Lib`
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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).
+
+*Extra:* ``type1``
+
+
+:py:mod:`fontTools.pens.cocoaPen`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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).
+
+
+:py:mod:`fontTools.pens.qtPen`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+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.
+
+
+:py:mod:`fontTools.pens.reportLabPen`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Pen to drawing glyphs as PNG images, requires:
+
+* `reportlab <https://pypi.python.org/pypi/reportlab>`__: Python toolkit for generating PDFs and
+  graphics.
diff --git a/Doc/source/otlLib/index.rst b/Doc/source/otlLib/index.rst
new file mode 100644
index 0000000..1984914
--- /dev/null
+++ b/Doc/source/otlLib/index.rst
@@ -0,0 +1,74 @@
+#################################################
+otlLib: Routines for working with OpenType Layout
+#################################################
+
+The ``fontTools.otlLib`` library provides routines to help you create the
+subtables and other data structures you need when you are editing a font's
+``GSUB`` and ``GPOS`` tables: substitution and positioning rules, anchors,
+lookups, coverage tables and so on.
+
+------------------------------------------
+High-level OpenType Layout Lookup Builders
+------------------------------------------
+
+.. automodule:: fontTools.otlLib.builder
+   :members: AlternateSubstBuilder, ChainContextPosBuilder, ChainContextSubstBuilder, LigatureSubstBuilder, MultipleSubstBuilder, CursivePosBuilder, MarkBasePosBuilder, MarkLigPosBuilder, MarkMarkPosBuilder, ReverseChainSingleSubstBuilder, SingleSubstBuilder, ClassPairPosSubtableBuilder, PairPosBuilder, SinglePosBuilder
+   :member-order: bysource
+
+--------------------------------------
+Common OpenType Layout Data Structures
+--------------------------------------
+
+.. automodule:: fontTools.otlLib.builder
+   :members: buildCoverage, buildLookup
+
+------------------------------------
+Low-level GSUB Table Lookup Builders
+------------------------------------
+
+These functions deal with the "simple" lookup types. See above for classes to
+help build more complex lookups (contextual and chaining lookups).
+
+.. automodule:: fontTools.otlLib.builder
+   :members: buildSingleSubstSubtable, buildMultipleSubstSubtable, buildAlternateSubstSubtable, buildLigatureSubstSubtable
+
+--------------------------
+GPOS Shared Table Builders
+--------------------------
+
+The functions help build the `GPOS shared tables <https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#shared-tables-value-record-anchor-table-and-mark-array-table>`_
+as defined in the OpenType spec: value records, anchors, mark arrays and
+mark record tables.
+
+.. automodule:: fontTools.otlLib.builder
+   :members: buildValue, buildAnchor, buildMarkArray, buildDevice, buildBaseArray, buildComponentRecord, buildMarkArray, buildValue
+   :member-order: bysource
+
+------------------------------------
+Low-level GPOS Table Lookup Builders
+------------------------------------
+
+These functions deal with the "simple" lookup types. See above for classes to
+help build more complex lookups (contextual and chaining lookups).
+
+.. automodule:: fontTools.otlLib.builder
+   :members: buildCursivePosSubtable, buildLigatureArray, buildMarkBasePos, buildMarkBasePosSubtable, buildMarkLigPos, buildMarkLigPosSubtable, buildPairPosClassesSubtable, buildPairPosGlyphs, buildPairPosGlyphsSubtable, buildSinglePos, buildSinglePosSubtable
+   :member-order: bysource
+
+----------------------------
+GDEF Table Subtable Builders
+----------------------------
+
+These functions build subtables for elements of the ``GDEF`` table.
+
+.. automodule:: fontTools.otlLib.builder
+   :members: buildAttachList, buildLigCaretList, buildMarkGlyphSetsDef
+   :member-order: bysource
+
+------------------
+STAT Table Builder
+------------------
+
+.. automodule:: fontTools.otlLib.builder
+   :members: buildStatTable
+   :member-order: bysource
diff --git a/Doc/source/pens/areaPen.rst b/Doc/source/pens/areaPen.rst
index 03a751b..f3f21bb 100644
--- a/Doc/source/pens/areaPen.rst
+++ b/Doc/source/pens/areaPen.rst
@@ -3,5 +3,6 @@
 #######
 
 .. automodule:: fontTools.pens.areaPen
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/pens/basePen.rst b/Doc/source/pens/basePen.rst
index f5965b1..87bf832 100644
--- a/Doc/source/pens/basePen.rst
+++ b/Doc/source/pens/basePen.rst
@@ -3,5 +3,6 @@
 #######
 
 .. automodule:: fontTools.pens.basePen
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/pens/boundsPen.rst b/Doc/source/pens/boundsPen.rst
index 8de5620..a0d9ab4 100644
--- a/Doc/source/pens/boundsPen.rst
+++ b/Doc/source/pens/boundsPen.rst
@@ -3,5 +3,6 @@
 #########
 
 .. automodule:: fontTools.pens.boundsPen
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/pens/cocoaPen.rst b/Doc/source/pens/cocoaPen.rst
new file mode 100644
index 0000000..bbe8050
--- /dev/null
+++ b/Doc/source/pens/cocoaPen.rst
@@ -0,0 +1,8 @@
+########
+cocoaPen
+########
+
+.. automodule:: fontTools.pens.cocoaPen
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/cu2quPen.rst b/Doc/source/pens/cu2quPen.rst
new file mode 100644
index 0000000..4ae0249
--- /dev/null
+++ b/Doc/source/pens/cu2quPen.rst
@@ -0,0 +1,8 @@
+########
+cu2quPen
+########
+
+.. automodule:: fontTools.pens.cu2quPen
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/filterPen.rst b/Doc/source/pens/filterPen.rst
index 0b484a4..c79b944 100644
--- a/Doc/source/pens/filterPen.rst
+++ b/Doc/source/pens/filterPen.rst
@@ -3,5 +3,6 @@
 #########
 
 .. automodule:: fontTools.pens.filterPen
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/pens/index.rst b/Doc/source/pens/index.rst
index 7e5a192..91175cf 100644
--- a/Doc/source/pens/index.rst
+++ b/Doc/source/pens/index.rst
@@ -5,14 +5,26 @@
 .. toctree::
    :maxdepth: 1
 
+   areaPen
    basePen
    boundsPen
-   pointInsidePen
+   cocoaPen
+   cu2quPen
    filterPen
-   transformPen
-   t2CharStringPen
-   statisticsPen
-   recordingPen
-   teePen
-   areaPen
+   momentsPen
    perimeterPen
+   pointInsidePen
+   pointPen
+   qtPen
+   recordingPen
+   reportLabPen
+   reverseContourPen
+   roundingPen
+   statisticsPen
+   svgPathPen
+   t2CharStringPen
+   teePen
+   transformPen
+   ttGlyphPen
+   wxPen
+
diff --git a/Doc/source/pens/momentsPen.rst b/Doc/source/pens/momentsPen.rst
new file mode 100644
index 0000000..4587f75
--- /dev/null
+++ b/Doc/source/pens/momentsPen.rst
@@ -0,0 +1,8 @@
+##########
+momentsPen
+##########
+
+.. automodule:: fontTools.pens.momentsPen
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/perimeterPen.rst b/Doc/source/pens/perimeterPen.rst
index 97fecca..c625a3d 100644
--- a/Doc/source/pens/perimeterPen.rst
+++ b/Doc/source/pens/perimeterPen.rst
@@ -3,5 +3,6 @@
 ############
 
 .. automodule:: fontTools.pens.perimeterPen
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/pens/pointInsidePen.rst b/Doc/source/pens/pointInsidePen.rst
index 9954e47..81a4b2e 100644
--- a/Doc/source/pens/pointInsidePen.rst
+++ b/Doc/source/pens/pointInsidePen.rst
@@ -3,5 +3,6 @@
 ##############
 
 .. automodule:: fontTools.pens.pointInsidePen
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/pens/pointPen.rst b/Doc/source/pens/pointPen.rst
new file mode 100644
index 0000000..09b9897
--- /dev/null
+++ b/Doc/source/pens/pointPen.rst
@@ -0,0 +1,8 @@
+########
+pointPen
+########
+
+.. automodule:: fontTools.pens.pointPen
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/qtPen.rst b/Doc/source/pens/qtPen.rst
new file mode 100644
index 0000000..bfaa9b5
--- /dev/null
+++ b/Doc/source/pens/qtPen.rst
@@ -0,0 +1,8 @@
+#####
+qtPen
+#####
+
+.. automodule:: fontTools.pens.qtPen
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/recordingPen.rst b/Doc/source/pens/recordingPen.rst
index 69e7ceb..ee9178c 100644
--- a/Doc/source/pens/recordingPen.rst
+++ b/Doc/source/pens/recordingPen.rst
@@ -3,5 +3,6 @@
 ############
 
 .. automodule:: fontTools.pens.recordingPen
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/pens/reportLabPen.rst b/Doc/source/pens/reportLabPen.rst
new file mode 100644
index 0000000..7fe8784
--- /dev/null
+++ b/Doc/source/pens/reportLabPen.rst
@@ -0,0 +1,8 @@
+############
+reportLabPen
+############
+
+.. automodule:: fontTools.pens.reportLabPen
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/reverseContourPen.rst b/Doc/source/pens/reverseContourPen.rst
new file mode 100644
index 0000000..8178e2c
--- /dev/null
+++ b/Doc/source/pens/reverseContourPen.rst
@@ -0,0 +1,8 @@
+#################
+reverseContourPen
+#################
+
+.. automodule:: fontTools.pens.reverseContourPen
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/roundingPen.rst b/Doc/source/pens/roundingPen.rst
new file mode 100644
index 0000000..7eb4214
--- /dev/null
+++ b/Doc/source/pens/roundingPen.rst
@@ -0,0 +1,8 @@
+###########
+roundingPen
+###########
+
+.. automodule:: fontTools.pens.roundingPen
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/statisticsPen.rst b/Doc/source/pens/statisticsPen.rst
index efa1608..e06e322 100644
--- a/Doc/source/pens/statisticsPen.rst
+++ b/Doc/source/pens/statisticsPen.rst
@@ -3,5 +3,6 @@
 #############
 
 .. automodule:: fontTools.pens.statisticsPen
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/pens/svgPathPen.rst b/Doc/source/pens/svgPathPen.rst
new file mode 100644
index 0000000..45bf151
--- /dev/null
+++ b/Doc/source/pens/svgPathPen.rst
@@ -0,0 +1,8 @@
+##########
+svgPathPen
+##########
+
+.. automodule:: fontTools.pens.svgPathPen
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/t2CharStringPen.rst b/Doc/source/pens/t2CharStringPen.rst
index d58c67a..9d55391 100644
--- a/Doc/source/pens/t2CharStringPen.rst
+++ b/Doc/source/pens/t2CharStringPen.rst
@@ -3,5 +3,6 @@
 ###############
 
 .. automodule:: fontTools.pens.t2CharStringPen
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/pens/teePen.rst b/Doc/source/pens/teePen.rst
index 7a0313c..2a4558d 100644
--- a/Doc/source/pens/teePen.rst
+++ b/Doc/source/pens/teePen.rst
@@ -3,5 +3,6 @@
 ######
 
 .. automodule:: fontTools.pens.teePen
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/pens/transformPen.rst b/Doc/source/pens/transformPen.rst
index 5b414f8..3bb802a 100644
--- a/Doc/source/pens/transformPen.rst
+++ b/Doc/source/pens/transformPen.rst
@@ -3,5 +3,6 @@
 ############
 
 .. automodule:: fontTools.pens.transformPen
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/pens/ttGlyphPen.rst b/Doc/source/pens/ttGlyphPen.rst
new file mode 100644
index 0000000..e1bf701
--- /dev/null
+++ b/Doc/source/pens/ttGlyphPen.rst
@@ -0,0 +1,8 @@
+##########
+ttGlyphPen
+##########
+
+.. automodule:: fontTools.pens.ttGlyphPen
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/pens/wxPen.rst b/Doc/source/pens/wxPen.rst
new file mode 100644
index 0000000..37ce50a
--- /dev/null
+++ b/Doc/source/pens/wxPen.rst
@@ -0,0 +1,8 @@
+#####
+wxPen
+#####
+
+.. automodule:: fontTools.pens.wxPen
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/subset.rst b/Doc/source/subset.rst
deleted file mode 100644
index a8fff95..0000000
--- a/Doc/source/subset.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-######
-subset
-######
-
-.. automodule:: fontTools.subset
-   :members:
-   :undoc-members:
diff --git a/Doc/source/subset/cff.rst b/Doc/source/subset/cff.rst
new file mode 100644
index 0000000..8c21c39
--- /dev/null
+++ b/Doc/source/subset/cff.rst
@@ -0,0 +1,8 @@
+###
+cff
+###
+
+.. automodule:: fontTools.subset.cff
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/subset/index.rst b/Doc/source/subset/index.rst
new file mode 100644
index 0000000..f32e0d4
--- /dev/null
+++ b/Doc/source/subset/index.rst
@@ -0,0 +1,13 @@
+######
+subset
+######
+
+.. toctree::
+   :maxdepth: 1
+
+   cff
+
+.. automodule:: fontTools.subset
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/svgLib/index.rst b/Doc/source/svgLib/index.rst
new file mode 100644
index 0000000..f86ff0a
--- /dev/null
+++ b/Doc/source/svgLib/index.rst
@@ -0,0 +1,8 @@
+######
+svgLib
+######
+
+.. toctree::
+   :maxdepth: 1
+
+   path/index
diff --git a/Doc/source/svgLib/path/index.rst b/Doc/source/svgLib/path/index.rst
new file mode 100644
index 0000000..0d7551b
--- /dev/null
+++ b/Doc/source/svgLib/path/index.rst
@@ -0,0 +1,13 @@
+####
+path
+####
+
+.. toctree::
+   :maxdepth: 1
+
+   parser
+
+.. automodule:: fontTools.svgLib.path
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/svgLib/path/parser.rst b/Doc/source/svgLib/path/parser.rst
new file mode 100644
index 0000000..fc7e7ff
--- /dev/null
+++ b/Doc/source/svgLib/path/parser.rst
@@ -0,0 +1,8 @@
+######
+parser
+######
+
+.. automodule:: fontTools.svgLib.path.parser
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/t1Lib.rst b/Doc/source/t1Lib.rst
index dcc0438..4436086 100644
--- a/Doc/source/t1Lib.rst
+++ b/Doc/source/t1Lib.rst
@@ -3,5 +3,6 @@
 #####
 
 .. automodule:: fontTools.t1Lib
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/tfmLib.rst b/Doc/source/tfmLib.rst
new file mode 100644
index 0000000..daa4125
--- /dev/null
+++ b/Doc/source/tfmLib.rst
@@ -0,0 +1,8 @@
+###########################################
+tfmLib: Read TeX Font Metrics files
+###########################################
+
+.. automodule:: fontTools.tfmLib
+
+.. autoclass:: fontTools.tfmLib.TFM
+   :members:
diff --git a/Doc/source/ttLib/index.rst b/Doc/source/ttLib/index.rst
index d273934..4dfa2d6 100644
--- a/Doc/source/ttLib/index.rst
+++ b/Doc/source/ttLib/index.rst
@@ -7,9 +7,13 @@
 
    macUtils
    sfnt
+   standardGlyphOrder
    tables
+   ttCollection
+   ttFont
    woff2
 
 .. automodule:: fontTools.ttLib
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/ttLib/macUtils.rst b/Doc/source/ttLib/macUtils.rst
index cb014d7..356a911 100644
--- a/Doc/source/ttLib/macUtils.rst
+++ b/Doc/source/ttLib/macUtils.rst
@@ -3,5 +3,6 @@
 ########
 
 .. automodule:: fontTools.ttLib.macUtils
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/ttLib/sfnt.rst b/Doc/source/ttLib/sfnt.rst
index 41e3032..29566ab 100644
--- a/Doc/source/ttLib/sfnt.rst
+++ b/Doc/source/ttLib/sfnt.rst
@@ -3,5 +3,6 @@
 ####
 
 .. automodule:: fontTools.ttLib.sfnt
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/ttLib/standardGlyphOrder.rst b/Doc/source/ttLib/standardGlyphOrder.rst
new file mode 100644
index 0000000..ca2557b
--- /dev/null
+++ b/Doc/source/ttLib/standardGlyphOrder.rst
@@ -0,0 +1,12 @@
+##################
+standardGlyphOrder
+##################
+
+.. automodule:: fontTools.ttLib.standardGlyphOrder
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+.. data:: fontTools.ttLib.standardGlyphOrder.standardGlyphOrder
+
+A Python list of "standard" glyphs required by post table formats 1.0 and 2.0.
diff --git a/Doc/source/ttLib/tables.rst b/Doc/source/ttLib/tables.rst
index 6ae705f..45c76b8 100644
--- a/Doc/source/ttLib/tables.rst
+++ b/Doc/source/ttLib/tables.rst
@@ -2,7 +2,114 @@
 tables
 ######
 
+This folder is a subpackage of :py:mod:`fontTools.ttLib`. Each module here is a
+specialized TT/OT table converter: they can convert raw data
+to Python objects and vice versa. Usually you don't need to
+use the modules directly: they are imported and used
+automatically when needed by :py:mod:`fontTools.ttLib`.
+
+If you are writing you own table converter the following is
+important.
+
+The modules here have pretty strange names: this is due to the
+fact that we need to map TT table tags (which are case sensitive)
+to filenames (which on Mac and Win aren't case sensitive) as well
+as to Python identifiers. The latter means it can only contain
+[A-Za-z0-9_] and cannot start with a number.
+
+:py:mod:`fontTools.ttLib` provides functions to expand a tag into the format used here::
+
+    >>> from fontTools import ttLib
+    >>> ttLib.tagToIdentifier("FOO ")
+    'F_O_O_'
+    >>> ttLib.tagToIdentifier("cvt ")
+    '_c_v_t'
+    >>> ttLib.tagToIdentifier("OS/2")
+    'O_S_2f_2'
+    >>> ttLib.tagToIdentifier("glyf")
+    '_g_l_y_f'
+    >>>
+
+And vice versa::
+
+    >>> ttLib.identifierToTag("F_O_O_")
+    'FOO '
+    >>> ttLib.identifierToTag("_c_v_t")
+    'cvt '
+    >>> ttLib.identifierToTag("O_S_2f_2")
+    'OS/2'
+    >>> ttLib.identifierToTag("_g_l_y_f")
+    'glyf'
+    >>>
+
+Eg. the 'glyf' table converter lives in a Python file called::
+
+	_g_l_y_f.py
+
+The converter itself is a class, named "table_" + expandedtag. Eg::
+
+
+	class table__g_l_y_f:
+		etc.
+
+
+Note that if you _do_ need to use such modules or classes manually,
+there are two convenient API functions that let you find them by tag::
+
+    >>> ttLib.getTableModule('glyf')
+    <module 'ttLib.tables._g_l_y_f'>
+    >>> ttLib.getTableClass('glyf')
+    <class ttLib.tables._g_l_y_f.table__g_l_y_f at 645f400>
+    >>
+
+You must subclass from :py:mod:`fontTools.ttLib.tables.DefaultTable.DefaultTable`. It provides some default
+behavior, as well as a constructor method (__init__) that you don't need to
+override.
+
+Your converter should minimally provide two methods::
+
+
+    class table_F_O_O_(DefaultTable.DefaultTable): # converter for table 'FOO '
+
+        def decompile(self, data, ttFont):
+            # 'data' is the raw table data. Unpack it into a
+            # Python data structure.
+            # 'ttFont' is a ttLib.TTfile instance, enabling you to
+            # refer to other tables. Do ***not*** keep a reference to
+            # it: it will cause a circular reference (ttFont saves
+            # a reference to us), and that means we'll be leaking
+            # memory. If you need to use it in other methods, just
+            # pass it around as a method argument.
+
+        def compile(self, ttFont):
+            # Return the raw data, as converted from the Python
+            # data structure.
+            # Again, 'ttFont' is there so you can access other tables.
+            # Same warning applies.
+
+
+If you want to support TTX import/export as well, you need to provide two
+additional methods::
+
+
+	def toXML(self, writer, ttFont):
+		# XXX
+
+	def fromXML(self, (name, attrs, content), ttFont):
+		# XXX
+
+
+
 .. automodule:: fontTools.ttLib.tables
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+_a_n_k_r
+--------
+
+.. automodule:: fontTools.ttLib.tables._a_n_k_r
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -10,6 +117,23 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._a_v_a_r
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+_b_s_l_n
+--------
+
+.. automodule:: fontTools.ttLib.tables._b_s_l_n
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+_c_i_d_g
+--------
+
+.. automodule:: fontTools.ttLib.tables._c_i_d_g
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -17,6 +141,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._c_m_a_p
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -24,6 +149,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._c_v_a_r
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -31,6 +157,7 @@
 ------
 
 .. automodule:: fontTools.ttLib.tables._c_v_t
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -38,6 +165,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._f_e_a_t
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -45,6 +173,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._f_p_g_m
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -52,6 +181,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._f_v_a_r
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -59,6 +189,16 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._g_a_s_p
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+
+_g_c_i_d
+--------
+
+.. automodule:: fontTools.ttLib.tables._g_c_i_d
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -66,6 +206,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._g_l_y_f
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -73,6 +214,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._g_v_a_r
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -80,6 +222,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._h_d_m_x
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -87,6 +230,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._h_e_a_d
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -94,6 +238,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._h_h_e_a
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -101,6 +246,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._h_m_t_x
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -108,6 +254,15 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._k_e_r_n
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+_l_c_a_r
+--------
+
+.. automodule:: fontTools.ttLib.tables._l_c_a_r
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -115,6 +270,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._l_o_c_a
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -122,6 +278,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._l_t_a_g
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -129,6 +286,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._m_a_x_p
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -136,6 +294,24 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._m_e_t_a
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+_m_o_r_t
+--------
+
+.. automodule:: fontTools.ttLib.tables._m_o_r_t
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+
+_m_o_r_x
+--------
+
+.. automodule:: fontTools.ttLib.tables._m_o_r_x
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -143,6 +319,15 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._n_a_m_e
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+_o_p_b_d
+--------
+
+.. automodule:: fontTools.ttLib.tables._o_p_b_d
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -150,6 +335,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._p_o_s_t
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -157,6 +343,16 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._p_r_e_p
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+
+_p_r_o_p
+--------
+
+.. automodule:: fontTools.ttLib.tables._p_r_o_p
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -164,6 +360,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._s_b_i_x
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -171,6 +368,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._t_r_a_k
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -178,6 +376,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._v_h_e_a
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -185,6 +384,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables._v_m_t_x
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -192,6 +392,7 @@
 ----------
 
 .. automodule:: fontTools.ttLib.tables.asciiTable
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -199,6 +400,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.B_A_S_E_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -206,6 +408,7 @@
 ------------------
 
 .. automodule:: fontTools.ttLib.tables.BitmapGlyphMetrics
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -213,6 +416,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.C_B_D_T_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -220,6 +424,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.C_B_L_C_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -227,6 +432,7 @@
 ------
 
 .. automodule:: fontTools.ttLib.tables.C_F_F_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -234,6 +440,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.C_F_F__2
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -241,6 +448,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.C_O_L_R_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -248,6 +456,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.C_P_A_L_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -255,6 +464,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.D_S_I_G_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -262,6 +472,7 @@
 ------------
 
 .. automodule:: fontTools.ttLib.tables.DefaultTable
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -269,6 +480,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.E_B_D_T_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -276,13 +488,41 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.E_B_L_C_
+   :inherited-members:
    :members:
    :undoc-members:
 
+F__e_a_t
+--------
+
+.. automodule:: fontTools.ttLib.tables.F__e_a_t
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+
 F_F_T_M_
 --------
 
 .. automodule:: fontTools.ttLib.tables.F_F_T_M_
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+
+G__l_a_t
+--------
+
+.. automodule:: fontTools.ttLib.tables.G__l_a_t
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+G__l_o_c
+--------
+
+.. automodule:: fontTools.ttLib.tables.G__l_o_c
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -290,6 +530,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.G_D_E_F_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -297,6 +538,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.G_M_A_P_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -304,6 +546,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.G_P_K_G_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -311,6 +554,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.G_P_O_S_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -318,6 +562,15 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.G_S_U_B_
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+grUtils
+-------
+
+.. automodule:: fontTools.ttLib.tables.grUtils
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -325,6 +578,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.H_V_A_R_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -332,6 +586,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.J_S_T_F_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -339,6 +594,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.L_T_S_H_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -346,6 +602,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.M_A_T_H_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -353,6 +610,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.M_E_T_A_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -360,6 +618,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.M_V_A_R_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -367,6 +626,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.O_S_2f_2
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -374,6 +634,7 @@
 ------
 
 .. automodule:: fontTools.ttLib.tables.otBase
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -381,6 +642,7 @@
 ------------
 
 .. automodule:: fontTools.ttLib.tables.otConverters
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -388,6 +650,7 @@
 ------
 
 .. automodule:: fontTools.ttLib.tables.otData
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -395,6 +658,23 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.otTables
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+S__i_l_f
+--------
+
+.. automodule:: fontTools.ttLib.tables.S__i_l_f
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+S__i_l_l
+--------
+
+.. automodule:: fontTools.ttLib.tables.S__i_l_l
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -402,6 +682,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.S_I_N_G_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -409,6 +690,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.S_T_A_T_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -416,6 +698,7 @@
 ------
 
 .. automodule:: fontTools.ttLib.tables.S_V_G_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -423,6 +706,7 @@
 ---------
 
 .. automodule:: fontTools.ttLib.tables.sbixGlyph
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -430,6 +714,7 @@
 ----------
 
 .. automodule:: fontTools.ttLib.tables.sbixStrike
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -437,6 +722,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.T_S_I__0
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -444,6 +730,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.T_S_I__1
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -451,6 +738,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.T_S_I__2
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -458,6 +746,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.T_S_I__3
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -465,6 +754,71 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.T_S_I__5
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+T_S_I_B_
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_S_I_B_
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+T_S_I_C_
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_S_I_C_
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+T_S_I_D_
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_S_I_D_
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+T_S_I_J_
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_S_I_J_
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+T_S_I_P_
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_S_I_P_
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+T_S_I_S_
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_S_I_S_
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+T_S_I_V_
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_S_I_V_
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+T_T_F_A_
+--------
+
+.. automodule:: fontTools.ttLib.tables.T_T_F_A_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -472,6 +826,7 @@
 ---------
 
 .. automodule:: fontTools.ttLib.tables.ttProgram
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -479,6 +834,7 @@
 --------------
 
 .. automodule:: fontTools.ttLib.tables.TupleVariation
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -486,6 +842,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.V_D_M_X_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -493,6 +850,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.V_O_R_G_
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -500,6 +858,7 @@
 --------
 
 .. automodule:: fontTools.ttLib.tables.V_V_A_R_
+   :inherited-members:
    :members:
    :undoc-members:
 
diff --git a/Doc/source/ttLib/ttCollection.rst b/Doc/source/ttLib/ttCollection.rst
new file mode 100644
index 0000000..0ca4ebd
--- /dev/null
+++ b/Doc/source/ttLib/ttCollection.rst
@@ -0,0 +1,8 @@
+############
+ttCollection
+############
+
+.. automodule:: fontTools.ttLib.ttCollection
+   :inherited-members:
+   :members:
+   :undoc-members:
\ No newline at end of file
diff --git a/Doc/source/ttLib/ttFont.rst b/Doc/source/ttLib/ttFont.rst
new file mode 100644
index 0000000..a571050
--- /dev/null
+++ b/Doc/source/ttLib/ttFont.rst
@@ -0,0 +1,9 @@
+######
+ttFont
+######
+
+.. automodule:: fontTools.ttLib.ttFont
+   :inherited-members:
+   :members:
+   :undoc-members:
+   :private-members:
diff --git a/Doc/source/ttLib/woff2.rst b/Doc/source/ttLib/woff2.rst
index 0c464be..327bb5b 100644
--- a/Doc/source/ttLib/woff2.rst
+++ b/Doc/source/ttLib/woff2.rst
@@ -3,5 +3,6 @@
 #####
 
 .. automodule:: fontTools.ttLib.woff2
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/ttx.rst b/Doc/source/ttx.rst
index 1c90901..b6e43f5 100644
--- a/Doc/source/ttx.rst
+++ b/Doc/source/ttx.rst
@@ -2,6 +2,61 @@
 ttx
 ###
 
+
+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::
+
+    ttx /path/to/font.otf
+    ttx /path/to/font.ttx
+
+The TTX application can be used in two ways, depending on what platform you run it on:
+
+* As a command line tool (Windows/DOS, Unix, macOS)
+* 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
+^^^^^^^^^^^^^^^^^^^
+
+.. begin table list
+
+The following tables are currently supported::
+
+    BASE, CBDT, CBLC, CFF, CFF2, COLR, CPAL, DSIG, Debg, 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, TSIC, 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.
+
 .. automodule:: fontTools.ttx
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/ufoLib/converters.rst b/Doc/source/ufoLib/converters.rst
new file mode 100644
index 0000000..2b37e56
--- /dev/null
+++ b/Doc/source/ufoLib/converters.rst
@@ -0,0 +1,9 @@
+
+##########
+converters
+##########
+
+.. automodule:: fontTools.ufoLib.converters
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ufoLib/errors.rst b/Doc/source/ufoLib/errors.rst
new file mode 100644
index 0000000..ba079ab
--- /dev/null
+++ b/Doc/source/ufoLib/errors.rst
@@ -0,0 +1,9 @@
+
+######
+errors
+######
+
+.. automodule:: fontTools.ufoLib.errors
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ufoLib/filenames.rst b/Doc/source/ufoLib/filenames.rst
new file mode 100644
index 0000000..33a3c1b
--- /dev/null
+++ b/Doc/source/ufoLib/filenames.rst
@@ -0,0 +1,9 @@
+
+#########
+filenames
+#########
+
+.. automodule:: fontTools.ufoLib.filenames
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ufoLib/glifLib.rst b/Doc/source/ufoLib/glifLib.rst
new file mode 100644
index 0000000..15bde5a
--- /dev/null
+++ b/Doc/source/ufoLib/glifLib.rst
@@ -0,0 +1,9 @@
+
+#######
+glifLib
+#######
+
+.. automodule:: fontTools.ufoLib.glifLib
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ufoLib/index.rst b/Doc/source/ufoLib/index.rst
new file mode 100644
index 0000000..514c5c9
--- /dev/null
+++ b/Doc/source/ufoLib/index.rst
@@ -0,0 +1,22 @@
+
+######
+ufoLib
+######
+
+.. toctree::
+   :maxdepth: 1
+
+   converters
+   errors
+   filenames
+   glifLib
+   kerning
+   plistlib
+   pointpen
+   utils
+   validators
+
+.. automodule:: fontTools.ufoLib
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ufoLib/kerning.rst b/Doc/source/ufoLib/kerning.rst
new file mode 100644
index 0000000..4d9d0c1
--- /dev/null
+++ b/Doc/source/ufoLib/kerning.rst
@@ -0,0 +1,9 @@
+
+#######
+kerning
+#######
+
+.. automodule:: fontTools.ufoLib.kerning
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ufoLib/plistlib.rst b/Doc/source/ufoLib/plistlib.rst
new file mode 100644
index 0000000..d639947
--- /dev/null
+++ b/Doc/source/ufoLib/plistlib.rst
@@ -0,0 +1,9 @@
+
+########
+plistlib
+########
+
+.. automodule:: fontTools.ufoLib.plistlib
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ufoLib/pointpen.rst b/Doc/source/ufoLib/pointpen.rst
new file mode 100644
index 0000000..5fb8c1c
--- /dev/null
+++ b/Doc/source/ufoLib/pointpen.rst
@@ -0,0 +1,9 @@
+
+########
+pointPen
+########
+
+.. automodule:: fontTools.ufoLib.pointPen
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ufoLib/utils.rst b/Doc/source/ufoLib/utils.rst
new file mode 100644
index 0000000..d5ab143
--- /dev/null
+++ b/Doc/source/ufoLib/utils.rst
@@ -0,0 +1,9 @@
+
+#####
+utils
+#####
+
+.. automodule:: fontTools.ufoLib.utils
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/ufoLib/validators.rst b/Doc/source/ufoLib/validators.rst
new file mode 100644
index 0000000..363d864
--- /dev/null
+++ b/Doc/source/ufoLib/validators.rst
@@ -0,0 +1,9 @@
+
+##########
+validators
+##########
+
+.. automodule:: fontTools.ufoLib.validators
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/unicode.rst b/Doc/source/unicode.rst
new file mode 100644
index 0000000..65481d5
--- /dev/null
+++ b/Doc/source/unicode.rst
@@ -0,0 +1,8 @@
+#######
+unicode
+#######
+
+.. automodule:: fontTools.unicode
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/unicodedata/Blocks.rst b/Doc/source/unicodedata/Blocks.rst
new file mode 100644
index 0000000..5d01da7
--- /dev/null
+++ b/Doc/source/unicodedata/Blocks.rst
@@ -0,0 +1,13 @@
+######
+Blocks
+######
+
+.. automodule:: fontTools.unicodedata.Blocks
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+.. data:: fontTools.unicodedata.Blocks.RANGES
+
+.. data:: fontTools.unicodedata.Blocks.VALUES
+
diff --git a/Doc/source/unicodedata/OTTags.rst b/Doc/source/unicodedata/OTTags.rst
new file mode 100644
index 0000000..a436bdc
--- /dev/null
+++ b/Doc/source/unicodedata/OTTags.rst
@@ -0,0 +1,17 @@
+######
+OTTags
+######
+
+.. automodule:: fontTools.unicodedata.OTTags
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+.. data:: fontTools.unicodedata.OTTags.DEFAULT_SCRIPT
+
+.. data:: fontTools.unicodedata.OTTags.SCRIPT_EXCEPTIONS
+
+.. data:: fontTools.unicodedata.OTTags.NEW_SCRIPT_TAGS
+
+.. data:: fontTools.unicodedata.OTTags.NEW_SCRIPT_TAGS_REVERSED
+
diff --git a/Doc/source/unicodedata/ScriptExtensions.rst b/Doc/source/unicodedata/ScriptExtensions.rst
new file mode 100644
index 0000000..dce2bbc
--- /dev/null
+++ b/Doc/source/unicodedata/ScriptExtensions.rst
@@ -0,0 +1,12 @@
+################
+ScriptExtensions
+################
+
+.. automodule:: fontTools.unicodedata.ScriptExtensions
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+.. data:: fontTools.unicodedata.ScriptExtensions.RANGES
+
+.. data:: fontTools.unicodedata.ScriptExtensions.VALUES
diff --git a/Doc/source/unicodedata/Scripts.rst b/Doc/source/unicodedata/Scripts.rst
new file mode 100644
index 0000000..2ec6e34
--- /dev/null
+++ b/Doc/source/unicodedata/Scripts.rst
@@ -0,0 +1,16 @@
+#######
+Scripts
+#######
+
+.. automodule:: fontTools.unicodedata.Scripts
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+.. data:: fontTools.unicodedata.Scripts.NAMES
+
+.. data:: fontTools.unicodedata.Scripts.RANGES
+
+.. data:: fontTools.unicodedata.Scripts.VALUES
+
+
diff --git a/Doc/source/unicodedata/index.rst b/Doc/source/unicodedata/index.rst
new file mode 100644
index 0000000..811d65d
--- /dev/null
+++ b/Doc/source/unicodedata/index.rst
@@ -0,0 +1,16 @@
+###########
+unicodedata
+###########
+
+.. toctree::
+   :maxdepth: 1
+
+   Blocks
+   OTTags
+   ScriptExtensions
+   Scripts
+
+.. automodule:: fontTools.unicodedata
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/builder.rst b/Doc/source/varLib/builder.rst
new file mode 100644
index 0000000..3da3d32
--- /dev/null
+++ b/Doc/source/varLib/builder.rst
@@ -0,0 +1,8 @@
+#######
+builder
+#######
+
+.. automodule:: fontTools.varLib.builder
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/cff.rst b/Doc/source/varLib/cff.rst
new file mode 100644
index 0000000..62e11c7
--- /dev/null
+++ b/Doc/source/varLib/cff.rst
@@ -0,0 +1,8 @@
+###
+cff
+###
+
+.. automodule:: fontTools.varLib.cff
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/designspace.rst b/Doc/source/varLib/designspace.rst
deleted file mode 100644
index e3bbdf7..0000000
--- a/Doc/source/varLib/designspace.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-###########
-designspace
-###########
-
-.. automodule:: fontTools.varLib.designspace
-   :members:
-   :undoc-members:
diff --git a/Doc/source/varLib/errors.rst b/Doc/source/varLib/errors.rst
new file mode 100644
index 0000000..b761854
--- /dev/null
+++ b/Doc/source/varLib/errors.rst
@@ -0,0 +1,8 @@
+######
+errors
+######
+
+.. automodule:: fontTools.varLib.errors
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/featureVars.rst b/Doc/source/varLib/featureVars.rst
new file mode 100644
index 0000000..da73560
--- /dev/null
+++ b/Doc/source/varLib/featureVars.rst
@@ -0,0 +1,8 @@
+###########
+featureVars
+###########
+
+.. automodule:: fontTools.varLib.featureVars
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/index.rst b/Doc/source/varLib/index.rst
index 0e9f17b..7b22496 100644
--- a/Doc/source/varLib/index.rst
+++ b/Doc/source/varLib/index.rst
@@ -1,17 +1,114 @@
-######
-varLib
-######
+##################################
+varLib: OpenType Variation Support
+##################################
+
+The ``fontTools.varLib`` package contains a number of classes and routines
+for handling, building and interpolating variable font data. These routines
+rely on a common set of concepts, many of which are equivalent to concepts
+in the OpenType Specification, but some of which are unique to ``varLib``.
+
+Terminology
+-----------
+
+axis
+   "A designer-determined variable in a font face design that can be used to
+   derive multiple, variant designs within a family." (OpenType Specification)
+   An axis has a minimum value, a maximum value and a default value.
+
+designspace
+   The n-dimensional space formed by the font's axes. (OpenType Specification
+   calls this the "design-variation space")
+
+scalar
+   A value which is able to be varied at different points in the designspace:
+   for example, the horizontal advance width of the glyph "a" is a scalar.
+   However, see also *support scalar* below.
+
+default location
+   A point in the designspace whose coordinates are the default value of
+   all axes.
+
+location
+   A point in the designspace, specified as a set of coordinates on one or
+   more axes. In the context of ``varLib``, a location is a dictionary with
+   the keys being the axis tags and the values being the coordinates on the
+   respective axis. A ``varLib`` location dictionary may be "sparse", in the
+   sense that axes defined in the font may be omitted from the location's
+   coordinates, in which case the default value of the axis is assumed.
+   For example, given a font having a ``wght`` axis ranging from 200-1000
+   with default 400, and a ``wdth`` axis ranging 100-300 with default 150,
+   the location ``{"wdth": 200}`` represents the point ``wght=400,wdth=200``.
+
+master
+   The value of a scalar at a given location. **Note that this is a
+   considerably more general concept than the usual type design sense of
+   the term "master".**
+
+normalized location
+   While the range of an axis is determined by its minimum and maximum values
+   as set by the designer, locations are specified internally to the font binary
+   in the range -1 to 1, with 0 being the default, -1 being the minimum and
+   1 being the maximum. A normalized location is one which is scaled to the
+   range (-1,1) on all of its axes. Note that as the range from minimum to
+   default and from default to maximum on a given axis may differ (for
+   example, given ``wght min=200 default=500 max=1000``, the difference
+   between a normalized location -1 of a normalized location of 0 represents a
+   difference of 300 units while the difference between a normalized location
+   of 0 and a normalized location of 1 represents a difference of 700 units),
+   a location is scaled by a different factor depending on whether it is above
+   or below the axis' default value.
+
+support
+   While designers tend to think in terms of masters - that is, a precise
+   location having a particular value - OpenType Variations specifies the
+   variation of scalars in terms of deltas which are themselves composed of
+   the combined contributions of a set of triangular regions, each having
+   a contribution value of 0 at its minimum value, rising linearly to its
+   full contribution at the *peak* and falling linearly to zero from the
+   peak to the maximum value. The OpenType Specification calls these "regions",
+   while ``varLib`` calls them "supports" (a mathematical term used in real
+   analysis) and expresses them as a dictionary mapping each axis tag to a
+   tuple ``(min, peak, max)``.
+
+box
+   ``varLib`` uses the term "box" to denote the minimum and maximum "corners" of
+   a support, ignoring its peak value.
+
+delta
+   The term "delta" is used in OpenType Variations in two senses. In the
+   more general sense, a delta is the difference between a scalar at a
+   given location and its value at the default location. Additionally, inside
+   the font, variation data is stored as a mapping between supports and deltas.
+   The delta (in the first sense) is computed by summing the product of the
+   delta of each support by a factor representing the support's contribution
+   at this location (see "support scalar" below).
+
+support scalar
+   When interpolating a set of variation data, the support scalar represents
+   the scalar multiplier of the support's contribution at this location. For
+   example, the support scalar will be 1 at the support's peak location, and
+   0 below its minimum or above its maximum.
+
 
 .. toctree::
    :maxdepth: 2
 
-   designspace
+   builder
+   cff
+   errors
+   featureVars
+   instancer
    interpolatable
    interpolate_layout
+   iup
    merger
    models
    mutator
+   mvar
+   plot
+   varStore
 
 .. automodule:: fontTools.varLib
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/varLib/instancer.rst b/Doc/source/varLib/instancer.rst
new file mode 100644
index 0000000..8776de3
--- /dev/null
+++ b/Doc/source/varLib/instancer.rst
@@ -0,0 +1,8 @@
+#########
+instancer
+#########
+
+.. automodule:: fontTools.varLib.instancer
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/interpolatable.rst b/Doc/source/varLib/interpolatable.rst
index 969fb61..1120a98 100644
--- a/Doc/source/varLib/interpolatable.rst
+++ b/Doc/source/varLib/interpolatable.rst
@@ -3,5 +3,6 @@
 ##############
 
 .. automodule:: fontTools.varLib.interpolatable
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/varLib/interpolate_layout.rst b/Doc/source/varLib/interpolate_layout.rst
index 752f748..a9655b5 100644
--- a/Doc/source/varLib/interpolate_layout.rst
+++ b/Doc/source/varLib/interpolate_layout.rst
@@ -3,5 +3,6 @@
 ##################
 
 .. automodule:: fontTools.varLib.interpolate_layout
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/varLib/iup.rst b/Doc/source/varLib/iup.rst
new file mode 100644
index 0000000..b096788
--- /dev/null
+++ b/Doc/source/varLib/iup.rst
@@ -0,0 +1,8 @@
+###
+iup
+###
+
+.. automodule:: fontTools.varLib.iup
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/merger.rst b/Doc/source/varLib/merger.rst
index 37383aa..cf0a5a1 100644
--- a/Doc/source/varLib/merger.rst
+++ b/Doc/source/varLib/merger.rst
@@ -3,5 +3,6 @@
 ######
 
 .. automodule:: fontTools.varLib.merger
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/varLib/models.rst b/Doc/source/varLib/models.rst
index e6c7fa8..f59f0b8 100644
--- a/Doc/source/varLib/models.rst
+++ b/Doc/source/varLib/models.rst
@@ -3,5 +3,6 @@
 ######
 
 .. automodule:: fontTools.varLib.models
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/varLib/mutator.rst b/Doc/source/varLib/mutator.rst
index e606ab8..fffa803 100644
--- a/Doc/source/varLib/mutator.rst
+++ b/Doc/source/varLib/mutator.rst
@@ -3,5 +3,6 @@
 #######
 
 .. automodule:: fontTools.varLib.mutator
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Doc/source/varLib/mvar.rst b/Doc/source/varLib/mvar.rst
new file mode 100644
index 0000000..8c59a31
--- /dev/null
+++ b/Doc/source/varLib/mvar.rst
@@ -0,0 +1,10 @@
+####
+mvar
+####
+
+.. automodule:: fontTools.varLib.mvar
+   :inherited-members:
+   :members:
+   :undoc-members:
+
+.. data:: fontTools.varLib.mvar.MVAR_ENTRIES
\ No newline at end of file
diff --git a/Doc/source/varLib/plot.rst b/Doc/source/varLib/plot.rst
new file mode 100644
index 0000000..a722a2d
--- /dev/null
+++ b/Doc/source/varLib/plot.rst
@@ -0,0 +1,8 @@
+####
+plot
+####
+
+.. automodule:: fontTools.varLib.plot
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/varLib/varStore.rst b/Doc/source/varLib/varStore.rst
new file mode 100644
index 0000000..cc91101
--- /dev/null
+++ b/Doc/source/varLib/varStore.rst
@@ -0,0 +1,8 @@
+########
+varStore
+########
+
+.. automodule:: fontTools.varLib.varStore
+   :inherited-members:
+   :members:
+   :undoc-members:
diff --git a/Doc/source/voltLib.rst b/Doc/source/voltLib.rst
index 5906c4c..7695db7 100644
--- a/Doc/source/voltLib.rst
+++ b/Doc/source/voltLib.rst
@@ -3,6 +3,7 @@
 #######
 
 .. automodule:: fontTools.voltLib
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -10,6 +11,7 @@
 ---
 
 .. automodule:: fontTools.voltLib.ast
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -17,6 +19,7 @@
 -----
 
 .. automodule:: fontTools.voltLib.parser
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -24,6 +27,7 @@
 -----
 
 .. automodule:: fontTools.voltLib.lexer
+   :inherited-members:
    :members:
    :undoc-members:
 
@@ -31,5 +35,6 @@
 ------
 
 .. automodule:: fontTools.voltLib.parser
+   :inherited-members:
    :members:
    :undoc-members:
diff --git a/Icons/FontToolsIconGreenCircle.pdf b/Icons/FontToolsIconGreenCircle.pdf
new file mode 100644
index 0000000..f36817e
--- /dev/null
+++ b/Icons/FontToolsIconGreenCircle.pdf
Binary files differ
diff --git a/Icons/FontToolsIconGreenCircle.png b/Icons/FontToolsIconGreenCircle.png
new file mode 100644
index 0000000..47d5765
--- /dev/null
+++ b/Icons/FontToolsIconGreenCircle.png
Binary files differ
diff --git a/Icons/FontToolsIconGreenSquare.pdf b/Icons/FontToolsIconGreenSquare.pdf
new file mode 100644
index 0000000..b95057f
--- /dev/null
+++ b/Icons/FontToolsIconGreenSquare.pdf
Binary files differ
diff --git a/Icons/FontToolsIconGreenSquare.png b/Icons/FontToolsIconGreenSquare.png
new file mode 100644
index 0000000..a686561
--- /dev/null
+++ b/Icons/FontToolsIconGreenSquare.png
Binary files differ
diff --git a/Icons/FontToolsIconWhiteCircle.pdf b/Icons/FontToolsIconWhiteCircle.pdf
new file mode 100644
index 0000000..c53b81c
--- /dev/null
+++ b/Icons/FontToolsIconWhiteCircle.pdf
Binary files differ
diff --git a/Icons/FontToolsIconWhiteCircle.png b/Icons/FontToolsIconWhiteCircle.png
new file mode 100644
index 0000000..33601a5
--- /dev/null
+++ b/Icons/FontToolsIconWhiteCircle.png
Binary files differ
diff --git a/Icons/FontToolsIconWhiteSquare.pdf b/Icons/FontToolsIconWhiteSquare.pdf
new file mode 100644
index 0000000..3692e3a
--- /dev/null
+++ b/Icons/FontToolsIconWhiteSquare.pdf
Binary files differ
diff --git a/Icons/FontToolsIconWhiteSquare.png b/Icons/FontToolsIconWhiteSquare.png
new file mode 100644
index 0000000..2a2d797
--- /dev/null
+++ b/Icons/FontToolsIconWhiteSquare.png
Binary files differ
diff --git a/LICENSE b/LICENSE
index 2c90134..cc63390 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,25 +1,21 @@
-Copyright 1999-2004
-by Just van Rossum, Letterror, The Netherlands.
+MIT License
 
-                        All Rights Reserved
+Copyright (c) 2017 Just van Rossum
 
-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.
+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:
 
-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.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
 
-
-just@letterror.com
+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
index 4abc7d5..2bc4dab 100644
--- a/LICENSE.external
+++ b/LICENSE.external
@@ -26,6 +26,10 @@
 
   This Font Software is licensed under the SIL Open Font License, Version 1.1.
 
+Iosevka
+  Copyright (c) 2015-2020 Belleve Invis (belleve@typeof.net).
+  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
 
@@ -146,3 +150,210 @@
 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.
+
+=====
+
+FontTools includes cu2qu, which is Copyright 2016 Google Inc. All Rights Reserved.
+Licensed under the Apache License, Version 2.0, a copy of which is reproduced below:
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   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.
diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py
index d777e34..95292d6 100644
--- a/Lib/fontTools/__init__.py
+++ b/Lib/fontTools/__init__.py
@@ -1,10 +1,8 @@
-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.21.0.dev0"
+version = __version__ = "4.26.3.dev0"
 
 __all__ = ["version", "log", "configLogger"]
diff --git a/Lib/fontTools/__main__.py b/Lib/fontTools/__main__.py
index 7d45751..9b978aa 100644
--- a/Lib/fontTools/__main__.py
+++ b/Lib/fontTools/__main__.py
@@ -1,4 +1,3 @@
-from __future__ import print_function, division, absolute_import
 import sys
 
 
@@ -6,8 +5,6 @@
 	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
@@ -21,6 +18,10 @@
 	# can be added.  Should we just try importing the fonttools
 	# module first and try without if it fails?
 
+	if len(sys.argv) < 2:
+		sys.argv.append("help")
+	if sys.argv[1] == "-h" or sys.argv[1] == "--help":
+		sys.argv[1] = "help"
 	mod = 'fontTools.'+sys.argv[1]
 	sys.argv[1] = sys.argv[0] + ' ' + sys.argv[1]
 	del sys.argv[0]
diff --git a/Lib/fontTools/afmLib.py b/Lib/fontTools/afmLib.py
index e0ccafe..49d9951 100644
--- a/Lib/fontTools/afmLib.py
+++ b/Lib/fontTools/afmLib.py
@@ -1,60 +1,100 @@
-"""Module for reading and writing AFM files."""
+"""Module for reading and writing AFM (Adobe Font Metrics) files.
 
-# XXX reads AFM's generated by Fog, not tested with much else.
-# It does not implement the full spec (Adobe Technote 5004, Adobe Font Metrics
-# File Format Specification). Still, it should read most "common" AFM files.
+Note that this has been designed to read in AFM files generated by Fontographer
+and has not been tested on many other files. In particular, it does not
+implement the whole Adobe AFM specification [#f1]_ but, it should read most
+"common" AFM files.
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+Here is an example of using `afmLib` to read, modify and write an AFM file:
+
+	>>> from fontTools.afmLib import AFM
+	>>> f = AFM("Tests/afmLib/data/TestAFM.afm")
+	>>>
+	>>> # Accessing a pair gets you the kern value
+	>>> f[("V","A")]
+	-60
+	>>>
+	>>> # Accessing a glyph name gets you metrics
+	>>> f["A"]
+	(65, 668, (8, -25, 660, 666))
+	>>> # (charnum, width, bounding box)
+	>>>
+	>>> # Accessing an attribute gets you metadata
+	>>> f.FontName
+	'TestFont-Regular'
+	>>> f.FamilyName
+	'TestFont'
+	>>> f.Weight
+	'Regular'
+	>>> f.XHeight
+	500
+	>>> f.Ascender
+	750
+	>>>
+	>>> # Attributes and items can also be set
+	>>> f[("A","V")] = -150 # Tighten kerning
+	>>> f.FontName = "TestFont Squished"
+	>>>
+	>>> # And the font written out again (remove the # in front)
+	>>> #f.write("testfont-squished.afm")
+
+.. rubric:: Footnotes
+
+.. [#f1] `Adobe Technote 5004 <https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5004.AFM_Spec.pdf>`_,
+   Adobe Font Metrics File Format Specification.
+
+"""
+
+
 import re
 
 # every single line starts with a "word"
-identifierRE = re.compile("^([A-Za-z]+).*")
+identifierRE = re.compile(r"^([A-Za-z]+).*")
 
 # regular expression to parse char lines
 charRE = re.compile(
-		"(-?\d+)"			# charnum
-		"\s*;\s*WX\s+"			# ; WX
-		"(-?\d+)"			# width
-		"\s*;\s*N\s+"			# ; N
-		"([.A-Za-z0-9_]+)"		# charname
-		"\s*;\s*B\s+"			# ; B
-		"(-?\d+)"			# left
-		"\s+"
-		"(-?\d+)"			# bottom
-		"\s+"
-		"(-?\d+)"			# right
-		"\s+"
-		"(-?\d+)"			# top
-		"\s*;\s*"			# ;
+		r"(-?\d+)"			# charnum
+		r"\s*;\s*WX\s+"			# ; WX
+		r"(-?\d+)"			# width
+		r"\s*;\s*N\s+"			# ; N
+		r"([.A-Za-z0-9_]+)"		# charname
+		r"\s*;\s*B\s+"			# ; B
+		r"(-?\d+)"			# left
+		r"\s+"
+		r"(-?\d+)"			# bottom
+		r"\s+"
+		r"(-?\d+)"			# right
+		r"\s+"
+		r"(-?\d+)"			# top
+		r"\s*;\s*"			# ;
 		)
 
 # regular expression to parse kerning lines
 kernRE = re.compile(
-		"([.A-Za-z0-9_]+)"		# leftchar
-		"\s+"
-		"([.A-Za-z0-9_]+)"		# rightchar
-		"\s+"
-		"(-?\d+)"			# value
-		"\s*"
+		r"([.A-Za-z0-9_]+)"		# leftchar
+		r"\s+"
+		r"([.A-Za-z0-9_]+)"		# rightchar
+		r"\s+"
+		r"(-?\d+)"			# value
+		r"\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+"
-		"(\d+)"				# number of parts
-		"\s*;\s*"
+		r"([.A-Za-z0-9_]+)"		# char name
+		r"\s+"
+		r"(\d+)"				# number of parts
+		r"\s*;\s*"
 		)
 componentRE = re.compile(
-		"PCC\s+"			# PPC
-		"([.A-Za-z0-9_]+)"		# base char name
-		"\s+"
-		"(-?\d+)"			# x offset
-		"\s+"
-		"(-?\d+)"			# y offset
-		"\s*;\s*"
+		r"PCC\s+"			# PPC
+		r"([.A-Za-z0-9_]+)"		# base char name
+		r"\s+"
+		r"(-?\d+)"			# x offset
+		r"\s+"
+		r"(-?\d+)"			# y offset
+		r"\s*;\s*"
 		)
 
 preferredAttributeOrder = [
@@ -98,6 +138,11 @@
 			]
 
 	def __init__(self, path=None):
+		"""AFM file reader.
+
+		Instantiating an object with a path name will cause the file to be opened,
+		read, and parsed. Alternatively the path can be left unspecified, and a
+		file can be parsed later with the :meth:`read` method."""
 		self._attrs = {}
 		self._chars = {}
 		self._kerning = {}
@@ -108,6 +153,7 @@
 			self.read(path)
 
 	def read(self, path):
+		"""Opens, reads and parses a file."""
 		lines = readlines(path)
 		for line in lines:
 			if not line.strip():
@@ -190,6 +236,7 @@
 		self._composites[charname] = components
 
 	def write(self, path, sep='\r'):
+		"""Writes out an AFM font to the given path."""
 		import time
 		lines = [	"StartFontMetrics 2.0",
 				"Comment Generated by afmLib; at %s" % (
@@ -259,24 +306,40 @@
 		writelines(path, lines, sep)
 
 	def has_kernpair(self, pair):
+		"""Returns `True` if the given glyph pair (specified as a tuple) exists
+		in the kerning dictionary."""
 		return pair in self._kerning
 
 	def kernpairs(self):
+		"""Returns a list of all kern pairs in the kerning dictionary."""
 		return list(self._kerning.keys())
 
 	def has_char(self, char):
+		"""Returns `True` if the given glyph exists in the font."""
 		return char in self._chars
 
 	def chars(self):
+		"""Returns a list of all glyph names in the font."""
 		return list(self._chars.keys())
 
 	def comments(self):
+		"""Returns all comments from the file."""
 		return self._comments
 
 	def addComment(self, comment):
+		"""Adds a new comment to the file."""
 		self._comments.append(comment)
 
 	def addComposite(self, glyphName, components):
+		"""Specifies that the glyph `glyphName` is made up of the given components.
+		The components list should be of the following form::
+
+			[
+				(glyphname, xOffset, yOffset),
+				...
+			]
+		
+		"""
 		self._composites[glyphName] = components
 
 	def __getattr__(self, attr):
diff --git a/Lib/fontTools/agl.py b/Lib/fontTools/agl.py
index ec1a1b0..4f7ff92 100644
--- a/Lib/fontTools/agl.py
+++ b/Lib/fontTools/agl.py
@@ -1,17 +1,38 @@
 # -*- coding: utf-8 -*-
-# The table below is taken from
-# http://www.adobe.com/devnet/opentype/archives/aglfn.txt
+# The tables below are taken from
+# https://github.com/adobe-type-tools/agl-aglfn/raw/4036a9ca80a62f64f9de4f7321a9a045ad0ecfd6/glyphlist.txt
+# and
+# https://github.com/adobe-type-tools/agl-aglfn/raw/4036a9ca80a62f64f9de4f7321a9a045ad0ecfd6/aglfn.txt
+"""
+Interface to the Adobe Glyph List
 
-from __future__ import (print_function, division, absolute_import,
-                        unicode_literals)
-from fontTools.misc.py23 import *
+This module exists to convert glyph names from the Adobe Glyph List
+to their Unicode equivalents. Example usage:
+
+	>>> from fontTools.agl import toUnicode
+	>>> toUnicode("nahiragana")
+	'な'
+
+It also contains two dictionaries, ``UV2AGL`` and ``AGL2UV``, which map from
+Unicode codepoints to AGL names and vice versa:
+
+	>>> import fontTools
+	>>> fontTools.agl.UV2AGL[ord("?")]
+	'question'
+	>>> fontTools.agl.AGL2UV["wcircumflex"]
+	373
+
+This is used by fontTools when it has to construct glyph names for a font which
+doesn't include any (e.g. format 3.0 post tables).
+"""
+
+from fontTools.misc.py23 import tostr
 import re
 
 
 _aglText = """\
 # -----------------------------------------------------------
-# Copyright 2003, 2005-2008, 2010 Adobe Systems Incorporated.
-# All rights reserved.
+# Copyright 2002-2019 Adobe (http://www.adobe.com/).
 #
 # Redistribution and use in source and binary forms, with or
 # without modification, are permitted provided that the
@@ -26,10 +47,4338 @@
 # 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.
+# Neither the name of Adobe 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.
+# -----------------------------------------------------------
+# Name:          Adobe Glyph List
+# Table version: 2.0
+# Date:          September 20, 2002
+# URL:           https://github.com/adobe-type-tools/agl-aglfn
+#
+# Format: two semicolon-delimited fields:
+#   (1) glyph name--upper/lowercase letters and digits
+#   (2) Unicode scalar value--four uppercase hexadecimal digits
+#
+A;0041
+AE;00C6
+AEacute;01FC
+AEmacron;01E2
+AEsmall;F7E6
+Aacute;00C1
+Aacutesmall;F7E1
+Abreve;0102
+Abreveacute;1EAE
+Abrevecyrillic;04D0
+Abrevedotbelow;1EB6
+Abrevegrave;1EB0
+Abrevehookabove;1EB2
+Abrevetilde;1EB4
+Acaron;01CD
+Acircle;24B6
+Acircumflex;00C2
+Acircumflexacute;1EA4
+Acircumflexdotbelow;1EAC
+Acircumflexgrave;1EA6
+Acircumflexhookabove;1EA8
+Acircumflexsmall;F7E2
+Acircumflextilde;1EAA
+Acute;F6C9
+Acutesmall;F7B4
+Acyrillic;0410
+Adblgrave;0200
+Adieresis;00C4
+Adieresiscyrillic;04D2
+Adieresismacron;01DE
+Adieresissmall;F7E4
+Adotbelow;1EA0
+Adotmacron;01E0
+Agrave;00C0
+Agravesmall;F7E0
+Ahookabove;1EA2
+Aiecyrillic;04D4
+Ainvertedbreve;0202
+Alpha;0391
+Alphatonos;0386
+Amacron;0100
+Amonospace;FF21
+Aogonek;0104
+Aring;00C5
+Aringacute;01FA
+Aringbelow;1E00
+Aringsmall;F7E5
+Asmall;F761
+Atilde;00C3
+Atildesmall;F7E3
+Aybarmenian;0531
+B;0042
+Bcircle;24B7
+Bdotaccent;1E02
+Bdotbelow;1E04
+Becyrillic;0411
+Benarmenian;0532
+Beta;0392
+Bhook;0181
+Blinebelow;1E06
+Bmonospace;FF22
+Brevesmall;F6F4
+Bsmall;F762
+Btopbar;0182
+C;0043
+Caarmenian;053E
+Cacute;0106
+Caron;F6CA
+Caronsmall;F6F5
+Ccaron;010C
+Ccedilla;00C7
+Ccedillaacute;1E08
+Ccedillasmall;F7E7
+Ccircle;24B8
+Ccircumflex;0108
+Cdot;010A
+Cdotaccent;010A
+Cedillasmall;F7B8
+Chaarmenian;0549
+Cheabkhasiancyrillic;04BC
+Checyrillic;0427
+Chedescenderabkhasiancyrillic;04BE
+Chedescendercyrillic;04B6
+Chedieresiscyrillic;04F4
+Cheharmenian;0543
+Chekhakassiancyrillic;04CB
+Cheverticalstrokecyrillic;04B8
+Chi;03A7
+Chook;0187
+Circumflexsmall;F6F6
+Cmonospace;FF23
+Coarmenian;0551
+Csmall;F763
+D;0044
+DZ;01F1
+DZcaron;01C4
+Daarmenian;0534
+Dafrican;0189
+Dcaron;010E
+Dcedilla;1E10
+Dcircle;24B9
+Dcircumflexbelow;1E12
+Dcroat;0110
+Ddotaccent;1E0A
+Ddotbelow;1E0C
+Decyrillic;0414
+Deicoptic;03EE
+Delta;2206
+Deltagreek;0394
+Dhook;018A
+Dieresis;F6CB
+DieresisAcute;F6CC
+DieresisGrave;F6CD
+Dieresissmall;F7A8
+Digammagreek;03DC
+Djecyrillic;0402
+Dlinebelow;1E0E
+Dmonospace;FF24
+Dotaccentsmall;F6F7
+Dslash;0110
+Dsmall;F764
+Dtopbar;018B
+Dz;01F2
+Dzcaron;01C5
+Dzeabkhasiancyrillic;04E0
+Dzecyrillic;0405
+Dzhecyrillic;040F
+E;0045
+Eacute;00C9
+Eacutesmall;F7E9
+Ebreve;0114
+Ecaron;011A
+Ecedillabreve;1E1C
+Echarmenian;0535
+Ecircle;24BA
+Ecircumflex;00CA
+Ecircumflexacute;1EBE
+Ecircumflexbelow;1E18
+Ecircumflexdotbelow;1EC6
+Ecircumflexgrave;1EC0
+Ecircumflexhookabove;1EC2
+Ecircumflexsmall;F7EA
+Ecircumflextilde;1EC4
+Ecyrillic;0404
+Edblgrave;0204
+Edieresis;00CB
+Edieresissmall;F7EB
+Edot;0116
+Edotaccent;0116
+Edotbelow;1EB8
+Efcyrillic;0424
+Egrave;00C8
+Egravesmall;F7E8
+Eharmenian;0537
+Ehookabove;1EBA
+Eightroman;2167
+Einvertedbreve;0206
+Eiotifiedcyrillic;0464
+Elcyrillic;041B
+Elevenroman;216A
+Emacron;0112
+Emacronacute;1E16
+Emacrongrave;1E14
+Emcyrillic;041C
+Emonospace;FF25
+Encyrillic;041D
+Endescendercyrillic;04A2
+Eng;014A
+Enghecyrillic;04A4
+Enhookcyrillic;04C7
+Eogonek;0118
+Eopen;0190
+Epsilon;0395
+Epsilontonos;0388
+Ercyrillic;0420
+Ereversed;018E
+Ereversedcyrillic;042D
+Escyrillic;0421
+Esdescendercyrillic;04AA
+Esh;01A9
+Esmall;F765
+Eta;0397
+Etarmenian;0538
+Etatonos;0389
+Eth;00D0
+Ethsmall;F7F0
+Etilde;1EBC
+Etildebelow;1E1A
+Euro;20AC
+Ezh;01B7
+Ezhcaron;01EE
+Ezhreversed;01B8
+F;0046
+Fcircle;24BB
+Fdotaccent;1E1E
+Feharmenian;0556
+Feicoptic;03E4
+Fhook;0191
+Fitacyrillic;0472
+Fiveroman;2164
+Fmonospace;FF26
+Fourroman;2163
+Fsmall;F766
+G;0047
+GBsquare;3387
+Gacute;01F4
+Gamma;0393
+Gammaafrican;0194
+Gangiacoptic;03EA
+Gbreve;011E
+Gcaron;01E6
+Gcedilla;0122
+Gcircle;24BC
+Gcircumflex;011C
+Gcommaaccent;0122
+Gdot;0120
+Gdotaccent;0120
+Gecyrillic;0413
+Ghadarmenian;0542
+Ghemiddlehookcyrillic;0494
+Ghestrokecyrillic;0492
+Gheupturncyrillic;0490
+Ghook;0193
+Gimarmenian;0533
+Gjecyrillic;0403
+Gmacron;1E20
+Gmonospace;FF27
+Grave;F6CE
+Gravesmall;F760
+Gsmall;F767
+Gsmallhook;029B
+Gstroke;01E4
+H;0048
+H18533;25CF
+H18543;25AA
+H18551;25AB
+H22073;25A1
+HPsquare;33CB
+Haabkhasiancyrillic;04A8
+Hadescendercyrillic;04B2
+Hardsigncyrillic;042A
+Hbar;0126
+Hbrevebelow;1E2A
+Hcedilla;1E28
+Hcircle;24BD
+Hcircumflex;0124
+Hdieresis;1E26
+Hdotaccent;1E22
+Hdotbelow;1E24
+Hmonospace;FF28
+Hoarmenian;0540
+Horicoptic;03E8
+Hsmall;F768
+Hungarumlaut;F6CF
+Hungarumlautsmall;F6F8
+Hzsquare;3390
+I;0049
+IAcyrillic;042F
+IJ;0132
+IUcyrillic;042E
+Iacute;00CD
+Iacutesmall;F7ED
+Ibreve;012C
+Icaron;01CF
+Icircle;24BE
+Icircumflex;00CE
+Icircumflexsmall;F7EE
+Icyrillic;0406
+Idblgrave;0208
+Idieresis;00CF
+Idieresisacute;1E2E
+Idieresiscyrillic;04E4
+Idieresissmall;F7EF
+Idot;0130
+Idotaccent;0130
+Idotbelow;1ECA
+Iebrevecyrillic;04D6
+Iecyrillic;0415
+Ifraktur;2111
+Igrave;00CC
+Igravesmall;F7EC
+Ihookabove;1EC8
+Iicyrillic;0418
+Iinvertedbreve;020A
+Iishortcyrillic;0419
+Imacron;012A
+Imacroncyrillic;04E2
+Imonospace;FF29
+Iniarmenian;053B
+Iocyrillic;0401
+Iogonek;012E
+Iota;0399
+Iotaafrican;0196
+Iotadieresis;03AA
+Iotatonos;038A
+Ismall;F769
+Istroke;0197
+Itilde;0128
+Itildebelow;1E2C
+Izhitsacyrillic;0474
+Izhitsadblgravecyrillic;0476
+J;004A
+Jaarmenian;0541
+Jcircle;24BF
+Jcircumflex;0134
+Jecyrillic;0408
+Jheharmenian;054B
+Jmonospace;FF2A
+Jsmall;F76A
+K;004B
+KBsquare;3385
+KKsquare;33CD
+Kabashkircyrillic;04A0
+Kacute;1E30
+Kacyrillic;041A
+Kadescendercyrillic;049A
+Kahookcyrillic;04C3
+Kappa;039A
+Kastrokecyrillic;049E
+Kaverticalstrokecyrillic;049C
+Kcaron;01E8
+Kcedilla;0136
+Kcircle;24C0
+Kcommaaccent;0136
+Kdotbelow;1E32
+Keharmenian;0554
+Kenarmenian;053F
+Khacyrillic;0425
+Kheicoptic;03E6
+Khook;0198
+Kjecyrillic;040C
+Klinebelow;1E34
+Kmonospace;FF2B
+Koppacyrillic;0480
+Koppagreek;03DE
+Ksicyrillic;046E
+Ksmall;F76B
+L;004C
+LJ;01C7
+LL;F6BF
+Lacute;0139
+Lambda;039B
+Lcaron;013D
+Lcedilla;013B
+Lcircle;24C1
+Lcircumflexbelow;1E3C
+Lcommaaccent;013B
+Ldot;013F
+Ldotaccent;013F
+Ldotbelow;1E36
+Ldotbelowmacron;1E38
+Liwnarmenian;053C
+Lj;01C8
+Ljecyrillic;0409
+Llinebelow;1E3A
+Lmonospace;FF2C
+Lslash;0141
+Lslashsmall;F6F9
+Lsmall;F76C
+M;004D
+MBsquare;3386
+Macron;F6D0
+Macronsmall;F7AF
+Macute;1E3E
+Mcircle;24C2
+Mdotaccent;1E40
+Mdotbelow;1E42
+Menarmenian;0544
+Mmonospace;FF2D
+Msmall;F76D
+Mturned;019C
+Mu;039C
+N;004E
+NJ;01CA
+Nacute;0143
+Ncaron;0147
+Ncedilla;0145
+Ncircle;24C3
+Ncircumflexbelow;1E4A
+Ncommaaccent;0145
+Ndotaccent;1E44
+Ndotbelow;1E46
+Nhookleft;019D
+Nineroman;2168
+Nj;01CB
+Njecyrillic;040A
+Nlinebelow;1E48
+Nmonospace;FF2E
+Nowarmenian;0546
+Nsmall;F76E
+Ntilde;00D1
+Ntildesmall;F7F1
+Nu;039D
+O;004F
+OE;0152
+OEsmall;F6FA
+Oacute;00D3
+Oacutesmall;F7F3
+Obarredcyrillic;04E8
+Obarreddieresiscyrillic;04EA
+Obreve;014E
+Ocaron;01D1
+Ocenteredtilde;019F
+Ocircle;24C4
+Ocircumflex;00D4
+Ocircumflexacute;1ED0
+Ocircumflexdotbelow;1ED8
+Ocircumflexgrave;1ED2
+Ocircumflexhookabove;1ED4
+Ocircumflexsmall;F7F4
+Ocircumflextilde;1ED6
+Ocyrillic;041E
+Odblacute;0150
+Odblgrave;020C
+Odieresis;00D6
+Odieresiscyrillic;04E6
+Odieresissmall;F7F6
+Odotbelow;1ECC
+Ogoneksmall;F6FB
+Ograve;00D2
+Ogravesmall;F7F2
+Oharmenian;0555
+Ohm;2126
+Ohookabove;1ECE
+Ohorn;01A0
+Ohornacute;1EDA
+Ohorndotbelow;1EE2
+Ohorngrave;1EDC
+Ohornhookabove;1EDE
+Ohorntilde;1EE0
+Ohungarumlaut;0150
+Oi;01A2
+Oinvertedbreve;020E
+Omacron;014C
+Omacronacute;1E52
+Omacrongrave;1E50
+Omega;2126
+Omegacyrillic;0460
+Omegagreek;03A9
+Omegaroundcyrillic;047A
+Omegatitlocyrillic;047C
+Omegatonos;038F
+Omicron;039F
+Omicrontonos;038C
+Omonospace;FF2F
+Oneroman;2160
+Oogonek;01EA
+Oogonekmacron;01EC
+Oopen;0186
+Oslash;00D8
+Oslashacute;01FE
+Oslashsmall;F7F8
+Osmall;F76F
+Ostrokeacute;01FE
+Otcyrillic;047E
+Otilde;00D5
+Otildeacute;1E4C
+Otildedieresis;1E4E
+Otildesmall;F7F5
+P;0050
+Pacute;1E54
+Pcircle;24C5
+Pdotaccent;1E56
+Pecyrillic;041F
+Peharmenian;054A
+Pemiddlehookcyrillic;04A6
+Phi;03A6
+Phook;01A4
+Pi;03A0
+Piwrarmenian;0553
+Pmonospace;FF30
+Psi;03A8
+Psicyrillic;0470
+Psmall;F770
+Q;0051
+Qcircle;24C6
+Qmonospace;FF31
+Qsmall;F771
+R;0052
+Raarmenian;054C
+Racute;0154
+Rcaron;0158
+Rcedilla;0156
+Rcircle;24C7
+Rcommaaccent;0156
+Rdblgrave;0210
+Rdotaccent;1E58
+Rdotbelow;1E5A
+Rdotbelowmacron;1E5C
+Reharmenian;0550
+Rfraktur;211C
+Rho;03A1
+Ringsmall;F6FC
+Rinvertedbreve;0212
+Rlinebelow;1E5E
+Rmonospace;FF32
+Rsmall;F772
+Rsmallinverted;0281
+Rsmallinvertedsuperior;02B6
+S;0053
+SF010000;250C
+SF020000;2514
+SF030000;2510
+SF040000;2518
+SF050000;253C
+SF060000;252C
+SF070000;2534
+SF080000;251C
+SF090000;2524
+SF100000;2500
+SF110000;2502
+SF190000;2561
+SF200000;2562
+SF210000;2556
+SF220000;2555
+SF230000;2563
+SF240000;2551
+SF250000;2557
+SF260000;255D
+SF270000;255C
+SF280000;255B
+SF360000;255E
+SF370000;255F
+SF380000;255A
+SF390000;2554
+SF400000;2569
+SF410000;2566
+SF420000;2560
+SF430000;2550
+SF440000;256C
+SF450000;2567
+SF460000;2568
+SF470000;2564
+SF480000;2565
+SF490000;2559
+SF500000;2558
+SF510000;2552
+SF520000;2553
+SF530000;256B
+SF540000;256A
+Sacute;015A
+Sacutedotaccent;1E64
+Sampigreek;03E0
+Scaron;0160
+Scarondotaccent;1E66
+Scaronsmall;F6FD
+Scedilla;015E
+Schwa;018F
+Schwacyrillic;04D8
+Schwadieresiscyrillic;04DA
+Scircle;24C8
+Scircumflex;015C
+Scommaaccent;0218
+Sdotaccent;1E60
+Sdotbelow;1E62
+Sdotbelowdotaccent;1E68
+Seharmenian;054D
+Sevenroman;2166
+Shaarmenian;0547
+Shacyrillic;0428
+Shchacyrillic;0429
+Sheicoptic;03E2
+Shhacyrillic;04BA
+Shimacoptic;03EC
+Sigma;03A3
+Sixroman;2165
+Smonospace;FF33
+Softsigncyrillic;042C
+Ssmall;F773
+Stigmagreek;03DA
+T;0054
+Tau;03A4
+Tbar;0166
+Tcaron;0164
+Tcedilla;0162
+Tcircle;24C9
+Tcircumflexbelow;1E70
+Tcommaaccent;0162
+Tdotaccent;1E6A
+Tdotbelow;1E6C
+Tecyrillic;0422
+Tedescendercyrillic;04AC
+Tenroman;2169
+Tetsecyrillic;04B4
+Theta;0398
+Thook;01AC
+Thorn;00DE
+Thornsmall;F7FE
+Threeroman;2162
+Tildesmall;F6FE
+Tiwnarmenian;054F
+Tlinebelow;1E6E
+Tmonospace;FF34
+Toarmenian;0539
+Tonefive;01BC
+Tonesix;0184
+Tonetwo;01A7
+Tretroflexhook;01AE
+Tsecyrillic;0426
+Tshecyrillic;040B
+Tsmall;F774
+Twelveroman;216B
+Tworoman;2161
+U;0055
+Uacute;00DA
+Uacutesmall;F7FA
+Ubreve;016C
+Ucaron;01D3
+Ucircle;24CA
+Ucircumflex;00DB
+Ucircumflexbelow;1E76
+Ucircumflexsmall;F7FB
+Ucyrillic;0423
+Udblacute;0170
+Udblgrave;0214
+Udieresis;00DC
+Udieresisacute;01D7
+Udieresisbelow;1E72
+Udieresiscaron;01D9
+Udieresiscyrillic;04F0
+Udieresisgrave;01DB
+Udieresismacron;01D5
+Udieresissmall;F7FC
+Udotbelow;1EE4
+Ugrave;00D9
+Ugravesmall;F7F9
+Uhookabove;1EE6
+Uhorn;01AF
+Uhornacute;1EE8
+Uhorndotbelow;1EF0
+Uhorngrave;1EEA
+Uhornhookabove;1EEC
+Uhorntilde;1EEE
+Uhungarumlaut;0170
+Uhungarumlautcyrillic;04F2
+Uinvertedbreve;0216
+Ukcyrillic;0478
+Umacron;016A
+Umacroncyrillic;04EE
+Umacrondieresis;1E7A
+Umonospace;FF35
+Uogonek;0172
+Upsilon;03A5
+Upsilon1;03D2
+Upsilonacutehooksymbolgreek;03D3
+Upsilonafrican;01B1
+Upsilondieresis;03AB
+Upsilondieresishooksymbolgreek;03D4
+Upsilonhooksymbol;03D2
+Upsilontonos;038E
+Uring;016E
+Ushortcyrillic;040E
+Usmall;F775
+Ustraightcyrillic;04AE
+Ustraightstrokecyrillic;04B0
+Utilde;0168
+Utildeacute;1E78
+Utildebelow;1E74
+V;0056
+Vcircle;24CB
+Vdotbelow;1E7E
+Vecyrillic;0412
+Vewarmenian;054E
+Vhook;01B2
+Vmonospace;FF36
+Voarmenian;0548
+Vsmall;F776
+Vtilde;1E7C
+W;0057
+Wacute;1E82
+Wcircle;24CC
+Wcircumflex;0174
+Wdieresis;1E84
+Wdotaccent;1E86
+Wdotbelow;1E88
+Wgrave;1E80
+Wmonospace;FF37
+Wsmall;F777
+X;0058
+Xcircle;24CD
+Xdieresis;1E8C
+Xdotaccent;1E8A
+Xeharmenian;053D
+Xi;039E
+Xmonospace;FF38
+Xsmall;F778
+Y;0059
+Yacute;00DD
+Yacutesmall;F7FD
+Yatcyrillic;0462
+Ycircle;24CE
+Ycircumflex;0176
+Ydieresis;0178
+Ydieresissmall;F7FF
+Ydotaccent;1E8E
+Ydotbelow;1EF4
+Yericyrillic;042B
+Yerudieresiscyrillic;04F8
+Ygrave;1EF2
+Yhook;01B3
+Yhookabove;1EF6
+Yiarmenian;0545
+Yicyrillic;0407
+Yiwnarmenian;0552
+Ymonospace;FF39
+Ysmall;F779
+Ytilde;1EF8
+Yusbigcyrillic;046A
+Yusbigiotifiedcyrillic;046C
+Yuslittlecyrillic;0466
+Yuslittleiotifiedcyrillic;0468
+Z;005A
+Zaarmenian;0536
+Zacute;0179
+Zcaron;017D
+Zcaronsmall;F6FF
+Zcircle;24CF
+Zcircumflex;1E90
+Zdot;017B
+Zdotaccent;017B
+Zdotbelow;1E92
+Zecyrillic;0417
+Zedescendercyrillic;0498
+Zedieresiscyrillic;04DE
+Zeta;0396
+Zhearmenian;053A
+Zhebrevecyrillic;04C1
+Zhecyrillic;0416
+Zhedescendercyrillic;0496
+Zhedieresiscyrillic;04DC
+Zlinebelow;1E94
+Zmonospace;FF3A
+Zsmall;F77A
+Zstroke;01B5
+a;0061
+aabengali;0986
+aacute;00E1
+aadeva;0906
+aagujarati;0A86
+aagurmukhi;0A06
+aamatragurmukhi;0A3E
+aarusquare;3303
+aavowelsignbengali;09BE
+aavowelsigndeva;093E
+aavowelsigngujarati;0ABE
+abbreviationmarkarmenian;055F
+abbreviationsigndeva;0970
+abengali;0985
+abopomofo;311A
+abreve;0103
+abreveacute;1EAF
+abrevecyrillic;04D1
+abrevedotbelow;1EB7
+abrevegrave;1EB1
+abrevehookabove;1EB3
+abrevetilde;1EB5
+acaron;01CE
+acircle;24D0
+acircumflex;00E2
+acircumflexacute;1EA5
+acircumflexdotbelow;1EAD
+acircumflexgrave;1EA7
+acircumflexhookabove;1EA9
+acircumflextilde;1EAB
+acute;00B4
+acutebelowcmb;0317
+acutecmb;0301
+acutecomb;0301
+acutedeva;0954
+acutelowmod;02CF
+acutetonecmb;0341
+acyrillic;0430
+adblgrave;0201
+addakgurmukhi;0A71
+adeva;0905
+adieresis;00E4
+adieresiscyrillic;04D3
+adieresismacron;01DF
+adotbelow;1EA1
+adotmacron;01E1
+ae;00E6
+aeacute;01FD
+aekorean;3150
+aemacron;01E3
+afii00208;2015
+afii08941;20A4
+afii10017;0410
+afii10018;0411
+afii10019;0412
+afii10020;0413
+afii10021;0414
+afii10022;0415
+afii10023;0401
+afii10024;0416
+afii10025;0417
+afii10026;0418
+afii10027;0419
+afii10028;041A
+afii10029;041B
+afii10030;041C
+afii10031;041D
+afii10032;041E
+afii10033;041F
+afii10034;0420
+afii10035;0421
+afii10036;0422
+afii10037;0423
+afii10038;0424
+afii10039;0425
+afii10040;0426
+afii10041;0427
+afii10042;0428
+afii10043;0429
+afii10044;042A
+afii10045;042B
+afii10046;042C
+afii10047;042D
+afii10048;042E
+afii10049;042F
+afii10050;0490
+afii10051;0402
+afii10052;0403
+afii10053;0404
+afii10054;0405
+afii10055;0406
+afii10056;0407
+afii10057;0408
+afii10058;0409
+afii10059;040A
+afii10060;040B
+afii10061;040C
+afii10062;040E
+afii10063;F6C4
+afii10064;F6C5
+afii10065;0430
+afii10066;0431
+afii10067;0432
+afii10068;0433
+afii10069;0434
+afii10070;0435
+afii10071;0451
+afii10072;0436
+afii10073;0437
+afii10074;0438
+afii10075;0439
+afii10076;043A
+afii10077;043B
+afii10078;043C
+afii10079;043D
+afii10080;043E
+afii10081;043F
+afii10082;0440
+afii10083;0441
+afii10084;0442
+afii10085;0443
+afii10086;0444
+afii10087;0445
+afii10088;0446
+afii10089;0447
+afii10090;0448
+afii10091;0449
+afii10092;044A
+afii10093;044B
+afii10094;044C
+afii10095;044D
+afii10096;044E
+afii10097;044F
+afii10098;0491
+afii10099;0452
+afii10100;0453
+afii10101;0454
+afii10102;0455
+afii10103;0456
+afii10104;0457
+afii10105;0458
+afii10106;0459
+afii10107;045A
+afii10108;045B
+afii10109;045C
+afii10110;045E
+afii10145;040F
+afii10146;0462
+afii10147;0472
+afii10148;0474
+afii10192;F6C6
+afii10193;045F
+afii10194;0463
+afii10195;0473
+afii10196;0475
+afii10831;F6C7
+afii10832;F6C8
+afii10846;04D9
+afii299;200E
+afii300;200F
+afii301;200D
+afii57381;066A
+afii57388;060C
+afii57392;0660
+afii57393;0661
+afii57394;0662
+afii57395;0663
+afii57396;0664
+afii57397;0665
+afii57398;0666
+afii57399;0667
+afii57400;0668
+afii57401;0669
+afii57403;061B
+afii57407;061F
+afii57409;0621
+afii57410;0622
+afii57411;0623
+afii57412;0624
+afii57413;0625
+afii57414;0626
+afii57415;0627
+afii57416;0628
+afii57417;0629
+afii57418;062A
+afii57419;062B
+afii57420;062C
+afii57421;062D
+afii57422;062E
+afii57423;062F
+afii57424;0630
+afii57425;0631
+afii57426;0632
+afii57427;0633
+afii57428;0634
+afii57429;0635
+afii57430;0636
+afii57431;0637
+afii57432;0638
+afii57433;0639
+afii57434;063A
+afii57440;0640
+afii57441;0641
+afii57442;0642
+afii57443;0643
+afii57444;0644
+afii57445;0645
+afii57446;0646
+afii57448;0648
+afii57449;0649
+afii57450;064A
+afii57451;064B
+afii57452;064C
+afii57453;064D
+afii57454;064E
+afii57455;064F
+afii57456;0650
+afii57457;0651
+afii57458;0652
+afii57470;0647
+afii57505;06A4
+afii57506;067E
+afii57507;0686
+afii57508;0698
+afii57509;06AF
+afii57511;0679
+afii57512;0688
+afii57513;0691
+afii57514;06BA
+afii57519;06D2
+afii57534;06D5
+afii57636;20AA
+afii57645;05BE
+afii57658;05C3
+afii57664;05D0
+afii57665;05D1
+afii57666;05D2
+afii57667;05D3
+afii57668;05D4
+afii57669;05D5
+afii57670;05D6
+afii57671;05D7
+afii57672;05D8
+afii57673;05D9
+afii57674;05DA
+afii57675;05DB
+afii57676;05DC
+afii57677;05DD
+afii57678;05DE
+afii57679;05DF
+afii57680;05E0
+afii57681;05E1
+afii57682;05E2
+afii57683;05E3
+afii57684;05E4
+afii57685;05E5
+afii57686;05E6
+afii57687;05E7
+afii57688;05E8
+afii57689;05E9
+afii57690;05EA
+afii57694;FB2A
+afii57695;FB2B
+afii57700;FB4B
+afii57705;FB1F
+afii57716;05F0
+afii57717;05F1
+afii57718;05F2
+afii57723;FB35
+afii57793;05B4
+afii57794;05B5
+afii57795;05B6
+afii57796;05BB
+afii57797;05B8
+afii57798;05B7
+afii57799;05B0
+afii57800;05B2
+afii57801;05B1
+afii57802;05B3
+afii57803;05C2
+afii57804;05C1
+afii57806;05B9
+afii57807;05BC
+afii57839;05BD
+afii57841;05BF
+afii57842;05C0
+afii57929;02BC
+afii61248;2105
+afii61289;2113
+afii61352;2116
+afii61573;202C
+afii61574;202D
+afii61575;202E
+afii61664;200C
+afii63167;066D
+afii64937;02BD
+agrave;00E0
+agujarati;0A85
+agurmukhi;0A05
+ahiragana;3042
+ahookabove;1EA3
+aibengali;0990
+aibopomofo;311E
+aideva;0910
+aiecyrillic;04D5
+aigujarati;0A90
+aigurmukhi;0A10
+aimatragurmukhi;0A48
+ainarabic;0639
+ainfinalarabic;FECA
+aininitialarabic;FECB
+ainmedialarabic;FECC
+ainvertedbreve;0203
+aivowelsignbengali;09C8
+aivowelsigndeva;0948
+aivowelsigngujarati;0AC8
+akatakana;30A2
+akatakanahalfwidth;FF71
+akorean;314F
+alef;05D0
+alefarabic;0627
+alefdageshhebrew;FB30
+aleffinalarabic;FE8E
+alefhamzaabovearabic;0623
+alefhamzaabovefinalarabic;FE84
+alefhamzabelowarabic;0625
+alefhamzabelowfinalarabic;FE88
+alefhebrew;05D0
+aleflamedhebrew;FB4F
+alefmaddaabovearabic;0622
+alefmaddaabovefinalarabic;FE82
+alefmaksuraarabic;0649
+alefmaksurafinalarabic;FEF0
+alefmaksurainitialarabic;FEF3
+alefmaksuramedialarabic;FEF4
+alefpatahhebrew;FB2E
+alefqamatshebrew;FB2F
+aleph;2135
+allequal;224C
+alpha;03B1
+alphatonos;03AC
+amacron;0101
+amonospace;FF41
+ampersand;0026
+ampersandmonospace;FF06
+ampersandsmall;F726
+amsquare;33C2
+anbopomofo;3122
+angbopomofo;3124
+angkhankhuthai;0E5A
+angle;2220
+anglebracketleft;3008
+anglebracketleftvertical;FE3F
+anglebracketright;3009
+anglebracketrightvertical;FE40
+angleleft;2329
+angleright;232A
+angstrom;212B
+anoteleia;0387
+anudattadeva;0952
+anusvarabengali;0982
+anusvaradeva;0902
+anusvaragujarati;0A82
+aogonek;0105
+apaatosquare;3300
+aparen;249C
+apostrophearmenian;055A
+apostrophemod;02BC
+apple;F8FF
+approaches;2250
+approxequal;2248
+approxequalorimage;2252
+approximatelyequal;2245
+araeaekorean;318E
+araeakorean;318D
+arc;2312
+arighthalfring;1E9A
+aring;00E5
+aringacute;01FB
+aringbelow;1E01
+arrowboth;2194
+arrowdashdown;21E3
+arrowdashleft;21E0
+arrowdashright;21E2
+arrowdashup;21E1
+arrowdblboth;21D4
+arrowdbldown;21D3
+arrowdblleft;21D0
+arrowdblright;21D2
+arrowdblup;21D1
+arrowdown;2193
+arrowdownleft;2199
+arrowdownright;2198
+arrowdownwhite;21E9
+arrowheaddownmod;02C5
+arrowheadleftmod;02C2
+arrowheadrightmod;02C3
+arrowheadupmod;02C4
+arrowhorizex;F8E7
+arrowleft;2190
+arrowleftdbl;21D0
+arrowleftdblstroke;21CD
+arrowleftoverright;21C6
+arrowleftwhite;21E6
+arrowright;2192
+arrowrightdblstroke;21CF
+arrowrightheavy;279E
+arrowrightoverleft;21C4
+arrowrightwhite;21E8
+arrowtableft;21E4
+arrowtabright;21E5
+arrowup;2191
+arrowupdn;2195
+arrowupdnbse;21A8
+arrowupdownbase;21A8
+arrowupleft;2196
+arrowupleftofdown;21C5
+arrowupright;2197
+arrowupwhite;21E7
+arrowvertex;F8E6
+asciicircum;005E
+asciicircummonospace;FF3E
+asciitilde;007E
+asciitildemonospace;FF5E
+ascript;0251
+ascriptturned;0252
+asmallhiragana;3041
+asmallkatakana;30A1
+asmallkatakanahalfwidth;FF67
+asterisk;002A
+asteriskaltonearabic;066D
+asteriskarabic;066D
+asteriskmath;2217
+asteriskmonospace;FF0A
+asterisksmall;FE61
+asterism;2042
+asuperior;F6E9
+asymptoticallyequal;2243
+at;0040
+atilde;00E3
+atmonospace;FF20
+atsmall;FE6B
+aturned;0250
+aubengali;0994
+aubopomofo;3120
+audeva;0914
+augujarati;0A94
+augurmukhi;0A14
+aulengthmarkbengali;09D7
+aumatragurmukhi;0A4C
+auvowelsignbengali;09CC
+auvowelsigndeva;094C
+auvowelsigngujarati;0ACC
+avagrahadeva;093D
+aybarmenian;0561
+ayin;05E2
+ayinaltonehebrew;FB20
+ayinhebrew;05E2
+b;0062
+babengali;09AC
+backslash;005C
+backslashmonospace;FF3C
+badeva;092C
+bagujarati;0AAC
+bagurmukhi;0A2C
+bahiragana;3070
+bahtthai;0E3F
+bakatakana;30D0
+bar;007C
+barmonospace;FF5C
+bbopomofo;3105
+bcircle;24D1
+bdotaccent;1E03
+bdotbelow;1E05
+beamedsixteenthnotes;266C
+because;2235
+becyrillic;0431
+beharabic;0628
+behfinalarabic;FE90
+behinitialarabic;FE91
+behiragana;3079
+behmedialarabic;FE92
+behmeeminitialarabic;FC9F
+behmeemisolatedarabic;FC08
+behnoonfinalarabic;FC6D
+bekatakana;30D9
+benarmenian;0562
+bet;05D1
+beta;03B2
+betasymbolgreek;03D0
+betdagesh;FB31
+betdageshhebrew;FB31
+bethebrew;05D1
+betrafehebrew;FB4C
+bhabengali;09AD
+bhadeva;092D
+bhagujarati;0AAD
+bhagurmukhi;0A2D
+bhook;0253
+bihiragana;3073
+bikatakana;30D3
+bilabialclick;0298
+bindigurmukhi;0A02
+birusquare;3331
+blackcircle;25CF
+blackdiamond;25C6
+blackdownpointingtriangle;25BC
+blackleftpointingpointer;25C4
+blackleftpointingtriangle;25C0
+blacklenticularbracketleft;3010
+blacklenticularbracketleftvertical;FE3B
+blacklenticularbracketright;3011
+blacklenticularbracketrightvertical;FE3C
+blacklowerlefttriangle;25E3
+blacklowerrighttriangle;25E2
+blackrectangle;25AC
+blackrightpointingpointer;25BA
+blackrightpointingtriangle;25B6
+blacksmallsquare;25AA
+blacksmilingface;263B
+blacksquare;25A0
+blackstar;2605
+blackupperlefttriangle;25E4
+blackupperrighttriangle;25E5
+blackuppointingsmalltriangle;25B4
+blackuppointingtriangle;25B2
+blank;2423
+blinebelow;1E07
+block;2588
+bmonospace;FF42
+bobaimaithai;0E1A
+bohiragana;307C
+bokatakana;30DC
+bparen;249D
+bqsquare;33C3
+braceex;F8F4
+braceleft;007B
+braceleftbt;F8F3
+braceleftmid;F8F2
+braceleftmonospace;FF5B
+braceleftsmall;FE5B
+bracelefttp;F8F1
+braceleftvertical;FE37
+braceright;007D
+bracerightbt;F8FE
+bracerightmid;F8FD
+bracerightmonospace;FF5D
+bracerightsmall;FE5C
+bracerighttp;F8FC
+bracerightvertical;FE38
+bracketleft;005B
+bracketleftbt;F8F0
+bracketleftex;F8EF
+bracketleftmonospace;FF3B
+bracketlefttp;F8EE
+bracketright;005D
+bracketrightbt;F8FB
+bracketrightex;F8FA
+bracketrightmonospace;FF3D
+bracketrighttp;F8F9
+breve;02D8
+brevebelowcmb;032E
+brevecmb;0306
+breveinvertedbelowcmb;032F
+breveinvertedcmb;0311
+breveinverteddoublecmb;0361
+bridgebelowcmb;032A
+bridgeinvertedbelowcmb;033A
+brokenbar;00A6
+bstroke;0180
+bsuperior;F6EA
+btopbar;0183
+buhiragana;3076
+bukatakana;30D6
+bullet;2022
+bulletinverse;25D8
+bulletoperator;2219
+bullseye;25CE
+c;0063
+caarmenian;056E
+cabengali;099A
+cacute;0107
+cadeva;091A
+cagujarati;0A9A
+cagurmukhi;0A1A
+calsquare;3388
+candrabindubengali;0981
+candrabinducmb;0310
+candrabindudeva;0901
+candrabindugujarati;0A81
+capslock;21EA
+careof;2105
+caron;02C7
+caronbelowcmb;032C
+caroncmb;030C
+carriagereturn;21B5
+cbopomofo;3118
+ccaron;010D
+ccedilla;00E7
+ccedillaacute;1E09
+ccircle;24D2
+ccircumflex;0109
+ccurl;0255
+cdot;010B
+cdotaccent;010B
+cdsquare;33C5
+cedilla;00B8
+cedillacmb;0327
+cent;00A2
+centigrade;2103
+centinferior;F6DF
+centmonospace;FFE0
+centoldstyle;F7A2
+centsuperior;F6E0
+chaarmenian;0579
+chabengali;099B
+chadeva;091B
+chagujarati;0A9B
+chagurmukhi;0A1B
+chbopomofo;3114
+cheabkhasiancyrillic;04BD
+checkmark;2713
+checyrillic;0447
+chedescenderabkhasiancyrillic;04BF
+chedescendercyrillic;04B7
+chedieresiscyrillic;04F5
+cheharmenian;0573
+chekhakassiancyrillic;04CC
+cheverticalstrokecyrillic;04B9
+chi;03C7
+chieuchacirclekorean;3277
+chieuchaparenkorean;3217
+chieuchcirclekorean;3269
+chieuchkorean;314A
+chieuchparenkorean;3209
+chochangthai;0E0A
+chochanthai;0E08
+chochingthai;0E09
+chochoethai;0E0C
+chook;0188
+cieucacirclekorean;3276
+cieucaparenkorean;3216
+cieuccirclekorean;3268
+cieuckorean;3148
+cieucparenkorean;3208
+cieucuparenkorean;321C
+circle;25CB
+circlemultiply;2297
+circleot;2299
+circleplus;2295
+circlepostalmark;3036
+circlewithlefthalfblack;25D0
+circlewithrighthalfblack;25D1
+circumflex;02C6
+circumflexbelowcmb;032D
+circumflexcmb;0302
+clear;2327
+clickalveolar;01C2
+clickdental;01C0
+clicklateral;01C1
+clickretroflex;01C3
+club;2663
+clubsuitblack;2663
+clubsuitwhite;2667
+cmcubedsquare;33A4
+cmonospace;FF43
+cmsquaredsquare;33A0
+coarmenian;0581
+colon;003A
+colonmonetary;20A1
+colonmonospace;FF1A
+colonsign;20A1
+colonsmall;FE55
+colontriangularhalfmod;02D1
+colontriangularmod;02D0
+comma;002C
+commaabovecmb;0313
+commaaboverightcmb;0315
+commaaccent;F6C3
+commaarabic;060C
+commaarmenian;055D
+commainferior;F6E1
+commamonospace;FF0C
+commareversedabovecmb;0314
+commareversedmod;02BD
+commasmall;FE50
+commasuperior;F6E2
+commaturnedabovecmb;0312
+commaturnedmod;02BB
+compass;263C
+congruent;2245
+contourintegral;222E
+control;2303
+controlACK;0006
+controlBEL;0007
+controlBS;0008
+controlCAN;0018
+controlCR;000D
+controlDC1;0011
+controlDC2;0012
+controlDC3;0013
+controlDC4;0014
+controlDEL;007F
+controlDLE;0010
+controlEM;0019
+controlENQ;0005
+controlEOT;0004
+controlESC;001B
+controlETB;0017
+controlETX;0003
+controlFF;000C
+controlFS;001C
+controlGS;001D
+controlHT;0009
+controlLF;000A
+controlNAK;0015
+controlRS;001E
+controlSI;000F
+controlSO;000E
+controlSOT;0002
+controlSTX;0001
+controlSUB;001A
+controlSYN;0016
+controlUS;001F
+controlVT;000B
+copyright;00A9
+copyrightsans;F8E9
+copyrightserif;F6D9
+cornerbracketleft;300C
+cornerbracketlefthalfwidth;FF62
+cornerbracketleftvertical;FE41
+cornerbracketright;300D
+cornerbracketrighthalfwidth;FF63
+cornerbracketrightvertical;FE42
+corporationsquare;337F
+cosquare;33C7
+coverkgsquare;33C6
+cparen;249E
+cruzeiro;20A2
+cstretched;0297
+curlyand;22CF
+curlyor;22CE
+currency;00A4
+cyrBreve;F6D1
+cyrFlex;F6D2
+cyrbreve;F6D4
+cyrflex;F6D5
+d;0064
+daarmenian;0564
+dabengali;09A6
+dadarabic;0636
+dadeva;0926
+dadfinalarabic;FEBE
+dadinitialarabic;FEBF
+dadmedialarabic;FEC0
+dagesh;05BC
+dageshhebrew;05BC
+dagger;2020
+daggerdbl;2021
+dagujarati;0AA6
+dagurmukhi;0A26
+dahiragana;3060
+dakatakana;30C0
+dalarabic;062F
+dalet;05D3
+daletdagesh;FB33
+daletdageshhebrew;FB33
+dalethatafpatah;05D3 05B2
+dalethatafpatahhebrew;05D3 05B2
+dalethatafsegol;05D3 05B1
+dalethatafsegolhebrew;05D3 05B1
+dalethebrew;05D3
+dalethiriq;05D3 05B4
+dalethiriqhebrew;05D3 05B4
+daletholam;05D3 05B9
+daletholamhebrew;05D3 05B9
+daletpatah;05D3 05B7
+daletpatahhebrew;05D3 05B7
+daletqamats;05D3 05B8
+daletqamatshebrew;05D3 05B8
+daletqubuts;05D3 05BB
+daletqubutshebrew;05D3 05BB
+daletsegol;05D3 05B6
+daletsegolhebrew;05D3 05B6
+daletsheva;05D3 05B0
+daletshevahebrew;05D3 05B0
+dalettsere;05D3 05B5
+dalettserehebrew;05D3 05B5
+dalfinalarabic;FEAA
+dammaarabic;064F
+dammalowarabic;064F
+dammatanaltonearabic;064C
+dammatanarabic;064C
+danda;0964
+dargahebrew;05A7
+dargalefthebrew;05A7
+dasiapneumatacyrilliccmb;0485
+dblGrave;F6D3
+dblanglebracketleft;300A
+dblanglebracketleftvertical;FE3D
+dblanglebracketright;300B
+dblanglebracketrightvertical;FE3E
+dblarchinvertedbelowcmb;032B
+dblarrowleft;21D4
+dblarrowright;21D2
+dbldanda;0965
+dblgrave;F6D6
+dblgravecmb;030F
+dblintegral;222C
+dbllowline;2017
+dbllowlinecmb;0333
+dbloverlinecmb;033F
+dblprimemod;02BA
+dblverticalbar;2016
+dblverticallineabovecmb;030E
+dbopomofo;3109
+dbsquare;33C8
+dcaron;010F
+dcedilla;1E11
+dcircle;24D3
+dcircumflexbelow;1E13
+dcroat;0111
+ddabengali;09A1
+ddadeva;0921
+ddagujarati;0AA1
+ddagurmukhi;0A21
+ddalarabic;0688
+ddalfinalarabic;FB89
+dddhadeva;095C
+ddhabengali;09A2
+ddhadeva;0922
+ddhagujarati;0AA2
+ddhagurmukhi;0A22
+ddotaccent;1E0B
+ddotbelow;1E0D
+decimalseparatorarabic;066B
+decimalseparatorpersian;066B
+decyrillic;0434
+degree;00B0
+dehihebrew;05AD
+dehiragana;3067
+deicoptic;03EF
+dekatakana;30C7
+deleteleft;232B
+deleteright;2326
+delta;03B4
+deltaturned;018D
+denominatorminusonenumeratorbengali;09F8
+dezh;02A4
+dhabengali;09A7
+dhadeva;0927
+dhagujarati;0AA7
+dhagurmukhi;0A27
+dhook;0257
+dialytikatonos;0385
+dialytikatonoscmb;0344
+diamond;2666
+diamondsuitwhite;2662
+dieresis;00A8
+dieresisacute;F6D7
+dieresisbelowcmb;0324
+dieresiscmb;0308
+dieresisgrave;F6D8
+dieresistonos;0385
+dihiragana;3062
+dikatakana;30C2
+dittomark;3003
+divide;00F7
+divides;2223
+divisionslash;2215
+djecyrillic;0452
+dkshade;2593
+dlinebelow;1E0F
+dlsquare;3397
+dmacron;0111
+dmonospace;FF44
+dnblock;2584
+dochadathai;0E0E
+dodekthai;0E14
+dohiragana;3069
+dokatakana;30C9
+dollar;0024
+dollarinferior;F6E3
+dollarmonospace;FF04
+dollaroldstyle;F724
+dollarsmall;FE69
+dollarsuperior;F6E4
+dong;20AB
+dorusquare;3326
+dotaccent;02D9
+dotaccentcmb;0307
+dotbelowcmb;0323
+dotbelowcomb;0323
+dotkatakana;30FB
+dotlessi;0131
+dotlessj;F6BE
+dotlessjstrokehook;0284
+dotmath;22C5
+dottedcircle;25CC
+doubleyodpatah;FB1F
+doubleyodpatahhebrew;FB1F
+downtackbelowcmb;031E
+downtackmod;02D5
+dparen;249F
+dsuperior;F6EB
+dtail;0256
+dtopbar;018C
+duhiragana;3065
+dukatakana;30C5
+dz;01F3
+dzaltone;02A3
+dzcaron;01C6
+dzcurl;02A5
+dzeabkhasiancyrillic;04E1
+dzecyrillic;0455
+dzhecyrillic;045F
+e;0065
+eacute;00E9
+earth;2641
+ebengali;098F
+ebopomofo;311C
+ebreve;0115
+ecandradeva;090D
+ecandragujarati;0A8D
+ecandravowelsigndeva;0945
+ecandravowelsigngujarati;0AC5
+ecaron;011B
+ecedillabreve;1E1D
+echarmenian;0565
+echyiwnarmenian;0587
+ecircle;24D4
+ecircumflex;00EA
+ecircumflexacute;1EBF
+ecircumflexbelow;1E19
+ecircumflexdotbelow;1EC7
+ecircumflexgrave;1EC1
+ecircumflexhookabove;1EC3
+ecircumflextilde;1EC5
+ecyrillic;0454
+edblgrave;0205
+edeva;090F
+edieresis;00EB
+edot;0117
+edotaccent;0117
+edotbelow;1EB9
+eegurmukhi;0A0F
+eematragurmukhi;0A47
+efcyrillic;0444
+egrave;00E8
+egujarati;0A8F
+eharmenian;0567
+ehbopomofo;311D
+ehiragana;3048
+ehookabove;1EBB
+eibopomofo;311F
+eight;0038
+eightarabic;0668
+eightbengali;09EE
+eightcircle;2467
+eightcircleinversesansserif;2791
+eightdeva;096E
+eighteencircle;2471
+eighteenparen;2485
+eighteenperiod;2499
+eightgujarati;0AEE
+eightgurmukhi;0A6E
+eighthackarabic;0668
+eighthangzhou;3028
+eighthnotebeamed;266B
+eightideographicparen;3227
+eightinferior;2088
+eightmonospace;FF18
+eightoldstyle;F738
+eightparen;247B
+eightperiod;248F
+eightpersian;06F8
+eightroman;2177
+eightsuperior;2078
+eightthai;0E58
+einvertedbreve;0207
+eiotifiedcyrillic;0465
+ekatakana;30A8
+ekatakanahalfwidth;FF74
+ekonkargurmukhi;0A74
+ekorean;3154
+elcyrillic;043B
+element;2208
+elevencircle;246A
+elevenparen;247E
+elevenperiod;2492
+elevenroman;217A
+ellipsis;2026
+ellipsisvertical;22EE
+emacron;0113
+emacronacute;1E17
+emacrongrave;1E15
+emcyrillic;043C
+emdash;2014
+emdashvertical;FE31
+emonospace;FF45
+emphasismarkarmenian;055B
+emptyset;2205
+enbopomofo;3123
+encyrillic;043D
+endash;2013
+endashvertical;FE32
+endescendercyrillic;04A3
+eng;014B
+engbopomofo;3125
+enghecyrillic;04A5
+enhookcyrillic;04C8
+enspace;2002
+eogonek;0119
+eokorean;3153
+eopen;025B
+eopenclosed;029A
+eopenreversed;025C
+eopenreversedclosed;025E
+eopenreversedhook;025D
+eparen;24A0
+epsilon;03B5
+epsilontonos;03AD
+equal;003D
+equalmonospace;FF1D
+equalsmall;FE66
+equalsuperior;207C
+equivalence;2261
+erbopomofo;3126
+ercyrillic;0440
+ereversed;0258
+ereversedcyrillic;044D
+escyrillic;0441
+esdescendercyrillic;04AB
+esh;0283
+eshcurl;0286
+eshortdeva;090E
+eshortvowelsigndeva;0946
+eshreversedloop;01AA
+eshsquatreversed;0285
+esmallhiragana;3047
+esmallkatakana;30A7
+esmallkatakanahalfwidth;FF6A
+estimated;212E
+esuperior;F6EC
+eta;03B7
+etarmenian;0568
+etatonos;03AE
+eth;00F0
+etilde;1EBD
+etildebelow;1E1B
+etnahtafoukhhebrew;0591
+etnahtafoukhlefthebrew;0591
+etnahtahebrew;0591
+etnahtalefthebrew;0591
+eturned;01DD
+eukorean;3161
+euro;20AC
+evowelsignbengali;09C7
+evowelsigndeva;0947
+evowelsigngujarati;0AC7
+exclam;0021
+exclamarmenian;055C
+exclamdbl;203C
+exclamdown;00A1
+exclamdownsmall;F7A1
+exclammonospace;FF01
+exclamsmall;F721
+existential;2203
+ezh;0292
+ezhcaron;01EF
+ezhcurl;0293
+ezhreversed;01B9
+ezhtail;01BA
+f;0066
+fadeva;095E
+fagurmukhi;0A5E
+fahrenheit;2109
+fathaarabic;064E
+fathalowarabic;064E
+fathatanarabic;064B
+fbopomofo;3108
+fcircle;24D5
+fdotaccent;1E1F
+feharabic;0641
+feharmenian;0586
+fehfinalarabic;FED2
+fehinitialarabic;FED3
+fehmedialarabic;FED4
+feicoptic;03E5
+female;2640
+ff;FB00
+ffi;FB03
+ffl;FB04
+fi;FB01
+fifteencircle;246E
+fifteenparen;2482
+fifteenperiod;2496
+figuredash;2012
+filledbox;25A0
+filledrect;25AC
+finalkaf;05DA
+finalkafdagesh;FB3A
+finalkafdageshhebrew;FB3A
+finalkafhebrew;05DA
+finalkafqamats;05DA 05B8
+finalkafqamatshebrew;05DA 05B8
+finalkafsheva;05DA 05B0
+finalkafshevahebrew;05DA 05B0
+finalmem;05DD
+finalmemhebrew;05DD
+finalnun;05DF
+finalnunhebrew;05DF
+finalpe;05E3
+finalpehebrew;05E3
+finaltsadi;05E5
+finaltsadihebrew;05E5
+firsttonechinese;02C9
+fisheye;25C9
+fitacyrillic;0473
+five;0035
+fivearabic;0665
+fivebengali;09EB
+fivecircle;2464
+fivecircleinversesansserif;278E
+fivedeva;096B
+fiveeighths;215D
+fivegujarati;0AEB
+fivegurmukhi;0A6B
+fivehackarabic;0665
+fivehangzhou;3025
+fiveideographicparen;3224
+fiveinferior;2085
+fivemonospace;FF15
+fiveoldstyle;F735
+fiveparen;2478
+fiveperiod;248C
+fivepersian;06F5
+fiveroman;2174
+fivesuperior;2075
+fivethai;0E55
+fl;FB02
+florin;0192
+fmonospace;FF46
+fmsquare;3399
+fofanthai;0E1F
+fofathai;0E1D
+fongmanthai;0E4F
+forall;2200
+four;0034
+fourarabic;0664
+fourbengali;09EA
+fourcircle;2463
+fourcircleinversesansserif;278D
+fourdeva;096A
+fourgujarati;0AEA
+fourgurmukhi;0A6A
+fourhackarabic;0664
+fourhangzhou;3024
+fourideographicparen;3223
+fourinferior;2084
+fourmonospace;FF14
+fournumeratorbengali;09F7
+fouroldstyle;F734
+fourparen;2477
+fourperiod;248B
+fourpersian;06F4
+fourroman;2173
+foursuperior;2074
+fourteencircle;246D
+fourteenparen;2481
+fourteenperiod;2495
+fourthai;0E54
+fourthtonechinese;02CB
+fparen;24A1
+fraction;2044
+franc;20A3
+g;0067
+gabengali;0997
+gacute;01F5
+gadeva;0917
+gafarabic;06AF
+gaffinalarabic;FB93
+gafinitialarabic;FB94
+gafmedialarabic;FB95
+gagujarati;0A97
+gagurmukhi;0A17
+gahiragana;304C
+gakatakana;30AC
+gamma;03B3
+gammalatinsmall;0263
+gammasuperior;02E0
+gangiacoptic;03EB
+gbopomofo;310D
+gbreve;011F
+gcaron;01E7
+gcedilla;0123
+gcircle;24D6
+gcircumflex;011D
+gcommaaccent;0123
+gdot;0121
+gdotaccent;0121
+gecyrillic;0433
+gehiragana;3052
+gekatakana;30B2
+geometricallyequal;2251
+gereshaccenthebrew;059C
+gereshhebrew;05F3
+gereshmuqdamhebrew;059D
+germandbls;00DF
+gershayimaccenthebrew;059E
+gershayimhebrew;05F4
+getamark;3013
+ghabengali;0998
+ghadarmenian;0572
+ghadeva;0918
+ghagujarati;0A98
+ghagurmukhi;0A18
+ghainarabic;063A
+ghainfinalarabic;FECE
+ghaininitialarabic;FECF
+ghainmedialarabic;FED0
+ghemiddlehookcyrillic;0495
+ghestrokecyrillic;0493
+gheupturncyrillic;0491
+ghhadeva;095A
+ghhagurmukhi;0A5A
+ghook;0260
+ghzsquare;3393
+gihiragana;304E
+gikatakana;30AE
+gimarmenian;0563
+gimel;05D2
+gimeldagesh;FB32
+gimeldageshhebrew;FB32
+gimelhebrew;05D2
+gjecyrillic;0453
+glottalinvertedstroke;01BE
+glottalstop;0294
+glottalstopinverted;0296
+glottalstopmod;02C0
+glottalstopreversed;0295
+glottalstopreversedmod;02C1
+glottalstopreversedsuperior;02E4
+glottalstopstroke;02A1
+glottalstopstrokereversed;02A2
+gmacron;1E21
+gmonospace;FF47
+gohiragana;3054
+gokatakana;30B4
+gparen;24A2
+gpasquare;33AC
+gradient;2207
+grave;0060
+gravebelowcmb;0316
+gravecmb;0300
+gravecomb;0300
+gravedeva;0953
+gravelowmod;02CE
+gravemonospace;FF40
+gravetonecmb;0340
+greater;003E
+greaterequal;2265
+greaterequalorless;22DB
+greatermonospace;FF1E
+greaterorequivalent;2273
+greaterorless;2277
+greateroverequal;2267
+greatersmall;FE65
+gscript;0261
+gstroke;01E5
+guhiragana;3050
+guillemotleft;00AB
+guillemotright;00BB
+guilsinglleft;2039
+guilsinglright;203A
+gukatakana;30B0
+guramusquare;3318
+gysquare;33C9
+h;0068
+haabkhasiancyrillic;04A9
+haaltonearabic;06C1
+habengali;09B9
+hadescendercyrillic;04B3
+hadeva;0939
+hagujarati;0AB9
+hagurmukhi;0A39
+haharabic;062D
+hahfinalarabic;FEA2
+hahinitialarabic;FEA3
+hahiragana;306F
+hahmedialarabic;FEA4
+haitusquare;332A
+hakatakana;30CF
+hakatakanahalfwidth;FF8A
+halantgurmukhi;0A4D
+hamzaarabic;0621
+hamzadammaarabic;0621 064F
+hamzadammatanarabic;0621 064C
+hamzafathaarabic;0621 064E
+hamzafathatanarabic;0621 064B
+hamzalowarabic;0621
+hamzalowkasraarabic;0621 0650
+hamzalowkasratanarabic;0621 064D
+hamzasukunarabic;0621 0652
+hangulfiller;3164
+hardsigncyrillic;044A
+harpoonleftbarbup;21BC
+harpoonrightbarbup;21C0
+hasquare;33CA
+hatafpatah;05B2
+hatafpatah16;05B2
+hatafpatah23;05B2
+hatafpatah2f;05B2
+hatafpatahhebrew;05B2
+hatafpatahnarrowhebrew;05B2
+hatafpatahquarterhebrew;05B2
+hatafpatahwidehebrew;05B2
+hatafqamats;05B3
+hatafqamats1b;05B3
+hatafqamats28;05B3
+hatafqamats34;05B3
+hatafqamatshebrew;05B3
+hatafqamatsnarrowhebrew;05B3
+hatafqamatsquarterhebrew;05B3
+hatafqamatswidehebrew;05B3
+hatafsegol;05B1
+hatafsegol17;05B1
+hatafsegol24;05B1
+hatafsegol30;05B1
+hatafsegolhebrew;05B1
+hatafsegolnarrowhebrew;05B1
+hatafsegolquarterhebrew;05B1
+hatafsegolwidehebrew;05B1
+hbar;0127
+hbopomofo;310F
+hbrevebelow;1E2B
+hcedilla;1E29
+hcircle;24D7
+hcircumflex;0125
+hdieresis;1E27
+hdotaccent;1E23
+hdotbelow;1E25
+he;05D4
+heart;2665
+heartsuitblack;2665
+heartsuitwhite;2661
+hedagesh;FB34
+hedageshhebrew;FB34
+hehaltonearabic;06C1
+heharabic;0647
+hehebrew;05D4
+hehfinalaltonearabic;FBA7
+hehfinalalttwoarabic;FEEA
+hehfinalarabic;FEEA
+hehhamzaabovefinalarabic;FBA5
+hehhamzaaboveisolatedarabic;FBA4
+hehinitialaltonearabic;FBA8
+hehinitialarabic;FEEB
+hehiragana;3078
+hehmedialaltonearabic;FBA9
+hehmedialarabic;FEEC
+heiseierasquare;337B
+hekatakana;30D8
+hekatakanahalfwidth;FF8D
+hekutaarusquare;3336
+henghook;0267
+herutusquare;3339
+het;05D7
+hethebrew;05D7
+hhook;0266
+hhooksuperior;02B1
+hieuhacirclekorean;327B
+hieuhaparenkorean;321B
+hieuhcirclekorean;326D
+hieuhkorean;314E
+hieuhparenkorean;320D
+hihiragana;3072
+hikatakana;30D2
+hikatakanahalfwidth;FF8B
+hiriq;05B4
+hiriq14;05B4
+hiriq21;05B4
+hiriq2d;05B4
+hiriqhebrew;05B4
+hiriqnarrowhebrew;05B4
+hiriqquarterhebrew;05B4
+hiriqwidehebrew;05B4
+hlinebelow;1E96
+hmonospace;FF48
+hoarmenian;0570
+hohipthai;0E2B
+hohiragana;307B
+hokatakana;30DB
+hokatakanahalfwidth;FF8E
+holam;05B9
+holam19;05B9
+holam26;05B9
+holam32;05B9
+holamhebrew;05B9
+holamnarrowhebrew;05B9
+holamquarterhebrew;05B9
+holamwidehebrew;05B9
+honokhukthai;0E2E
+hookabovecomb;0309
+hookcmb;0309
+hookpalatalizedbelowcmb;0321
+hookretroflexbelowcmb;0322
+hoonsquare;3342
+horicoptic;03E9
+horizontalbar;2015
+horncmb;031B
+hotsprings;2668
+house;2302
+hparen;24A3
+hsuperior;02B0
+hturned;0265
+huhiragana;3075
+huiitosquare;3333
+hukatakana;30D5
+hukatakanahalfwidth;FF8C
+hungarumlaut;02DD
+hungarumlautcmb;030B
+hv;0195
+hyphen;002D
+hypheninferior;F6E5
+hyphenmonospace;FF0D
+hyphensmall;FE63
+hyphensuperior;F6E6
+hyphentwo;2010
+i;0069
+iacute;00ED
+iacyrillic;044F
+ibengali;0987
+ibopomofo;3127
+ibreve;012D
+icaron;01D0
+icircle;24D8
+icircumflex;00EE
+icyrillic;0456
+idblgrave;0209
+ideographearthcircle;328F
+ideographfirecircle;328B
+ideographicallianceparen;323F
+ideographiccallparen;323A
+ideographiccentrecircle;32A5
+ideographicclose;3006
+ideographiccomma;3001
+ideographiccommaleft;FF64
+ideographiccongratulationparen;3237
+ideographiccorrectcircle;32A3
+ideographicearthparen;322F
+ideographicenterpriseparen;323D
+ideographicexcellentcircle;329D
+ideographicfestivalparen;3240
+ideographicfinancialcircle;3296
+ideographicfinancialparen;3236
+ideographicfireparen;322B
+ideographichaveparen;3232
+ideographichighcircle;32A4
+ideographiciterationmark;3005
+ideographiclaborcircle;3298
+ideographiclaborparen;3238
+ideographicleftcircle;32A7
+ideographiclowcircle;32A6
+ideographicmedicinecircle;32A9
+ideographicmetalparen;322E
+ideographicmoonparen;322A
+ideographicnameparen;3234
+ideographicperiod;3002
+ideographicprintcircle;329E
+ideographicreachparen;3243
+ideographicrepresentparen;3239
+ideographicresourceparen;323E
+ideographicrightcircle;32A8
+ideographicsecretcircle;3299
+ideographicselfparen;3242
+ideographicsocietyparen;3233
+ideographicspace;3000
+ideographicspecialparen;3235
+ideographicstockparen;3231
+ideographicstudyparen;323B
+ideographicsunparen;3230
+ideographicsuperviseparen;323C
+ideographicwaterparen;322C
+ideographicwoodparen;322D
+ideographiczero;3007
+ideographmetalcircle;328E
+ideographmooncircle;328A
+ideographnamecircle;3294
+ideographsuncircle;3290
+ideographwatercircle;328C
+ideographwoodcircle;328D
+ideva;0907
+idieresis;00EF
+idieresisacute;1E2F
+idieresiscyrillic;04E5
+idotbelow;1ECB
+iebrevecyrillic;04D7
+iecyrillic;0435
+ieungacirclekorean;3275
+ieungaparenkorean;3215
+ieungcirclekorean;3267
+ieungkorean;3147
+ieungparenkorean;3207
+igrave;00EC
+igujarati;0A87
+igurmukhi;0A07
+ihiragana;3044
+ihookabove;1EC9
+iibengali;0988
+iicyrillic;0438
+iideva;0908
+iigujarati;0A88
+iigurmukhi;0A08
+iimatragurmukhi;0A40
+iinvertedbreve;020B
+iishortcyrillic;0439
+iivowelsignbengali;09C0
+iivowelsigndeva;0940
+iivowelsigngujarati;0AC0
+ij;0133
+ikatakana;30A4
+ikatakanahalfwidth;FF72
+ikorean;3163
+ilde;02DC
+iluyhebrew;05AC
+imacron;012B
+imacroncyrillic;04E3
+imageorapproximatelyequal;2253
+imatragurmukhi;0A3F
+imonospace;FF49
+increment;2206
+infinity;221E
+iniarmenian;056B
+integral;222B
+integralbottom;2321
+integralbt;2321
+integralex;F8F5
+integraltop;2320
+integraltp;2320
+intersection;2229
+intisquare;3305
+invbullet;25D8
+invcircle;25D9
+invsmileface;263B
+iocyrillic;0451
+iogonek;012F
+iota;03B9
+iotadieresis;03CA
+iotadieresistonos;0390
+iotalatin;0269
+iotatonos;03AF
+iparen;24A4
+irigurmukhi;0A72
+ismallhiragana;3043
+ismallkatakana;30A3
+ismallkatakanahalfwidth;FF68
+issharbengali;09FA
+istroke;0268
+isuperior;F6ED
+iterationhiragana;309D
+iterationkatakana;30FD
+itilde;0129
+itildebelow;1E2D
+iubopomofo;3129
+iucyrillic;044E
+ivowelsignbengali;09BF
+ivowelsigndeva;093F
+ivowelsigngujarati;0ABF
+izhitsacyrillic;0475
+izhitsadblgravecyrillic;0477
+j;006A
+jaarmenian;0571
+jabengali;099C
+jadeva;091C
+jagujarati;0A9C
+jagurmukhi;0A1C
+jbopomofo;3110
+jcaron;01F0
+jcircle;24D9
+jcircumflex;0135
+jcrossedtail;029D
+jdotlessstroke;025F
+jecyrillic;0458
+jeemarabic;062C
+jeemfinalarabic;FE9E
+jeeminitialarabic;FE9F
+jeemmedialarabic;FEA0
+jeharabic;0698
+jehfinalarabic;FB8B
+jhabengali;099D
+jhadeva;091D
+jhagujarati;0A9D
+jhagurmukhi;0A1D
+jheharmenian;057B
+jis;3004
+jmonospace;FF4A
+jparen;24A5
+jsuperior;02B2
+k;006B
+kabashkircyrillic;04A1
+kabengali;0995
+kacute;1E31
+kacyrillic;043A
+kadescendercyrillic;049B
+kadeva;0915
+kaf;05DB
+kafarabic;0643
+kafdagesh;FB3B
+kafdageshhebrew;FB3B
+kaffinalarabic;FEDA
+kafhebrew;05DB
+kafinitialarabic;FEDB
+kafmedialarabic;FEDC
+kafrafehebrew;FB4D
+kagujarati;0A95
+kagurmukhi;0A15
+kahiragana;304B
+kahookcyrillic;04C4
+kakatakana;30AB
+kakatakanahalfwidth;FF76
+kappa;03BA
+kappasymbolgreek;03F0
+kapyeounmieumkorean;3171
+kapyeounphieuphkorean;3184
+kapyeounpieupkorean;3178
+kapyeounssangpieupkorean;3179
+karoriisquare;330D
+kashidaautoarabic;0640
+kashidaautonosidebearingarabic;0640
+kasmallkatakana;30F5
+kasquare;3384
+kasraarabic;0650
+kasratanarabic;064D
+kastrokecyrillic;049F
+katahiraprolongmarkhalfwidth;FF70
+kaverticalstrokecyrillic;049D
+kbopomofo;310E
+kcalsquare;3389
+kcaron;01E9
+kcedilla;0137
+kcircle;24DA
+kcommaaccent;0137
+kdotbelow;1E33
+keharmenian;0584
+kehiragana;3051
+kekatakana;30B1
+kekatakanahalfwidth;FF79
+kenarmenian;056F
+kesmallkatakana;30F6
+kgreenlandic;0138
+khabengali;0996
+khacyrillic;0445
+khadeva;0916
+khagujarati;0A96
+khagurmukhi;0A16
+khaharabic;062E
+khahfinalarabic;FEA6
+khahinitialarabic;FEA7
+khahmedialarabic;FEA8
+kheicoptic;03E7
+khhadeva;0959
+khhagurmukhi;0A59
+khieukhacirclekorean;3278
+khieukhaparenkorean;3218
+khieukhcirclekorean;326A
+khieukhkorean;314B
+khieukhparenkorean;320A
+khokhaithai;0E02
+khokhonthai;0E05
+khokhuatthai;0E03
+khokhwaithai;0E04
+khomutthai;0E5B
+khook;0199
+khorakhangthai;0E06
+khzsquare;3391
+kihiragana;304D
+kikatakana;30AD
+kikatakanahalfwidth;FF77
+kiroguramusquare;3315
+kiromeetorusquare;3316
+kirosquare;3314
+kiyeokacirclekorean;326E
+kiyeokaparenkorean;320E
+kiyeokcirclekorean;3260
+kiyeokkorean;3131
+kiyeokparenkorean;3200
+kiyeoksioskorean;3133
+kjecyrillic;045C
+klinebelow;1E35
+klsquare;3398
+kmcubedsquare;33A6
+kmonospace;FF4B
+kmsquaredsquare;33A2
+kohiragana;3053
+kohmsquare;33C0
+kokaithai;0E01
+kokatakana;30B3
+kokatakanahalfwidth;FF7A
+kooposquare;331E
+koppacyrillic;0481
+koreanstandardsymbol;327F
+koroniscmb;0343
+kparen;24A6
+kpasquare;33AA
+ksicyrillic;046F
+ktsquare;33CF
+kturned;029E
+kuhiragana;304F
+kukatakana;30AF
+kukatakanahalfwidth;FF78
+kvsquare;33B8
+kwsquare;33BE
+l;006C
+labengali;09B2
+lacute;013A
+ladeva;0932
+lagujarati;0AB2
+lagurmukhi;0A32
+lakkhangyaothai;0E45
+lamaleffinalarabic;FEFC
+lamalefhamzaabovefinalarabic;FEF8
+lamalefhamzaaboveisolatedarabic;FEF7
+lamalefhamzabelowfinalarabic;FEFA
+lamalefhamzabelowisolatedarabic;FEF9
+lamalefisolatedarabic;FEFB
+lamalefmaddaabovefinalarabic;FEF6
+lamalefmaddaaboveisolatedarabic;FEF5
+lamarabic;0644
+lambda;03BB
+lambdastroke;019B
+lamed;05DC
+lameddagesh;FB3C
+lameddageshhebrew;FB3C
+lamedhebrew;05DC
+lamedholam;05DC 05B9
+lamedholamdagesh;05DC 05B9 05BC
+lamedholamdageshhebrew;05DC 05B9 05BC
+lamedholamhebrew;05DC 05B9
+lamfinalarabic;FEDE
+lamhahinitialarabic;FCCA
+laminitialarabic;FEDF
+lamjeeminitialarabic;FCC9
+lamkhahinitialarabic;FCCB
+lamlamhehisolatedarabic;FDF2
+lammedialarabic;FEE0
+lammeemhahinitialarabic;FD88
+lammeeminitialarabic;FCCC
+lammeemjeeminitialarabic;FEDF FEE4 FEA0
+lammeemkhahinitialarabic;FEDF FEE4 FEA8
+largecircle;25EF
+lbar;019A
+lbelt;026C
+lbopomofo;310C
+lcaron;013E
+lcedilla;013C
+lcircle;24DB
+lcircumflexbelow;1E3D
+lcommaaccent;013C
+ldot;0140
+ldotaccent;0140
+ldotbelow;1E37
+ldotbelowmacron;1E39
+leftangleabovecmb;031A
+lefttackbelowcmb;0318
+less;003C
+lessequal;2264
+lessequalorgreater;22DA
+lessmonospace;FF1C
+lessorequivalent;2272
+lessorgreater;2276
+lessoverequal;2266
+lesssmall;FE64
+lezh;026E
+lfblock;258C
+lhookretroflex;026D
+lira;20A4
+liwnarmenian;056C
+lj;01C9
+ljecyrillic;0459
+ll;F6C0
+lladeva;0933
+llagujarati;0AB3
+llinebelow;1E3B
+llladeva;0934
+llvocalicbengali;09E1
+llvocalicdeva;0961
+llvocalicvowelsignbengali;09E3
+llvocalicvowelsigndeva;0963
+lmiddletilde;026B
+lmonospace;FF4C
+lmsquare;33D0
+lochulathai;0E2C
+logicaland;2227
+logicalnot;00AC
+logicalnotreversed;2310
+logicalor;2228
+lolingthai;0E25
+longs;017F
+lowlinecenterline;FE4E
+lowlinecmb;0332
+lowlinedashed;FE4D
+lozenge;25CA
+lparen;24A7
+lslash;0142
+lsquare;2113
+lsuperior;F6EE
+ltshade;2591
+luthai;0E26
+lvocalicbengali;098C
+lvocalicdeva;090C
+lvocalicvowelsignbengali;09E2
+lvocalicvowelsigndeva;0962
+lxsquare;33D3
+m;006D
+mabengali;09AE
+macron;00AF
+macronbelowcmb;0331
+macroncmb;0304
+macronlowmod;02CD
+macronmonospace;FFE3
+macute;1E3F
+madeva;092E
+magujarati;0AAE
+magurmukhi;0A2E
+mahapakhhebrew;05A4
+mahapakhlefthebrew;05A4
+mahiragana;307E
+maichattawalowleftthai;F895
+maichattawalowrightthai;F894
+maichattawathai;0E4B
+maichattawaupperleftthai;F893
+maieklowleftthai;F88C
+maieklowrightthai;F88B
+maiekthai;0E48
+maiekupperleftthai;F88A
+maihanakatleftthai;F884
+maihanakatthai;0E31
+maitaikhuleftthai;F889
+maitaikhuthai;0E47
+maitholowleftthai;F88F
+maitholowrightthai;F88E
+maithothai;0E49
+maithoupperleftthai;F88D
+maitrilowleftthai;F892
+maitrilowrightthai;F891
+maitrithai;0E4A
+maitriupperleftthai;F890
+maiyamokthai;0E46
+makatakana;30DE
+makatakanahalfwidth;FF8F
+male;2642
+mansyonsquare;3347
+maqafhebrew;05BE
+mars;2642
+masoracirclehebrew;05AF
+masquare;3383
+mbopomofo;3107
+mbsquare;33D4
+mcircle;24DC
+mcubedsquare;33A5
+mdotaccent;1E41
+mdotbelow;1E43
+meemarabic;0645
+meemfinalarabic;FEE2
+meeminitialarabic;FEE3
+meemmedialarabic;FEE4
+meemmeeminitialarabic;FCD1
+meemmeemisolatedarabic;FC48
+meetorusquare;334D
+mehiragana;3081
+meizierasquare;337E
+mekatakana;30E1
+mekatakanahalfwidth;FF92
+mem;05DE
+memdagesh;FB3E
+memdageshhebrew;FB3E
+memhebrew;05DE
+menarmenian;0574
+merkhahebrew;05A5
+merkhakefulahebrew;05A6
+merkhakefulalefthebrew;05A6
+merkhalefthebrew;05A5
+mhook;0271
+mhzsquare;3392
+middledotkatakanahalfwidth;FF65
+middot;00B7
+mieumacirclekorean;3272
+mieumaparenkorean;3212
+mieumcirclekorean;3264
+mieumkorean;3141
+mieumpansioskorean;3170
+mieumparenkorean;3204
+mieumpieupkorean;316E
+mieumsioskorean;316F
+mihiragana;307F
+mikatakana;30DF
+mikatakanahalfwidth;FF90
+minus;2212
+minusbelowcmb;0320
+minuscircle;2296
+minusmod;02D7
+minusplus;2213
+minute;2032
+miribaarusquare;334A
+mirisquare;3349
+mlonglegturned;0270
+mlsquare;3396
+mmcubedsquare;33A3
+mmonospace;FF4D
+mmsquaredsquare;339F
+mohiragana;3082
+mohmsquare;33C1
+mokatakana;30E2
+mokatakanahalfwidth;FF93
+molsquare;33D6
+momathai;0E21
+moverssquare;33A7
+moverssquaredsquare;33A8
+mparen;24A8
+mpasquare;33AB
+mssquare;33B3
+msuperior;F6EF
+mturned;026F
+mu;00B5
+mu1;00B5
+muasquare;3382
+muchgreater;226B
+muchless;226A
+mufsquare;338C
+mugreek;03BC
+mugsquare;338D
+muhiragana;3080
+mukatakana;30E0
+mukatakanahalfwidth;FF91
+mulsquare;3395
+multiply;00D7
+mumsquare;339B
+munahhebrew;05A3
+munahlefthebrew;05A3
+musicalnote;266A
+musicalnotedbl;266B
+musicflatsign;266D
+musicsharpsign;266F
+mussquare;33B2
+muvsquare;33B6
+muwsquare;33BC
+mvmegasquare;33B9
+mvsquare;33B7
+mwmegasquare;33BF
+mwsquare;33BD
+n;006E
+nabengali;09A8
+nabla;2207
+nacute;0144
+nadeva;0928
+nagujarati;0AA8
+nagurmukhi;0A28
+nahiragana;306A
+nakatakana;30CA
+nakatakanahalfwidth;FF85
+napostrophe;0149
+nasquare;3381
+nbopomofo;310B
+nbspace;00A0
+ncaron;0148
+ncedilla;0146
+ncircle;24DD
+ncircumflexbelow;1E4B
+ncommaaccent;0146
+ndotaccent;1E45
+ndotbelow;1E47
+nehiragana;306D
+nekatakana;30CD
+nekatakanahalfwidth;FF88
+newsheqelsign;20AA
+nfsquare;338B
+ngabengali;0999
+ngadeva;0919
+ngagujarati;0A99
+ngagurmukhi;0A19
+ngonguthai;0E07
+nhiragana;3093
+nhookleft;0272
+nhookretroflex;0273
+nieunacirclekorean;326F
+nieunaparenkorean;320F
+nieuncieuckorean;3135
+nieuncirclekorean;3261
+nieunhieuhkorean;3136
+nieunkorean;3134
+nieunpansioskorean;3168
+nieunparenkorean;3201
+nieunsioskorean;3167
+nieuntikeutkorean;3166
+nihiragana;306B
+nikatakana;30CB
+nikatakanahalfwidth;FF86
+nikhahitleftthai;F899
+nikhahitthai;0E4D
+nine;0039
+ninearabic;0669
+ninebengali;09EF
+ninecircle;2468
+ninecircleinversesansserif;2792
+ninedeva;096F
+ninegujarati;0AEF
+ninegurmukhi;0A6F
+ninehackarabic;0669
+ninehangzhou;3029
+nineideographicparen;3228
+nineinferior;2089
+ninemonospace;FF19
+nineoldstyle;F739
+nineparen;247C
+nineperiod;2490
+ninepersian;06F9
+nineroman;2178
+ninesuperior;2079
+nineteencircle;2472
+nineteenparen;2486
+nineteenperiod;249A
+ninethai;0E59
+nj;01CC
+njecyrillic;045A
+nkatakana;30F3
+nkatakanahalfwidth;FF9D
+nlegrightlong;019E
+nlinebelow;1E49
+nmonospace;FF4E
+nmsquare;339A
+nnabengali;09A3
+nnadeva;0923
+nnagujarati;0AA3
+nnagurmukhi;0A23
+nnnadeva;0929
+nohiragana;306E
+nokatakana;30CE
+nokatakanahalfwidth;FF89
+nonbreakingspace;00A0
+nonenthai;0E13
+nonuthai;0E19
+noonarabic;0646
+noonfinalarabic;FEE6
+noonghunnaarabic;06BA
+noonghunnafinalarabic;FB9F
+noonhehinitialarabic;FEE7 FEEC
+nooninitialarabic;FEE7
+noonjeeminitialarabic;FCD2
+noonjeemisolatedarabic;FC4B
+noonmedialarabic;FEE8
+noonmeeminitialarabic;FCD5
+noonmeemisolatedarabic;FC4E
+noonnoonfinalarabic;FC8D
+notcontains;220C
+notelement;2209
+notelementof;2209
+notequal;2260
+notgreater;226F
+notgreaternorequal;2271
+notgreaternorless;2279
+notidentical;2262
+notless;226E
+notlessnorequal;2270
+notparallel;2226
+notprecedes;2280
+notsubset;2284
+notsucceeds;2281
+notsuperset;2285
+nowarmenian;0576
+nparen;24A9
+nssquare;33B1
+nsuperior;207F
+ntilde;00F1
+nu;03BD
+nuhiragana;306C
+nukatakana;30CC
+nukatakanahalfwidth;FF87
+nuktabengali;09BC
+nuktadeva;093C
+nuktagujarati;0ABC
+nuktagurmukhi;0A3C
+numbersign;0023
+numbersignmonospace;FF03
+numbersignsmall;FE5F
+numeralsigngreek;0374
+numeralsignlowergreek;0375
+numero;2116
+nun;05E0
+nundagesh;FB40
+nundageshhebrew;FB40
+nunhebrew;05E0
+nvsquare;33B5
+nwsquare;33BB
+nyabengali;099E
+nyadeva;091E
+nyagujarati;0A9E
+nyagurmukhi;0A1E
+o;006F
+oacute;00F3
+oangthai;0E2D
+obarred;0275
+obarredcyrillic;04E9
+obarreddieresiscyrillic;04EB
+obengali;0993
+obopomofo;311B
+obreve;014F
+ocandradeva;0911
+ocandragujarati;0A91
+ocandravowelsigndeva;0949
+ocandravowelsigngujarati;0AC9
+ocaron;01D2
+ocircle;24DE
+ocircumflex;00F4
+ocircumflexacute;1ED1
+ocircumflexdotbelow;1ED9
+ocircumflexgrave;1ED3
+ocircumflexhookabove;1ED5
+ocircumflextilde;1ED7
+ocyrillic;043E
+odblacute;0151
+odblgrave;020D
+odeva;0913
+odieresis;00F6
+odieresiscyrillic;04E7
+odotbelow;1ECD
+oe;0153
+oekorean;315A
+ogonek;02DB
+ogonekcmb;0328
+ograve;00F2
+ogujarati;0A93
+oharmenian;0585
+ohiragana;304A
+ohookabove;1ECF
+ohorn;01A1
+ohornacute;1EDB
+ohorndotbelow;1EE3
+ohorngrave;1EDD
+ohornhookabove;1EDF
+ohorntilde;1EE1
+ohungarumlaut;0151
+oi;01A3
+oinvertedbreve;020F
+okatakana;30AA
+okatakanahalfwidth;FF75
+okorean;3157
+olehebrew;05AB
+omacron;014D
+omacronacute;1E53
+omacrongrave;1E51
+omdeva;0950
+omega;03C9
+omega1;03D6
+omegacyrillic;0461
+omegalatinclosed;0277
+omegaroundcyrillic;047B
+omegatitlocyrillic;047D
+omegatonos;03CE
+omgujarati;0AD0
+omicron;03BF
+omicrontonos;03CC
+omonospace;FF4F
+one;0031
+onearabic;0661
+onebengali;09E7
+onecircle;2460
+onecircleinversesansserif;278A
+onedeva;0967
+onedotenleader;2024
+oneeighth;215B
+onefitted;F6DC
+onegujarati;0AE7
+onegurmukhi;0A67
+onehackarabic;0661
+onehalf;00BD
+onehangzhou;3021
+oneideographicparen;3220
+oneinferior;2081
+onemonospace;FF11
+onenumeratorbengali;09F4
+oneoldstyle;F731
+oneparen;2474
+oneperiod;2488
+onepersian;06F1
+onequarter;00BC
+oneroman;2170
+onesuperior;00B9
+onethai;0E51
+onethird;2153
+oogonek;01EB
+oogonekmacron;01ED
+oogurmukhi;0A13
+oomatragurmukhi;0A4B
+oopen;0254
+oparen;24AA
+openbullet;25E6
+option;2325
+ordfeminine;00AA
+ordmasculine;00BA
+orthogonal;221F
+oshortdeva;0912
+oshortvowelsigndeva;094A
+oslash;00F8
+oslashacute;01FF
+osmallhiragana;3049
+osmallkatakana;30A9
+osmallkatakanahalfwidth;FF6B
+ostrokeacute;01FF
+osuperior;F6F0
+otcyrillic;047F
+otilde;00F5
+otildeacute;1E4D
+otildedieresis;1E4F
+oubopomofo;3121
+overline;203E
+overlinecenterline;FE4A
+overlinecmb;0305
+overlinedashed;FE49
+overlinedblwavy;FE4C
+overlinewavy;FE4B
+overscore;00AF
+ovowelsignbengali;09CB
+ovowelsigndeva;094B
+ovowelsigngujarati;0ACB
+p;0070
+paampssquare;3380
+paasentosquare;332B
+pabengali;09AA
+pacute;1E55
+padeva;092A
+pagedown;21DF
+pageup;21DE
+pagujarati;0AAA
+pagurmukhi;0A2A
+pahiragana;3071
+paiyannoithai;0E2F
+pakatakana;30D1
+palatalizationcyrilliccmb;0484
+palochkacyrillic;04C0
+pansioskorean;317F
+paragraph;00B6
+parallel;2225
+parenleft;0028
+parenleftaltonearabic;FD3E
+parenleftbt;F8ED
+parenleftex;F8EC
+parenleftinferior;208D
+parenleftmonospace;FF08
+parenleftsmall;FE59
+parenleftsuperior;207D
+parenlefttp;F8EB
+parenleftvertical;FE35
+parenright;0029
+parenrightaltonearabic;FD3F
+parenrightbt;F8F8
+parenrightex;F8F7
+parenrightinferior;208E
+parenrightmonospace;FF09
+parenrightsmall;FE5A
+parenrightsuperior;207E
+parenrighttp;F8F6
+parenrightvertical;FE36
+partialdiff;2202
+paseqhebrew;05C0
+pashtahebrew;0599
+pasquare;33A9
+patah;05B7
+patah11;05B7
+patah1d;05B7
+patah2a;05B7
+patahhebrew;05B7
+patahnarrowhebrew;05B7
+patahquarterhebrew;05B7
+patahwidehebrew;05B7
+pazerhebrew;05A1
+pbopomofo;3106
+pcircle;24DF
+pdotaccent;1E57
+pe;05E4
+pecyrillic;043F
+pedagesh;FB44
+pedageshhebrew;FB44
+peezisquare;333B
+pefinaldageshhebrew;FB43
+peharabic;067E
+peharmenian;057A
+pehebrew;05E4
+pehfinalarabic;FB57
+pehinitialarabic;FB58
+pehiragana;307A
+pehmedialarabic;FB59
+pekatakana;30DA
+pemiddlehookcyrillic;04A7
+perafehebrew;FB4E
+percent;0025
+percentarabic;066A
+percentmonospace;FF05
+percentsmall;FE6A
+period;002E
+periodarmenian;0589
+periodcentered;00B7
+periodhalfwidth;FF61
+periodinferior;F6E7
+periodmonospace;FF0E
+periodsmall;FE52
+periodsuperior;F6E8
+perispomenigreekcmb;0342
+perpendicular;22A5
+perthousand;2030
+peseta;20A7
+pfsquare;338A
+phabengali;09AB
+phadeva;092B
+phagujarati;0AAB
+phagurmukhi;0A2B
+phi;03C6
+phi1;03D5
+phieuphacirclekorean;327A
+phieuphaparenkorean;321A
+phieuphcirclekorean;326C
+phieuphkorean;314D
+phieuphparenkorean;320C
+philatin;0278
+phinthuthai;0E3A
+phisymbolgreek;03D5
+phook;01A5
+phophanthai;0E1E
+phophungthai;0E1C
+phosamphaothai;0E20
+pi;03C0
+pieupacirclekorean;3273
+pieupaparenkorean;3213
+pieupcieuckorean;3176
+pieupcirclekorean;3265
+pieupkiyeokkorean;3172
+pieupkorean;3142
+pieupparenkorean;3205
+pieupsioskiyeokkorean;3174
+pieupsioskorean;3144
+pieupsiostikeutkorean;3175
+pieupthieuthkorean;3177
+pieuptikeutkorean;3173
+pihiragana;3074
+pikatakana;30D4
+pisymbolgreek;03D6
+piwrarmenian;0583
+plus;002B
+plusbelowcmb;031F
+pluscircle;2295
+plusminus;00B1
+plusmod;02D6
+plusmonospace;FF0B
+plussmall;FE62
+plussuperior;207A
+pmonospace;FF50
+pmsquare;33D8
+pohiragana;307D
+pointingindexdownwhite;261F
+pointingindexleftwhite;261C
+pointingindexrightwhite;261E
+pointingindexupwhite;261D
+pokatakana;30DD
+poplathai;0E1B
+postalmark;3012
+postalmarkface;3020
+pparen;24AB
+precedes;227A
+prescription;211E
+primemod;02B9
+primereversed;2035
+product;220F
+projective;2305
+prolongedkana;30FC
+propellor;2318
+propersubset;2282
+propersuperset;2283
+proportion;2237
+proportional;221D
+psi;03C8
+psicyrillic;0471
+psilipneumatacyrilliccmb;0486
+pssquare;33B0
+puhiragana;3077
+pukatakana;30D7
+pvsquare;33B4
+pwsquare;33BA
+q;0071
+qadeva;0958
+qadmahebrew;05A8
+qafarabic;0642
+qaffinalarabic;FED6
+qafinitialarabic;FED7
+qafmedialarabic;FED8
+qamats;05B8
+qamats10;05B8
+qamats1a;05B8
+qamats1c;05B8
+qamats27;05B8
+qamats29;05B8
+qamats33;05B8
+qamatsde;05B8
+qamatshebrew;05B8
+qamatsnarrowhebrew;05B8
+qamatsqatanhebrew;05B8
+qamatsqatannarrowhebrew;05B8
+qamatsqatanquarterhebrew;05B8
+qamatsqatanwidehebrew;05B8
+qamatsquarterhebrew;05B8
+qamatswidehebrew;05B8
+qarneyparahebrew;059F
+qbopomofo;3111
+qcircle;24E0
+qhook;02A0
+qmonospace;FF51
+qof;05E7
+qofdagesh;FB47
+qofdageshhebrew;FB47
+qofhatafpatah;05E7 05B2
+qofhatafpatahhebrew;05E7 05B2
+qofhatafsegol;05E7 05B1
+qofhatafsegolhebrew;05E7 05B1
+qofhebrew;05E7
+qofhiriq;05E7 05B4
+qofhiriqhebrew;05E7 05B4
+qofholam;05E7 05B9
+qofholamhebrew;05E7 05B9
+qofpatah;05E7 05B7
+qofpatahhebrew;05E7 05B7
+qofqamats;05E7 05B8
+qofqamatshebrew;05E7 05B8
+qofqubuts;05E7 05BB
+qofqubutshebrew;05E7 05BB
+qofsegol;05E7 05B6
+qofsegolhebrew;05E7 05B6
+qofsheva;05E7 05B0
+qofshevahebrew;05E7 05B0
+qoftsere;05E7 05B5
+qoftserehebrew;05E7 05B5
+qparen;24AC
+quarternote;2669
+qubuts;05BB
+qubuts18;05BB
+qubuts25;05BB
+qubuts31;05BB
+qubutshebrew;05BB
+qubutsnarrowhebrew;05BB
+qubutsquarterhebrew;05BB
+qubutswidehebrew;05BB
+question;003F
+questionarabic;061F
+questionarmenian;055E
+questiondown;00BF
+questiondownsmall;F7BF
+questiongreek;037E
+questionmonospace;FF1F
+questionsmall;F73F
+quotedbl;0022
+quotedblbase;201E
+quotedblleft;201C
+quotedblmonospace;FF02
+quotedblprime;301E
+quotedblprimereversed;301D
+quotedblright;201D
+quoteleft;2018
+quoteleftreversed;201B
+quotereversed;201B
+quoteright;2019
+quoterightn;0149
+quotesinglbase;201A
+quotesingle;0027
+quotesinglemonospace;FF07
+r;0072
+raarmenian;057C
+rabengali;09B0
+racute;0155
+radeva;0930
+radical;221A
+radicalex;F8E5
+radoverssquare;33AE
+radoverssquaredsquare;33AF
+radsquare;33AD
+rafe;05BF
+rafehebrew;05BF
+ragujarati;0AB0
+ragurmukhi;0A30
+rahiragana;3089
+rakatakana;30E9
+rakatakanahalfwidth;FF97
+ralowerdiagonalbengali;09F1
+ramiddlediagonalbengali;09F0
+ramshorn;0264
+ratio;2236
+rbopomofo;3116
+rcaron;0159
+rcedilla;0157
+rcircle;24E1
+rcommaaccent;0157
+rdblgrave;0211
+rdotaccent;1E59
+rdotbelow;1E5B
+rdotbelowmacron;1E5D
+referencemark;203B
+reflexsubset;2286
+reflexsuperset;2287
+registered;00AE
+registersans;F8E8
+registerserif;F6DA
+reharabic;0631
+reharmenian;0580
+rehfinalarabic;FEAE
+rehiragana;308C
+rehyehaleflamarabic;0631 FEF3 FE8E 0644
+rekatakana;30EC
+rekatakanahalfwidth;FF9A
+resh;05E8
+reshdageshhebrew;FB48
+reshhatafpatah;05E8 05B2
+reshhatafpatahhebrew;05E8 05B2
+reshhatafsegol;05E8 05B1
+reshhatafsegolhebrew;05E8 05B1
+reshhebrew;05E8
+reshhiriq;05E8 05B4
+reshhiriqhebrew;05E8 05B4
+reshholam;05E8 05B9
+reshholamhebrew;05E8 05B9
+reshpatah;05E8 05B7
+reshpatahhebrew;05E8 05B7
+reshqamats;05E8 05B8
+reshqamatshebrew;05E8 05B8
+reshqubuts;05E8 05BB
+reshqubutshebrew;05E8 05BB
+reshsegol;05E8 05B6
+reshsegolhebrew;05E8 05B6
+reshsheva;05E8 05B0
+reshshevahebrew;05E8 05B0
+reshtsere;05E8 05B5
+reshtserehebrew;05E8 05B5
+reversedtilde;223D
+reviahebrew;0597
+reviamugrashhebrew;0597
+revlogicalnot;2310
+rfishhook;027E
+rfishhookreversed;027F
+rhabengali;09DD
+rhadeva;095D
+rho;03C1
+rhook;027D
+rhookturned;027B
+rhookturnedsuperior;02B5
+rhosymbolgreek;03F1
+rhotichookmod;02DE
+rieulacirclekorean;3271
+rieulaparenkorean;3211
+rieulcirclekorean;3263
+rieulhieuhkorean;3140
+rieulkiyeokkorean;313A
+rieulkiyeoksioskorean;3169
+rieulkorean;3139
+rieulmieumkorean;313B
+rieulpansioskorean;316C
+rieulparenkorean;3203
+rieulphieuphkorean;313F
+rieulpieupkorean;313C
+rieulpieupsioskorean;316B
+rieulsioskorean;313D
+rieulthieuthkorean;313E
+rieultikeutkorean;316A
+rieulyeorinhieuhkorean;316D
+rightangle;221F
+righttackbelowcmb;0319
+righttriangle;22BF
+rihiragana;308A
+rikatakana;30EA
+rikatakanahalfwidth;FF98
+ring;02DA
+ringbelowcmb;0325
+ringcmb;030A
+ringhalfleft;02BF
+ringhalfleftarmenian;0559
+ringhalfleftbelowcmb;031C
+ringhalfleftcentered;02D3
+ringhalfright;02BE
+ringhalfrightbelowcmb;0339
+ringhalfrightcentered;02D2
+rinvertedbreve;0213
+rittorusquare;3351
+rlinebelow;1E5F
+rlongleg;027C
+rlonglegturned;027A
+rmonospace;FF52
+rohiragana;308D
+rokatakana;30ED
+rokatakanahalfwidth;FF9B
+roruathai;0E23
+rparen;24AD
+rrabengali;09DC
+rradeva;0931
+rragurmukhi;0A5C
+rreharabic;0691
+rrehfinalarabic;FB8D
+rrvocalicbengali;09E0
+rrvocalicdeva;0960
+rrvocalicgujarati;0AE0
+rrvocalicvowelsignbengali;09C4
+rrvocalicvowelsigndeva;0944
+rrvocalicvowelsigngujarati;0AC4
+rsuperior;F6F1
+rtblock;2590
+rturned;0279
+rturnedsuperior;02B4
+ruhiragana;308B
+rukatakana;30EB
+rukatakanahalfwidth;FF99
+rupeemarkbengali;09F2
+rupeesignbengali;09F3
+rupiah;F6DD
+ruthai;0E24
+rvocalicbengali;098B
+rvocalicdeva;090B
+rvocalicgujarati;0A8B
+rvocalicvowelsignbengali;09C3
+rvocalicvowelsigndeva;0943
+rvocalicvowelsigngujarati;0AC3
+s;0073
+sabengali;09B8
+sacute;015B
+sacutedotaccent;1E65
+sadarabic;0635
+sadeva;0938
+sadfinalarabic;FEBA
+sadinitialarabic;FEBB
+sadmedialarabic;FEBC
+sagujarati;0AB8
+sagurmukhi;0A38
+sahiragana;3055
+sakatakana;30B5
+sakatakanahalfwidth;FF7B
+sallallahoualayhewasallamarabic;FDFA
+samekh;05E1
+samekhdagesh;FB41
+samekhdageshhebrew;FB41
+samekhhebrew;05E1
+saraaathai;0E32
+saraaethai;0E41
+saraaimaimalaithai;0E44
+saraaimaimuanthai;0E43
+saraamthai;0E33
+saraathai;0E30
+saraethai;0E40
+saraiileftthai;F886
+saraiithai;0E35
+saraileftthai;F885
+saraithai;0E34
+saraothai;0E42
+saraueeleftthai;F888
+saraueethai;0E37
+saraueleftthai;F887
+sarauethai;0E36
+sarauthai;0E38
+sarauuthai;0E39
+sbopomofo;3119
+scaron;0161
+scarondotaccent;1E67
+scedilla;015F
+schwa;0259
+schwacyrillic;04D9
+schwadieresiscyrillic;04DB
+schwahook;025A
+scircle;24E2
+scircumflex;015D
+scommaaccent;0219
+sdotaccent;1E61
+sdotbelow;1E63
+sdotbelowdotaccent;1E69
+seagullbelowcmb;033C
+second;2033
+secondtonechinese;02CA
+section;00A7
+seenarabic;0633
+seenfinalarabic;FEB2
+seeninitialarabic;FEB3
+seenmedialarabic;FEB4
+segol;05B6
+segol13;05B6
+segol1f;05B6
+segol2c;05B6
+segolhebrew;05B6
+segolnarrowhebrew;05B6
+segolquarterhebrew;05B6
+segoltahebrew;0592
+segolwidehebrew;05B6
+seharmenian;057D
+sehiragana;305B
+sekatakana;30BB
+sekatakanahalfwidth;FF7E
+semicolon;003B
+semicolonarabic;061B
+semicolonmonospace;FF1B
+semicolonsmall;FE54
+semivoicedmarkkana;309C
+semivoicedmarkkanahalfwidth;FF9F
+sentisquare;3322
+sentosquare;3323
+seven;0037
+sevenarabic;0667
+sevenbengali;09ED
+sevencircle;2466
+sevencircleinversesansserif;2790
+sevendeva;096D
+seveneighths;215E
+sevengujarati;0AED
+sevengurmukhi;0A6D
+sevenhackarabic;0667
+sevenhangzhou;3027
+sevenideographicparen;3226
+seveninferior;2087
+sevenmonospace;FF17
+sevenoldstyle;F737
+sevenparen;247A
+sevenperiod;248E
+sevenpersian;06F7
+sevenroman;2176
+sevensuperior;2077
+seventeencircle;2470
+seventeenparen;2484
+seventeenperiod;2498
+seventhai;0E57
+sfthyphen;00AD
+shaarmenian;0577
+shabengali;09B6
+shacyrillic;0448
+shaddaarabic;0651
+shaddadammaarabic;FC61
+shaddadammatanarabic;FC5E
+shaddafathaarabic;FC60
+shaddafathatanarabic;0651 064B
+shaddakasraarabic;FC62
+shaddakasratanarabic;FC5F
+shade;2592
+shadedark;2593
+shadelight;2591
+shademedium;2592
+shadeva;0936
+shagujarati;0AB6
+shagurmukhi;0A36
+shalshelethebrew;0593
+shbopomofo;3115
+shchacyrillic;0449
+sheenarabic;0634
+sheenfinalarabic;FEB6
+sheeninitialarabic;FEB7
+sheenmedialarabic;FEB8
+sheicoptic;03E3
+sheqel;20AA
+sheqelhebrew;20AA
+sheva;05B0
+sheva115;05B0
+sheva15;05B0
+sheva22;05B0
+sheva2e;05B0
+shevahebrew;05B0
+shevanarrowhebrew;05B0
+shevaquarterhebrew;05B0
+shevawidehebrew;05B0
+shhacyrillic;04BB
+shimacoptic;03ED
+shin;05E9
+shindagesh;FB49
+shindageshhebrew;FB49
+shindageshshindot;FB2C
+shindageshshindothebrew;FB2C
+shindageshsindot;FB2D
+shindageshsindothebrew;FB2D
+shindothebrew;05C1
+shinhebrew;05E9
+shinshindot;FB2A
+shinshindothebrew;FB2A
+shinsindot;FB2B
+shinsindothebrew;FB2B
+shook;0282
+sigma;03C3
+sigma1;03C2
+sigmafinal;03C2
+sigmalunatesymbolgreek;03F2
+sihiragana;3057
+sikatakana;30B7
+sikatakanahalfwidth;FF7C
+siluqhebrew;05BD
+siluqlefthebrew;05BD
+similar;223C
+sindothebrew;05C2
+siosacirclekorean;3274
+siosaparenkorean;3214
+sioscieuckorean;317E
+sioscirclekorean;3266
+sioskiyeokkorean;317A
+sioskorean;3145
+siosnieunkorean;317B
+siosparenkorean;3206
+siospieupkorean;317D
+siostikeutkorean;317C
+six;0036
+sixarabic;0666
+sixbengali;09EC
+sixcircle;2465
+sixcircleinversesansserif;278F
+sixdeva;096C
+sixgujarati;0AEC
+sixgurmukhi;0A6C
+sixhackarabic;0666
+sixhangzhou;3026
+sixideographicparen;3225
+sixinferior;2086
+sixmonospace;FF16
+sixoldstyle;F736
+sixparen;2479
+sixperiod;248D
+sixpersian;06F6
+sixroman;2175
+sixsuperior;2076
+sixteencircle;246F
+sixteencurrencydenominatorbengali;09F9
+sixteenparen;2483
+sixteenperiod;2497
+sixthai;0E56
+slash;002F
+slashmonospace;FF0F
+slong;017F
+slongdotaccent;1E9B
+smileface;263A
+smonospace;FF53
+sofpasuqhebrew;05C3
+softhyphen;00AD
+softsigncyrillic;044C
+sohiragana;305D
+sokatakana;30BD
+sokatakanahalfwidth;FF7F
+soliduslongoverlaycmb;0338
+solidusshortoverlaycmb;0337
+sorusithai;0E29
+sosalathai;0E28
+sosothai;0E0B
+sosuathai;0E2A
+space;0020
+spacehackarabic;0020
+spade;2660
+spadesuitblack;2660
+spadesuitwhite;2664
+sparen;24AE
+squarebelowcmb;033B
+squarecc;33C4
+squarecm;339D
+squarediagonalcrosshatchfill;25A9
+squarehorizontalfill;25A4
+squarekg;338F
+squarekm;339E
+squarekmcapital;33CE
+squareln;33D1
+squarelog;33D2
+squaremg;338E
+squaremil;33D5
+squaremm;339C
+squaremsquared;33A1
+squareorthogonalcrosshatchfill;25A6
+squareupperlefttolowerrightfill;25A7
+squareupperrighttolowerleftfill;25A8
+squareverticalfill;25A5
+squarewhitewithsmallblack;25A3
+srsquare;33DB
+ssabengali;09B7
+ssadeva;0937
+ssagujarati;0AB7
+ssangcieuckorean;3149
+ssanghieuhkorean;3185
+ssangieungkorean;3180
+ssangkiyeokkorean;3132
+ssangnieunkorean;3165
+ssangpieupkorean;3143
+ssangsioskorean;3146
+ssangtikeutkorean;3138
+ssuperior;F6F2
+sterling;00A3
+sterlingmonospace;FFE1
+strokelongoverlaycmb;0336
+strokeshortoverlaycmb;0335
+subset;2282
+subsetnotequal;228A
+subsetorequal;2286
+succeeds;227B
+suchthat;220B
+suhiragana;3059
+sukatakana;30B9
+sukatakanahalfwidth;FF7D
+sukunarabic;0652
+summation;2211
+sun;263C
+superset;2283
+supersetnotequal;228B
+supersetorequal;2287
+svsquare;33DC
+syouwaerasquare;337C
+t;0074
+tabengali;09A4
+tackdown;22A4
+tackleft;22A3
+tadeva;0924
+tagujarati;0AA4
+tagurmukhi;0A24
+taharabic;0637
+tahfinalarabic;FEC2
+tahinitialarabic;FEC3
+tahiragana;305F
+tahmedialarabic;FEC4
+taisyouerasquare;337D
+takatakana;30BF
+takatakanahalfwidth;FF80
+tatweelarabic;0640
+tau;03C4
+tav;05EA
+tavdages;FB4A
+tavdagesh;FB4A
+tavdageshhebrew;FB4A
+tavhebrew;05EA
+tbar;0167
+tbopomofo;310A
+tcaron;0165
+tccurl;02A8
+tcedilla;0163
+tcheharabic;0686
+tchehfinalarabic;FB7B
+tchehinitialarabic;FB7C
+tchehmedialarabic;FB7D
+tchehmeeminitialarabic;FB7C FEE4
+tcircle;24E3
+tcircumflexbelow;1E71
+tcommaaccent;0163
+tdieresis;1E97
+tdotaccent;1E6B
+tdotbelow;1E6D
+tecyrillic;0442
+tedescendercyrillic;04AD
+teharabic;062A
+tehfinalarabic;FE96
+tehhahinitialarabic;FCA2
+tehhahisolatedarabic;FC0C
+tehinitialarabic;FE97
+tehiragana;3066
+tehjeeminitialarabic;FCA1
+tehjeemisolatedarabic;FC0B
+tehmarbutaarabic;0629
+tehmarbutafinalarabic;FE94
+tehmedialarabic;FE98
+tehmeeminitialarabic;FCA4
+tehmeemisolatedarabic;FC0E
+tehnoonfinalarabic;FC73
+tekatakana;30C6
+tekatakanahalfwidth;FF83
+telephone;2121
+telephoneblack;260E
+telishagedolahebrew;05A0
+telishaqetanahebrew;05A9
+tencircle;2469
+tenideographicparen;3229
+tenparen;247D
+tenperiod;2491
+tenroman;2179
+tesh;02A7
+tet;05D8
+tetdagesh;FB38
+tetdageshhebrew;FB38
+tethebrew;05D8
+tetsecyrillic;04B5
+tevirhebrew;059B
+tevirlefthebrew;059B
+thabengali;09A5
+thadeva;0925
+thagujarati;0AA5
+thagurmukhi;0A25
+thalarabic;0630
+thalfinalarabic;FEAC
+thanthakhatlowleftthai;F898
+thanthakhatlowrightthai;F897
+thanthakhatthai;0E4C
+thanthakhatupperleftthai;F896
+theharabic;062B
+thehfinalarabic;FE9A
+thehinitialarabic;FE9B
+thehmedialarabic;FE9C
+thereexists;2203
+therefore;2234
+theta;03B8
+theta1;03D1
+thetasymbolgreek;03D1
+thieuthacirclekorean;3279
+thieuthaparenkorean;3219
+thieuthcirclekorean;326B
+thieuthkorean;314C
+thieuthparenkorean;320B
+thirteencircle;246C
+thirteenparen;2480
+thirteenperiod;2494
+thonangmonthothai;0E11
+thook;01AD
+thophuthaothai;0E12
+thorn;00FE
+thothahanthai;0E17
+thothanthai;0E10
+thothongthai;0E18
+thothungthai;0E16
+thousandcyrillic;0482
+thousandsseparatorarabic;066C
+thousandsseparatorpersian;066C
+three;0033
+threearabic;0663
+threebengali;09E9
+threecircle;2462
+threecircleinversesansserif;278C
+threedeva;0969
+threeeighths;215C
+threegujarati;0AE9
+threegurmukhi;0A69
+threehackarabic;0663
+threehangzhou;3023
+threeideographicparen;3222
+threeinferior;2083
+threemonospace;FF13
+threenumeratorbengali;09F6
+threeoldstyle;F733
+threeparen;2476
+threeperiod;248A
+threepersian;06F3
+threequarters;00BE
+threequartersemdash;F6DE
+threeroman;2172
+threesuperior;00B3
+threethai;0E53
+thzsquare;3394
+tihiragana;3061
+tikatakana;30C1
+tikatakanahalfwidth;FF81
+tikeutacirclekorean;3270
+tikeutaparenkorean;3210
+tikeutcirclekorean;3262
+tikeutkorean;3137
+tikeutparenkorean;3202
+tilde;02DC
+tildebelowcmb;0330
+tildecmb;0303
+tildecomb;0303
+tildedoublecmb;0360
+tildeoperator;223C
+tildeoverlaycmb;0334
+tildeverticalcmb;033E
+timescircle;2297
+tipehahebrew;0596
+tipehalefthebrew;0596
+tippigurmukhi;0A70
+titlocyrilliccmb;0483
+tiwnarmenian;057F
+tlinebelow;1E6F
+tmonospace;FF54
+toarmenian;0569
+tohiragana;3068
+tokatakana;30C8
+tokatakanahalfwidth;FF84
+tonebarextrahighmod;02E5
+tonebarextralowmod;02E9
+tonebarhighmod;02E6
+tonebarlowmod;02E8
+tonebarmidmod;02E7
+tonefive;01BD
+tonesix;0185
+tonetwo;01A8
+tonos;0384
+tonsquare;3327
+topatakthai;0E0F
+tortoiseshellbracketleft;3014
+tortoiseshellbracketleftsmall;FE5D
+tortoiseshellbracketleftvertical;FE39
+tortoiseshellbracketright;3015
+tortoiseshellbracketrightsmall;FE5E
+tortoiseshellbracketrightvertical;FE3A
+totaothai;0E15
+tpalatalhook;01AB
+tparen;24AF
+trademark;2122
+trademarksans;F8EA
+trademarkserif;F6DB
+tretroflexhook;0288
+triagdn;25BC
+triaglf;25C4
+triagrt;25BA
+triagup;25B2
+ts;02A6
+tsadi;05E6
+tsadidagesh;FB46
+tsadidageshhebrew;FB46
+tsadihebrew;05E6
+tsecyrillic;0446
+tsere;05B5
+tsere12;05B5
+tsere1e;05B5
+tsere2b;05B5
+tserehebrew;05B5
+tserenarrowhebrew;05B5
+tserequarterhebrew;05B5
+tserewidehebrew;05B5
+tshecyrillic;045B
+tsuperior;F6F3
+ttabengali;099F
+ttadeva;091F
+ttagujarati;0A9F
+ttagurmukhi;0A1F
+tteharabic;0679
+ttehfinalarabic;FB67
+ttehinitialarabic;FB68
+ttehmedialarabic;FB69
+tthabengali;09A0
+tthadeva;0920
+tthagujarati;0AA0
+tthagurmukhi;0A20
+tturned;0287
+tuhiragana;3064
+tukatakana;30C4
+tukatakanahalfwidth;FF82
+tusmallhiragana;3063
+tusmallkatakana;30C3
+tusmallkatakanahalfwidth;FF6F
+twelvecircle;246B
+twelveparen;247F
+twelveperiod;2493
+twelveroman;217B
+twentycircle;2473
+twentyhangzhou;5344
+twentyparen;2487
+twentyperiod;249B
+two;0032
+twoarabic;0662
+twobengali;09E8
+twocircle;2461
+twocircleinversesansserif;278B
+twodeva;0968
+twodotenleader;2025
+twodotleader;2025
+twodotleadervertical;FE30
+twogujarati;0AE8
+twogurmukhi;0A68
+twohackarabic;0662
+twohangzhou;3022
+twoideographicparen;3221
+twoinferior;2082
+twomonospace;FF12
+twonumeratorbengali;09F5
+twooldstyle;F732
+twoparen;2475
+twoperiod;2489
+twopersian;06F2
+tworoman;2171
+twostroke;01BB
+twosuperior;00B2
+twothai;0E52
+twothirds;2154
+u;0075
+uacute;00FA
+ubar;0289
+ubengali;0989
+ubopomofo;3128
+ubreve;016D
+ucaron;01D4
+ucircle;24E4
+ucircumflex;00FB
+ucircumflexbelow;1E77
+ucyrillic;0443
+udattadeva;0951
+udblacute;0171
+udblgrave;0215
+udeva;0909
+udieresis;00FC
+udieresisacute;01D8
+udieresisbelow;1E73
+udieresiscaron;01DA
+udieresiscyrillic;04F1
+udieresisgrave;01DC
+udieresismacron;01D6
+udotbelow;1EE5
+ugrave;00F9
+ugujarati;0A89
+ugurmukhi;0A09
+uhiragana;3046
+uhookabove;1EE7
+uhorn;01B0
+uhornacute;1EE9
+uhorndotbelow;1EF1
+uhorngrave;1EEB
+uhornhookabove;1EED
+uhorntilde;1EEF
+uhungarumlaut;0171
+uhungarumlautcyrillic;04F3
+uinvertedbreve;0217
+ukatakana;30A6
+ukatakanahalfwidth;FF73
+ukcyrillic;0479
+ukorean;315C
+umacron;016B
+umacroncyrillic;04EF
+umacrondieresis;1E7B
+umatragurmukhi;0A41
+umonospace;FF55
+underscore;005F
+underscoredbl;2017
+underscoremonospace;FF3F
+underscorevertical;FE33
+underscorewavy;FE4F
+union;222A
+universal;2200
+uogonek;0173
+uparen;24B0
+upblock;2580
+upperdothebrew;05C4
+upsilon;03C5
+upsilondieresis;03CB
+upsilondieresistonos;03B0
+upsilonlatin;028A
+upsilontonos;03CD
+uptackbelowcmb;031D
+uptackmod;02D4
+uragurmukhi;0A73
+uring;016F
+ushortcyrillic;045E
+usmallhiragana;3045
+usmallkatakana;30A5
+usmallkatakanahalfwidth;FF69
+ustraightcyrillic;04AF
+ustraightstrokecyrillic;04B1
+utilde;0169
+utildeacute;1E79
+utildebelow;1E75
+uubengali;098A
+uudeva;090A
+uugujarati;0A8A
+uugurmukhi;0A0A
+uumatragurmukhi;0A42
+uuvowelsignbengali;09C2
+uuvowelsigndeva;0942
+uuvowelsigngujarati;0AC2
+uvowelsignbengali;09C1
+uvowelsigndeva;0941
+uvowelsigngujarati;0AC1
+v;0076
+vadeva;0935
+vagujarati;0AB5
+vagurmukhi;0A35
+vakatakana;30F7
+vav;05D5
+vavdagesh;FB35
+vavdagesh65;FB35
+vavdageshhebrew;FB35
+vavhebrew;05D5
+vavholam;FB4B
+vavholamhebrew;FB4B
+vavvavhebrew;05F0
+vavyodhebrew;05F1
+vcircle;24E5
+vdotbelow;1E7F
+vecyrillic;0432
+veharabic;06A4
+vehfinalarabic;FB6B
+vehinitialarabic;FB6C
+vehmedialarabic;FB6D
+vekatakana;30F9
+venus;2640
+verticalbar;007C
+verticallineabovecmb;030D
+verticallinebelowcmb;0329
+verticallinelowmod;02CC
+verticallinemod;02C8
+vewarmenian;057E
+vhook;028B
+vikatakana;30F8
+viramabengali;09CD
+viramadeva;094D
+viramagujarati;0ACD
+visargabengali;0983
+visargadeva;0903
+visargagujarati;0A83
+vmonospace;FF56
+voarmenian;0578
+voicediterationhiragana;309E
+voicediterationkatakana;30FE
+voicedmarkkana;309B
+voicedmarkkanahalfwidth;FF9E
+vokatakana;30FA
+vparen;24B1
+vtilde;1E7D
+vturned;028C
+vuhiragana;3094
+vukatakana;30F4
+w;0077
+wacute;1E83
+waekorean;3159
+wahiragana;308F
+wakatakana;30EF
+wakatakanahalfwidth;FF9C
+wakorean;3158
+wasmallhiragana;308E
+wasmallkatakana;30EE
+wattosquare;3357
+wavedash;301C
+wavyunderscorevertical;FE34
+wawarabic;0648
+wawfinalarabic;FEEE
+wawhamzaabovearabic;0624
+wawhamzaabovefinalarabic;FE86
+wbsquare;33DD
+wcircle;24E6
+wcircumflex;0175
+wdieresis;1E85
+wdotaccent;1E87
+wdotbelow;1E89
+wehiragana;3091
+weierstrass;2118
+wekatakana;30F1
+wekorean;315E
+weokorean;315D
+wgrave;1E81
+whitebullet;25E6
+whitecircle;25CB
+whitecircleinverse;25D9
+whitecornerbracketleft;300E
+whitecornerbracketleftvertical;FE43
+whitecornerbracketright;300F
+whitecornerbracketrightvertical;FE44
+whitediamond;25C7
+whitediamondcontainingblacksmalldiamond;25C8
+whitedownpointingsmalltriangle;25BF
+whitedownpointingtriangle;25BD
+whiteleftpointingsmalltriangle;25C3
+whiteleftpointingtriangle;25C1
+whitelenticularbracketleft;3016
+whitelenticularbracketright;3017
+whiterightpointingsmalltriangle;25B9
+whiterightpointingtriangle;25B7
+whitesmallsquare;25AB
+whitesmilingface;263A
+whitesquare;25A1
+whitestar;2606
+whitetelephone;260F
+whitetortoiseshellbracketleft;3018
+whitetortoiseshellbracketright;3019
+whiteuppointingsmalltriangle;25B5
+whiteuppointingtriangle;25B3
+wihiragana;3090
+wikatakana;30F0
+wikorean;315F
+wmonospace;FF57
+wohiragana;3092
+wokatakana;30F2
+wokatakanahalfwidth;FF66
+won;20A9
+wonmonospace;FFE6
+wowaenthai;0E27
+wparen;24B2
+wring;1E98
+wsuperior;02B7
+wturned;028D
+wynn;01BF
+x;0078
+xabovecmb;033D
+xbopomofo;3112
+xcircle;24E7
+xdieresis;1E8D
+xdotaccent;1E8B
+xeharmenian;056D
+xi;03BE
+xmonospace;FF58
+xparen;24B3
+xsuperior;02E3
+y;0079
+yaadosquare;334E
+yabengali;09AF
+yacute;00FD
+yadeva;092F
+yaekorean;3152
+yagujarati;0AAF
+yagurmukhi;0A2F
+yahiragana;3084
+yakatakana;30E4
+yakatakanahalfwidth;FF94
+yakorean;3151
+yamakkanthai;0E4E
+yasmallhiragana;3083
+yasmallkatakana;30E3
+yasmallkatakanahalfwidth;FF6C
+yatcyrillic;0463
+ycircle;24E8
+ycircumflex;0177
+ydieresis;00FF
+ydotaccent;1E8F
+ydotbelow;1EF5
+yeharabic;064A
+yehbarreearabic;06D2
+yehbarreefinalarabic;FBAF
+yehfinalarabic;FEF2
+yehhamzaabovearabic;0626
+yehhamzaabovefinalarabic;FE8A
+yehhamzaaboveinitialarabic;FE8B
+yehhamzaabovemedialarabic;FE8C
+yehinitialarabic;FEF3
+yehmedialarabic;FEF4
+yehmeeminitialarabic;FCDD
+yehmeemisolatedarabic;FC58
+yehnoonfinalarabic;FC94
+yehthreedotsbelowarabic;06D1
+yekorean;3156
+yen;00A5
+yenmonospace;FFE5
+yeokorean;3155
+yeorinhieuhkorean;3186
+yerahbenyomohebrew;05AA
+yerahbenyomolefthebrew;05AA
+yericyrillic;044B
+yerudieresiscyrillic;04F9
+yesieungkorean;3181
+yesieungpansioskorean;3183
+yesieungsioskorean;3182
+yetivhebrew;059A
+ygrave;1EF3
+yhook;01B4
+yhookabove;1EF7
+yiarmenian;0575
+yicyrillic;0457
+yikorean;3162
+yinyang;262F
+yiwnarmenian;0582
+ymonospace;FF59
+yod;05D9
+yoddagesh;FB39
+yoddageshhebrew;FB39
+yodhebrew;05D9
+yodyodhebrew;05F2
+yodyodpatahhebrew;FB1F
+yohiragana;3088
+yoikorean;3189
+yokatakana;30E8
+yokatakanahalfwidth;FF96
+yokorean;315B
+yosmallhiragana;3087
+yosmallkatakana;30E7
+yosmallkatakanahalfwidth;FF6E
+yotgreek;03F3
+yoyaekorean;3188
+yoyakorean;3187
+yoyakthai;0E22
+yoyingthai;0E0D
+yparen;24B4
+ypogegrammeni;037A
+ypogegrammenigreekcmb;0345
+yr;01A6
+yring;1E99
+ysuperior;02B8
+ytilde;1EF9
+yturned;028E
+yuhiragana;3086
+yuikorean;318C
+yukatakana;30E6
+yukatakanahalfwidth;FF95
+yukorean;3160
+yusbigcyrillic;046B
+yusbigiotifiedcyrillic;046D
+yuslittlecyrillic;0467
+yuslittleiotifiedcyrillic;0469
+yusmallhiragana;3085
+yusmallkatakana;30E5
+yusmallkatakanahalfwidth;FF6D
+yuyekorean;318B
+yuyeokorean;318A
+yyabengali;09DF
+yyadeva;095F
+z;007A
+zaarmenian;0566
+zacute;017A
+zadeva;095B
+zagurmukhi;0A5B
+zaharabic;0638
+zahfinalarabic;FEC6
+zahinitialarabic;FEC7
+zahiragana;3056
+zahmedialarabic;FEC8
+zainarabic;0632
+zainfinalarabic;FEB0
+zakatakana;30B6
+zaqefgadolhebrew;0595
+zaqefqatanhebrew;0594
+zarqahebrew;0598
+zayin;05D6
+zayindagesh;FB36
+zayindageshhebrew;FB36
+zayinhebrew;05D6
+zbopomofo;3117
+zcaron;017E
+zcircle;24E9
+zcircumflex;1E91
+zcurl;0291
+zdot;017C
+zdotaccent;017C
+zdotbelow;1E93
+zecyrillic;0437
+zedescendercyrillic;0499
+zedieresiscyrillic;04DF
+zehiragana;305C
+zekatakana;30BC
+zero;0030
+zeroarabic;0660
+zerobengali;09E6
+zerodeva;0966
+zerogujarati;0AE6
+zerogurmukhi;0A66
+zerohackarabic;0660
+zeroinferior;2080
+zeromonospace;FF10
+zerooldstyle;F730
+zeropersian;06F0
+zerosuperior;2070
+zerothai;0E50
+zerowidthjoiner;FEFF
+zerowidthnonjoiner;200C
+zerowidthspace;200B
+zeta;03B6
+zhbopomofo;3113
+zhearmenian;056A
+zhebrevecyrillic;04C2
+zhecyrillic;0436
+zhedescendercyrillic;0497
+zhedieresiscyrillic;04DD
+zihiragana;3058
+zikatakana;30B8
+zinorhebrew;05AE
+zlinebelow;1E95
+zmonospace;FF5A
+zohiragana;305E
+zokatakana;30BE
+zparen;24B5
+zretroflexhook;0290
+zstroke;01B6
+zuhiragana;305A
+zukatakana;30BA
+# END
+"""
+
+
+_aglfnText = """\
+# -----------------------------------------------------------
+# Copyright 2002-2019 Adobe (http://www.adobe.com/).
+#
+# 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 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,
@@ -48,7 +4397,7 @@
 # Name:          Adobe Glyph List For New Fonts
 # Table version: 1.7
 # Date:          November 6, 2008
-# URL:           http://sourceforge.net/adobe/aglfn/
+# URL:           https://github.com/adobe-type-tools/agl-aglfn
 #
 # Description:
 #
@@ -705,13 +5054,14 @@
 017C;zdotaccent;LATIN SMALL LETTER Z WITH DOT ABOVE
 0030;zero;DIGIT ZERO
 03B6;zeta;GREEK SMALL LETTER ZETA
-#END
+# END
 """
 
 
 class AGLError(Exception):
 	pass
 
+LEGACY_AGL2UV = {}
 AGL2UV = {}
 UV2AGL = {}
 
@@ -720,7 +5070,7 @@
 
 	lines = _aglText.splitlines()
 
-	parseAGL_RE = re.compile("([0-9A-F]{4});([A-Za-z_0-9.]+);.*?$")
+	parseAGL_RE = re.compile("([A-Za-z0-9]+);((?:[0-9A-F]{4})(?: (?:[0-9A-F]{4}))*)$")
 
 	for line in lines:
 		if not line or line[:1] == '#':
@@ -728,24 +5078,36 @@
 		m = parseAGL_RE.match(line)
 		if not m:
 			raise AGLError("syntax error in glyphlist.txt: %s" % repr(line[:20]))
+		unicodes = m.group(2)
+		assert len(unicodes) % 5 == 4
+		unicodes = [int(unicode, 16) for unicode in unicodes.split()]
+		glyphName = tostr(m.group(1))
+		LEGACY_AGL2UV[glyphName] = unicodes
+
+	lines = _aglfnText.splitlines()
+
+	parseAGLFN_RE = re.compile("([0-9A-F]{4});([A-Za-z0-9]+);.*?$")
+
+	for line in lines:
+		if not line or line[:1] == '#':
+			continue
+		m = parseAGLFN_RE.match(line)
+		if not m:
+			raise AGLError("syntax error in aglfn.txt: %s" % repr(line[:20]))
 		unicode = m.group(1)
 		assert len(unicode) == 4
 		unicode = int(unicode, 16)
 		glyphName = tostr(m.group(2))
-		if glyphName in AGL2UV:
-			# the above table contains identical duplicates
-			assert AGL2UV[glyphName] == unicode
-		else:
-			AGL2UV[glyphName] = unicode
+		AGL2UV[glyphName] = unicode
 		UV2AGL[unicode] = glyphName
 
 _builddicts()
 
 
 def toUnicode(glyph, isZapfDingbats=False):
-	"""Convert glyph names to Unicode, such as 'longs_t.oldstyle' --> u'ſt'
+	"""Convert glyph names to Unicode, such as ``'longs_t.oldstyle'`` --> ``u'ſt'``
 
-	If isZapfDingbats is True, the implementation recognizes additional
+	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
@@ -776,14 +5138,9 @@
 
 	# 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)
+	uchars = LEGACY_AGL2UV.get(component)
+	if uchars:
+		return "".join(map(chr, uchars))
 
 	# Otherwise, if the component is of the form "uni" (U+0075,
 	# U+006E, and U+0069) followed by a sequence of uppercase
@@ -853,7 +5210,7 @@
 	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])
+	return ''.join([chr(c) for c in chars])
 
 
 _re_u = re.compile("^u([0-9A-F]{4,6})$")
@@ -871,5 +5228,5 @@
 		return None
 	if ((value >= 0x0000 and value <= 0xD7FF) or
 	    (value >= 0xE000 and value <= 0x10FFFF)):
-		return unichr(value)
+		return chr(value)
 	return None
diff --git a/Lib/fontTools/cffLib/__init__.py b/Lib/fontTools/cffLib/__init__.py
index 59f5ab5..d4cd7a1 100644
--- a/Lib/fontTools/cffLib/__init__.py
+++ b/Lib/fontTools/cffLib/__init__.py
@@ -1,7 +1,17 @@
-"""cffLib.py -- read/write tools for Adobe CFF fonts."""
+"""cffLib: read/write Adobe CFF fonts
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+OpenType fonts with PostScript outlines contain a completely independent
+font file, Adobe's *Compact Font Format*. So dealing with OpenType fonts
+requires also dealing with CFF. This module allows you to read and write
+fonts written in the CFF format.
+
+In 2016, OpenType 1.8 introduced the `CFF2 <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2>`_
+format which, along with other changes, extended the CFF format to deal with
+the demands of variable fonts. This module parses both original CFF and CFF2.
+
+"""
+
+from fontTools.misc.py23 import bytechr, byteord, bytesjoin, tobytes, tostr
 from fontTools.misc import sstruct
 from fontTools.misc import psCharStrings
 from fontTools.misc.arrayTools import unionRect, intRect
@@ -10,6 +20,7 @@
 from fontTools.ttLib.tables.otBase import OTTableWriter
 from fontTools.ttLib.tables.otBase import OTTableReader
 from fontTools.ttLib.tables import otTables as ot
+from io import BytesIO
 import struct
 import logging
 import re
@@ -29,8 +40,37 @@
 
 
 class CFFFontSet(object):
+	"""A CFF font "file" can contain more than one font, although this is
+	extremely rare (and not allowed within OpenType fonts).
+
+	This class is the entry point for parsing a CFF table. To actually
+	manipulate the data inside the CFF font, you will want to access the
+	``CFFFontSet``'s :class:`TopDict` object. To do this, a ``CFFFontSet``
+	object can either be treated as a dictionary (with appropriate
+	``keys()`` and ``values()`` methods) mapping font names to :class:`TopDict`
+	objects, or as a list.
+
+	.. code:: python
+
+		from fontTools import ttLib
+		tt = ttLib.TTFont("Tests/cffLib/data/LinLibertine_RBI.otf")
+		tt["CFF "].cff
+		# <fontTools.cffLib.CFFFontSet object at 0x101e24c90>
+		tt["CFF "].cff[0] # Here's your actual font data
+		# <fontTools.cffLib.TopDict object at 0x1020f1fd0>
+	
+	"""
 
 	def decompile(self, file, otFont, isCFF2=None):
+		"""Parse a binary CFF file into an internal representation. ``file``
+		should be a file handle object. ``otFont`` is the top-level
+		:py:class:`fontTools.ttLib.ttFont.TTFont` object containing this CFF file.
+
+		If ``isCFF2`` is passed and set to ``True`` or ``False``, then the
+		library makes an assertion that the CFF header is of the appropriate
+		version.
+		"""
+
 		self.otFont = otFont
 		sstruct.unpack(cffHeaderFormat, file.read(3), self)
 		if isCFF2 is not None:
@@ -79,7 +119,7 @@
 		"""
 		if hasattr(nameOrIndex, "__index__"):
 			index = nameOrIndex.__index__()
-		elif isinstance(nameOrIndex, basestring):
+		elif isinstance(nameOrIndex, str):
 			name = nameOrIndex
 			try:
 				index = self.fontNames.index(name)
@@ -90,6 +130,14 @@
 		return self.topDictIndex[index]
 
 	def compile(self, file, otFont, isCFF2=None):
+		"""Write the object back into binary representation onto the given file.
+		``file`` should be a file handle object. ``otFont`` is the top-level
+		:py:class:`fontTools.ttLib.ttFont.TTFont` object containing this CFF file.
+
+		If ``isCFF2`` is passed and set to ``True`` or ``False``, then the
+		library makes an assertion that the CFF header is of the appropriate
+		version.
+		"""
 		self.otFont = otFont
 		if isCFF2 is not None:
 			# called from ttLib: assert 'major' value matches expected version
@@ -144,7 +192,17 @@
 
 		writer.toFile(file)
 
-	def toXML(self, xmlWriter, progress=None):
+	def toXML(self, xmlWriter):
+		"""Write the object into XML representation onto the given
+		:class:`fontTools.misc.xmlWriter.XMLWriter`.
+
+		.. code:: python
+
+			writer = xmlWriter.XMLWriter(sys.stdout)
+			tt["CFF "].cff.toXML(writer)
+
+		"""
+
 		xmlWriter.simpletag("major", value=self.major)
 		xmlWriter.newline()
 		xmlWriter.simpletag("minor", value=self.minor)
@@ -153,17 +211,18 @@
 			xmlWriter.begintag("CFFFont", name=tostr(fontName))
 			xmlWriter.newline()
 			font = self[fontName]
-			font.toXML(xmlWriter, progress)
+			font.toXML(xmlWriter)
 			xmlWriter.endtag("CFFFont")
 			xmlWriter.newline()
 		xmlWriter.newline()
 		xmlWriter.begintag("GlobalSubrs")
 		xmlWriter.newline()
-		self.GlobalSubrs.toXML(xmlWriter, progress)
+		self.GlobalSubrs.toXML(xmlWriter)
 		xmlWriter.endtag("GlobalSubrs")
 		xmlWriter.newline()
 
 	def fromXML(self, name, attrs, content, otFont=None):
+		"""Reads data from the XML element into the ``CFFFontSet`` object."""
 		self.otFont = otFont
 
 		# set defaults. These will be replaced if there are entries for them
@@ -203,16 +262,23 @@
 				self.topDictIndex = TopDictIndex(None, cff2GetGlyphOrder, None)
 			self.topDictIndex.append(topDict)
 			for element in content:
-				if isinstance(element, basestring):
+				if isinstance(element, str):
 					continue
 				name, attrs, content = element
 				topDict.fromXML(name, attrs, content)
+
+			if hasattr(topDict, "VarStore") and topDict.FDArray[0].vstore is None:
+				fdArray = topDict.FDArray
+				for fontDict in fdArray:
+					if hasattr(fontDict, "Private"):
+						fontDict.Private.vstore = topDict.VarStore
+
 		elif name == "GlobalSubrs":
 			subrCharStringClass = psCharStrings.T2CharString
 			if not hasattr(self, "GlobalSubrs"):
 				self.GlobalSubrs = GlobalSubrsIndex()
 			for element in content:
-				if isinstance(element, basestring):
+				if isinstance(element, str):
 					continue
 				name, attrs, content = element
 				subr = subrCharStringClass()
@@ -224,7 +290,11 @@
 			self.minor = int(attrs['value'])
 
 	def convertCFFToCFF2(self, otFont):
-		# This assumes a decompiled CFF table.
+		"""Converts this object from CFF format to CFF2 format. This conversion
+		is done 'in-place'. The conversion cannot be reversed.
+
+		This assumes a decompiled CFF table. (i.e. that the object has been
+		filled via :meth:`decompile`.)"""
 		self.major = 2
 		cff2GetGlyphOrder = self.otFont.getGlyphOrder
 		topDictData = TopDictIndex(None, cff2GetGlyphOrder, None)
@@ -244,7 +314,7 @@
 				if key in topDict.rawDict:
 					del topDict.rawDict[key]
 				if hasattr(topDict, key):
-					exec("del topDict.%s" % (key))
+					delattr(topDict, key)
 
 		if not hasattr(topDict, "FDArray"):
 			fdArray = topDict.FDArray = FDArrayIndex()
@@ -257,6 +327,7 @@
 			else:
 				charStrings.fdArray = fdArray
 			fontDict = FontDict()
+			fontDict.setCFF2(True)
 			fdArray.append(fontDict)
 			fontDict.Private = privateDict
 			privateOpOrder = buildOrder(privateDictOperators2)
@@ -267,12 +338,20 @@
 						# print "Removing private dict", key
 						del privateDict.rawDict[key]
 					if hasattr(privateDict, key):
-						exec("del privateDict.%s" % (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]
@@ -281,7 +360,7 @@
 							# print "Removing private dict", key
 							del privateDict.rawDict[key]
 						if hasattr(privateDict, key):
-							exec("del privateDict.%s" % (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
@@ -292,7 +371,8 @@
 
 
 class CFFWriter(object):
-
+	"""Helper class for serializing CFF data to binary. Used by
+	:meth:`CFFFontSet.compile`."""
 	def __init__(self, isCFF2):
 		self.data = []
 		self.isCFF2 = isCFF2
@@ -352,6 +432,8 @@
 
 
 class IndexCompiler(object):
+	"""Base class for writing CFF `INDEX data <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#5-index-data>`_
+	to binary."""
 
 	def __init__(self, items, strings, parent, isCFF2=None):
 		if isCFF2 is None and hasattr(parent, "isCFF2"):
@@ -431,6 +513,7 @@
 
 
 class TopDictIndexCompiler(IndexCompiler):
+	"""Helper class for writing the TopDict to binary."""
 
 	def getItems(self, items, strings):
 		out = []
@@ -466,6 +549,9 @@
 
 
 class FDArrayIndexCompiler(IndexCompiler):
+	"""Helper class for writing the
+	`Font DICT INDEX <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#10-font-dict-index-font-dicts-and-fdselect>`_
+	to binary."""
 
 	def getItems(self, items, strings):
 		out = []
@@ -504,6 +590,8 @@
 
 
 class GlobalSubrsCompiler(IndexCompiler):
+	"""Helper class for writing the `global subroutine INDEX <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#9-local-and-global-subr-indexes>`_
+	to binary."""
 
 	def getItems(self, items, strings):
 		out = []
@@ -514,14 +602,17 @@
 
 
 class SubrsCompiler(GlobalSubrsCompiler):
-
+	"""Helper class for writing the `local subroutine INDEX <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#9-local-and-global-subr-indexes>`_
+	to binary."""
+	
 	def setPos(self, pos, endPos):
 		offset = pos - self.parent.pos
 		self.parent.rawDict["Subrs"] = offset
 
 
 class CharStringsCompiler(GlobalSubrsCompiler):
-
+	"""Helper class for writing the `CharStrings INDEX <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#9-local-and-global-subr-indexes>`_
+	to binary."""
 	def getItems(self, items, strings):
 		out = []
 		for cs in items:
@@ -534,8 +625,9 @@
 
 
 class Index(object):
-
-	"""This class represents what the CFF spec calls an INDEX."""
+	"""This class represents what the CFF spec calls an INDEX (an array of
+	variable-sized objects). `Index` items can be addressed and set using
+	Python list indexing."""
 
 	compilerClass = IndexCompiler
 
@@ -593,13 +685,50 @@
 		return data
 
 	def append(self, item):
+		"""Add an item to an INDEX."""
 		self.items.append(item)
 
 	def getCompiler(self, strings, parent, isCFF2=None):
 		return self.compilerClass(self, strings, parent, isCFF2=isCFF2)
 
+	def clear(self):
+		"""Empty the INDEX."""
+		del self.items[:]
+
 
 class GlobalSubrsIndex(Index):
+	"""This index contains all the global subroutines in the font. A global
+	subroutine is a set of ``CharString`` data which is accessible to any
+	glyph in the font, and are used to store repeated instructions - for
+	example, components may be encoded as global subroutines, but so could
+	hinting instructions.
+
+	Remember that when interpreting a ``callgsubr`` instruction (or indeed
+	a ``callsubr`` instruction) that you will need to add the "subroutine
+	number bias" to number given:
+
+	.. code:: python
+
+		tt = ttLib.TTFont("Almendra-Bold.otf")
+		u = tt["CFF "].cff[0].CharStrings["udieresis"]
+		u.decompile()
+
+		u.toXML(XMLWriter(sys.stdout))
+		# <some stuff>
+		# -64 callgsubr <-- Subroutine which implements the dieresis mark
+		# <other stuff>
+
+		tt["CFF "].cff[0].GlobalSubrs[-64] # <-- WRONG
+		# <T2CharString (bytecode) at 103451d10>
+
+		tt["CFF "].cff[0].GlobalSubrs[-64 + 107] # <-- RIGHT
+		# <T2CharString (source) at 103451390>
+
+	("The bias applied depends on the number of subrs (gsubrs). If the number of
+	subrs (gsubrs) is less than 1240, the bias is 107. Otherwise if it is less
+	than 33900, it is 1131; otherwise it is 32768.",
+	`Subroutine Operators <https://docs.microsoft.com/en-us/typography/opentype/otspec180/cff2charstr#section4.4>`)
+	"""
 
 	compilerClass = GlobalSubrsCompiler
 	subrClass = psCharStrings.T2CharString
@@ -614,11 +743,6 @@
 			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:
@@ -633,7 +757,16 @@
 			private = None
 		return self.subrClass(data, private=private, globalSubrs=self.globalSubrs)
 
-	def toXML(self, xmlWriter, progress):
+	def toXML(self, xmlWriter):
+		"""Write the subroutines index into XML representation onto the given
+		:class:`fontTools.misc.xmlWriter.XMLWriter`.
+
+		.. code:: python
+
+			writer = xmlWriter.XMLWriter(sys.stdout)
+			tt["CFF "].cff[0].GlobalSubrs.toXML(writer)
+
+		"""
 		xmlWriter.comment(
 			"The 'index' attribute is only for humans; "
 			"it is ignored when parsed.")
@@ -664,10 +797,26 @@
 
 
 class SubrsIndex(GlobalSubrsIndex):
+	"""This index contains a glyph's local subroutines. A local subroutine is a
+	private set of ``CharString`` data which is accessible only to the glyph to
+	which the index is attached."""
+
 	compilerClass = SubrsCompiler
 
 
 class TopDictIndex(Index):
+	"""This index represents the array of ``TopDict`` structures in the font
+	(again, usually only one entry is present). Hence the following calls are
+	equivalent:
+
+	.. code:: python
+
+		tt["CFF "].cff[0]
+		# <fontTools.cffLib.TopDict object at 0x102ed6e50>
+		tt["CFF "].cff.topDictIndex[0]
+		# <fontTools.cffLib.TopDict object at 0x102ed6e50>
+
+	"""
 
 	compilerClass = TopDictIndexCompiler
 
@@ -698,11 +847,11 @@
 		top.decompile(data)
 		return top
 
-	def toXML(self, xmlWriter, progress):
+	def toXML(self, xmlWriter):
 		for i in range(len(self)):
 			xmlWriter.begintag("FontDict", index=i)
 			xmlWriter.newline()
-			self[i].toXML(xmlWriter, progress)
+			self[i].toXML(xmlWriter)
 			xmlWriter.endtag("FontDict")
 			xmlWriter.newline()
 
@@ -711,11 +860,11 @@
 
 	compilerClass = FDArrayIndexCompiler
 
-	def toXML(self, xmlWriter, progress):
+	def toXML(self, xmlWriter):
 		for i in range(len(self)):
 			xmlWriter.begintag("FontDict", index=i)
 			xmlWriter.newline()
-			self[i].toXML(xmlWriter, progress)
+			self[i].toXML(xmlWriter)
 			xmlWriter.endtag("FontDict")
 			xmlWriter.newline()
 
@@ -731,7 +880,7 @@
 			return
 		fontDict = FontDict()
 		for element in content:
-			if isinstance(element, basestring):
+			if isinstance(element, str):
 				continue
 			name, attrs, content = element
 			fontDict.fromXML(name, attrs, content)
@@ -817,6 +966,23 @@
 					for glyphID in range(prev, first):
 						gidArray[glyphID] = fd
 				self.gidArray = gidArray
+			elif self.format == 4:
+				gidArray = [None] * numGlyphs
+				nRanges = readCard32(file)
+				fd = None
+				prev = None
+				for i in range(nRanges):
+					first = readCard32(file)
+					if prev is not None:
+						for glyphID in range(prev, first):
+							gidArray[glyphID] = fd
+					prev = first
+					fd = readCard16(file)
+				if prev is not None:
+					first = readCard32(file)
+					for glyphID in range(prev, first):
+						gidArray[glyphID] = fd
+				self.gidArray = gidArray
 			else:
 				assert False, "unsupported FDSelect format: %s" % format
 		else:
@@ -839,6 +1005,20 @@
 
 
 class CharStrings(object):
+	"""The ``CharStrings`` in the font represent the instructions for drawing 
+	each glyph. This object presents a dictionary interface to the font's
+	CharStrings, indexed by glyph name:
+
+	.. code:: python
+	
+		tt["CFF "].cff[0].CharStrings["a"]
+		# <T2CharString (bytecode) at 103451e90>
+
+	See :class:`fontTools.misc.psCharStrings.T1CharString` and
+	:class:`fontTools.misc.psCharStrings.T2CharString` for how to decompile,
+	compile and interpret the glyph drawing instructions in the returned objects.
+
+	"""
 
 	def __init__(self, file, charset, globalSubrs, private, fdSelect, fdArray,
 			isCFF2=None):
@@ -906,11 +1086,8 @@
 				sel = None
 			return self.charStrings[name], sel
 
-	def toXML(self, xmlWriter, progress):
+	def toXML(self, xmlWriter):
 		names = sorted(self.keys())
-		i = 0
-		step = 10
-		numGlyphs = len(names)
 		for name in names:
 			charStr, fdSelectIndex = self.getItemAndSelector(name)
 			if charStr.needsDecompilation():
@@ -927,14 +1104,10 @@
 			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):
+			if isinstance(element, str):
 				continue
 			name, attrs, content = element
 			if name != "CharString":
@@ -994,6 +1167,10 @@
 	return struct.pack(">H", value)
 
 
+def packCard32(value):
+	return struct.pack(">L", value)
+
+
 def buildOperatorDict(table):
 	d = {}
 	for op, name, arg, default, conv in table:
@@ -1037,12 +1214,22 @@
 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, progress):
+	def xmlWrite(self, xmlWriter, name, value):
 		xmlWriter.simpletag(name, value=value)
 		xmlWriter.newline()
 
@@ -1052,14 +1239,14 @@
 
 class ASCIIConverter(SimpleConverter):
 
-	def read(self, parent, value):
+	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=tounicode(value, encoding="ascii"))
+	def xmlWrite(self, xmlWriter, name, value):
+		xmlWriter.simpletag(name, value=tostr(value, encoding="ascii"))
 		xmlWriter.newline()
 
 	def xmlRead(self, name, attrs, content, parent):
@@ -1068,14 +1255,14 @@
 
 class Latin1Converter(SimpleConverter):
 
-	def read(self, parent, value):
+	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):
-		value = tounicode(value, encoding="latin1")
+	def xmlWrite(self, xmlWriter, name, value):
+		value = tostr(value, encoding="latin1")
 		if name in ['Notice', 'Copyright']:
 			value = re.sub(r"[\r\n]\s+", " ", value)
 		xmlWriter.simpletag(name, value=value)
@@ -1096,7 +1283,7 @@
 def parseBlendList(s):
 	valueList = []
 	for element in s:
-		if isinstance(element, basestring):
+		if isinstance(element, str):
 			continue
 		name, attrs, content = element
 		blendList = attrs["value"].split()
@@ -1108,7 +1295,7 @@
 
 
 class NumberConverter(SimpleConverter):
-	def xmlWrite(self, xmlWriter, name, value, progress):
+	def xmlWrite(self, xmlWriter, name, value):
 		if isinstance(value, list):
 			xmlWriter.begintag(name)
 			xmlWriter.newline()
@@ -1133,7 +1320,7 @@
 
 
 class ArrayConverter(SimpleConverter):
-	def xmlWrite(self, xmlWriter, name, value, progress):
+	def xmlWrite(self, xmlWriter, name, value):
 		if value and isinstance(value[0], list):
 			xmlWriter.begintag(name)
 			xmlWriter.newline()
@@ -1162,17 +1349,17 @@
 
 class TableConverter(SimpleConverter):
 
-	def xmlWrite(self, xmlWriter, name, value, progress):
+	def xmlWrite(self, xmlWriter, name, value):
 		xmlWriter.begintag(name)
 		xmlWriter.newline()
-		value.toXML(xmlWriter, progress)
+		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):
+			if isinstance(element, str):
 				continue
 			name, attrs, content = element
 			ob.fromXML(name, attrs, content)
@@ -1184,7 +1371,7 @@
 	def getClass(self):
 		return PrivateDict
 
-	def read(self, parent, value):
+	def _read(self, parent, value):
 		size, offset = value
 		file = parent.file
 		isCFF2 = parent._isCFF2
@@ -1209,7 +1396,7 @@
 	def getClass(self):
 		return SubrsIndex
 
-	def read(self, parent, value):
+	def _read(self, parent, value):
 		file = parent.file
 		isCFF2 = parent._isCFF2
 		file.seek(parent.offset + value)  # Offset(self)
@@ -1221,7 +1408,7 @@
 
 class CharStringsConverter(TableConverter):
 
-	def read(self, parent, value):
+	def _read(self, parent, value):
 		file = parent.file
 		isCFF2 = parent._isCFF2
 		charset = parent.charset
@@ -1265,8 +1452,8 @@
 		return charStrings
 
 
-class CharsetConverter(object):
-	def read(self, parent, value):
+class CharsetConverter(SimpleConverter):
+	def _read(self, parent, value):
 		isCID = hasattr(parent, "ROS")
 		if value > 2:
 			numGlyphs = parent.numGlyphs
@@ -1282,6 +1469,20 @@
 				raise NotImplementedError
 			assert len(charset) == numGlyphs
 			log.log(DEBUG, "    charset end at %s", file.tell())
+			# make sure glyph names are unique
+			allNames = {}
+			newCharset = []
+			for glyphName in charset:
+				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 + "#" + str(n)
+				allNames[glyphName] = 1
+				newCharset.append(glyphName)
+			charset = newCharset
 		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
@@ -1301,7 +1502,7 @@
 	def write(self, parent, value):
 		return 0  # dummy value
 
-	def xmlWrite(self, xmlWriter, name, value, progress):
+	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")
@@ -1450,7 +1651,7 @@
 class EncodingCompiler(object):
 
 	def __init__(self, strings, encoding, parent):
-		assert not isinstance(encoding, basestring)
+		assert not isinstance(encoding, str)
 		data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings)
 		data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings)
 		if len(data0) < len(data1):
@@ -1471,7 +1672,7 @@
 
 class EncodingConverter(SimpleConverter):
 
-	def read(self, parent, value):
+	def _read(self, parent, value):
 		if value == 0:
 			return "StandardEncoding"
 		elif value == 1:
@@ -1501,7 +1702,7 @@
 			return 1
 		return 0  # dummy value
 
-	def xmlWrite(self, xmlWriter, name, value, progress):
+	def xmlWrite(self, xmlWriter, name, value):
 		if value in ("StandardEncoding", "ExpertEncoding"):
 			xmlWriter.simpletag(name, name=value)
 			xmlWriter.newline()
@@ -1521,7 +1722,7 @@
 			return attrs["name"]
 		encoding = [".notdef"] * 256
 		for element in content:
-			if isinstance(element, basestring):
+			if isinstance(element, str):
 				continue
 			name, attrs, content = element
 			code = safeEval(attrs["code"])
@@ -1613,7 +1814,7 @@
 
 class FDArrayConverter(TableConverter):
 
-	def read(self, parent, value):
+	def _read(self, parent, value):
 		try:
 			vstore = parent.VarStore
 		except AttributeError:
@@ -1633,16 +1834,16 @@
 	def xmlRead(self, name, attrs, content, parent):
 		fdArray = FDArrayIndex()
 		for element in content:
-			if isinstance(element, basestring):
+			if isinstance(element, str):
 				continue
 			name, attrs, content = element
 			fdArray.fromXML(name, attrs, content)
 		return fdArray
 
 
-class FDSelectConverter(object):
+class FDSelectConverter(SimpleConverter):
 
-	def read(self, parent, value):
+	def _read(self, parent, value):
 		file = parent.file
 		file.seek(value)
 		fdSelect = FDSelect(file, parent.numGlyphs)
@@ -1653,7 +1854,7 @@
 
 	# 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):
+	def xmlWrite(self, xmlWriter, name, value):
 		xmlWriter.simpletag(name, [('format', value.format)])
 		xmlWriter.newline()
 
@@ -1667,7 +1868,7 @@
 
 class VarStoreConverter(SimpleConverter):
 
-	def read(self, parent, value):
+	def _read(self, parent, value):
 		file = parent.file
 		file.seek(value)
 		varStore = VarStoreData(file)
@@ -1677,7 +1878,7 @@
 	def write(self, parent, value):
 		return 0  # dummy value
 
-	def xmlWrite(self, xmlWriter, name, value, progress):
+	def xmlWrite(self, xmlWriter, name, value):
 		value.writeXML(xmlWriter, name)
 
 	def xmlRead(self, name, attrs, content, parent):
@@ -1715,6 +1916,27 @@
 	return bytesjoin(data)
 
 
+def packFDSelect4(fdSelectArray):
+	fmt = 4
+	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(packCard32(len(fdRanges)))
+	for fdRange in fdRanges:
+		data.append(packCard32(fdRange[0]))
+		data.append(packCard16(fdRange[1]))
+	data.append(packCard32(sentinelGID))
+	return bytesjoin(data)
+
+
 class FDSelectCompiler(object):
 
 	def __init__(self, fdSelect, parent):
@@ -1724,6 +1946,8 @@
 			self.data = packFDSelect0(fdSelectArray)
 		elif fmt == 3:
 			self.data = packFDSelect3(fdSelectArray)
+		elif fmt == 4:
+			self.data = packFDSelect4(fdSelectArray)
 		else:
 			# choose smaller of the two formats
 			data0 = packFDSelect0(fdSelectArray)
@@ -1771,7 +1995,7 @@
 
 class ROSConverter(SimpleConverter):
 
-	def xmlWrite(self, xmlWriter, name, value, progress):
+	def xmlWrite(self, xmlWriter, name, value):
 		registry, order, supplement = value
 		xmlWriter.simpletag(
 			name,
@@ -1882,6 +2106,8 @@
 	(11,		'StdVW',		'number',	None,	None),
 	((12, 12),	'StemSnapH',		'delta',	None,	None),
 	((12, 13),	'StemSnapV',		'delta',	None,	None),
+	((12, 17),	'LanguageGroup',	'number',	0,	None),
+	((12, 18),	'ExpansionFactor',	'number',	0.06,	None),
 	(19,		'Subrs',		'number',	None,	SubrsConverter()),
 ]
 
@@ -2013,27 +2239,33 @@
 
 
 	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.
+		"""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)
@@ -2103,7 +2335,7 @@
 					self.rawDict["charset"] = charsetCode
 			if hasattr(self.dictObj, "Encoding") and self.dictObj.Encoding:
 				encoding = self.dictObj.Encoding
-				if not isinstance(encoding, basestring):
+				if not isinstance(encoding, str):
 					children.append(EncodingCompiler(strings, encoding, self))
 		else:
 			if hasattr(self.dictObj, "VarStore"):
@@ -2235,6 +2467,12 @@
 		return self.compilerClass(self, strings, parent, isCFF2=isCFF2)
 
 	def __getattr__(self, name):
+		if name[:2] == name[-2:] == "__":
+			# to make deepcopy() and pickle.load() work, we need to signal with
+			# AttributeError that dunder methods like '__deepcopy__' or '__getstate__'
+			# aren't implemented. For more details, see:
+			# https://github.com/fonttools/fonttools/pull/1488
+			raise AttributeError(name)
 		value = self.rawDict.get(name, None)
 		if value is None:
 			value = self.defaults.get(name)
@@ -2245,7 +2483,7 @@
 		setattr(self, name, value)
 		return value
 
-	def toXML(self, xmlWriter, progress):
+	def toXML(self, xmlWriter):
 		for name in self.order:
 			if name in self.skipNames:
 				continue
@@ -2262,7 +2500,7 @@
 			if value is None and name != "charset":
 				continue
 			conv = self.converters[name]
-			conv.xmlWrite(xmlWriter, name, value, progress)
+			conv.xmlWrite(xmlWriter, name, value)
 		ignoredNames = set(self.rawDict) - set(self.order)
 		if ignoredNames:
 			xmlWriter.comment(
@@ -2276,6 +2514,30 @@
 
 
 class TopDict(BaseDict):
+	"""The ``TopDict`` represents the top-level dictionary holding font
+	information. CFF2 tables contain a restricted set of top-level entries
+	as described `here <https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#7-top-dict-data>`_,
+	but CFF tables may contain a wider range of information. This information
+	can be accessed through attributes or through the dictionary returned
+	through the ``rawDict`` property:
+
+	.. code:: python
+
+		font = tt["CFF "].cff[0]
+		font.FamilyName
+		# 'Linux Libertine O'
+		font.rawDict["FamilyName"]
+		# 'Linux Libertine O'
+
+	More information is available in the CFF file's private dictionary, accessed
+	via the ``Private`` property:
+
+	.. code:: python
+
+		tt["CFF "].cff[0].Private.BlueValues
+		# [-15, 0, 515, 515, 666, 666]
+	
+	"""
 
 	defaults = buildDefaults(topDictOperators)
 	converters = buildConverters(topDictOperators)
@@ -2297,6 +2559,7 @@
 			self.order = buildOrder(topDictOperators)
 
 	def getGlyphOrder(self):
+		"""Returns a list of glyph names in the CFF font."""
 		return self.charset
 
 	def postDecompile(self):
@@ -2310,9 +2573,9 @@
 		else:
 			self.numGlyphs = readCard16(self.file)
 
-	def toXML(self, xmlWriter, progress):
+	def toXML(self, xmlWriter):
 		if hasattr(self, "CharStrings"):
-			self.decompileAllCharStrings(progress)
+			self.decompileAllCharStrings()
 		if hasattr(self, "ROS"):
 			self.skipNames = ['Encoding']
 		if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"):
@@ -2320,25 +2583,21 @@
 			# in CID fonts.
 			self.skipNames = [
 				'CIDFontVersion', 'CIDFontRevision', 'CIDFontType', 'CIDCount']
-		BaseDict.toXML(self, xmlWriter, progress)
+		BaseDict.toXML(self, xmlWriter)
 
-	def decompileAllCharStrings(self, progress):
+	def decompileAllCharStrings(self):
 		# Make sure that all the Private Dicts have been instantiated.
-		i = 0
-		for charString in self.CharStrings.values():
+		for i, charString in enumerate(self.CharStrings.values()):
 			try:
 				charString.decompile()
 			except:
 				log.error("Error in charstring %s", i)
 				raise
-			if not i % 30 and progress:
-				progress.increment(0)  # update
-			i = i + 1
 
 	def recalcFontBBox(self):
 		fontBBox = None
 		for charString in self.CharStrings.values():
-			bounds = charString.calcBounds()
+			bounds = charString.calcBounds(self.CharStrings)
 			if bounds is not None:
 				if fontBBox is not None:
 					fontBBox = unionRect(fontBBox, bounds)
@@ -2382,13 +2641,24 @@
 	defaults = {}
 	converters = buildConverters(topDictOperators)
 	compilerClass = FontDictCompiler
-	order = ['FontName', 'FontMatrix', 'Weight', 'Private']
+	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):
@@ -2405,10 +2675,17 @@
 		if isCFF2:
 			self.defaults = buildDefaults(privateDictOperators2)
 			self.order = buildOrder(privateDictOperators2)
+			# Provide dummy values. This avoids needing to provide
+			# an isCFF2 state in a lot of places.
+			self.nominalWidthX = self.defaultWidthX = None
 		else:
 			self.defaults = buildDefaults(privateDictOperators)
 			self.order = buildOrder(privateDictOperators)
 
+	@property
+	def in_cff2(self):
+		return self._isCFF2
+
 	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:
diff --git a/Lib/fontTools/cffLib/specializer.py b/Lib/fontTools/cffLib/specializer.py
index b1244bc..fbfefa9 100644
--- a/Lib/fontTools/cffLib/specializer.py
+++ b/Lib/fontTools/cffLib/specializer.py
@@ -1,13 +1,23 @@
 # -*- coding: utf-8 -*-
 
-"""T2CharString operator specializer and generalizer."""
+"""T2CharString operator specializer and generalizer.
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+PostScript glyph drawing operations can be expressed in multiple different
+ways. For example, as well as the ``lineto`` operator, there is also a
+``hlineto`` operator which draws a horizontal line, removing the need to
+specify a ``dx`` coordinate, and a ``vlineto`` operator which draws a
+vertical line, removing the need to specify a ``dy`` coordinate. As well
+as decompiling :class:`fontTools.misc.psCharStrings.T2CharString` objects
+into lists of operations, this module allows for conversion between general
+and specific forms of the operation.
+
+"""
+
+from fontTools.cffLib import maxStackLimit
 
 
 def stringToProgram(string):
-	if isinstance(string, basestring):
+	if isinstance(string, str):
 		string = string.split()
 	program = []
 	for token in string:
@@ -21,32 +31,80 @@
 		program.append(token)
 	return program
 
+
 def programToString(program):
 	return ' '.join(str(x) for x in program)
 
 
-def programToCommands(program):
+def programToCommands(program, getNumRegions=None):
 	"""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 (¯\_(ツ)_/¯)."""
+	program (¯\_(ツ)_/¯).
+	'getNumRegions' may be None, or a callable object. It must return the
+	number of regions. 'getNumRegions' takes a single argument, vsindex. If
+	the vsindex argument is None, getNumRegions returns the default number
+	of regions for the charstring, else it returns the numRegions for
+	the vsindex.
+	The Charstring may or may not start with a width value. If the first
+	non-blend operator has an odd number of arguments, then the first argument is
+	a width, and is popped off. This is complicated with blend operators, as
+	there may be more than one before the first hint or moveto operator, and each
+	one reduces several arguments to just one list argument. We have to sum the
+	number of arguments that are not part of the blend arguments, and all the
+	'numBlends' values. We could instead have said that by definition, if there
+	is a blend operator, there is no width value, since CFF2 Charstrings don't
+	have width values. I discussed this with Behdad, and we are allowing for an
+	initial width value in this case because developers may assemble a CFF2
+	charstring from CFF Charstrings, which could have width values.
+	"""
 
-	width = None
+	seenWidthOp = False
+	vsIndex = None
+	lenBlendStack = 0
+	lastBlendIndex = 0
 	commands = []
 	stack = []
 	it = iter(program)
+
 	for token in it:
-		if not isinstance(token, basestring):
+		if not isinstance(token, str):
 			stack.append(token)
 			continue
 
-		if width is None and token in {'hstem', 'hstemhm', 'vstem', 'vstemhm',
-					       'cntrmask', 'hintmask',
-					       'hmoveto', 'vmoveto', 'rmoveto',
-					       'endchar'}:
+		if token == 'blend':
+			assert getNumRegions is not None
+			numSourceFonts = 1 + getNumRegions(vsIndex)
+			# replace the blend op args on the stack with a single list
+			# containing all the blend op args.
+			numBlends = stack[-1]
+			numBlendArgs = numBlends * numSourceFonts + 1
+			# replace first blend op by a list of the blend ops.
+			stack[-numBlendArgs:] = [stack[-numBlendArgs:]]
+			lenBlendStack += numBlends + len(stack) - 1
+			lastBlendIndex = len(stack)
+			# if a blend op exists, this is or will be a CFF2 charstring.
+			continue
+
+		elif token == 'vsindex':
+			vsIndex = stack[-1]
+			assert type(vsIndex) is int
+
+		elif (not seenWidthOp) and token in {'hstem', 'hstemhm', 'vstem', 'vstemhm',
+			'cntrmask', 'hintmask',
+			'hmoveto', 'vmoveto', 'rmoveto',
+			'endchar'}:
+			seenWidthOp = True
 			parity = token in {'hmoveto', 'vmoveto'}
-			if stack and (len(stack) % 2) ^ parity:
+			if lenBlendStack:
+				# lenBlendStack has the number of args represented by the last blend
+				# arg and all the preceding args. We need to now add the number of
+				# args following the last blend arg.
+				numArgs = lenBlendStack + len(stack[lastBlendIndex:])
+			else:
+				numArgs = len(stack)
+			if numArgs and (numArgs % 2) ^ parity:
 				width = stack.pop(0)
 				commands.append(('', [width]))
 
@@ -56,17 +114,30 @@
 			commands.append((token, []))
 			commands.append(('', [next(it)]))
 		else:
-			commands.append((token,stack))
+			commands.append((token, stack))
 		stack = []
 	if stack:
 		commands.append(('', stack))
 	return commands
 
+
+def _flattenBlendArgs(args):
+	token_list = []
+	for arg in args:
+		if isinstance(arg, list):
+			token_list.extend(arg)
+			token_list.append('blend')
+		else:
+			token_list.append(arg)
+	return token_list
+
 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:
+		if any(isinstance(arg, list) for arg in args):
+			args = _flattenBlendArgs(args)
 		program.extend(args)
 		if op:
 			program.append(op)
@@ -201,11 +272,58 @@
 			yield ('rlineto', args)
 		yield ('rrcurveto', last_args)
 
+def _convertBlendOpToArgs(blendList):
+	# args is list of blend op args. Since we are supporting
+	# recursive blend op calls, some of these args may also
+	# be a list of blend op args, and need to be converted before
+	# we convert the current list.
+	if any([isinstance(arg, list) for arg in blendList]):
+		args =  [i for e in blendList for i in 
+					(_convertBlendOpToArgs(e) if isinstance(e,list) else [e]) ]
+	else:
+		args = blendList
+
+	# We now know that blendList contains a blend op argument list, even if
+	# some of the args are lists that each contain a blend op argument list.
+	# 	Convert from:
+	# 		[default font arg sequence x0,...,xn] + [delta tuple for x0] + ... + [delta tuple for xn]
+	# 	to:
+	# 		[ [x0] + [delta tuple for x0],
+	#                 ...,
+	#          [xn] + [delta tuple for xn] ]
+	numBlends = args[-1]
+	# Can't use args.pop() when the args are being used in a nested list
+	# comprehension. See calling context
+	args = args[:-1]
+
+	numRegions = len(args)//numBlends - 1
+	if not (numBlends*(numRegions + 1) == len(args)):
+		raise ValueError(blendList)
+
+	defaultArgs = [[arg] for arg in args[:numBlends]]
+	deltaArgs = args[numBlends:]
+	numDeltaValues = len(deltaArgs)
+	deltaList = [ deltaArgs[i:i + numRegions] for i in range(0, numDeltaValues, numRegions) ]
+	blend_args = [ a + b for a, b in zip(defaultArgs,deltaList)]
+	return blend_args
 
 def generalizeCommands(commands, ignoreErrors=False):
 	result = []
 	mapping = _GeneralizerDecombinerCommandsMap
-	for op,args in commands:
+	for op, args in commands:
+		# First, generalize any blend args in the arg list.
+		if any([isinstance(arg, list) for arg in args]):
+			try:
+				args = [n for arg in args for n in (_convertBlendOpToArgs(arg) if isinstance(arg, list) else [arg])]
+			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
+
 		func = getattr(mapping, op, None)
 		if not func:
 			result.append((op,args))
@@ -223,8 +341,8 @@
 				raise
 	return result
 
-def generalizeProgram(program, **kwargs):
-	return commandsToProgram(generalizeCommands(programToCommands(program), **kwargs))
+def generalizeProgram(program, getNumRegions=None, **kwargs):
+	return commandsToProgram(generalizeCommands(programToCommands(program, getNumRegions), **kwargs))
 
 
 def _categorizeVector(v):
@@ -265,6 +383,70 @@
 	assert a in '0r'
 	return a
 
+def _convertToBlendCmds(args):
+	# return a list of blend commands, and
+	# the remaining non-blended args, if any.
+	num_args = len(args)
+	stack_use = 0
+	new_args = []
+	i = 0
+	while i < num_args:
+		arg = args[i]
+		if not isinstance(arg, list):
+			new_args.append(arg)
+			i += 1
+			stack_use += 1
+		else:
+			prev_stack_use = stack_use
+			# The arg is a tuple of blend values.
+			# These are each (master 0,delta 1..delta n)
+			# Combine as many successive tuples as we can,
+			# up to the max stack limit.
+			num_sources = len(arg)
+			blendlist = [arg]
+			i += 1
+			stack_use += 1 + num_sources  # 1 for the num_blends arg
+			while (i < num_args) and isinstance(args[i], list):
+				blendlist.append(args[i])
+				i += 1
+				stack_use += num_sources
+				if stack_use + num_sources > maxStackLimit:
+					# if we are here, max stack is the CFF2 max stack.
+					# I use the CFF2 max stack limit here rather than
+					# the 'maxstack' chosen by the client, as the default
+					#  maxstack may have been used unintentionally. For all
+					# the other operators, this just produces a little less
+					# optimization, but here it puts a hard (and low) limit
+					# on the number of source fonts that can be used.
+					break
+			# blendList now contains as many single blend tuples as can be
+			# combined without exceeding the CFF2 stack limit.
+			num_blends = len(blendlist)
+			# append the 'num_blends' default font values
+			blend_args = []
+			for arg in blendlist:
+				blend_args.append(arg[0])
+			for arg in blendlist:
+				blend_args.extend(arg[1:])
+			blend_args.append(num_blends)
+			new_args.append(blend_args)
+			stack_use = prev_stack_use + num_blends
+
+	return new_args
+
+def _addArgs(a, b):
+	if isinstance(b, list):
+		if isinstance(a, list):
+			if len(a) != len(b):
+				raise ValueError()
+			return [_addArgs(va, vb) for va,vb in zip(a, b)]
+		else:
+			a, b = b, a
+	if isinstance(a, list):
+		return [_addArgs(a[0], b)] + a[1:]
+	return a + b
+
+
 def specializeCommands(commands,
 		       ignoreErrors=False,
 		       generalizeFirst=True,
@@ -300,6 +482,8 @@
 	# 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.
+	#
+	# 7. For any args which are blend lists, convert them to a blend command.
 
 
 	# 0. Generalize commands.
@@ -415,10 +599,18 @@
 				continue
 
 			# Merge adjacent hlineto's and vlineto's.
-			if i and op in {'hlineto', 'vlineto'} and op == commands[i-1][0]:
+			# In CFF2 charstrings from variable fonts, each
+			# arg item may be a list of blendable values, one from
+			# each source font.
+			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]])
+				try:
+					new_args = [_addArgs(args[0], other_args[0])]
+				except ValueError:
+					continue
+				commands[i-1] = (op, new_args)
 				del commands[i]
 				continue
 
@@ -436,7 +628,6 @@
 
 		if op[2:] == 'curveto' and len(args) == 5 and prv == nxt == 'rrcurveto':
 			assert (op[0] == 'r') ^ (op[1] == 'r')
-			args = list(args)
 			if op[0] == 'v':
 				pos = 0
 			elif op[0] != 'r':
@@ -445,7 +636,8 @@
 				pos = 4
 			else:
 				pos = 5
-			args.insert(pos, 0)
+			# 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
 
@@ -493,7 +685,9 @@
 				if d0 is None: continue
 				new_op = d0+d+'curveto'
 
-		if new_op and len(args1) + len(args2) <= maxstack:
+		# Make sure the stack depth does not exceed (maxstack - 1), so
+		# that subroutinizer can insert subroutine calls at any point.
+		if new_op and len(args1) + len(args2) < maxstack:
 			commands[i-1] = (new_op, args1+args2)
 			del commands[i]
 
@@ -528,10 +722,16 @@
 			commands[i] = op0+op1+'curveto', args
 			continue
 
+	# 7. For any series of args which are blend lists, convert the series to a single blend arg.
+	for i in range(len(commands)):
+		op, args = commands[i]
+		if any(isinstance(arg, list) for arg in args):
+			commands[i] = op, _convertToBlendCmds(args)
+
 	return commands
 
-def specializeProgram(program, **kwargs):
-	return commandsToProgram(specializeCommands(programToCommands(program), **kwargs))
+def specializeProgram(program, getNumRegions=None, **kwargs):
+	return commandsToProgram(specializeCommands(programToCommands(program, getNumRegions), **kwargs))
 
 
 if __name__ == '__main__':
@@ -548,4 +748,3 @@
 	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..00b859b
--- /dev/null
+++ b/Lib/fontTools/cffLib/width.py
@@ -0,0 +1,183 @@
+# -*- coding: utf-8 -*-
+
+"""T2CharString glyph width optimizer.
+
+CFF glyphs whose width equals the CFF Private dictionary's ``defaultWidthX``
+value do not need to specify their width in their charstring, saving bytes.
+This module determines the optimum ``defaultWidthX`` and ``nominalWidthX``
+values for a font, when provided with a list of glyph widths."""
+
+from fontTools.ttLib import TTFont
+from collections import defaultdict
+from operator import add
+from functools import 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
+
+def main(args=None):
+	"""Calculate optimum defaultWidthX/nominalWidthX values"""
+
+	import argparse
+	parser = argparse.ArgumentParser(
+		"fonttools cffLib.width",
+		description=main.__doc__,
+	)
+	parser.add_argument('inputs', metavar='FILE', type=str, nargs='+',
+		help="Input TTF files")
+	parser.add_argument('-b', '--brute-force', dest="brute", action="store_true",
+		help="Use brute-force approach (VERY slow)")
+
+	args = parser.parse_args(args)
+
+	for fontfile in args.inputs:
+		font = TTFont(fontfile)
+		hmtx = font['hmtx']
+		widths = [m[0] for m in hmtx.metrics.values()]
+		if args.brute:
+			default, nominal = optimizeWidthsBruteforce(widths)
+		else:
+			default, nominal = optimizeWidths(widths)
+		print("glyphs=%d default=%d nominal=%d byteCost=%d" % (len(widths), default, nominal, byteCost(widths, default, nominal)))
+
+if __name__ == '__main__':
+	import sys
+	if len(sys.argv) == 1:
+		import doctest
+		sys.exit(doctest.testmod().failed)
+	main()
diff --git a/Lib/fontTools/colorLib/__init__.py b/Lib/fontTools/colorLib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/fontTools/colorLib/__init__.py
diff --git a/Lib/fontTools/colorLib/builder.py b/Lib/fontTools/colorLib/builder.py
new file mode 100644
index 0000000..d2e35d8
--- /dev/null
+++ b/Lib/fontTools/colorLib/builder.py
@@ -0,0 +1,673 @@
+"""
+colorLib.builder: Build COLR/CPAL tables from scratch
+
+"""
+import collections
+import copy
+import enum
+from functools import partial
+from math import ceil, log
+from typing import (
+    Any,
+    Dict,
+    Generator,
+    Iterable,
+    List,
+    Mapping,
+    Optional,
+    Sequence,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+)
+from fontTools.misc.arrayTools import intRect
+from fontTools.misc.fixedTools import fixedToFloat
+from fontTools.ttLib.tables import C_O_L_R_
+from fontTools.ttLib.tables import C_P_A_L_
+from fontTools.ttLib.tables import _n_a_m_e
+from fontTools.ttLib.tables import otTables as ot
+from fontTools.ttLib.tables.otTables import ExtendMode, CompositeMode
+from .errors import ColorLibError
+from .geometry import round_start_circle_stable_containment
+from .table_builder import BuildCallback, TableBuilder
+
+
+# TODO move type aliases to colorLib.types?
+T = TypeVar("T")
+_Kwargs = Mapping[str, Any]
+_PaintInput = Union[int, _Kwargs, ot.Paint, Tuple[str, "_PaintInput"]]
+_PaintInputList = Sequence[_PaintInput]
+_ColorGlyphsDict = Dict[str, Union[_PaintInputList, _PaintInput]]
+_ColorGlyphsV0Dict = Dict[str, Sequence[Tuple[str, int]]]
+_ClipBoxInput = Union[
+    Tuple[int, int, int, int, int],  # format 1, variable
+    Tuple[int, int, int, int],  # format 0, non-variable
+    ot.ClipBox,
+]
+
+
+MAX_PAINT_COLR_LAYER_COUNT = 255
+_DEFAULT_ALPHA = 1.0
+_MAX_REUSE_LEN = 32
+
+
+def _beforeBuildPaintRadialGradient(paint, source):
+    x0 = source["x0"]
+    y0 = source["y0"]
+    r0 = source["r0"]
+    x1 = source["x1"]
+    y1 = source["y1"]
+    r1 = source["r1"]
+
+    # TODO apparently no builder_test confirms this works (?)
+
+    # avoid abrupt change after rounding when c0 is near c1's perimeter
+    c = round_start_circle_stable_containment((x0, y0), r0, (x1, y1), r1)
+    x0, y0 = c.centre
+    r0 = c.radius
+
+    # update source to ensure paint is built with corrected values
+    source["x0"] = x0
+    source["y0"] = y0
+    source["r0"] = r0
+    source["x1"] = x1
+    source["y1"] = y1
+    source["r1"] = r1
+
+    return paint, source
+
+
+def _defaultColorStop():
+    colorStop = ot.ColorStop()
+    colorStop.Alpha = _DEFAULT_ALPHA
+    return colorStop
+
+
+def _defaultVarColorStop():
+    colorStop = ot.VarColorStop()
+    colorStop.Alpha = _DEFAULT_ALPHA
+    return colorStop
+
+
+def _defaultColorLine():
+    colorLine = ot.ColorLine()
+    colorLine.Extend = ExtendMode.PAD
+    return colorLine
+
+
+def _defaultVarColorLine():
+    colorLine = ot.VarColorLine()
+    colorLine.Extend = ExtendMode.PAD
+    return colorLine
+
+
+def _defaultPaintSolid():
+    paint = ot.Paint()
+    paint.Alpha = _DEFAULT_ALPHA
+    return paint
+
+
+def _buildPaintCallbacks():
+    return {
+        (
+            BuildCallback.BEFORE_BUILD,
+            ot.Paint,
+            ot.PaintFormat.PaintRadialGradient,
+        ): _beforeBuildPaintRadialGradient,
+        (
+            BuildCallback.BEFORE_BUILD,
+            ot.Paint,
+            ot.PaintFormat.PaintVarRadialGradient,
+        ): _beforeBuildPaintRadialGradient,
+        (BuildCallback.CREATE_DEFAULT, ot.ColorStop): _defaultColorStop,
+        (BuildCallback.CREATE_DEFAULT, ot.VarColorStop): _defaultVarColorStop,
+        (BuildCallback.CREATE_DEFAULT, ot.ColorLine): _defaultColorLine,
+        (BuildCallback.CREATE_DEFAULT, ot.VarColorLine): _defaultVarColorLine,
+        (
+            BuildCallback.CREATE_DEFAULT,
+            ot.Paint,
+            ot.PaintFormat.PaintSolid,
+        ): _defaultPaintSolid,
+        (
+            BuildCallback.CREATE_DEFAULT,
+            ot.Paint,
+            ot.PaintFormat.PaintVarSolid,
+        ): _defaultPaintSolid,
+    }
+
+
+def populateCOLRv0(
+    table: ot.COLR,
+    colorGlyphsV0: _ColorGlyphsV0Dict,
+    glyphMap: Optional[Mapping[str, int]] = None,
+):
+    """Build v0 color layers and add to existing COLR table.
+
+    Args:
+        table: a raw otTables.COLR() object (not ttLib's table_C_O_L_R_).
+        colorGlyphsV0: map of base glyph names to lists of (layer glyph names,
+            color palette index) tuples. Can be empty.
+        glyphMap: a map from glyph names to glyph indices, as returned from
+            TTFont.getReverseGlyphMap(), to optionally sort base records by GID.
+    """
+    if glyphMap is not None:
+        colorGlyphItems = sorted(
+            colorGlyphsV0.items(), key=lambda item: glyphMap[item[0]]
+        )
+    else:
+        colorGlyphItems = colorGlyphsV0.items()
+    baseGlyphRecords = []
+    layerRecords = []
+    for baseGlyph, layers in colorGlyphItems:
+        baseRec = ot.BaseGlyphRecord()
+        baseRec.BaseGlyph = baseGlyph
+        baseRec.FirstLayerIndex = len(layerRecords)
+        baseRec.NumLayers = len(layers)
+        baseGlyphRecords.append(baseRec)
+
+        for layerGlyph, paletteIndex in layers:
+            layerRec = ot.LayerRecord()
+            layerRec.LayerGlyph = layerGlyph
+            layerRec.PaletteIndex = paletteIndex
+            layerRecords.append(layerRec)
+
+    table.BaseGlyphRecordArray = table.LayerRecordArray = None
+    if baseGlyphRecords:
+        table.BaseGlyphRecordArray = ot.BaseGlyphRecordArray()
+        table.BaseGlyphRecordArray.BaseGlyphRecord = baseGlyphRecords
+    if layerRecords:
+        table.LayerRecordArray = ot.LayerRecordArray()
+        table.LayerRecordArray.LayerRecord = layerRecords
+    table.BaseGlyphRecordCount = len(baseGlyphRecords)
+    table.LayerRecordCount = len(layerRecords)
+
+
+def buildCOLR(
+    colorGlyphs: _ColorGlyphsDict,
+    version: Optional[int] = None,
+    glyphMap: Optional[Mapping[str, int]] = None,
+    varStore: Optional[ot.VarStore] = None,
+    varIndexMap: Optional[ot.DeltaSetIndexMap] = None,
+    clipBoxes: Optional[Dict[str, _ClipBoxInput]] = None,
+) -> C_O_L_R_.table_C_O_L_R_:
+    """Build COLR table from color layers mapping.
+    Args:
+        colorGlyphs: map of base glyph name to, either list of (layer glyph name,
+            color palette index) tuples for COLRv0; or a single Paint (dict) or
+            list of Paint for COLRv1.
+        version: the version of COLR table. If None, the version is determined
+            by the presence of COLRv1 paints or variation data (varStore), which
+            require version 1; otherwise, if all base glyphs use only simple color
+            layers, version 0 is used.
+        glyphMap: a map from glyph names to glyph indices, as returned from
+            TTFont.getReverseGlyphMap(), to optionally sort base records by GID.
+        varStore: Optional ItemVarationStore for deltas associated with v1 layer.
+        varIndexMap: Optional DeltaSetIndexMap for deltas associated with v1 layer.
+        clipBoxes: Optional map of base glyph name to clip box 4- or 5-tuples:
+            (xMin, yMin, xMax, yMax) or (xMin, yMin, xMax, yMax, varIndexBase).
+    Return:
+        A new COLR table.
+    """
+    self = C_O_L_R_.table_C_O_L_R_()
+
+    if varStore is not None and version == 0:
+        raise ValueError("Can't add VarStore to COLRv0")
+
+    if version in (None, 0) and not varStore:
+        # split color glyphs into v0 and v1 and encode separately
+        colorGlyphsV0, colorGlyphsV1 = _split_color_glyphs_by_version(colorGlyphs)
+        if version == 0 and colorGlyphsV1:
+            raise ValueError("Can't encode COLRv1 glyphs in COLRv0")
+    else:
+        # unless explicitly requested for v1 or have variations, in which case
+        # we encode all color glyph as v1
+        colorGlyphsV0, colorGlyphsV1 = {}, colorGlyphs
+
+    colr = ot.COLR()
+
+    populateCOLRv0(colr, colorGlyphsV0, glyphMap)
+
+    colr.LayerList, colr.BaseGlyphList = buildColrV1(colorGlyphsV1, glyphMap)
+
+    if version is None:
+        version = 1 if (varStore or colorGlyphsV1) else 0
+    elif version not in (0, 1):
+        raise NotImplementedError(version)
+    self.version = colr.Version = version
+
+    if version == 0:
+        self.ColorLayers = self._decompileColorLayersV0(colr)
+    else:
+        clipBoxes = {
+            name: clipBoxes[name] for name in clipBoxes or {} if name in colorGlyphsV1
+        }
+        colr.ClipList = buildClipList(clipBoxes) if clipBoxes else None
+        colr.VarIndexMap = varIndexMap
+        colr.VarStore = varStore
+        self.table = colr
+
+    return self
+
+
+def buildClipList(clipBoxes: Dict[str, _ClipBoxInput]) -> ot.ClipList:
+    clipList = ot.ClipList()
+    clipList.Format = 1
+    clipList.clips = {name: buildClipBox(box) for name, box in clipBoxes.items()}
+    return clipList
+
+
+def buildClipBox(clipBox: _ClipBoxInput) -> ot.ClipBox:
+    if isinstance(clipBox, ot.ClipBox):
+        return clipBox
+    n = len(clipBox)
+    clip = ot.ClipBox()
+    if n not in (4, 5):
+        raise ValueError(f"Invalid ClipBox: expected 4 or 5 values, found {n}")
+    clip.xMin, clip.yMin, clip.xMax, clip.yMax = intRect(clipBox[:4])
+    clip.Format = int(n == 5) + 1
+    if n == 5:
+        clip.VarIndexBase = int(clipBox[4])
+    return clip
+
+
+class ColorPaletteType(enum.IntFlag):
+    USABLE_WITH_LIGHT_BACKGROUND = 0x0001
+    USABLE_WITH_DARK_BACKGROUND = 0x0002
+
+    @classmethod
+    def _missing_(cls, value):
+        # enforce reserved bits
+        if isinstance(value, int) and (value < 0 or value & 0xFFFC != 0):
+            raise ValueError(f"{value} is not a valid {cls.__name__}")
+        return super()._missing_(value)
+
+
+# None, 'abc' or {'en': 'abc', 'de': 'xyz'}
+_OptionalLocalizedString = Union[None, str, Dict[str, str]]
+
+
+def buildPaletteLabels(
+    labels: Iterable[_OptionalLocalizedString], nameTable: _n_a_m_e.table__n_a_m_e
+) -> List[Optional[int]]:
+    return [
+        nameTable.addMultilingualName(l, mac=False)
+        if isinstance(l, dict)
+        else C_P_A_L_.table_C_P_A_L_.NO_NAME_ID
+        if l is None
+        else nameTable.addMultilingualName({"en": l}, mac=False)
+        for l in labels
+    ]
+
+
+def buildCPAL(
+    palettes: Sequence[Sequence[Tuple[float, float, float, float]]],
+    paletteTypes: Optional[Sequence[ColorPaletteType]] = None,
+    paletteLabels: Optional[Sequence[_OptionalLocalizedString]] = None,
+    paletteEntryLabels: Optional[Sequence[_OptionalLocalizedString]] = None,
+    nameTable: Optional[_n_a_m_e.table__n_a_m_e] = None,
+) -> C_P_A_L_.table_C_P_A_L_:
+    """Build CPAL table from list of color palettes.
+
+    Args:
+        palettes: list of lists of colors encoded as tuples of (R, G, B, A) floats
+            in the range [0..1].
+        paletteTypes: optional list of ColorPaletteType, one for each palette.
+        paletteLabels: optional list of palette labels. Each lable can be either:
+            None (no label), a string (for for default English labels), or a
+            localized string (as a dict keyed with BCP47 language codes).
+        paletteEntryLabels: optional list of palette entry labels, one for each
+            palette entry (see paletteLabels).
+        nameTable: optional name table where to store palette and palette entry
+            labels. Required if either paletteLabels or paletteEntryLabels is set.
+
+    Return:
+        A new CPAL v0 or v1 table, if custom palette types or labels are specified.
+    """
+    if len({len(p) for p in palettes}) != 1:
+        raise ColorLibError("color palettes have different lengths")
+
+    if (paletteLabels or paletteEntryLabels) and not nameTable:
+        raise TypeError(
+            "nameTable is required if palette or palette entries have labels"
+        )
+
+    cpal = C_P_A_L_.table_C_P_A_L_()
+    cpal.numPaletteEntries = len(palettes[0])
+
+    cpal.palettes = []
+    for i, palette in enumerate(palettes):
+        colors = []
+        for j, color in enumerate(palette):
+            if not isinstance(color, tuple) or len(color) != 4:
+                raise ColorLibError(
+                    f"In palette[{i}][{j}]: expected (R, G, B, A) tuple, got {color!r}"
+                )
+            if any(v > 1 or v < 0 for v in color):
+                raise ColorLibError(
+                    f"palette[{i}][{j}] has invalid out-of-range [0..1] color: {color!r}"
+                )
+            # input colors are RGBA, CPAL encodes them as BGRA
+            red, green, blue, alpha = color
+            colors.append(
+                C_P_A_L_.Color(*(round(v * 255) for v in (blue, green, red, alpha)))
+            )
+        cpal.palettes.append(colors)
+
+    if any(v is not None for v in (paletteTypes, paletteLabels, paletteEntryLabels)):
+        cpal.version = 1
+
+        if paletteTypes is not None:
+            if len(paletteTypes) != len(palettes):
+                raise ColorLibError(
+                    f"Expected {len(palettes)} paletteTypes, got {len(paletteTypes)}"
+                )
+            cpal.paletteTypes = [ColorPaletteType(t).value for t in paletteTypes]
+        else:
+            cpal.paletteTypes = [C_P_A_L_.table_C_P_A_L_.DEFAULT_PALETTE_TYPE] * len(
+                palettes
+            )
+
+        if paletteLabels is not None:
+            if len(paletteLabels) != len(palettes):
+                raise ColorLibError(
+                    f"Expected {len(palettes)} paletteLabels, got {len(paletteLabels)}"
+                )
+            cpal.paletteLabels = buildPaletteLabels(paletteLabels, nameTable)
+        else:
+            cpal.paletteLabels = [C_P_A_L_.table_C_P_A_L_.NO_NAME_ID] * len(palettes)
+
+        if paletteEntryLabels is not None:
+            if len(paletteEntryLabels) != cpal.numPaletteEntries:
+                raise ColorLibError(
+                    f"Expected {cpal.numPaletteEntries} paletteEntryLabels, "
+                    f"got {len(paletteEntryLabels)}"
+                )
+            cpal.paletteEntryLabels = buildPaletteLabels(paletteEntryLabels, nameTable)
+        else:
+            cpal.paletteEntryLabels = [
+                C_P_A_L_.table_C_P_A_L_.NO_NAME_ID
+            ] * cpal.numPaletteEntries
+    else:
+        cpal.version = 0
+
+    return cpal
+
+
+# COLR v1 tables
+# See draft proposal at: https://github.com/googlefonts/colr-gradients-spec
+
+
+def _is_colrv0_layer(layer: Any) -> bool:
+    # Consider as COLRv0 layer any sequence of length 2 (be it tuple or list) in which
+    # the first element is a str (the layerGlyph) and the second element is an int
+    # (CPAL paletteIndex).
+    # https://github.com/googlefonts/ufo2ft/issues/426
+    try:
+        layerGlyph, paletteIndex = layer
+    except (TypeError, ValueError):
+        return False
+    else:
+        return isinstance(layerGlyph, str) and isinstance(paletteIndex, int)
+
+
+def _split_color_glyphs_by_version(
+    colorGlyphs: _ColorGlyphsDict,
+) -> Tuple[_ColorGlyphsV0Dict, _ColorGlyphsDict]:
+    colorGlyphsV0 = {}
+    colorGlyphsV1 = {}
+    for baseGlyph, layers in colorGlyphs.items():
+        if all(_is_colrv0_layer(l) for l in layers):
+            colorGlyphsV0[baseGlyph] = layers
+        else:
+            colorGlyphsV1[baseGlyph] = layers
+
+    # sanity check
+    assert set(colorGlyphs) == (set(colorGlyphsV0) | set(colorGlyphsV1))
+
+    return colorGlyphsV0, colorGlyphsV1
+
+
+def _reuse_ranges(num_layers: int) -> Generator[Tuple[int, int], None, None]:
+    # TODO feels like something itertools might have already
+    for lbound in range(num_layers):
+        # Reuse of very large #s of layers is relatively unlikely
+        # +2: we want sequences of at least 2
+        # otData handles single-record duplication
+        for ubound in range(
+            lbound + 2, min(num_layers + 1, lbound + 2 + _MAX_REUSE_LEN)
+        ):
+            yield (lbound, ubound)
+
+
+class LayerListBuilder:
+    slices: List[ot.Paint]
+    layers: List[ot.Paint]
+    reusePool: Mapping[Tuple[Any, ...], int]
+    tuples: Mapping[int, Tuple[Any, ...]]
+    keepAlive: List[ot.Paint]  # we need id to remain valid
+
+    def __init__(self):
+        self.slices = []
+        self.layers = []
+        self.reusePool = {}
+        self.tuples = {}
+        self.keepAlive = []
+
+        # We need to intercept construction of PaintColrLayers
+        callbacks = _buildPaintCallbacks()
+        callbacks[
+            (
+                BuildCallback.BEFORE_BUILD,
+                ot.Paint,
+                ot.PaintFormat.PaintColrLayers,
+            )
+        ] = self._beforeBuildPaintColrLayers
+        self.tableBuilder = TableBuilder(callbacks)
+
+    def _paint_tuple(self, paint: ot.Paint):
+        # start simple, who even cares about cyclic graphs or interesting field types
+        def _tuple_safe(value):
+            if isinstance(value, enum.Enum):
+                return value
+            elif hasattr(value, "__dict__"):
+                return tuple(
+                    (k, _tuple_safe(v)) for k, v in sorted(value.__dict__.items())
+                )
+            elif isinstance(value, collections.abc.MutableSequence):
+                return tuple(_tuple_safe(e) for e in value)
+            return value
+
+        # Cache the tuples for individual Paint instead of the whole sequence
+        # because the seq could be a transient slice
+        result = self.tuples.get(id(paint), None)
+        if result is None:
+            result = _tuple_safe(paint)
+            self.tuples[id(paint)] = result
+            self.keepAlive.append(paint)
+        return result
+
+    def _as_tuple(self, paints: Sequence[ot.Paint]) -> Tuple[Any, ...]:
+        return tuple(self._paint_tuple(p) for p in paints)
+
+    # COLR layers is unusual in that it modifies shared state
+    # so we need a callback into an object
+    def _beforeBuildPaintColrLayers(self, dest, source):
+        paint = ot.Paint()
+        paint.Format = int(ot.PaintFormat.PaintColrLayers)
+        self.slices.append(paint)
+
+        # Sketchy gymnastics: a sequence input will have dropped it's layers
+        # into NumLayers; get it back
+        if isinstance(source.get("NumLayers", None), collections.abc.Sequence):
+            layers = source["NumLayers"]
+        else:
+            layers = source["Layers"]
+
+        # Convert maps seqs or whatever into typed objects
+        layers = [self.buildPaint(l) for l in layers]
+
+        # No reason to have a colr layers with just one entry
+        if len(layers) == 1:
+            return layers[0], {}
+
+        # Look for reuse, with preference to longer sequences
+        # This may make the layer list smaller
+        found_reuse = True
+        while found_reuse:
+            found_reuse = False
+
+            ranges = sorted(
+                _reuse_ranges(len(layers)),
+                key=lambda t: (t[1] - t[0], t[1], t[0]),
+                reverse=True,
+            )
+            for lbound, ubound in ranges:
+                reuse_lbound = self.reusePool.get(
+                    self._as_tuple(layers[lbound:ubound]), -1
+                )
+                if reuse_lbound == -1:
+                    continue
+                new_slice = ot.Paint()
+                new_slice.Format = int(ot.PaintFormat.PaintColrLayers)
+                new_slice.NumLayers = ubound - lbound
+                new_slice.FirstLayerIndex = reuse_lbound
+                layers = layers[:lbound] + [new_slice] + layers[ubound:]
+                found_reuse = True
+                break
+
+        # The layer list is now final; if it's too big we need to tree it
+        is_tree = len(layers) > MAX_PAINT_COLR_LAYER_COUNT
+        layers = _build_n_ary_tree(layers, n=MAX_PAINT_COLR_LAYER_COUNT)
+
+        # We now have a tree of sequences with Paint leaves.
+        # Convert the sequences into PaintColrLayers.
+        def listToColrLayers(layer):
+            if isinstance(layer, collections.abc.Sequence):
+                return self.buildPaint(
+                    {
+                        "Format": ot.PaintFormat.PaintColrLayers,
+                        "Layers": [listToColrLayers(l) for l in layer],
+                    }
+                )
+            return layer
+
+        layers = [listToColrLayers(l) for l in layers]
+
+        paint.NumLayers = len(layers)
+        paint.FirstLayerIndex = len(self.layers)
+        self.layers.extend(layers)
+
+        # Register our parts for reuse provided we aren't a tree
+        # If we are a tree the leaves registered for reuse and that will suffice
+        if not is_tree:
+            for lbound, ubound in _reuse_ranges(len(layers)):
+                self.reusePool[self._as_tuple(layers[lbound:ubound])] = (
+                    lbound + paint.FirstLayerIndex
+                )
+
+        # we've fully built dest; empty source prevents generalized build from kicking in
+        return paint, {}
+
+    def buildPaint(self, paint: _PaintInput) -> ot.Paint:
+        return self.tableBuilder.build(ot.Paint, paint)
+
+    def build(self) -> Optional[ot.LayerList]:
+        if not self.layers:
+            return None
+        layers = ot.LayerList()
+        layers.LayerCount = len(self.layers)
+        layers.Paint = self.layers
+        return layers
+
+
+def buildBaseGlyphPaintRecord(
+    baseGlyph: str, layerBuilder: LayerListBuilder, paint: _PaintInput
+) -> ot.BaseGlyphList:
+    self = ot.BaseGlyphPaintRecord()
+    self.BaseGlyph = baseGlyph
+    self.Paint = layerBuilder.buildPaint(paint)
+    return self
+
+
+def _format_glyph_errors(errors: Mapping[str, Exception]) -> str:
+    lines = []
+    for baseGlyph, error in sorted(errors.items()):
+        lines.append(f"    {baseGlyph} => {type(error).__name__}: {error}")
+    return "\n".join(lines)
+
+
+def buildColrV1(
+    colorGlyphs: _ColorGlyphsDict,
+    glyphMap: Optional[Mapping[str, int]] = None,
+) -> Tuple[Optional[ot.LayerList], ot.BaseGlyphList]:
+    if glyphMap is not None:
+        colorGlyphItems = sorted(
+            colorGlyphs.items(), key=lambda item: glyphMap[item[0]]
+        )
+    else:
+        colorGlyphItems = colorGlyphs.items()
+
+    errors = {}
+    baseGlyphs = []
+    layerBuilder = LayerListBuilder()
+    for baseGlyph, paint in colorGlyphItems:
+        try:
+            baseGlyphs.append(buildBaseGlyphPaintRecord(baseGlyph, layerBuilder, paint))
+
+        except (ColorLibError, OverflowError, ValueError, TypeError) as e:
+            errors[baseGlyph] = e
+
+    if errors:
+        failed_glyphs = _format_glyph_errors(errors)
+        exc = ColorLibError(f"Failed to build BaseGlyphList:\n{failed_glyphs}")
+        exc.errors = errors
+        raise exc from next(iter(errors.values()))
+
+    layers = layerBuilder.build()
+    glyphs = ot.BaseGlyphList()
+    glyphs.BaseGlyphCount = len(baseGlyphs)
+    glyphs.BaseGlyphPaintRecord = baseGlyphs
+    return (layers, glyphs)
+
+
+def _build_n_ary_tree(leaves, n):
+    """Build N-ary tree from sequence of leaf nodes.
+
+    Return a list of lists where each non-leaf node is a list containing
+    max n nodes.
+    """
+    if not leaves:
+        return []
+
+    assert n > 1
+
+    depth = ceil(log(len(leaves), n))
+
+    if depth <= 1:
+        return list(leaves)
+
+    # Fully populate complete subtrees of root until we have enough leaves left
+    root = []
+    unassigned = None
+    full_step = n ** (depth - 1)
+    for i in range(0, len(leaves), full_step):
+        subtree = leaves[i : i + full_step]
+        if len(subtree) < full_step:
+            unassigned = subtree
+            break
+        while len(subtree) > n:
+            subtree = [subtree[k : k + n] for k in range(0, len(subtree), n)]
+        root.append(subtree)
+
+    if unassigned:
+        # Recurse to fill the last subtree, which is the only partially populated one
+        subtree = _build_n_ary_tree(unassigned, n)
+        if len(subtree) <= n - len(root):
+            # replace last subtree with its children if they can still fit
+            root.extend(subtree)
+        else:
+            root.append(subtree)
+        assert len(root) <= n
+
+    return root
diff --git a/Lib/fontTools/colorLib/errors.py b/Lib/fontTools/colorLib/errors.py
new file mode 100644
index 0000000..a0bdda1
--- /dev/null
+++ b/Lib/fontTools/colorLib/errors.py
@@ -0,0 +1,3 @@
+
+class ColorLibError(Exception):
+    pass
diff --git a/Lib/fontTools/colorLib/geometry.py b/Lib/fontTools/colorLib/geometry.py
new file mode 100644
index 0000000..e62aead
--- /dev/null
+++ b/Lib/fontTools/colorLib/geometry.py
@@ -0,0 +1,145 @@
+"""Helpers for manipulating 2D points and vectors in COLR table."""
+
+from math import copysign, cos, hypot, pi
+from fontTools.misc.roundTools import otRound
+
+
+def _vector_between(origin, target):
+    return (target[0] - origin[0], target[1] - origin[1])
+
+
+def _round_point(pt):
+    return (otRound(pt[0]), otRound(pt[1]))
+
+
+def _unit_vector(vec):
+    length = hypot(*vec)
+    if length == 0:
+        return None
+    return (vec[0] / length, vec[1] / length)
+
+
+# This is the same tolerance used by Skia's SkTwoPointConicalGradient.cpp to detect
+# when a radial gradient's focal point lies on the end circle.
+_NEARLY_ZERO = 1 / (1 << 12)  # 0.000244140625
+
+
+# The unit vector's X and Y components are respectively
+#   U = (cos(α), sin(α))
+# where α is the angle between the unit vector and the positive x axis.
+_UNIT_VECTOR_THRESHOLD = cos(3 / 8 * pi)  # == sin(1/8 * pi) == 0.38268343236508984
+
+
+def _rounding_offset(direction):
+    # Return 2-tuple of -/+ 1.0 or 0.0 approximately based on the direction vector.
+    # We divide the unit circle in 8 equal slices oriented towards the cardinal
+    # (N, E, S, W) and intermediate (NE, SE, SW, NW) directions. To each slice we
+    # map one of the possible cases: -1, 0, +1 for either X and Y coordinate.
+    # E.g. Return (+1.0, -1.0) if unit vector is oriented towards SE, or
+    # (-1.0, 0.0) if it's pointing West, etc.
+    uv = _unit_vector(direction)
+    if not uv:
+        return (0, 0)
+
+    result = []
+    for uv_component in uv:
+        if -_UNIT_VECTOR_THRESHOLD <= uv_component < _UNIT_VECTOR_THRESHOLD:
+            # unit vector component near 0: direction almost orthogonal to the
+            # direction of the current axis, thus keep coordinate unchanged
+            result.append(0)
+        else:
+            # nudge coord by +/- 1.0 in direction of unit vector
+            result.append(copysign(1.0, uv_component))
+    return tuple(result)
+
+
+class Circle:
+    def __init__(self, centre, radius):
+        self.centre = centre
+        self.radius = radius
+
+    def __repr__(self):
+        return f"Circle(centre={self.centre}, radius={self.radius})"
+
+    def round(self):
+        return Circle(_round_point(self.centre), otRound(self.radius))
+
+    def inside(self, outer_circle):
+        dist = self.radius + hypot(*_vector_between(self.centre, outer_circle.centre))
+        return (
+            abs(outer_circle.radius - dist) <= _NEARLY_ZERO
+            or outer_circle.radius > dist
+        )
+
+    def concentric(self, other):
+        return self.centre == other.centre
+
+    def move(self, dx, dy):
+        self.centre = (self.centre[0] + dx, self.centre[1] + dy)
+
+
+def round_start_circle_stable_containment(c0, r0, c1, r1):
+    """Round start circle so that it stays inside/outside end circle after rounding.
+
+    The rounding of circle coordinates to integers may cause an abrupt change
+    if the start circle c0 is so close to the end circle c1's perimiter that
+    it ends up falling outside (or inside) as a result of the rounding.
+    To keep the gradient unchanged, we nudge it in the right direction.
+
+    See:
+    https://github.com/googlefonts/colr-gradients-spec/issues/204
+    https://github.com/googlefonts/picosvg/issues/158
+    """
+    start, end = Circle(c0, r0), Circle(c1, r1)
+
+    inside_before_round = start.inside(end)
+
+    round_start = start.round()
+    round_end = end.round()
+    inside_after_round = round_start.inside(round_end)
+
+    if inside_before_round == inside_after_round:
+        return round_start
+    elif inside_after_round:
+        # start was outside before rounding: we need to push start away from end
+        direction = _vector_between(round_end.centre, round_start.centre)
+        radius_delta = +1.0
+    else:
+        # start was inside before rounding: we need to push start towards end
+        direction = _vector_between(round_start.centre, round_end.centre)
+        radius_delta = -1.0
+    dx, dy = _rounding_offset(direction)
+
+    # At most 2 iterations ought to be enough to converge. Before the loop, we
+    # know the start circle didn't keep containment after normal rounding; thus
+    # we continue adjusting by -/+ 1.0 until containment is restored.
+    # Normal rounding can at most move each coordinates -/+0.5; in the worst case
+    # both the start and end circle's centres and radii will be rounded in opposite
+    # directions, e.g. when they move along a 45 degree diagonal:
+    #   c0 = (1.5, 1.5) ===> (2.0, 2.0)
+    #   r0 = 0.5 ===> 1.0
+    #   c1 = (0.499, 0.499) ===> (0.0, 0.0)
+    #   r1 = 2.499 ===> 2.0
+    # In this example, the relative distance between the circles, calculated
+    # as r1 - (r0 + distance(c0, c1)) is initially 0.57437 (c0 is inside c1), and
+    # -1.82842 after rounding (c0 is now outside c1). Nudging c0 by -1.0 on both
+    # x and y axes moves it towards c1 by hypot(-1.0, -1.0) = 1.41421. Two of these
+    # moves cover twice that distance, which is enough to restore containment.
+    max_attempts = 2
+    for _ in range(max_attempts):
+        if round_start.concentric(round_end):
+            # can't move c0 towards c1 (they are the same), so we change the radius
+            round_start.radius += radius_delta
+            assert round_start.radius >= 0
+        else:
+            round_start.move(dx, dy)
+        if inside_before_round == round_start.inside(round_end):
+            break
+    else:  # likely a bug
+        raise AssertionError(
+            f"Rounding circle {start} "
+            f"{'inside' if inside_before_round else 'outside'} "
+            f"{end} failed after {max_attempts} attempts!"
+        )
+
+    return round_start
diff --git a/Lib/fontTools/colorLib/table_builder.py b/Lib/fontTools/colorLib/table_builder.py
new file mode 100644
index 0000000..763115b
--- /dev/null
+++ b/Lib/fontTools/colorLib/table_builder.py
@@ -0,0 +1,225 @@
+"""
+colorLib.table_builder: Generic helper for filling in BaseTable derivatives from tuples and maps and such.
+
+"""
+
+import collections
+import enum
+from fontTools.ttLib.tables.otBase import (
+    BaseTable,
+    FormatSwitchingBaseTable,
+    UInt8FormatSwitchingBaseTable,
+)
+from fontTools.ttLib.tables.otConverters import (
+    ComputedInt,
+    SimpleValue,
+    Struct,
+    Short,
+    UInt8,
+    UShort,
+    IntValue,
+    FloatValue,
+    OptionalValue,
+)
+from fontTools.misc.roundTools import otRound
+
+
+class BuildCallback(enum.Enum):
+    """Keyed on (BEFORE_BUILD, class[, Format if available]).
+    Receives (dest, source).
+    Should return (dest, source), which can be new objects.
+    """
+
+    BEFORE_BUILD = enum.auto()
+
+    """Keyed on (AFTER_BUILD, class[, Format if available]).
+    Receives (dest).
+    Should return dest, which can be a new object.
+    """
+    AFTER_BUILD = enum.auto()
+
+    """Keyed on (CREATE_DEFAULT, class[, Format if available]).
+    Receives no arguments.
+    Should return a new instance of class.
+    """
+    CREATE_DEFAULT = enum.auto()
+
+
+def _assignable(convertersByName):
+    return {k: v for k, v in convertersByName.items() if not isinstance(v, ComputedInt)}
+
+
+def _isNonStrSequence(value):
+    return isinstance(value, collections.abc.Sequence) and not isinstance(value, str)
+
+
+def _split_format(cls, source):
+    if _isNonStrSequence(source):
+        assert len(source) > 0, f"{cls} needs at least format from {source}"
+        fmt, remainder = source[0], source[1:]
+    elif isinstance(source, collections.abc.Mapping):
+        assert "Format" in source, f"{cls} needs at least Format from {source}"
+        remainder = source.copy()
+        fmt = remainder.pop("Format")
+    else:
+        raise ValueError(f"Not sure how to populate {cls} from {source}")
+
+    assert isinstance(
+        fmt, collections.abc.Hashable
+    ), f"{cls} Format is not hashable: {fmt!r}"
+    assert (
+        fmt in cls.convertersByName
+    ), f"{cls} invalid Format: {fmt!r}"
+
+    return fmt, remainder
+
+
+class TableBuilder:
+    """
+    Helps to populate things derived from BaseTable from maps, tuples, etc.
+
+    A table of lifecycle callbacks may be provided to add logic beyond what is possible
+    based on otData info for the target class. See BuildCallbacks.
+    """
+
+    def __init__(self, callbackTable=None):
+        if callbackTable is None:
+            callbackTable = {}
+        self._callbackTable = callbackTable
+
+    def _convert(self, dest, field, converter, value):
+        enumClass = getattr(converter, "enumClass", None)
+
+        if enumClass:
+            if isinstance(value, enumClass):
+                pass
+            elif isinstance(value, str):
+                try:
+                    value = getattr(enumClass, value.upper())
+                except AttributeError:
+                    raise ValueError(f"{value} is not a valid {enumClass}")
+            else:
+                value = enumClass(value)
+
+        elif isinstance(converter, IntValue):
+            value = otRound(value)
+        elif isinstance(converter, FloatValue):
+            value = float(value)
+
+        elif isinstance(converter, Struct):
+            if converter.repeat:
+                if _isNonStrSequence(value):
+                    value = [self.build(converter.tableClass, v) for v in value]
+                else:
+                    value = [self.build(converter.tableClass, value)]
+                setattr(dest, converter.repeat, len(value))
+            else:
+                value = self.build(converter.tableClass, value)
+        elif callable(converter):
+            value = converter(value)
+
+        setattr(dest, field, value)
+
+    def build(self, cls, source):
+        assert issubclass(cls, BaseTable)
+
+        if isinstance(source, cls):
+            return source
+
+        callbackKey = (cls,)
+        fmt = None
+        if issubclass(cls, FormatSwitchingBaseTable):
+            fmt, source = _split_format(cls, source)
+            callbackKey = (cls, fmt)
+
+        dest = self._callbackTable.get(
+            (BuildCallback.CREATE_DEFAULT,) + callbackKey, lambda: cls()
+        )()
+        assert isinstance(dest, cls)
+
+        convByName = _assignable(cls.convertersByName)
+        skippedFields = set()
+
+        # For format switchers we need to resolve converters based on format
+        if issubclass(cls, FormatSwitchingBaseTable):
+            dest.Format = fmt
+            convByName = _assignable(convByName[dest.Format])
+            skippedFields.add("Format")
+
+        # Convert sequence => mapping so before thunk only has to handle one format
+        if _isNonStrSequence(source):
+            # Sequence (typically list or tuple) assumed to match fields in declaration order
+            assert len(source) <= len(
+                convByName
+            ), f"Sequence of {len(source)} too long for {cls}; expected <= {len(convByName)} values"
+            source = dict(zip(convByName.keys(), source))
+
+        dest, source = self._callbackTable.get(
+            (BuildCallback.BEFORE_BUILD,) + callbackKey, lambda d, s: (d, s)
+        )(dest, source)
+
+        if isinstance(source, collections.abc.Mapping):
+            for field, value in source.items():
+                if field in skippedFields:
+                    continue
+                converter = convByName.get(field, None)
+                if not converter:
+                    raise ValueError(
+                        f"Unrecognized field {field} for {cls}; expected one of {sorted(convByName.keys())}"
+                    )
+                self._convert(dest, field, converter, value)
+        else:
+            # let's try as a 1-tuple
+            dest = self.build(cls, (source,))
+
+        for field, conv in convByName.items():
+            if not hasattr(dest, field) and isinstance(conv, OptionalValue):
+                setattr(dest, field, conv.DEFAULT)
+
+        dest = self._callbackTable.get(
+            (BuildCallback.AFTER_BUILD,) + callbackKey, lambda d: d
+        )(dest)
+
+        return dest
+
+
+class TableUnbuilder:
+    def __init__(self, callbackTable=None):
+        if callbackTable is None:
+            callbackTable = {}
+        self._callbackTable = callbackTable
+
+    def unbuild(self, table):
+        assert isinstance(table, BaseTable)
+
+        source = {}
+
+        callbackKey = (type(table),)
+        if isinstance(table, FormatSwitchingBaseTable):
+            source["Format"] = int(table.Format)
+            callbackKey += (table.Format,)
+
+        for converter in table.getConverters():
+            if isinstance(converter, ComputedInt):
+                continue
+            value = getattr(table, converter.name)
+
+            enumClass = getattr(converter, "enumClass", None)
+            if enumClass:
+                source[converter.name] = value.name.lower()
+            elif isinstance(converter, Struct):
+                if converter.repeat:
+                    source[converter.name] = [self.unbuild(v) for v in value]
+                else:
+                    source[converter.name] = self.unbuild(value)
+            elif isinstance(converter, SimpleValue):
+                # "simple" values (e.g. int, float, str) need no further un-building
+                source[converter.name] = value
+            else:
+                raise NotImplementedError(
+                    "Don't know how unbuild {value!r} with {converter!r}"
+                )
+
+        source = self._callbackTable.get(callbackKey, lambda s: s)(source)
+
+        return source
diff --git a/Lib/fontTools/colorLib/unbuilder.py b/Lib/fontTools/colorLib/unbuilder.py
new file mode 100644
index 0000000..0d10cff
--- /dev/null
+++ b/Lib/fontTools/colorLib/unbuilder.py
@@ -0,0 +1,79 @@
+from fontTools.ttLib.tables import otTables as ot
+from .table_builder import TableUnbuilder
+
+
+def unbuildColrV1(layerV1List, baseGlyphV1List):
+    unbuilder = LayerListUnbuilder(layerV1List.Paint)
+    return {
+        rec.BaseGlyph: unbuilder.unbuildPaint(rec.Paint)
+        for rec in baseGlyphV1List.BaseGlyphPaintRecord
+    }
+
+
+def _flatten(lst):
+    for el in lst:
+        if isinstance(el, list):
+            yield from _flatten(el)
+        else:
+            yield el
+
+
+class LayerListUnbuilder:
+    def __init__(self, layers):
+        self.layers = layers
+
+        callbacks = {
+            (
+                ot.Paint,
+                ot.PaintFormat.PaintColrLayers,
+            ): self._unbuildPaintColrLayers,
+        }
+        self.tableUnbuilder = TableUnbuilder(callbacks)
+
+    def unbuildPaint(self, paint):
+        assert isinstance(paint, ot.Paint)
+        return self.tableUnbuilder.unbuild(paint)
+
+    def _unbuildPaintColrLayers(self, source):
+        assert source["Format"] == ot.PaintFormat.PaintColrLayers
+
+        layers = list(
+            _flatten(
+                [
+                    self.unbuildPaint(childPaint)
+                    for childPaint in self.layers[
+                        source["FirstLayerIndex"] : source["FirstLayerIndex"]
+                        + source["NumLayers"]
+                    ]
+                ]
+            )
+        )
+
+        if len(layers) == 1:
+            return layers[0]
+
+        return {"Format": source["Format"], "Layers": layers}
+
+
+if __name__ == "__main__":
+    from pprint import pprint
+    import sys
+    from fontTools.ttLib import TTFont
+
+    try:
+        fontfile = sys.argv[1]
+    except IndexError:
+        sys.exit("usage: fonttools colorLib.unbuilder FONTFILE")
+
+    font = TTFont(fontfile)
+    colr = font["COLR"]
+    if colr.version < 1:
+        sys.exit(f"error: No COLR table version=1 found in {fontfile}")
+
+    colorGlyphs = unbuildColrV1(
+        colr.table.LayerList,
+        colr.table.BaseGlyphList,
+        ignoreVarIdx=not colr.table.VarStore,
+    )
+
+    pprint(colorGlyphs)
diff --git a/Lib/fontTools/cu2qu/__init__.py b/Lib/fontTools/cu2qu/__init__.py
new file mode 100644
index 0000000..4ae6356
--- /dev/null
+++ b/Lib/fontTools/cu2qu/__init__.py
@@ -0,0 +1,15 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+from .cu2qu import *
diff --git a/Lib/fontTools/cu2qu/__main__.py b/Lib/fontTools/cu2qu/__main__.py
new file mode 100644
index 0000000..084bf8f
--- /dev/null
+++ b/Lib/fontTools/cu2qu/__main__.py
@@ -0,0 +1,6 @@
+import sys
+from .cli import main
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Lib/fontTools/cu2qu/cli.py b/Lib/fontTools/cu2qu/cli.py
new file mode 100644
index 0000000..d4e83b8
--- /dev/null
+++ b/Lib/fontTools/cu2qu/cli.py
@@ -0,0 +1,177 @@
+import os
+import argparse
+import logging
+import shutil
+import multiprocessing as mp
+from contextlib import closing
+from functools import partial
+
+import fontTools
+from .ufo import font_to_quadratic, fonts_to_quadratic
+
+ufo_module = None
+try:
+    import ufoLib2 as ufo_module
+except ImportError:
+    try:
+        import defcon as ufo_module
+    except ImportError as e:
+        pass
+
+
+logger = logging.getLogger("fontTools.cu2qu")
+
+
+def _cpu_count():
+    try:
+        return mp.cpu_count()
+    except NotImplementedError:  # pragma: no cover
+        return 1
+
+
+def _font_to_quadratic(input_path, output_path=None, **kwargs):
+    ufo = ufo_module.Font(input_path)
+    logger.info('Converting curves for %s', input_path)
+    if font_to_quadratic(ufo, **kwargs):
+        logger.info("Saving %s", output_path)
+        if output_path:
+            ufo.save(output_path)
+        else:
+            ufo.save()  # save in-place
+    elif output_path:
+        _copytree(input_path, output_path)
+
+
+def _samepath(path1, path2):
+    # TODO on python3+, there's os.path.samefile
+    path1 = os.path.normcase(os.path.abspath(os.path.realpath(path1)))
+    path2 = os.path.normcase(os.path.abspath(os.path.realpath(path2)))
+    return path1 == path2
+
+
+def _copytree(input_path, output_path):
+    if _samepath(input_path, output_path):
+        logger.debug("input and output paths are the same file; skipped copy")
+        return
+    if os.path.exists(output_path):
+        shutil.rmtree(output_path)
+    shutil.copytree(input_path, output_path)
+
+
+def main(args=None):
+    """Convert a UFO font from cubic to quadratic curves"""
+    parser = argparse.ArgumentParser(prog="cu2qu")
+    parser.add_argument(
+        "--version", action="version", version=fontTools.__version__)
+    parser.add_argument(
+        "infiles",
+        nargs="+",
+        metavar="INPUT",
+        help="one or more input UFO source file(s).")
+    parser.add_argument("-v", "--verbose", action="count", default=0)
+    parser.add_argument(
+        "-e",
+        "--conversion-error",
+        type=float,
+        metavar="ERROR",
+        default=None,
+        help="maxiumum approximation error measured in EM (default: 0.001)")
+    parser.add_argument(
+        "--keep-direction",
+        dest="reverse_direction",
+        action="store_false",
+        help="do not reverse the contour direction")
+
+    mode_parser = parser.add_mutually_exclusive_group()
+    mode_parser.add_argument(
+        "-i",
+        "--interpolatable",
+        action="store_true",
+        help="whether curve conversion should keep interpolation compatibility"
+    )
+    mode_parser.add_argument(
+        "-j",
+        "--jobs",
+        type=int,
+        nargs="?",
+        default=1,
+        const=_cpu_count(),
+        metavar="N",
+        help="Convert using N multiple processes (default: %(default)s)")
+
+    output_parser = parser.add_mutually_exclusive_group()
+    output_parser.add_argument(
+        "-o",
+        "--output-file",
+        default=None,
+        metavar="OUTPUT",
+        help=("output filename for the converted UFO. By default fonts are "
+              "modified in place. This only works with a single input."))
+    output_parser.add_argument(
+        "-d",
+        "--output-dir",
+        default=None,
+        metavar="DIRECTORY",
+        help="output directory where to save converted UFOs")
+
+    options = parser.parse_args(args)
+
+    if ufo_module is None:
+        parser.error("Either ufoLib2 or defcon are required to run this script.")
+
+    if not options.verbose:
+        level = "WARNING"
+    elif options.verbose == 1:
+        level = "INFO"
+    else:
+        level = "DEBUG"
+    logging.basicConfig(level=level)
+
+    if len(options.infiles) > 1 and options.output_file:
+        parser.error("-o/--output-file can't be used with multile inputs")
+
+    if options.output_dir:
+        output_dir = options.output_dir
+        if not os.path.exists(output_dir):
+            os.mkdir(output_dir)
+        elif not os.path.isdir(output_dir):
+            parser.error("'%s' is not a directory" % output_dir)
+        output_paths = [
+            os.path.join(output_dir, os.path.basename(p))
+            for p in options.infiles
+        ]
+    elif options.output_file:
+        output_paths = [options.output_file]
+    else:
+        # save in-place
+        output_paths = [None] * len(options.infiles)
+
+    kwargs = dict(dump_stats=options.verbose > 0,
+                  max_err_em=options.conversion_error,
+                  reverse_direction=options.reverse_direction)
+
+    if options.interpolatable:
+        logger.info('Converting curves compatibly')
+        ufos = [ufo_module.Font(infile) for infile in options.infiles]
+        if fonts_to_quadratic(ufos, **kwargs):
+            for ufo, output_path in zip(ufos, output_paths):
+                logger.info("Saving %s", output_path)
+                if output_path:
+                    ufo.save(output_path)
+                else:
+                    ufo.save()
+        else:
+            for input_path, output_path in zip(options.infiles, output_paths):
+                if output_path:
+                    _copytree(input_path, output_path)
+    else:
+        jobs = min(len(options.infiles),
+                   options.jobs) if options.jobs > 1 else 1
+        if jobs > 1:
+            func = partial(_font_to_quadratic, **kwargs)
+            logger.info('Running %d parallel processes', jobs)
+            with closing(mp.Pool(jobs)) as pool:
+                pool.starmap(func, zip(options.infiles, output_paths))
+        else:
+            for input_path, output_path in zip(options.infiles, output_paths):
+                _font_to_quadratic(input_path, output_path, **kwargs)
diff --git a/Lib/fontTools/cu2qu/cu2qu.py b/Lib/fontTools/cu2qu/cu2qu.py
new file mode 100644
index 0000000..c9ce93a
--- /dev/null
+++ b/Lib/fontTools/cu2qu/cu2qu.py
@@ -0,0 +1,496 @@
+#cython: language_level=3
+#distutils: define_macros=CYTHON_TRACE_NOGIL=1
+
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+try:
+    import cython
+except ImportError:
+    # if cython not installed, use mock module with no-op decorators and types
+    from fontTools.misc import cython
+
+import math
+
+from .errors import Error as Cu2QuError, ApproxNotFoundError
+
+
+__all__ = ['curve_to_quadratic', 'curves_to_quadratic']
+
+MAX_N = 100
+
+NAN = float("NaN")
+
+
+if cython.compiled:
+    # Yep, I'm compiled.
+    COMPILED = True
+else:
+    # Just a lowly interpreted script.
+    COMPILED = False
+
+
+@cython.cfunc
+@cython.inline
+@cython.returns(cython.double)
+@cython.locals(v1=cython.complex, v2=cython.complex)
+def dot(v1, v2):
+    """Return the dot product of two vectors.
+
+    Args:
+        v1 (complex): First vector.
+        v2 (complex): Second vector.
+
+    Returns:
+        double: Dot product.
+    """
+    return (v1 * v2.conjugate()).real
+
+
+@cython.cfunc
+@cython.inline
+@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex)
+@cython.locals(_1=cython.complex, _2=cython.complex, _3=cython.complex, _4=cython.complex)
+def calc_cubic_points(a, b, c, d):
+    _1 = d
+    _2 = (c / 3.0) + d
+    _3 = (b + c) / 3.0 + _2
+    _4 = a + d + c + b
+    return _1, _2, _3, _4
+
+
+@cython.cfunc
+@cython.inline
+@cython.locals(p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex)
+@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex)
+def calc_cubic_parameters(p0, p1, p2, p3):
+    c = (p1 - p0) * 3.0
+    b = (p2 - p1) * 3.0 - c
+    d = p0
+    a = p3 - d - c - b
+    return a, b, c, d
+
+
+@cython.cfunc
+@cython.locals(p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex)
+def split_cubic_into_n_iter(p0, p1, p2, p3, n):
+    """Split a cubic Bezier into n equal parts.
+
+    Splits the curve into `n` equal parts by curve time.
+    (t=0..1/n, t=1/n..2/n, ...)
+
+    Args:
+        p0 (complex): Start point of curve.
+        p1 (complex): First handle of curve.
+        p2 (complex): Second handle of curve.
+        p3 (complex): End point of curve.
+
+    Returns:
+        An iterator yielding the control points (four complex values) of the
+        subcurves.
+    """
+    # Hand-coded special-cases
+    if n == 2:
+        return iter(split_cubic_into_two(p0, p1, p2, p3))
+    if n == 3:
+        return iter(split_cubic_into_three(p0, p1, p2, p3))
+    if n == 4:
+        a, b = split_cubic_into_two(p0, p1, p2, p3)
+        return iter(split_cubic_into_two(*a) + split_cubic_into_two(*b))
+    if n == 6:
+        a, b = split_cubic_into_two(p0, p1, p2, p3)
+        return iter(split_cubic_into_three(*a) + split_cubic_into_three(*b))
+
+    return _split_cubic_into_n_gen(p0,p1,p2,p3,n)
+
+
+@cython.locals(p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex, n=cython.int)
+@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex)
+@cython.locals(dt=cython.double, delta_2=cython.double, delta_3=cython.double, i=cython.int)
+@cython.locals(a1=cython.complex, b1=cython.complex, c1=cython.complex, d1=cython.complex)
+def _split_cubic_into_n_gen(p0, p1, p2, p3, n):
+    a, b, c, d = calc_cubic_parameters(p0, p1, p2, p3)
+    dt = 1 / n
+    delta_2 = dt * dt
+    delta_3 = dt * delta_2
+    for i in range(n):
+        t1 = i * dt
+        t1_2 = t1 * t1
+        # calc new a, b, c and d
+        a1 = a * delta_3
+        b1 = (3*a*t1 + b) * delta_2
+        c1 = (2*b*t1 + c + 3*a*t1_2) * dt
+        d1 = a*t1*t1_2 + b*t1_2 + c*t1 + d
+        yield calc_cubic_points(a1, b1, c1, d1)
+
+
+@cython.locals(p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex)
+@cython.locals(mid=cython.complex, deriv3=cython.complex)
+def split_cubic_into_two(p0, p1, p2, p3):
+    """Split a cubic Bezier into two equal parts.
+
+    Splits the curve into two equal parts at t = 0.5
+
+    Args:
+        p0 (complex): Start point of curve.
+        p1 (complex): First handle of curve.
+        p2 (complex): Second handle of curve.
+        p3 (complex): End point of curve.
+
+    Returns:
+        tuple: Two cubic Beziers (each expressed as a tuple of four complex
+        values).
+    """
+    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))
+
+
+@cython.locals(p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex, _27=cython.double)
+@cython.locals(mid1=cython.complex, deriv1=cython.complex, mid2=cython.complex, deriv2=cython.complex)
+def split_cubic_into_three(p0, p1, p2, p3, _27=1/27):
+    """Split a cubic Bezier into three equal parts.
+
+    Splits the curve into three equal parts at t = 1/3 and t = 2/3
+
+    Args:
+        p0 (complex): Start point of curve.
+        p1 (complex): First handle of curve.
+        p2 (complex): Second handle of curve.
+        p3 (complex): End point of curve.
+
+    Returns:
+        tuple: Three cubic Beziers (each expressed as a tuple of four complex
+        values).
+    """
+    # we define 1/27 as a keyword argument so that it will be evaluated only
+    # once but still in the scope of this function
+    mid1 = (8*p0 + 12*p1 + 6*p2 + p3) * _27
+    deriv1 = (p3 + 3*p2 - 4*p0) * _27
+    mid2 = (p0 + 6*p1 + 12*p2 + 8*p3) * _27
+    deriv2 = (4*p3 - 3*p1 - p0) * _27
+    return ((p0, (2*p0 + p1) / 3.0, mid1 - deriv1, mid1),
+            (mid1, mid1 + deriv1, mid2 - deriv2, mid2),
+            (mid2, mid2 + deriv2, (p2 + 2*p3) / 3.0, p3))
+
+
+@cython.returns(cython.complex)
+@cython.locals(t=cython.double, p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex)
+@cython.locals(_p1=cython.complex, _p2=cython.complex)
+def cubic_approx_control(t, p0, p1, p2, p3):
+    """Approximate a cubic Bezier using a quadratic one.
+
+    Args:
+        t (double): Position of control point.
+        p0 (complex): Start point of curve.
+        p1 (complex): First handle of curve.
+        p2 (complex): Second handle of curve.
+        p3 (complex): End point of curve.
+
+    Returns:
+        complex: Location of candidate control point on quadratic curve.
+    """
+    _p1 = p0 + (p1 - p0) * 1.5
+    _p2 = p3 + (p2 - p3) * 1.5
+    return _p1 + (_p2 - _p1) * t
+
+
+@cython.returns(cython.complex)
+@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex)
+@cython.locals(ab=cython.complex, cd=cython.complex, p=cython.complex, h=cython.double)
+def calc_intersect(a, b, c, d):
+    """Calculate the intersection of two lines.
+
+    Args:
+        a (complex): Start point of first line.
+        b (complex): End point of first line.
+        c (complex): Start point of second line.
+        d (complex): End point of second line.
+
+    Returns:
+        complex: Location of intersection if one present, ``complex(NaN,NaN)``
+        if no intersection was found.
+    """
+    ab = b - a
+    cd = d - c
+    p = ab * 1j
+    try:
+        h = dot(p, a - c) / dot(p, cd)
+    except ZeroDivisionError:
+        return complex(NAN, NAN)
+    return c + cd * h
+
+
+@cython.cfunc
+@cython.returns(cython.int)
+@cython.locals(tolerance=cython.double, p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex)
+@cython.locals(mid=cython.complex, deriv3=cython.complex)
+def cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance):
+    """Check if a cubic Bezier lies within a given distance of the origin.
+
+    "Origin" means *the* origin (0,0), not the start of the curve. Note that no
+    checks are made on the start and end positions of the curve; this function
+    only checks the inside of the curve.
+
+    Args:
+        p0 (complex): Start point of curve.
+        p1 (complex): First handle of curve.
+        p2 (complex): Second handle of curve.
+        p3 (complex): End point of curve.
+        tolerance (double): Distance from origin.
+
+    Returns:
+        bool: True if the cubic Bezier ``p`` entirely lies within a distance
+        ``tolerance`` of the origin, False otherwise.
+    """
+    # First check p2 then p1, as p2 has higher error early on.
+    if abs(p2) <= tolerance and abs(p1) <= tolerance:
+        return True
+
+    # Split.
+    mid = (p0 + 3 * (p1 + p2) + p3) * .125
+    if abs(mid) > tolerance:
+        return False
+    deriv3 = (p3 + p2 - p1 - p0) * .125
+    return (cubic_farthest_fit_inside(p0, (p0+p1)*.5, mid-deriv3, mid, tolerance) and
+            cubic_farthest_fit_inside(mid, mid+deriv3, (p2+p3)*.5, p3, tolerance))
+
+
+@cython.cfunc
+@cython.locals(tolerance=cython.double, _2_3=cython.double)
+@cython.locals(q1=cython.complex, c0=cython.complex, c1=cython.complex, c2=cython.complex, c3=cython.complex)
+def cubic_approx_quadratic(cubic, tolerance, _2_3=2/3):
+    """Approximate a cubic Bezier with a single quadratic within a given tolerance.
+
+    Args:
+        cubic (sequence): Four complex numbers representing control points of
+            the cubic Bezier curve.
+        tolerance (double): Permitted deviation from the original curve.
+
+    Returns:
+        Three complex numbers representing control points of the quadratic
+        curve if it fits within the given tolerance, or ``None`` if no suitable
+        curve could be calculated.
+    """
+    # we define 2/3 as a keyword argument so that it will be evaluated only
+    # once but still in the scope of this function
+
+    q1 = calc_intersect(*cubic)
+    if math.isnan(q1.imag):
+        return None
+    c0 = cubic[0]
+    c3 = cubic[3]
+    c1 = c0 + (q1 - c0) * _2_3
+    c2 = c3 + (q1 - c3) * _2_3
+    if not cubic_farthest_fit_inside(0,
+                                     c1 - cubic[1],
+                                     c2 - cubic[2],
+                                     0, tolerance):
+        return None
+    return c0, q1, c3
+
+
+@cython.cfunc
+@cython.locals(n=cython.int, tolerance=cython.double, _2_3=cython.double)
+@cython.locals(i=cython.int)
+@cython.locals(c0=cython.complex, c1=cython.complex, c2=cython.complex, c3=cython.complex)
+@cython.locals(q0=cython.complex, q1=cython.complex, next_q1=cython.complex, q2=cython.complex, d1=cython.complex)
+def cubic_approx_spline(cubic, n, tolerance, _2_3=2/3):
+    """Approximate a cubic Bezier curve with a spline of n quadratics.
+
+    Args:
+        cubic (sequence): Four complex numbers representing control points of
+            the cubic Bezier curve.
+        n (int): Number of quadratic Bezier curves in the spline.
+        tolerance (double): Permitted deviation from the original curve.
+
+    Returns:
+        A list of ``n+2`` complex numbers, representing control points of the
+        quadratic spline if it fits within the given tolerance, or ``None`` if
+        no suitable spline could be calculated.
+    """
+    # we define 2/3 as a keyword argument so that it will be evaluated only
+    # once but still in the scope of this function
+
+    if n == 1:
+        return cubic_approx_quadratic(cubic, tolerance)
+
+    cubics = split_cubic_into_n_iter(cubic[0], cubic[1], cubic[2], cubic[3], n)
+
+    # calculate the spline of quadratics and check errors at the same time.
+    next_cubic = next(cubics)
+    next_q1 = cubic_approx_control(0, *next_cubic)
+    q2 = cubic[0]
+    d1 = 0j
+    spline = [cubic[0], next_q1]
+    for i in range(1, n+1):
+
+        # Current cubic to convert
+        c0, c1, c2, c3 = next_cubic
+
+        # Current quadratic approximation of current cubic
+        q0 = q2
+        q1 = next_q1
+        if i < n:
+            next_cubic = next(cubics)
+            next_q1 = cubic_approx_control(i / (n-1), *next_cubic)
+            spline.append(next_q1)
+            q2 = (q1 + next_q1) * .5
+        else:
+            q2 = c3
+
+        # End-point deltas
+        d0 = d1
+        d1 = q2 - c3
+
+        if (abs(d1) > tolerance or
+            not cubic_farthest_fit_inside(d0,
+                                          q0 + (q1 - q0) * _2_3 - c1,
+                                          q2 + (q1 - q2) * _2_3 - c2,
+                                          d1,
+                                          tolerance)):
+            return None
+    spline.append(cubic[3])
+
+    return spline
+
+
+@cython.locals(max_err=cython.double)
+@cython.locals(n=cython.int)
+def curve_to_quadratic(curve, max_err):
+    """Approximate a cubic Bezier curve with a spline of n quadratics.
+
+    Args:
+        cubic (sequence): Four 2D tuples representing control points of
+            the cubic Bezier curve.
+        max_err (double): Permitted deviation from the original curve.
+
+    Returns:
+        A list of 2D tuples, representing control points of the quadratic
+        spline if it fits within the given tolerance, or ``None`` if no
+        suitable spline could be calculated.
+    """
+
+    curve = [complex(*p) for p in curve]
+
+    for n in range(1, MAX_N + 1):
+        spline = cubic_approx_spline(curve, n, max_err)
+        if spline is not None:
+            # done. go home
+            return [(s.real, s.imag) for s in spline]
+
+    raise ApproxNotFoundError(curve)
+
+
+
+@cython.locals(l=cython.int, last_i=cython.int, i=cython.int)
+def curves_to_quadratic(curves, max_errors):
+    """Return quadratic Bezier splines approximating the input cubic Beziers.
+
+    Args:
+        curves: A sequence of *n* curves, each curve being a sequence of four
+            2D tuples.
+        max_errors: A sequence of *n* floats representing the maximum permissible
+            deviation from each of the cubic Bezier curves.
+
+    Example::
+
+        >>> curves_to_quadratic( [
+        ...   [ (50,50), (100,100), (150,100), (200,50) ],
+        ...   [ (75,50), (120,100), (150,75),  (200,60) ]
+        ... ], [1,1] )
+        [[(50.0, 50.0), (75.0, 75.0), (125.0, 91.66666666666666), (175.0, 75.0), (200.0, 50.0)], [(75.0, 50.0), (97.5, 75.0), (135.41666666666666, 82.08333333333333), (175.0, 67.5), (200.0, 60.0)]]
+
+    The returned splines have "implied oncurve points" suitable for use in
+    TrueType ``glif`` outlines - i.e. in the first spline returned above,
+    the first quadratic segment runs from (50,50) to
+    ( (75 + 125)/2 , (120 + 91.666..)/2 ) = (100, 83.333...).
+
+    Returns:
+        A list of splines, each spline being a list of 2D tuples.
+
+    Raises:
+        fontTools.cu2qu.Errors.ApproxNotFoundError: if no suitable approximation
+        can be found for all curves with the given parameters.
+    """
+
+    curves = [[complex(*p) for p in curve] for curve in curves]
+    assert len(max_errors) == len(curves)
+
+    l = len(curves)
+    splines = [None] * l
+    last_i = i = 0
+    n = 1
+    while True:
+        spline = cubic_approx_spline(curves[i], n, max_errors[i])
+        if spline is None:
+            if n == MAX_N:
+                break
+            n += 1
+            last_i = i
+            continue
+        splines[i] = spline
+        i = (i + 1) % l
+        if i == last_i:
+            # done. go home
+            return [[(s.real, s.imag) for s in spline] for spline in splines]
+
+    raise ApproxNotFoundError(curves)
+
+
+if __name__ == '__main__':
+    import random
+    import timeit
+
+    MAX_ERR = 5
+
+    def generate_curve():
+        return [
+            tuple(float(random.randint(0, 2048)) for coord in range(2))
+            for point in range(4)]
+
+    def setup_curve_to_quadratic():
+        return generate_curve(), MAX_ERR
+
+    def setup_curves_to_quadratic():
+        num_curves = 3
+        return (
+            [generate_curve() for curve in range(num_curves)],
+            [MAX_ERR] * num_curves)
+
+    def run_benchmark(
+            benchmark_module, module, function, setup_suffix='', repeat=5, number=1000):
+        setup_func = 'setup_' + function
+        if setup_suffix:
+            print('%s with %s:' % (function, setup_suffix), end='')
+            setup_func += '_' + setup_suffix
+        else:
+            print('%s:' % function, end='')
+
+        def wrapper(function, setup_func):
+            function = globals()[function]
+            setup_func = globals()[setup_func]
+            def wrapped():
+                return function(*setup_func())
+            return wrapped
+        results = timeit.repeat(wrapper(function, setup_func), repeat=repeat, number=number)
+        print('\t%5.1fus' % (min(results) * 1000000. / number))
+
+    def main():
+        run_benchmark('cu2qu.benchmark', 'cu2qu', 'curve_to_quadratic')
+        run_benchmark('cu2qu.benchmark', 'cu2qu', 'curves_to_quadratic')
+
+    random.seed(1)
+    main()
diff --git a/Lib/fontTools/cu2qu/errors.py b/Lib/fontTools/cu2qu/errors.py
new file mode 100644
index 0000000..74c4c22
--- /dev/null
+++ b/Lib/fontTools/cu2qu/errors.py
@@ -0,0 +1,76 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+class Error(Exception):
+    """Base Cu2Qu exception class for all other errors."""
+
+
+class ApproxNotFoundError(Error):
+    def __init__(self, curve):
+        message = "no approximation found: %s" % curve
+        super().__init__(message)
+        self.curve = curve
+
+
+class UnequalZipLengthsError(Error):
+    pass
+
+
+class IncompatibleGlyphsError(Error):
+    def __init__(self, glyphs):
+        assert len(glyphs) > 1
+        self.glyphs = glyphs
+        names = set(repr(g.name) for g in glyphs)
+        if len(names) > 1:
+            self.combined_name = "{%s}" % ", ".join(sorted(names))
+        else:
+            self.combined_name = names.pop()
+
+    def __repr__(self):
+        return "<%s %s>" % (type(self).__name__, self.combined_name)
+
+
+class IncompatibleSegmentNumberError(IncompatibleGlyphsError):
+    def __str__(self):
+        return "Glyphs named %s have different number of segments" % (
+            self.combined_name
+        )
+
+
+class IncompatibleSegmentTypesError(IncompatibleGlyphsError):
+    def __init__(self, glyphs, segments):
+        IncompatibleGlyphsError.__init__(self, glyphs)
+        self.segments = segments
+
+    def __str__(self):
+        lines = []
+        ndigits = len(str(max(self.segments)))
+        for i, tags in sorted(self.segments.items()):
+            lines.append(
+                "%s: (%s)" % (str(i).rjust(ndigits), ", ".join(repr(t) for t in tags))
+            )
+        return "Glyphs named %s have incompatible segment types:\n  %s" % (
+            self.combined_name,
+            "\n  ".join(lines),
+        )
+
+
+class IncompatibleFontsError(Error):
+    def __init__(self, glyph_errors):
+        self.glyph_errors = glyph_errors
+
+    def __str__(self):
+        return "fonts contains incompatible glyphs: %s" % (
+            ", ".join(repr(g) for g in sorted(self.glyph_errors.keys()))
+        )
diff --git a/Lib/fontTools/cu2qu/ufo.py b/Lib/fontTools/cu2qu/ufo.py
new file mode 100644
index 0000000..447de7b
--- /dev/null
+++ b/Lib/fontTools/cu2qu/ufo.py
@@ -0,0 +1,324 @@
+# Copyright 2015 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+
+"""Converts cubic bezier curves to quadratic splines.
+
+Conversion is performed such that the quadratic splines keep the same end-curve
+tangents as the original cubics. The approach is iterative, increasing the
+number of segments for a spline until the error gets below a bound.
+
+Respective curves from multiple fonts will be converted at once to ensure that
+the resulting splines are interpolation-compatible.
+"""
+
+import logging
+from fontTools.pens.basePen import AbstractPen
+from fontTools.pens.pointPen import PointToSegmentPen
+from fontTools.pens.reverseContourPen import ReverseContourPen
+
+from . import curves_to_quadratic
+from .errors import (
+    UnequalZipLengthsError, IncompatibleSegmentNumberError,
+    IncompatibleSegmentTypesError, IncompatibleGlyphsError,
+    IncompatibleFontsError)
+
+
+__all__ = ['fonts_to_quadratic', 'font_to_quadratic']
+
+# The default approximation error below is a relative value (1/1000 of the EM square).
+# Later on, we convert it to absolute font units by multiplying it by a font's UPEM
+# (see fonts_to_quadratic).
+DEFAULT_MAX_ERR = 0.001
+CURVE_TYPE_LIB_KEY = "com.github.googlei18n.cu2qu.curve_type"
+
+logger = logging.getLogger(__name__)
+
+
+_zip = zip
+def zip(*args):
+    """Ensure each argument to zip has the same length. Also make sure a list is
+    returned for python 2/3 compatibility.
+    """
+
+    if len(set(len(a) for a in args)) != 1:
+        raise UnequalZipLengthsError(*args)
+    return list(_zip(*args))
+
+
+class GetSegmentsPen(AbstractPen):
+    """Pen to collect segments into lists of points for conversion.
+
+    Curves always include their initial on-curve point, so some points are
+    duplicated between segments.
+    """
+
+    def __init__(self):
+        self._last_pt = None
+        self.segments = []
+
+    def _add_segment(self, tag, *args):
+        if tag in ['move', 'line', 'qcurve', 'curve']:
+            self._last_pt = args[-1]
+        self.segments.append((tag, args))
+
+    def moveTo(self, pt):
+        self._add_segment('move', pt)
+
+    def lineTo(self, pt):
+        self._add_segment('line', pt)
+
+    def qCurveTo(self, *points):
+        self._add_segment('qcurve', self._last_pt, *points)
+
+    def curveTo(self, *points):
+        self._add_segment('curve', self._last_pt, *points)
+
+    def closePath(self):
+        self._add_segment('close')
+
+    def endPath(self):
+        self._add_segment('end')
+
+    def addComponent(self, glyphName, transformation):
+        pass
+
+
+def _get_segments(glyph):
+    """Get a glyph's segments as extracted by GetSegmentsPen."""
+
+    pen = GetSegmentsPen()
+    # glyph.draw(pen)
+    # We can't simply draw the glyph with the pen, but we must initialize the
+    # PointToSegmentPen explicitly with outputImpliedClosingLine=True.
+    # By default PointToSegmentPen does not outputImpliedClosingLine -- unless
+    # last and first point on closed contour are duplicated. Because we are
+    # converting multiple glyphs at the same time, we want to make sure
+    # this function returns the same number of segments, whether or not
+    # the last and first point overlap.
+    # https://github.com/googlefonts/fontmake/issues/572
+    # https://github.com/fonttools/fonttools/pull/1720
+    pointPen = PointToSegmentPen(pen, outputImpliedClosingLine=True)
+    glyph.drawPoints(pointPen)
+    return pen.segments
+
+
+def _set_segments(glyph, segments, reverse_direction):
+    """Draw segments as extracted by GetSegmentsPen back to a glyph."""
+
+    glyph.clearContours()
+    pen = glyph.getPen()
+    if reverse_direction:
+        pen = ReverseContourPen(pen)
+    for tag, args in segments:
+        if tag == 'move':
+            pen.moveTo(*args)
+        elif tag == 'line':
+            pen.lineTo(*args)
+        elif tag == 'curve':
+            pen.curveTo(*args[1:])
+        elif tag == 'qcurve':
+            pen.qCurveTo(*args[1:])
+        elif tag == 'close':
+            pen.closePath()
+        elif tag == 'end':
+            pen.endPath()
+        else:
+            raise AssertionError('Unhandled segment type "%s"' % tag)
+
+
+def _segments_to_quadratic(segments, max_err, stats):
+    """Return quadratic approximations of cubic segments."""
+
+    assert all(s[0] == 'curve' for s in segments), 'Non-cubic given to convert'
+
+    new_points = curves_to_quadratic([s[1] for s in segments], max_err)
+    n = len(new_points[0])
+    assert all(len(s) == n for s in new_points[1:]), 'Converted incompatibly'
+
+    spline_length = str(n - 2)
+    stats[spline_length] = stats.get(spline_length, 0) + 1
+
+    return [('qcurve', p) for p in new_points]
+
+
+def _glyphs_to_quadratic(glyphs, max_err, reverse_direction, stats):
+    """Do the actual conversion of a set of compatible glyphs, after arguments
+    have been set up.
+
+    Return True if the glyphs were modified, else return False.
+    """
+
+    try:
+        segments_by_location = zip(*[_get_segments(g) for g in glyphs])
+    except UnequalZipLengthsError:
+        raise IncompatibleSegmentNumberError(glyphs)
+    if not any(segments_by_location):
+        return False
+
+    # always modify input glyphs if reverse_direction is True
+    glyphs_modified = reverse_direction
+
+    new_segments_by_location = []
+    incompatible = {}
+    for i, segments in enumerate(segments_by_location):
+        tag = segments[0][0]
+        if not all(s[0] == tag for s in segments[1:]):
+            incompatible[i] = [s[0] for s in segments]
+        elif tag == 'curve':
+            segments = _segments_to_quadratic(segments, max_err, stats)
+            glyphs_modified = True
+        new_segments_by_location.append(segments)
+
+    if glyphs_modified:
+        new_segments_by_glyph = zip(*new_segments_by_location)
+        for glyph, new_segments in zip(glyphs, new_segments_by_glyph):
+            _set_segments(glyph, new_segments, reverse_direction)
+
+    if incompatible:
+        raise IncompatibleSegmentTypesError(glyphs, segments=incompatible)
+    return glyphs_modified
+
+
+def glyphs_to_quadratic(
+        glyphs, max_err=None, reverse_direction=False, stats=None):
+    """Convert the curves of a set of compatible of glyphs to quadratic.
+
+    All curves will be converted to quadratic at once, ensuring interpolation
+    compatibility. If this is not required, calling glyphs_to_quadratic with one
+    glyph at a time may yield slightly more optimized results.
+
+    Return True if glyphs were modified, else return False.
+
+    Raises IncompatibleGlyphsError if glyphs have non-interpolatable outlines.
+    """
+    if stats is None:
+        stats = {}
+
+    if not max_err:
+        # assume 1000 is the default UPEM
+        max_err = DEFAULT_MAX_ERR * 1000
+
+    if isinstance(max_err, (list, tuple)):
+        max_errors = max_err
+    else:
+        max_errors = [max_err] * len(glyphs)
+    assert len(max_errors) == len(glyphs)
+
+    return _glyphs_to_quadratic(glyphs, max_errors, reverse_direction, stats)
+
+
+def fonts_to_quadratic(
+        fonts, max_err_em=None, max_err=None, reverse_direction=False,
+        stats=None, dump_stats=False, remember_curve_type=True):
+    """Convert the curves of a collection of fonts to quadratic.
+
+    All curves will be converted to quadratic at once, ensuring interpolation
+    compatibility. If this is not required, calling fonts_to_quadratic with one
+    font at a time may yield slightly more optimized results.
+
+    Return True if fonts were modified, else return False.
+
+    By default, cu2qu stores the curve type in the fonts' lib, under a private
+    key "com.github.googlei18n.cu2qu.curve_type", and will not try to convert
+    them again if the curve type is already set to "quadratic".
+    Setting 'remember_curve_type' to False disables this optimization.
+
+    Raises IncompatibleFontsError if same-named glyphs from different fonts
+    have non-interpolatable outlines.
+    """
+
+    if remember_curve_type:
+        curve_types = {f.lib.get(CURVE_TYPE_LIB_KEY, "cubic") for f in fonts}
+        if len(curve_types) == 1:
+            curve_type = next(iter(curve_types))
+            if curve_type == "quadratic":
+                logger.info("Curves already converted to quadratic")
+                return False
+            elif curve_type == "cubic":
+                pass  # keep converting
+            else:
+                raise NotImplementedError(curve_type)
+        elif len(curve_types) > 1:
+            # going to crash later if they do differ
+            logger.warning("fonts may contain different curve types")
+
+    if stats is None:
+        stats = {}
+
+    if max_err_em and max_err:
+        raise TypeError('Only one of max_err and max_err_em can be specified.')
+    if not (max_err_em or max_err):
+        max_err_em = DEFAULT_MAX_ERR
+
+    if isinstance(max_err, (list, tuple)):
+        assert len(max_err) == len(fonts)
+        max_errors = max_err
+    elif max_err:
+        max_errors = [max_err] * len(fonts)
+
+    if isinstance(max_err_em, (list, tuple)):
+        assert len(fonts) == len(max_err_em)
+        max_errors = [f.info.unitsPerEm * e
+                      for f, e in zip(fonts, max_err_em)]
+    elif max_err_em:
+        max_errors = [f.info.unitsPerEm * max_err_em for f in fonts]
+
+    modified = False
+    glyph_errors = {}
+    for name in set().union(*(f.keys() for f in fonts)):
+        glyphs = []
+        cur_max_errors = []
+        for font, error in zip(fonts, max_errors):
+            if name in font:
+                glyphs.append(font[name])
+                cur_max_errors.append(error)
+        try:
+            modified |= _glyphs_to_quadratic(
+                glyphs, cur_max_errors, reverse_direction, stats)
+        except IncompatibleGlyphsError as exc:
+            logger.error(exc)
+            glyph_errors[name] = exc
+
+    if glyph_errors:
+        raise IncompatibleFontsError(glyph_errors)
+
+    if modified and dump_stats:
+        spline_lengths = sorted(stats.keys())
+        logger.info('New spline lengths: %s' % (', '.join(
+                    '%s: %d' % (l, stats[l]) for l in spline_lengths)))
+
+    if remember_curve_type:
+        for font in fonts:
+            curve_type = font.lib.get(CURVE_TYPE_LIB_KEY, "cubic")
+            if curve_type != "quadratic":
+                font.lib[CURVE_TYPE_LIB_KEY] = "quadratic"
+                modified = True
+    return modified
+
+
+def glyph_to_quadratic(glyph, **kwargs):
+    """Convenience wrapper around glyphs_to_quadratic, for just one glyph.
+    Return True if the glyph was modified, else return False.
+    """
+
+    return glyphs_to_quadratic([glyph], **kwargs)
+
+
+def font_to_quadratic(font, **kwargs):
+    """Convenience wrapper around fonts_to_quadratic, for just one font.
+    Return True if the font was modified, else return False.
+    """
+
+    return fonts_to_quadratic([font], **kwargs)
diff --git a/Lib/fontTools/designspaceLib/__init__.py b/Lib/fontTools/designspaceLib/__init__.py
new file mode 100644
index 0000000..3bb2bbf
--- /dev/null
+++ b/Lib/fontTools/designspaceLib/__init__.py
@@ -0,0 +1,1407 @@
+# -*- coding: utf-8 -*-
+
+from fontTools.misc.py23 import tobytes, tostr
+from fontTools.misc.loggingTools import LogMixin
+import collections
+from io import BytesIO, StringIO
+import os
+import posixpath
+from fontTools.misc import etree as ET
+from fontTools.misc import plistlib
+
+"""
+    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 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
+    elif path.startswith(r'\\'):
+        # The above transformation loses leading slashes of UNC path mounts
+        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 "")
+
+
+class AsDictMixin(object):
+
+    def asdict(self):
+        d = {}
+        for attr, value in self.__dict__.items():
+            if attr.startswith("_"):
+                continue
+            if hasattr(value, "asdict"):
+                value = value.asdict()
+            elif isinstance(value, list):
+                value = [
+                    v.asdict() if hasattr(v, "asdict") else v for v in value
+                ]
+            d[attr] = value
+        return d
+
+
+class SimpleDescriptor(AsDictMixin):
+    """ 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,
+        *,
+        filename=None,
+        path=None,
+        font=None,
+        name=None,
+        location=None,
+        layerName=None,
+        familyName=None,
+        styleName=None,
+        copyLib=False,
+        copyInfo=False,
+        copyGroups=False,
+        copyFeatures=False,
+        muteKerning=False,
+        muteInfo=False,
+        mutedGlyphNames=None,
+    ):
+        self.filename = filename
+        """The original path as found in the document."""
+
+        self.path = path
+        """The absolute path, calculated from filename."""
+
+        self.font = 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.
+        """
+
+        self.name = name
+        self.location = location
+        self.layerName = layerName
+        self.familyName = familyName
+        self.styleName = styleName
+
+        self.copyLib = copyLib
+        self.copyInfo = copyInfo
+        self.copyGroups = copyGroups
+        self.copyFeatures = copyFeatures
+        self.muteKerning = muteKerning
+        self.muteInfo = muteInfo
+        self.mutedGlyphNames = mutedGlyphNames or []
+
+    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, *, name=None, conditionSets=None, subs=None):
+        self.name = name
+        # list of lists of dict(name='aaaa', minimum=0, maximum=1000)
+        self.conditionSets = conditionSets or []
+        # list of substitutions stored as tuples of glyphnames ("a", "a.alt")
+        self.subs = subs or []
+
+
+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
+        - 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,
+        *,
+        filename=None,
+        path=None,
+        font=None,
+        name=None,
+        location=None,
+        familyName=None,
+        styleName=None,
+        postScriptFontName=None,
+        styleMapFamilyName=None,
+        styleMapStyleName=None,
+        localisedFamilyName=None,
+        localisedStyleName=None,
+        localisedStyleMapFamilyName=None,
+        localisedStyleMapStyleName=None,
+        glyphs=None,
+        kerning=True,
+        info=True,
+        lib=None,
+    ):
+        # the original path as found in the document
+        self.filename = filename
+        # the absolute path, calculated from filename
+        self.path = path
+        # Same as in SourceDescriptor.
+        self.font = font
+        self.name = name
+        self.location = location
+        self.familyName = familyName
+        self.styleName = styleName
+        self.postScriptFontName = postScriptFontName
+        self.styleMapFamilyName = styleMapFamilyName
+        self.styleMapStyleName = styleMapStyleName
+        self.localisedFamilyName = localisedFamilyName or {}
+        self.localisedStyleName = localisedStyleName or {}
+        self.localisedStyleMapFamilyName = localisedStyleMapFamilyName or {}
+        self.localisedStyleMapStyleName = localisedStyleMapStyleName or {}
+        self.glyphs = glyphs or {}
+        self.kerning = kerning
+        self.info = info
+
+        self.lib = lib or {}
+        """Custom data associated with this instance."""
+
+    path = posixpath_property("_path")
+    filename = posixpath_property("_filename")
+
+    def setStyleName(self, styleName, languageCode="en"):
+        self.localisedStyleName[languageCode] = tostr(styleName)
+
+    def getStyleName(self, languageCode="en"):
+        return self.localisedStyleName.get(languageCode)
+
+    def setFamilyName(self, familyName, languageCode="en"):
+        self.localisedFamilyName[languageCode] = tostr(familyName)
+
+    def getFamilyName(self, languageCode="en"):
+        return self.localisedFamilyName.get(languageCode)
+
+    def setStyleMapStyleName(self, styleMapStyleName, languageCode="en"):
+        self.localisedStyleMapStyleName[languageCode] = tostr(styleMapStyleName)
+
+    def getStyleMapStyleName(self, languageCode="en"):
+        return self.localisedStyleMapStyleName.get(languageCode)
+
+    def setStyleMapFamilyName(self, styleMapFamilyName, languageCode="en"):
+        self.localisedStyleMapFamilyName[languageCode] = tostr(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,
+        *,
+        tag=None,
+        name=None,
+        labelNames=None,
+        minimum=None,
+        default=None,
+        maximum=None,
+        hidden=False,
+        map=None,
+    ):
+        # opentype tag for this axis
+        self.tag = tag
+        # name of the axis used in locations
+        self.name = name
+        # names for UI purposes, if this is not a standard axis,
+        self.labelNames = labelNames or {}
+        self.minimum = minimum
+        self.maximum = maximum
+        self.default = default
+        self.hidden = hidden
+        self.map = map or []
+
+    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,
+        )
+
+    def map_forward(self, v):
+        from fontTools.varLib.models import piecewiseLinearMap
+
+        if not self.map:
+            return v
+        return piecewiseLinearMap(v, {k: v for k, v in self.map})
+
+    def map_backward(self, v):
+        from fontTools.varLib.models import piecewiseLinearMap
+
+        if not self.map:
+            return v
+        return piecewiseLinearMap(v, {v: k for k, v in 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.1"
+        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, encoding="UTF-8", xml_declaration=True):
+        if self.documentObject.axes:
+            self.root.append(ET.Element("axes"))
+        for axisObject in self.documentObject.axes:
+            self._addAxis(axisObject)
+
+        if self.documentObject.rules:
+            if getattr(self.documentObject, "rulesProcessingLast", False):
+                attributes = {"processing": "last"}
+            else:
+                attributes = {}
+            self.root.append(ET.Element("rules", attributes))
+        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)
+
+        tree = ET.ElementTree(self.root)
+        tree.write(
+            self.path,
+            encoding=encoding,
+            method='xml',
+            xml_declaration=xml_declaration,
+            pretty_print=pretty,
+        )
+
+    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[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(plistlib.totree(instanceObject.lib, indent_level=4))
+            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(plistlib.totree(dict, indent_level=2))
+        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
+
+    @classmethod
+    def fromstring(cls, string, documentObject):
+        f = BytesIO(tobytes(string, encoding="utf-8"))
+        self = cls(f, documentObject)
+        self.path = None
+        return self
+
+    def read(self):
+        self.readAxes()
+        self.readRules()
+        self.readSources()
+        self.readInstances()
+        self.readLib()
+
+    def readRules(self):
+        # we also need to read any conditions that are outside of a condition set.
+        rules = []
+        rulesElement = self.root.find(".rules")
+        if rulesElement is not None:
+            processingValue = rulesElement.attrib.get("processing", "first")
+            if processingValue not in {"first", "last"}:
+                raise DesignSpaceDocumentError(
+                    "<rules> processing attribute value is not valid: %r, "
+                    "expected 'first' or 'last'" % processingValue)
+            self.documentObject.rulesProcessingLast = processingValue == "last"
+        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.
+        axisElements = self.root.findall(".axes/axis")
+        if not axisElements:
+            return
+        for axisElement in axisElements:
+            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:
+                        axisObject.labelNames[lang] = tostr(labelNameElement.text)
+            self.documentObject.axes.append(axisObject)
+            self.axisDefaults[axisObject.name] = axisObject.default
+
+    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 self._strictAxisNames and 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 and self.documentObject.path 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 = plistlib.fromtree(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 = plistlib.fromtree(libElement[0])
+
+
+class DesignSpaceDocument(LogMixin, AsDictMixin):
+    """ 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.rulesProcessingLast = False
+        self.default = None         # name of the default master
+
+        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
+
+    @classmethod
+    def fromfile(cls, path, readerClass=None, writerClass=None):
+        self = cls(readerClass=readerClass, writerClass=writerClass)
+        self.read(path)
+        return self
+
+    @classmethod
+    def fromstring(cls, string, readerClass=None, writerClass=None):
+        self = cls(readerClass=readerClass, writerClass=writerClass)
+        reader = self.readerClass.fromstring(string, self)
+        reader.read()
+        if self.sources:
+            self.findDefault()
+        return self
+
+    def tostring(self, encoding=None):
+        if encoding is str or (
+            encoding is not None and encoding.lower() == "unicode"
+        ):
+            f = StringIO()
+            xml_declaration = False
+        elif encoding is None or encoding == "utf-8":
+            f = BytesIO()
+            encoding = "UTF-8"
+            xml_declaration = True
+        else:
+            raise ValueError("unsupported encoding: '%s'" % encoding)
+        writer = self.writerClass(f, self)
+        writer.write(encoding=encoding, xml_declaration=xml_declaration)
+        return f.getvalue()
+
+    def read(self, path):
+        if hasattr(path, "__fspath__"):  # support os.PathLike objects
+            path = path.__fspath__()
+        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):
+        if hasattr(path, "__fspath__"):  # support os.PathLike objects
+            path = path.__fspath__()
+        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.
+
+
+        """
+        assert self.path is not None
+        for descriptor in self.sources + self.instances:
+            if descriptor.path is not None:
+                # case 3 and 4: filename gets updated and relativized
+                descriptor.filename = self._posixRelativePath(descriptor.path)
+
+    def addSource(self, sourceDescriptor):
+        self.sources.append(sourceDescriptor)
+
+    def addSourceDescriptor(self, **kwargs):
+        source = self.writerClass.sourceDescriptorClass(**kwargs)
+        self.addSource(source)
+        return source
+
+    def addInstance(self, instanceDescriptor):
+        self.instances.append(instanceDescriptor)
+
+    def addInstanceDescriptor(self, **kwargs):
+        instance = self.writerClass.instanceDescriptorClass(**kwargs)
+        self.addInstance(instance)
+        return instance
+
+    def addAxis(self, axisDescriptor):
+        self.axes.append(axisDescriptor)
+
+    def addAxisDescriptor(self, **kwargs):
+        axis = self.writerClass.axisDescriptorClass(**kwargs)
+        self.addAxis(axis)
+        return axis
+
+    def addRule(self, ruleDescriptor):
+        self.rules.append(ruleDescriptor)
+
+    def addRuleDescriptor(self, **kwargs):
+        rule = self.writerClass.ruleDescriptorClass(**kwargs)
+        self.addRule(rule)
+        return rule
+
+    def newDefaultLocation(self):
+        """Return default location in design space."""
+        # 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.map_forward(
+                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):
+        """Set and return SourceDescriptor at the default location or None.
+
+        The default location is the set of all `default` values in user space
+        of all axes.
+        """
+        self.default = None
+
+        # Convert the default location from user space to design space before comparing
+        # it against the SourceDescriptor locations (always in design space).
+        default_location_design = self.newDefaultLocation()
+
+        for sourceDescriptor in self.sources:
+            if sourceDescriptor.location == default_location_design:
+                self.default = sourceDescriptor
+                return sourceDescriptor
+
+        return None
+
+    def normalizeLocation(self, location):
+        from fontTools.varLib.models import normalizeValue
+
+        new = {}
+        for axis in self.axes:
+            if axis.name not in location:
+                # skipping this dimension it seems
+                continue
+            value = location[axis.name]
+            # 'anisotropic' location, take first coord only
+            if isinstance(value, tuple):
+                value = value[0]
+            triple = [
+                axis.map_forward(v) for v in (axis.minimum, axis.default, axis.maximum)
+            ]
+            new[axis.name] = normalizeValue(value, triple)
+        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
+
+    def loadSourceFonts(self, opener, **kwargs):
+        """Ensure SourceDescriptor.font attributes are loaded, and return list of fonts.
+
+        Takes a callable which initializes a new font object (e.g. TTFont, or
+        defcon.Font, etc.) from the SourceDescriptor.path, and sets the
+        SourceDescriptor.font attribute.
+        If the font attribute is already not None, it is not loaded again.
+        Fonts with the same path are only loaded once and shared among SourceDescriptors.
+
+        For example, to load UFO sources using defcon:
+
+            designspace = DesignSpaceDocument.fromfile("path/to/my.designspace")
+            designspace.loadSourceFonts(defcon.Font)
+
+        Or to load masters as FontTools binary fonts, including extra options:
+
+            designspace.loadSourceFonts(ttLib.TTFont, recalcBBoxes=False)
+
+        Args:
+            opener (Callable): takes one required positional argument, the source.path,
+                and an optional list of keyword arguments, and returns a new font object
+                loaded from the path.
+            **kwargs: extra options passed on to the opener function.
+
+        Returns:
+            List of font objects in the order they appear in the sources list.
+        """
+        # we load fonts with the same source.path only once
+        loaded = {}
+        fonts = []
+        for source in self.sources:
+            if source.font is not None:  # font already loaded
+                fonts.append(source.font)
+                continue
+            if source.path in loaded:
+                source.font = loaded[source.path]
+            else:
+                if source.path is None:
+                    raise DesignSpaceDocumentError(
+                        "Designspace source '%s' has no 'path' attribute"
+                        % (source.name or "<Unknown>")
+                    )
+                source.font = opener(source.path, **kwargs)
+                loaded[source.path] = source.font
+            fonts.append(source.font)
+        return fonts
diff --git a/Lib/fontTools/encodings/MacRoman.py b/Lib/fontTools/encodings/MacRoman.py
index 43c58eb..25232d3 100644
--- a/Lib/fontTools/encodings/MacRoman.py
+++ b/Lib/fontTools/encodings/MacRoman.py
@@ -1,6 +1,3 @@
-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',
diff --git a/Lib/fontTools/encodings/StandardEncoding.py b/Lib/fontTools/encodings/StandardEncoding.py
index dc01ef8..810b2a0 100644
--- a/Lib/fontTools/encodings/StandardEncoding.py
+++ b/Lib/fontTools/encodings/StandardEncoding.py
@@ -1,6 +1,3 @@
-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 3f9abc9..156cb23 100644
--- a/Lib/fontTools/encodings/__init__.py
+++ b/Lib/fontTools/encodings/__init__.py
@@ -1,4 +1 @@
 """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
index 30e4691..3b1a825 100644
--- a/Lib/fontTools/encodings/codecs.py
+++ b/Lib/fontTools/encodings/codecs.py
@@ -1,8 +1,6 @@
 """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."""
+but missing from Python.  See https://github.com/fonttools/fonttools/issues/236 for details."""
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 import codecs
 import encodings
 
@@ -17,43 +15,29 @@
 		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
-
+	def _map(self, mapper, output_type, exc_type, input, errors):
+		base_error_handler = codecs.lookup_error(errors)
 		length = len(input)
-		out = b''
+		out = output_type()
 		while input:
+			# first try to use self.error as the error handler
 			try:
-				part = codecs.encode(input, self.base_encoding)
+				part = mapper(input, self.base_encoding, errors=self.name)
 				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)
+				break  # All converted
+			except exc_type as e:
+				# else convert the correct part, handle error as requested and continue
+				out += mapper(input[:e.start], self.base_encoding, self.name)
+				replacement, pos = base_error_handler(e)
 				out += replacement
 				input = input[pos:]
 		return out, length
 
+	def encode(self, input, errors='strict'):
+		return self._map(codecs.encode, bytes, UnicodeEncodeError, input, errors)
+
 	def decode(self, input, errors='strict'):
-		assert errors == 'strict'
-		return codecs.decode(input, self.base_encoding, self.name), len(input)
+		return self._map(codecs.decode, str, UnicodeDecodeError, input, errors)
 
 	def error(self, e):
 		if isinstance(e, UnicodeDecodeError):
@@ -72,35 +56,35 @@
 
 _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),
+					b"\xFC": chr(0x007C),
+					b"\x7E": chr(0x007E),
+					b"\x80": chr(0x005C),
+					b"\xA0": chr(0x00A0),
+					b"\xFD": chr(0x00A9),
+					b"\xFE": chr(0x2122),
+					b"\xFF": chr(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),
+					b"\x80": chr(0x005C),
+					b"\xA0": chr(0x00A0),
+					b"\xFD": chr(0x00A9),
+					b"\xFE": chr(0x2122),
+					b"\xFF": chr(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),
+					b"\x80": chr(0x00A0),
+					b"\x81": chr(0x20A9),
+					b"\x82": chr(0x2014),
+					b"\x83": chr(0x00A9),
+					b"\xFE": chr(0x2122),
+					b"\xFF": chr(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),
+					b"\x80": chr(0x00FC),
+					b"\xA0": chr(0x00A0),
+					b"\xFD": chr(0x00A9),
+					b"\xFE": chr(0x2122),
+					b"\xFF": chr(0x2026),
 				}),
 }
 
diff --git a/Lib/fontTools/feaLib/__main__.py b/Lib/fontTools/feaLib/__main__.py
index e446db6..99c6423 100644
--- a/Lib/fontTools/feaLib/__main__.py
+++ b/Lib/fontTools/feaLib/__main__.py
@@ -1,7 +1,6 @@
-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.feaLib.builder import addOpenTypeFeatures, Builder
+from fontTools.feaLib.error import FeatureLibError
 from fontTools import configLogger
 from fontTools.misc.cliTools import makeOutputFileName
 import sys
@@ -13,18 +12,47 @@
 
 
 def main(args=None):
+    """Add features from a feature file (.fea) into a OTF font"""
     parser = argparse.ArgumentParser(
-        description="Use fontTools to compile OpenType feature files (*.fea).")
+        description="Use fontTools to compile OpenType feature files (*.fea)."
+    )
     parser.add_argument(
-        "input_fea", metavar="FEATURES", help="Path to the feature file")
+        "input_fea", metavar="FEATURES", help="Path to the feature file"
+    )
     parser.add_argument(
-        "input_font", metavar="INPUT_FONT", help="Path to the input font")
+        "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.")
+        "-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)
+        "-t",
+        "--tables",
+        metavar="TABLE_TAG",
+        choices=Builder.supportedTables,
+        nargs="+",
+        help="Specify the table(s) to be built.",
+    )
+    parser.add_argument(
+        "-d",
+        "--debug",
+        action="store_true",
+        help="Add source-level debugging information to font.",
+    )
+    parser.add_argument(
+        "-v",
+        "--verbose",
+        help="increase the logger verbosity. Multiple -v " "options are allowed.",
+        action="count",
+        default=0,
+    )
+    parser.add_argument(
+        "--traceback", help="show traceback for exceptions.", action="store_true"
+    )
     options = parser.parse_args(args)
 
     levels = ["WARNING", "INFO", "DEBUG"]
@@ -34,9 +62,16 @@
     log.info("Compiling features to '%s'" % (output_font))
 
     font = TTFont(options.input_font)
-    addOpenTypeFeatures(font, options.input_fea)
+    try:
+        addOpenTypeFeatures(
+            font, options.input_fea, tables=options.tables, debug=options.debug
+        )
+    except FeatureLibError as e:
+        if options.traceback:
+            raise
+        log.error(e)
     font.save(output_font)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     sys.exit(main())
diff --git a/Lib/fontTools/feaLib/ast.py b/Lib/fontTools/feaLib/ast.py
index 5f7ad29..5bf8b62 100644
--- a/Lib/fontTools/feaLib/ast.py
+++ b/Lib/fontTools/feaLib/ast.py
@@ -1,13 +1,80 @@
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import byteord, tobytes
 from fontTools.feaLib.error import FeatureLibError
+from fontTools.feaLib.location import FeatureLibLocation
 from fontTools.misc.encodingTools import getEncoding
 from collections import OrderedDict
 import itertools
 
 SHIFT = " " * 4
 
+__all__ = [
+    "Element",
+    "FeatureFile",
+    "Comment",
+    "GlyphName",
+    "GlyphClass",
+    "GlyphClassName",
+    "MarkClassName",
+    "AnonymousBlock",
+    "Block",
+    "FeatureBlock",
+    "NestedBlock",
+    "LookupBlock",
+    "GlyphClassDefinition",
+    "GlyphClassDefStatement",
+    "MarkClass",
+    "MarkClassDefinition",
+    "AlternateSubstStatement",
+    "Anchor",
+    "AnchorDefinition",
+    "AttachStatement",
+    "AxisValueLocationStatement",
+    "BaseAxis",
+    "CVParametersNameStatement",
+    "ChainContextPosStatement",
+    "ChainContextSubstStatement",
+    "CharacterStatement",
+    "CursivePosStatement",
+    "ElidedFallbackName",
+    "ElidedFallbackNameID",
+    "Expression",
+    "FeatureNameStatement",
+    "FeatureReferenceStatement",
+    "FontRevisionStatement",
+    "HheaField",
+    "IgnorePosStatement",
+    "IgnoreSubstStatement",
+    "IncludeStatement",
+    "LanguageStatement",
+    "LanguageSystemStatement",
+    "LigatureCaretByIndexStatement",
+    "LigatureCaretByPosStatement",
+    "LigatureSubstStatement",
+    "LookupFlagStatement",
+    "LookupReferenceStatement",
+    "MarkBasePosStatement",
+    "MarkLigPosStatement",
+    "MarkMarkPosStatement",
+    "MultipleSubstStatement",
+    "NameRecord",
+    "OS2Field",
+    "PairPosStatement",
+    "ReverseChainSingleSubstStatement",
+    "ScriptStatement",
+    "SinglePosStatement",
+    "SingleSubstStatement",
+    "SizeParameters",
+    "Statement",
+    "STATAxisValueStatement",
+    "STATDesignAxisStatement",
+    "STATNameStatement",
+    "SubtableStatement",
+    "TableBlock",
+    "ValueRecord",
+    "ValueRecordDefinition",
+    "VheaField",
+]
+
 
 def deviceToString(device):
     if device is None:
@@ -16,31 +83,69 @@
         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"]
+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",
+        "base",
+        "gdef",
+        "head",
+        "hhea",
+        "name",
+        "vhea",
+        "vmtx",
+    ]
 )
 
 
 def asFea(g):
-    if hasattr(g, 'asFea'):
+    if hasattr(g, "asFea"):
         return g.asFea()
     elif isinstance(g, tuple) and len(g) == 2:
-        return asFea(g[0]) + "-" + asFea(g[1])   # a range
+        return asFea(g[0]) + " - " + asFea(g[1])  # a range
     elif g.lower() in fea_keywords:
         return "\\" + g
     else:
@@ -48,14 +153,21 @@
 
 
 class Element(object):
+    """A base class representing "something" in a feature file."""
 
-    def __init__(self, location):
+    def __init__(self, location=None):
+        #: location of this element as a `FeatureLibLocation` object.
+        if location and not isinstance(location, FeatureLibLocation):
+            location = FeatureLibLocation(*location)
         self.location = location
 
     def build(self, builder):
         pass
 
     def asFea(self, indent=""):
+        """Returns this element as a string of feature code. For block-type
+        elements (such as :class:`FeatureBlock`), the `indent` string is
+        added to the start of each line in the output."""
         raise NotImplementedError
 
     def __str__(self):
@@ -71,83 +183,121 @@
 
 
 class Comment(Element):
-    def __init__(self, location, text):
+    """A comment in a feature file."""
+
+    def __init__(self, text, location=None):
         super(Comment, self).__init__(location)
+        #: Text of the comment
         self.text = text
 
     def asFea(self, indent=""):
         return self.text
 
 
-class GlyphName(Expression):
-    """A single glyph name, such as cedilla."""
-    def __init__(self, location, glyph):
+class NullGlyph(Expression):
+    """The NULL glyph, used in glyph deletion substitutions."""
+
+    def __init__(self, location=None):
         Expression.__init__(self, location)
+        #: The name itself as a string
+
+    def glyphSet(self):
+        """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
+        return ()
+
+    def asFea(self, indent=""):
+        return "NULL"
+
+
+class GlyphName(Expression):
+    """A single glyph name, such as ``cedilla``."""
+
+    def __init__(self, glyph, location=None):
+        Expression.__init__(self, location)
+        #: The name itself as a string
         self.glyph = glyph
 
     def glyphSet(self):
+        """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
         return (self.glyph,)
 
     def asFea(self, indent=""):
-        return str(self.glyph)
+        return asFea(self.glyph)
 
 
 class GlyphClass(Expression):
-    """A glyph class, such as [acute cedilla grave]."""
-    def __init__(self, location, glyphs=None):
+    """A glyph class, such as ``[acute cedilla grave]``."""
+
+    def __init__(self, glyphs=None, location=None):
         Expression.__init__(self, location)
+        #: The list of glyphs in this class, as :class:`GlyphName` objects.
         self.glyphs = glyphs if glyphs is not None else []
         self.original = []
         self.curr = 0
 
     def glyphSet(self):
+        """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
         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.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):
+        """Add a list of :class:`GlyphName` objects to the class."""
         self.glyphs.extend(glyphs)
 
     def append(self, glyph):
+        """Add a single :class:`GlyphName` object to the class."""
         self.glyphs.append(glyph)
 
     def add_range(self, start, end, glyphs):
+        """Add a range (e.g. ``A-Z``) to the class. ``start`` and ``end``
+        are either :class:`GlyphName` objects or strings representing the
+        start and end glyphs in the class, and ``glyphs`` is the full list of
+        :class:`GlyphName` objects in the range."""
         if self.curr < len(self.glyphs):
-            self.original.extend(self.glyphs[self.curr:])
+            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):
+        """Add a range to the class by glyph ID. ``start`` and ``end`` are the
+        initial and final IDs, and ``glyphs`` is the full list of
+        :class:`GlyphName` objects in the range."""
         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.original.extend(self.glyphs[self.curr :])
+        self.original.append(("\\{}".format(start), "\\{}".format(end)))
         self.glyphs.extend(glyphs)
         self.curr = len(self.glyphs)
 
     def add_class(self, gc):
+        """Add glyphs from the given :class:`GlyphClassName` object to the
+        class."""
         if self.curr < len(self.glyphs):
-            self.original.extend(self.glyphs[self.curr:])
+            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, location, glyphclass):
+    """A glyph class name, such as ``@FRENCH_MARKS``. This must be instantiated
+    with a :class:`GlyphClassDefinition` object."""
+
+    def __init__(self, glyphclass, location=None):
         Expression.__init__(self, location)
         assert isinstance(glyphclass, GlyphClassDefinition)
         self.glyphclass = glyphclass
 
     def glyphSet(self):
+        """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
         return tuple(self.glyphclass.glyphSet())
 
     def asFea(self, indent=""):
@@ -155,13 +305,16 @@
 
 
 class MarkClassName(Expression):
-    """A mark class name, such as @FRENCH_MARKS defined with markClass."""
-    def __init__(self, location, markClass):
+    """A mark class name, such as ``@FRENCH_MARKS`` defined with ``markClass``.
+    This must be instantiated with a :class:`MarkClass` object."""
+
+    def __init__(self, markClass, location=None):
         Expression.__init__(self, location)
         assert isinstance(markClass, MarkClass)
         self.markClass = markClass
 
     def glyphSet(self):
+        """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
         return self.markClass.glyphSet()
 
     def asFea(self, indent=""):
@@ -169,9 +322,12 @@
 
 
 class AnonymousBlock(Statement):
-    def __init__(self, tag, content, location):
+    """An anonymous data block."""
+
+    def __init__(self, tag, content, location=None):
         Statement.__init__(self, location)
-        self.tag, self.content = tag, content
+        self.tag = tag  #: string containing the block's "tag"
+        self.content = content  #: block data as string
 
     def asFea(self, indent=""):
         res = "anon {} {{\n".format(self.tag)
@@ -181,21 +337,32 @@
 
 
 class Block(Statement):
-    def __init__(self, location):
+    """A block of statements: feature, lookup, etc."""
+
+    def __init__(self, location=None):
         Statement.__init__(self, location)
-        self.statements = []
+        self.statements = []  #: Statements contained in the block
 
     def build(self, builder):
+        """When handed a 'builder' object of comparable interface to
+        :class:`fontTools.feaLib.builder`, walks the statements in this
+        block, calling the builder callbacks."""
         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"
+        return (
+            indent
+            + ("\n" + indent).join([s.asFea(indent=indent) for s in self.statements])
+            + "\n"
+        )
 
 
 class FeatureFile(Block):
+    """The top-level element of the syntax tree, containing the whole feature
+    file in its ``statements`` attribute."""
+
     def __init__(self):
         Block.__init__(self, location=None)
         self.markClasses = {}  # name --> ast.MarkClass
@@ -205,11 +372,15 @@
 
 
 class FeatureBlock(Block):
-    def __init__(self, location, name, use_extension):
+    """A named feature 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):
+        """Call the ``start_feature`` callback on the builder object, visit
+        all the statements in this feature, and then call ``end_feature``."""
         # TODO(sascha): Handle use_extension.
         builder.start_feature(self.location, self.name)
         # language exclude_dflt statements modify builder.features_
@@ -223,25 +394,40 @@
         builder.end_feature()
 
     def asFea(self, indent=""):
-        res = indent + "feature %s {\n" % self.name.strip()
+        res = indent + "feature %s " % self.name.strip()
+        if self.use_extension:
+            res += "useExtension "
+        res += "{\n"
         res += Block.asFea(self, indent=indent)
         res += indent + "} %s;\n" % self.name.strip()
         return res
 
 
-class FeatureNamesBlock(Block):
-    def __init__(self, location):
+class NestedBlock(Block):
+    """A block inside another block, for example when found inside a
+    ``cvParameters`` 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 = indent + "featureNames {\n"
+        res = "{}{} {{\n".format(indent, self.block_name)
         res += Block.asFea(self, indent=indent)
-        res += indent + "};\n"
+        res += "{}}};\n".format(indent)
         return res
 
 
 class LookupBlock(Block):
-    def __init__(self, location, name, use_extension):
+    """A named lookup, containing ``statements``."""
+
+    def __init__(self, name, use_extension=False, location=None):
         Block.__init__(self, location)
         self.name, self.use_extension = name, use_extension
 
@@ -252,14 +438,19 @@
         builder.end_lookup_block()
 
     def asFea(self, indent=""):
-        res = "lookup {} {{\n".format(self.name)
+        res = "lookup {} ".format(self.name)
+        if self.use_extension:
+            res += "useExtension "
+        res += "{\n"
         res += Block.asFea(self, indent=indent)
         res += "{}}} {};\n".format(indent, self.name)
         return res
 
 
 class TableBlock(Block):
-    def __init__(self, location, name):
+    """A ``table ... { }`` block."""
+
+    def __init__(self, name, location=None):
         Block.__init__(self, location)
         self.name = name
 
@@ -271,13 +462,15 @@
 
 
 class GlyphClassDefinition(Statement):
-    """Example: @UPPERCASE = [A-Z];"""
-    def __init__(self, location, name, glyphs):
+    """Example: ``@UPPERCASE = [A-Z];``."""
+
+    def __init__(self, name, glyphs, location=None):
         Statement.__init__(self, location)
-        self.name = name
-        self.glyphs = glyphs
+        self.name = name  #: class name as a string, without initial ``@``
+        self.glyphs = glyphs  #: a :class:`GlyphClass` object
 
     def glyphSet(self):
+        """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
         return tuple(self.glyphs.glyphSet())
 
     def asFea(self, indent=""):
@@ -285,21 +478,24 @@
 
 
 class GlyphClassDefStatement(Statement):
-    """Example: GlyphClassDef @UPPERCASE, [B], [C], [D];"""
-    def __init__(self, location, baseGlyphs, markGlyphs,
-                 ligatureGlyphs, componentGlyphs):
+    """Example: ``GlyphClassDef @UPPERCASE, [B], [C], [D];``. The parameters
+    must be either :class:`GlyphClass` or :class:`GlyphClassName` objects, or
+    ``None``."""
+
+    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):
+        """Calls the builder's ``add_glyphClassDef`` callback."""
         base = self.baseGlyphs.glyphSet() if self.baseGlyphs else tuple()
-        liga = self.ligatureGlyphs.glyphSet() \
-            if self.ligatureGlyphs 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())
+        comp = self.componentGlyphs.glyphSet() if self.componentGlyphs else tuple()
         builder.add_glyphClassDef(self.location, base, liga, mark, comp)
 
     def asFea(self, indent=""):
@@ -307,79 +503,121 @@
             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 "")
+            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):
+    """One `or more` ``markClass`` statements for the same mark class.
+
+    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;
+
+    The ``MarkClass`` object is therefore just a container for a list of
+    :class:`MarkClassDefinition` statements.
+    """
+
     def __init__(self, name):
         self.name = name
         self.definitions = []
         self.glyphs = OrderedDict()  # glyph --> ast.MarkClassDefinitions
 
     def addDefinition(self, definition):
+        """Add a :class:`MarkClassDefinition` statement to this mark class."""
         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 = f" at {otherLoc}"
                 raise FeatureLibError(
-                    "Glyph %s already defined at %s:%d:%d" % (
-                        glyph, otherLoc[0], otherLoc[1], otherLoc[2]),
-                    definition.location)
+                    "Glyph %s already defined%s" % (glyph, end), definition.location
+                )
             self.glyphs[glyph] = definition
 
     def glyphSet(self):
+        """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
         return tuple(self.glyphs.keys())
 
     def asFea(self, indent=""):
-        res = "\n".join(d.asFea(indent=indent) for d in self.definitions)
+        res = "\n".join(d.asFea() for d in self.definitions)
         return res
 
 
 class MarkClassDefinition(Statement):
-    def __init__(self, location, markClass, anchor, glyphs):
+    """A single ``markClass`` statement. The ``markClass`` should be a
+    :class:`MarkClass` object, the ``anchor`` an :class:`Anchor` object,
+    and the ``glyphs`` parameter should be a `glyph-containing object`_ .
+
+    Example:
+
+        .. code:: python
+
+            mc = MarkClass("FRENCH_ACCENTS")
+            mc.addDefinition( MarkClassDefinition(mc, Anchor(350, 800),
+                GlyphClass([ GlyphName("acute"), GlyphName("grave") ])
+            ) )
+            mc.addDefinition( MarkClassDefinition(mc, Anchor(350, -200),
+                GlyphClass([ GlyphName("cedilla") ])
+            ) )
+
+            mc.asFea()
+            # markClass [acute grave] <anchor 350 800> @FRENCH_ACCENTS;
+            # markClass [cedilla] <anchor 350 -200> @FRENCH_ACCENTS;
+
+    """
+
+    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):
+        """The glyphs in this class as a tuple of :class:`GlyphName` objects."""
         return self.glyphs.glyphSet()
 
     def asFea(self, indent=""):
-        return "{}markClass {} {} @{};".format(
-            indent, self.glyphs.asFea(), self.anchor.asFea(),
-            self.markClass.name)
+        return "markClass {} {} @{};".format(
+            self.glyphs.asFea(), self.anchor.asFea(), self.markClass.name
+        )
 
 
 class AlternateSubstStatement(Statement):
-    def __init__(self, location, prefix, glyph, suffix, replacement):
+    """A ``sub ... from ...`` statement.
+
+    ``prefix``, ``glyph``, ``suffix`` and ``replacement`` should be lists of
+    `glyph-containing objects`_. ``glyph`` should be a `one element list`."""
+
+    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):
+        """Calls the builder's ``add_alternate_subst`` callback."""
         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)
+        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
+            res += asFea(self.glyph) + "'"  # even though we really only use 1
             if len(self.suffix):
                 res += " " + " ".join(map(asFea, self.suffix))
         else:
@@ -391,8 +629,22 @@
 
 
 class Anchor(Expression):
-    def __init__(self, location, name, x, y, contourpoint,
-                 xDeviceTable, yDeviceTable):
+    """An ``Anchor`` element, used inside a ``pos`` rule.
+
+    If a ``name`` is given, this will be used in preference to the coordinates.
+    Other values should be integer.
+    """
+
+    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
@@ -414,7 +666,9 @@
 
 
 class AnchorDefinition(Statement):
-    def __init__(self, location, name, x, y, contourpoint):
+    """A named anchor definition. (2.e.viii). ``name`` should be a string."""
+
+    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
 
@@ -427,41 +681,72 @@
 
 
 class AttachStatement(Statement):
-    def __init__(self, location, glyphs, contourPoints):
+    """A ``GDEF`` table ``Attach`` statement."""
+
+    def __init__(self, glyphs, contourPoints, location=None):
         Statement.__init__(self, location)
-        self.glyphs, self.contourPoints = (glyphs, contourPoints)
+        self.glyphs = glyphs  #: A `glyph-containing object`_
+        self.contourPoints = contourPoints  #: A list of integer contour points
 
     def build(self, builder):
+        """Calls the builder's ``add_attach_points`` callback."""
         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))
+            self.glyphs.asFea(), " ".join(str(c) for c in self.contourPoints)
+        )
 
 
 class ChainContextPosStatement(Statement):
-    def __init__(self, location, prefix, glyphs, suffix, lookups):
+    r"""A chained contextual positioning statement.
+
+    ``prefix``, ``glyphs``, and ``suffix`` should be lists of
+    `glyph-containing objects`_ .
+
+    ``lookups`` should be a list of elements representing what lookups
+    to apply at each glyph position. Each element should be a
+    :class:`LookupBlock` to apply a single chaining lookup at the given
+    position, a list of :class:`LookupBlock`\ s to apply multiple
+    lookups, or ``None`` to apply no lookup. The length of the outer
+    list should equal the length of ``glyphs``; the inner lists can be
+    of variable length."""
+
+    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
+        self.lookups = list(lookups)
+        for i, lookup in enumerate(lookups):
+            if lookup:
+                try:
+                    (_ for _ in lookup)
+                except TypeError:
+                    self.lookups[i] = [lookup]
 
     def build(self, builder):
+        """Calls the builder's ``add_chain_context_pos`` callback."""
         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)
+            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)
+            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 self.lookups[i]:
+                    for lu in self.lookups[i]:
+                        res += " lookup " + lu.name
                 if i < len(self.glyphs) - 1:
                     res += " "
             if len(self.suffix):
@@ -473,27 +758,53 @@
 
 
 class ChainContextSubstStatement(Statement):
-    def __init__(self, location, prefix, glyphs, suffix, lookups):
+    r"""A chained contextual substitution statement.
+
+    ``prefix``, ``glyphs``, and ``suffix`` should be lists of
+    `glyph-containing objects`_ .
+
+    ``lookups`` should be a list of elements representing what lookups
+    to apply at each glyph position. Each element should be a
+    :class:`LookupBlock` to apply a single chaining lookup at the given
+    position, a list of :class:`LookupBlock`\ s to apply multiple
+    lookups, or ``None`` to apply no lookup. The length of the outer
+    list should equal the length of ``glyphs``; the inner lists can be
+    of variable length."""
+
+    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
+        self.lookups = list(lookups)
+        for i, lookup in enumerate(lookups):
+            if lookup:
+                try:
+                    (_ for _ in lookup)
+                except TypeError:
+                    self.lookups[i] = [lookup]
 
     def build(self, builder):
+        """Calls the builder's ``add_chain_context_subst`` callback."""
         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)
+            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)
+            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 self.lookups[i]:
+                    for lu in self.lookups[i]:
+                        res += " lookup " + lu.name
                 if i < len(self.glyphs) - 1:
                     res += " "
             if len(self.suffix):
@@ -505,14 +816,19 @@
 
 
 class CursivePosStatement(Statement):
-    def __init__(self, location, glyphclass, entryAnchor, exitAnchor):
+    """A cursive positioning statement. Entry and exit anchors can either
+    be :class:`Anchor` objects or ``None``."""
+
+    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):
+        """Calls the builder object's ``add_cursive_pos`` callback."""
         builder.add_cursive_pos(
-            self.location, self.glyphclass.glyphSet(), self.entryAnchor, self.exitAnchor)
+            self.location, self.glyphclass.glyphSet(), self.entryAnchor, self.exitAnchor
+        )
 
     def asFea(self, indent=""):
         entry = self.entryAnchor.asFea() if self.entryAnchor else "<anchor NULL>"
@@ -521,12 +837,14 @@
 
 
 class FeatureReferenceStatement(Statement):
-    """Example: feature salt;"""
-    def __init__(self, location, featureName):
+    """Example: ``feature salt;``"""
+
+    def __init__(self, featureName, location=None):
         Statement.__init__(self, location)
         self.location, self.featureName = (location, featureName)
 
     def build(self, builder):
+        """Calls the builder object's ``add_feature_reference`` callback."""
         builder.add_feature_reference(self.location, self.featureName)
 
     def asFea(self, indent=""):
@@ -534,17 +852,24 @@
 
 
 class IgnorePosStatement(Statement):
-    def __init__(self, location, chainContexts):
+    """An ``ignore pos`` statement, containing `one or more` contexts to ignore.
+
+    ``chainContexts`` should be a list of ``(prefix, glyphs, suffix)`` tuples,
+    with each of ``prefix``, ``glyphs`` and ``suffix`` being
+    `glyph-containing objects`_ ."""
+
+    def __init__(self, chainContexts, location=None):
         Statement.__init__(self, location)
         self.chainContexts = chainContexts
 
     def build(self, builder):
+        """Calls the builder object's ``add_chain_context_pos`` callback on each
+        rule context."""
         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, [])
+            builder.add_chain_context_pos(self.location, prefix, glyphs, suffix, [])
 
     def asFea(self, indent=""):
         contexts = []
@@ -563,17 +888,24 @@
 
 
 class IgnoreSubstStatement(Statement):
-    def __init__(self, location, chainContexts):
+    """An ``ignore sub`` statement, containing `one or more` contexts to ignore.
+
+    ``chainContexts`` should be a list of ``(prefix, glyphs, suffix)`` tuples,
+    with each of ``prefix``, ``glyphs`` and ``suffix`` being
+    `glyph-containing objects`_ ."""
+
+    def __init__(self, chainContexts, location=None):
         Statement.__init__(self, location)
         self.chainContexts = chainContexts
 
     def build(self, builder):
+        """Calls the builder object's ``add_chain_context_subst`` callback on
+        each rule context."""
         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, [])
+            builder.add_chain_context_subst(self.location, prefix, glyphs, suffix, [])
 
     def asFea(self, indent=""):
         contexts = []
@@ -591,18 +923,43 @@
         return "ignore sub " + ", ".join(contexts) + ";"
 
 
+class IncludeStatement(Statement):
+    """An ``include()`` statement."""
+
+    def __init__(self, filename, location=None):
+        super(IncludeStatement, self).__init__(location)
+        self.filename = filename  #: String containing name of file to include
+
+    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, location, language, include_default, required):
+    """A ``language`` statement within a feature."""
+
+    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
+        assert len(language) == 4
+        self.language = language  #: A four-character language tag
+        self.include_default = include_default  #: If false, "exclude_dflt"
         self.required = required
 
     def build(self, builder):
-        builder.set_language(location=self.location, language=self.language,
-                             include_default=self.include_default,
-                             required=self.required)
+        """Call the builder object's ``set_language`` callback."""
+        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())
@@ -615,11 +972,14 @@
 
 
 class LanguageSystemStatement(Statement):
-    def __init__(self, location, script, language):
+    """A top-level ``languagesystem`` statement."""
+
+    def __init__(self, script, language, location=None):
         Statement.__init__(self, location)
         self.script, self.language = (script, language)
 
     def build(self, builder):
+        """Calls the builder object's ``add_language_system`` callback."""
         builder.add_language_system(self.location, self.script, self.language)
 
     def asFea(self, indent=""):
@@ -627,7 +987,10 @@
 
 
 class FontRevisionStatement(Statement):
-    def __init__(self, location, revision):
+    """A ``head`` table ``FontRevision`` statement. ``revision`` should be a
+    number, and will be formatted to three significant decimal places."""
+
+    def __init__(self, revision, location=None):
         Statement.__init__(self, location)
         self.revision = revision
 
@@ -639,36 +1002,54 @@
 
 
 class LigatureCaretByIndexStatement(Statement):
-    def __init__(self, location, glyphs, carets):
+    """A ``GDEF`` table ``LigatureCaretByIndex`` statement. ``glyphs`` should be
+    a `glyph-containing object`_, and ``carets`` should be a list of integers."""
+
+    def __init__(self, glyphs, carets, location=None):
         Statement.__init__(self, location)
         self.glyphs, self.carets = (glyphs, carets)
 
     def build(self, builder):
+        """Calls the builder object's ``add_ligatureCaretByIndex_`` callback."""
         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))
+            self.glyphs.asFea(), " ".join(str(x) for x in self.carets)
+        )
 
 
 class LigatureCaretByPosStatement(Statement):
-    def __init__(self, location, glyphs, carets):
+    """A ``GDEF`` table ``LigatureCaretByPos`` statement. ``glyphs`` should be
+    a `glyph-containing object`_, and ``carets`` should be a list of integers."""
+
+    def __init__(self, glyphs, carets, location=None):
         Statement.__init__(self, location)
         self.glyphs, self.carets = (glyphs, carets)
 
     def build(self, builder):
+        """Calls the builder object's ``add_ligatureCaretByPos_`` callback."""
         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))
+            self.glyphs.asFea(), " ".join(str(x) for x in self.carets)
+        )
 
 
 class LigatureSubstStatement(Statement):
-    def __init__(self, location, prefix, glyphs, suffix, replacement,
-                 forceChain):
+    """A chained contextual substitution statement.
+
+    ``prefix``, ``glyphs``, and ``suffix`` should be lists of
+    `glyph-containing objects`_; ``replacement`` should be a single
+    `glyph-containing object`_.
+
+    If ``forceChain`` is True, this is expressed as a chaining rule
+    (e.g. ``sub f' i' by f_i``) even when no context is given."""
+
+    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
@@ -678,8 +1059,8 @@
         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)
+            self.location, prefix, glyphs, suffix, self.replacement, self.forceChain
+        )
 
     def asFea(self, indent=""):
         res = "sub "
@@ -698,44 +1079,57 @@
 
 
 class LookupFlagStatement(Statement):
-    def __init__(self, location, value, markAttachment, markFilteringSet):
+    """A ``lookupflag`` statement. The ``value`` should be an integer value
+    representing the flags in use, but not including the ``markAttachment``
+    class and ``markFilteringSet`` values, which must be specified as
+    glyph-containing objects."""
+
+    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):
+        """Calls the builder object's ``set_lookup_flag`` callback."""
         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)
+        builder.set_lookup_flag(self.location, self.value, markAttach, markFilter)
 
     def asFea(self, indent=""):
-        res = "lookupflag"
+        res = []
         flags = ["RightToLeft", "IgnoreBaseGlyphs", "IgnoreLigatures", "IgnoreMarks"]
         curr = 1
         for i in range(len(flags)):
             if self.value & curr != 0:
-                res += " " + flags[i]
+                res.append(flags[i])
             curr = curr << 1
         if self.markAttachment is not None:
-            res += " MarkAttachmentType {}".format(self.markAttachment.asFea())
+            res.append("MarkAttachmentType {}".format(self.markAttachment.asFea()))
         if self.markFilteringSet is not None:
-            res += " UseMarkFilteringSet {}".format(self.markFilteringSet.asFea())
-        res += ";"
-        return res
+            res.append("UseMarkFilteringSet {}".format(self.markFilteringSet.asFea()))
+        if not res:
+            res = ["0"]
+        return "lookupflag {};".format(" ".join(res))
 
 
 class LookupReferenceStatement(Statement):
-    def __init__(self, location, lookup):
+    """Represents a ``lookup ...;`` statement to include a lookup in a feature.
+
+    The ``lookup`` should be a :class:`LookupBlock` object."""
+
+    def __init__(self, lookup, location=None):
         Statement.__init__(self, location)
         self.location, self.lookup = (location, lookup)
 
     def build(self, builder):
+        """Calls the builder object's ``add_lookup_call`` callback."""
         builder.add_lookup_call(self.lookup.name)
 
     def asFea(self, indent=""):
@@ -743,27 +1137,59 @@
 
 
 class MarkBasePosStatement(Statement):
-    def __init__(self, location, base, marks):
+    """A mark-to-base positioning rule. The ``base`` should be a
+    `glyph-containing object`_. The ``marks`` should be a list of
+    (:class:`Anchor`, :class:`MarkClass`) tuples."""
+
+    def __init__(self, base, marks, location=None):
         Statement.__init__(self, location)
         self.base, self.marks = base, marks
 
     def build(self, builder):
+        """Calls the builder object's ``add_mark_base_pos`` callback."""
         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 += "\n" + indent + SHIFT + "{} mark @{}".format(a.asFea(), m.name)
         res += ";"
         return res
 
 
 class MarkLigPosStatement(Statement):
-    def __init__(self, location, ligatures, marks):
+    """A mark-to-ligature positioning rule. The ``ligatures`` must be a
+    `glyph-containing object`_. The ``marks`` should be a list of lists: each
+    element in the top-level list represents a component glyph, and is made
+    up of a list of (:class:`Anchor`, :class:`MarkClass`) tuples representing
+    mark attachment points for that position.
+
+    Example::
+
+        m1 = MarkClass("TOP_MARKS")
+        m2 = MarkClass("BOTTOM_MARKS")
+        # ... add definitions to mark classes...
+
+        glyph = GlyphName("lam_meem_jeem")
+        marks = [
+            [ (Anchor(625,1800), m1) ], # Attachments on 1st component (lam)
+            [ (Anchor(376,-378), m2) ], # Attachments on 2nd component (meem)
+            [ ]                         # No attachments on the jeem
+        ]
+        mlp = MarkLigPosStatement(glyph, marks)
+
+        mlp.asFea()
+        # pos ligature lam_meem_jeem <anchor 625 1800> mark @TOP_MARKS
+        # ligComponent <anchor 376 -378> mark @BOTTOM_MARKS;
+
+    """
+
+    def __init__(self, ligatures, marks, location=None):
         Statement.__init__(self, location)
         self.ligatures, self.marks = ligatures, marks
 
     def build(self, builder):
+        """Calls the builder object's ``add_mark_lig_pos`` callback."""
         builder.add_mark_lig_pos(self.location, self.ligatures.glyphSet(), self.marks)
 
     def asFea(self, indent=""):
@@ -772,10 +1198,15 @@
         for l in self.marks:
             temp = ""
             if l is None or not len(l):
-                temp = " <anchor NULL>"
+                temp = "\n" + indent + SHIFT * 2 + "<anchor NULL>"
             else:
                 for a, m in l:
-                    temp += " {} mark @{}".format(a.asFea(), m.name)
+                    temp += (
+                        "\n"
+                        + indent
+                        + SHIFT * 2
+                        + "{} mark @{}".format(a.asFea(), m.name)
+                    )
             ligs.append(temp)
         res += ("\n" + indent + SHIFT + "ligComponent").join(ligs)
         res += ";"
@@ -783,36 +1214,63 @@
 
 
 class MarkMarkPosStatement(Statement):
-    def __init__(self, location, baseMarks, marks):
+    """A mark-to-mark positioning rule. The ``baseMarks`` must be a
+    `glyph-containing object`_. The ``marks`` should be a list of
+    (:class:`Anchor`, :class:`MarkClass`) tuples."""
+
+    def __init__(self, baseMarks, marks, location=None):
         Statement.__init__(self, location)
         self.baseMarks, self.marks = baseMarks, marks
 
     def build(self, builder):
+        """Calls the builder object's ``add_mark_mark_pos`` callback."""
         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 += "\n" + indent + SHIFT + "{} mark @{}".format(a.asFea(), m.name)
         res += ";"
         return res
 
 
 class MultipleSubstStatement(Statement):
-    def __init__(self, location, prefix, glyph, suffix, replacement):
+    """A multiple substitution statement.
+
+    Args:
+        prefix: a list of `glyph-containing objects`_.
+        glyph: a single glyph-containing object.
+        suffix: a list of glyph-containing objects.
+        replacement: a list of glyph-containing objects.
+        forceChain: If true, the statement is expressed as a chaining rule
+            (e.g. ``sub f' i' by f_i``) even when no context is given.
+    """
+
+    def __init__(
+        self, prefix, glyph, suffix, replacement, forceChain=False, location=None
+    ):
         Statement.__init__(self, location)
         self.prefix, self.glyph, self.suffix = prefix, glyph, suffix
         self.replacement = replacement
+        self.forceChain = forceChain
 
     def build(self, builder):
+        """Calls the builder object's ``add_multiple_subst`` callback."""
         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)
+        if not self.replacement and hasattr(self.glyph, "glyphSet"):
+            for glyph in self.glyph.glyphSet():
+                builder.add_multiple_subst(
+                    self.location, prefix, glyph, suffix, self.replacement, self.forceChain
+                )
+        else:
+            builder.add_multiple_subst(
+                self.location, prefix, self.glyph, suffix, self.replacement, self.forceChain
+            )
 
     def asFea(self, indent=""):
         res = "sub "
-        if len(self.prefix) or len(self.suffix):
+        if len(self.prefix) or len(self.suffix) or self.forceChain:
             if len(self.prefix):
                 res += " ".join(map(asFea, self.prefix)) + " "
             res += asFea(self.glyph) + "'"
@@ -820,55 +1278,100 @@
                 res += " " + " ".join(map(asFea, self.suffix))
         else:
             res += asFea(self.glyph)
+        replacement = self.replacement or [NullGlyph()]
         res += " by "
-        res += " ".join(map(asFea, self.replacement))
+        res += " ".join(map(asFea, replacement))
         res += ";"
         return res
 
 
 class PairPosStatement(Statement):
-    def __init__(self, location, enumerated,
-                 glyphs1, valuerecord1, glyphs2, valuerecord2):
+    """A pair positioning statement.
+
+    ``glyphs1`` and ``glyphs2`` should be `glyph-containing objects`_.
+    ``valuerecord1`` should be a :class:`ValueRecord` object;
+    ``valuerecord2`` should be either a :class:`ValueRecord` object or ``None``.
+    If ``enumerated`` is true, then this is expressed as an
+    `enumerated pair <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#6.b.ii>`_.
+    """
+
+    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):
+        """Calls a callback on the builder object:
+
+        * If the rule is enumerated, calls ``add_specific_pair_pos`` on each
+          combination of first and second glyphs.
+        * If the glyphs are both single :class:`GlyphName` objects, calls
+          ``add_specific_pair_pos``.
+        * Else, calls ``add_class_pair_pos``.
+        """
         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)
+                    self.location, glyph1, self.valuerecord1, glyph2, self.valuerecord2
+                )
             return
 
-        is_specific = (isinstance(self.glyphs1, GlyphName) and
-                       isinstance(self.glyphs2, GlyphName))
+        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)
+                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)
+                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())
+                self.glyphs1.asFea(),
+                self.valuerecord1.asFea(),
+                self.glyphs2.asFea(),
+                self.valuerecord2.asFea(),
+            )
         else:
             res += "pos {} {} {};".format(
-                self.glyphs1.asFea(), self.glyphs2.asFea(),
-                self.valuerecord1.makeString())
+                self.glyphs1.asFea(), self.glyphs2.asFea(), self.valuerecord1.asFea()
+            )
         return res
 
 
 class ReverseChainSingleSubstStatement(Statement):
-    def __init__(self, location, old_prefix, old_suffix, glyphs, replacements):
+    """A reverse chaining substitution statement. You don't see those every day.
+
+    Note the unusual argument order: ``suffix`` comes `before` ``glyphs``.
+    ``old_prefix``, ``old_suffix``, ``glyphs`` and ``replacements`` should be
+    lists of `glyph-containing objects`_. ``glyphs`` and ``replacements`` should
+    be one-item lists.
+    """
+
+    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
@@ -882,7 +1385,8 @@
         if len(replaces) == 1:
             replaces = replaces * len(originals)
         builder.add_reverse_chain_single_subst(
-            self.location, prefix, suffix, dict(zip(originals, replaces)))
+            self.location, prefix, suffix, dict(zip(originals, replaces))
+        )
 
     def asFea(self, indent=""):
         res = "rsub "
@@ -899,7 +1403,15 @@
 
 
 class SingleSubstStatement(Statement):
-    def __init__(self, location, glyphs, replace, prefix, suffix, forceChain):
+    """A single substitution statement.
+
+    Note the unusual argument order: ``prefix`` and suffix come `after`
+    the replacement ``glyphs``. ``prefix``, ``suffix``, ``glyphs`` and
+    ``replace`` should be lists of `glyph-containing objects`_. ``glyphs`` and
+    ``replace`` should be one-item lists.
+    """
+
+    def __init__(self, glyphs, replace, prefix, suffix, forceChain, location=None):
         Statement.__init__(self, location)
         self.prefix, self.suffix = prefix, suffix
         self.forceChain = forceChain
@@ -907,15 +1419,20 @@
         self.replacements = replace
 
     def build(self, builder):
+        """Calls the builder object's ``add_single_subst`` callback."""
         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)
+        builder.add_single_subst(
+            self.location,
+            prefix,
+            suffix,
+            OrderedDict(zip(originals, replaces)),
+            self.forceChain,
+        )
 
     def asFea(self, indent=""):
         res = "sub "
@@ -932,11 +1449,14 @@
 
 
 class ScriptStatement(Statement):
-    def __init__(self, location, script):
+    """A ``script`` statement."""
+
+    def __init__(self, script, location=None):
         Statement.__init__(self, location)
-        self.script = script
+        self.script = script  #: the script code
 
     def build(self, builder):
+        """Calls the builder's ``set_script`` callback."""
         builder.set_script(self.location, self.script)
 
     def asFea(self, indent=""):
@@ -944,43 +1464,75 @@
 
 
 class SinglePosStatement(Statement):
-    def __init__(self, location, pos, prefix, suffix, forceChain):
+    """A single position statement. ``prefix`` and ``suffix`` should be
+    lists of `glyph-containing objects`_.
+
+    ``pos`` should be a one-element list containing a (`glyph-containing object`_,
+    :class:`ValueRecord`) tuple."""
+
+    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):
+        """Calls the builder object's ``add_single_pos`` callback."""
         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)
+        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])
+            res += " ".join(
+                [
+                    asFea(x[0]) + "'" + ((" " + x[1].asFea()) 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 += " ".join(
+                [asFea(x[0]) + " " + (x[1].asFea() if x[1] else "") for x in self.pos]
+            )
         res += ";"
         return res
 
 
 class SubtableStatement(Statement):
-    def __init__(self, location):
+    """Represents a subtable break."""
+
+    def __init__(self, location=None):
         Statement.__init__(self, location)
 
+    def build(self, builder):
+        """Calls the builder objects's ``add_subtable_break`` callback."""
+        builder.add_subtable_break(self.location)
+
+    def asFea(self, indent=""):
+        return "subtable;"
+
 
 class ValueRecord(Expression):
-    def __init__(self, location, vertical,
-                 xPlacement, yPlacement, xAdvance, yAdvance,
-                 xPlaDevice, yPlaDevice, xAdvDevice, yAdvDevice):
+    """Represents a value record."""
+
+    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)
@@ -989,29 +1541,39 @@
         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)
+        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))
+        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):
+    def asFea(self, indent=""):
+        if not self:
+            return "<NULL>"
+
         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
+        vertical = self.vertical
 
         # Try format A, if possible.
         if x is None and y is None:
@@ -1020,23 +1582,58 @@
             elif yAdvance is None and not vertical:
                 return str(xAdvance)
 
+        # Make any remaining None value 0 to avoid generating invalid records.
+        x = x or 0
+        y = y or 0
+        xAdvance = xAdvance or 0
+        yAdvance = yAdvance or 0
+
         # Try format B, if possible.
-        if (xPlaDevice is None and yPlaDevice is None and
-                xAdvDevice is None and yAdvDevice is None):
+        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))
+            x,
+            y,
+            xAdvance,
+            yAdvance,
+            deviceToString(xPlaDevice),
+            deviceToString(yPlaDevice),
+            deviceToString(xAdvDevice),
+            deviceToString(yAdvDevice),
+        )
+
+    def __bool__(self):
+        return any(
+            getattr(self, v) is not None
+            for v in [
+                "xPlacement",
+                "yPlacement",
+                "xAdvance",
+                "yAdvance",
+                "xPlaDevice",
+                "yPlaDevice",
+                "xAdvDevice",
+                "yAdvDevice",
+            ]
+        )
+
+    __nonzero__ = __bool__
 
 
 class ValueRecordDefinition(Statement):
-    def __init__(self, location, name, value):
+    """Represents a named value record definition."""
+
+    def __init__(self, name, value, location=None):
         Statement.__init__(self, location)
-        self.name = name
-        self.value = value
+        self.name = name  #: Value record name as string
+        self.value = value  #: :class:`ValueRecord` object
 
     def asFea(self, indent=""):
         return "valueRecordDef {} {};".format(self.value.asFea(), self.name)
@@ -1052,48 +1649,61 @@
 
 
 class NameRecord(Statement):
-    def __init__(self, location, nameID, platformID,
-                 platEncID, langID, string):
+    """Represents a name record. (`Section 9.e. <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#9.e>`_)"""
+
+    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
+        self.nameID = nameID  #: Name ID as integer (e.g. 9 for designer's name)
+        self.platformID = platformID  #: Platform ID as integer
+        self.platEncID = platEncID  #: Platform encoding ID as integer
+        self.langID = langID  #: Language ID as integer
+        self.string = string  #: Name record value
 
     def build(self, builder):
+        """Calls the builder object's ``add_name_record`` callback."""
         builder.add_name_record(
-            self.location, self.nameID, self.platformID,
-            self.platEncID, self.langID, self.string)
+            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)
+                return chr(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)])
+            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)
+        plat = simplify_name_attributes(self.platformID, self.platEncID, self.langID)
         if plat != "":
             plat += " "
-        return "nameid {} {}\"{}\";".format(self.nameID, plat, escaped_string)
+        return 'nameid {} {}"{}";'.format(self.nameID, plat, escaped_string)
 
 
 class FeatureNameStatement(NameRecord):
+    """Represents a ``sizemenuname`` or ``name`` statement."""
+
     def build(self, builder):
+        """Calls the builder object's ``add_featureName`` callback."""
         NameRecord.build(self, builder)
-        builder.add_featureName(self.location, self.nameID)
+        builder.add_featureName(self.nameID)
 
     def asFea(self, indent=""):
         if self.nameID == "size":
@@ -1103,12 +1713,23 @@
         plat = simplify_name_attributes(self.platformID, self.platEncID, self.langID)
         if plat != "":
             plat += " "
-        return "{} {}\"{}\";".format(tag, plat, self.string)
+        return '{} {}"{}";'.format(tag, plat, self.string)
+
+
+class STATNameStatement(NameRecord):
+    """Represents a STAT table ``name`` statement."""
+
+    def asFea(self, indent=""):
+        plat = simplify_name_attributes(self.platformID, self.platEncID, self.langID)
+        if plat != "":
+            plat += " "
+        return 'name {}"{}";'.format(plat, self.string)
 
 
 class SizeParameters(Statement):
-    def __init__(self, location, DesignSize, SubfamilyID, RangeStart,
-                 RangeEnd):
+    """A ``parameters`` statement."""
+
+    def __init__(self, DesignSize, SubfamilyID, RangeStart, RangeEnd, location=None):
         Statement.__init__(self, location)
         self.DesignSize = DesignSize
         self.SubfamilyID = SubfamilyID
@@ -1116,8 +1737,14 @@
         self.RangeEnd = RangeEnd
 
     def build(self, builder):
-        builder.set_size_parameters(self.location, self.DesignSize,
-                                    self.SubfamilyID, self.RangeStart, self.RangeEnd)
+        """Calls the builder object's ``set_size_parameters`` callback."""
+        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)
@@ -1126,55 +1753,133 @@
         return res + ";"
 
 
-class BaseAxis(Statement):
-    def __init__(self, location, bases, scripts, vertical):
-        Statement.__init__(self, location)
-        self.bases = bases
-        self.scripts = scripts
-        self.vertical = vertical
+class CVParametersNameStatement(NameRecord):
+    """Represent a name statement inside a ``cvParameters`` block."""
+
+    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):
+        """Calls the builder object's ``add_cv_parameter`` callback."""
+        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):
+        """Calls the builder object's ``add_cv_character`` callback."""
+        builder.add_cv_character(self.character, self.tag)
+
+    def asFea(self, indent=""):
+        return "Character {:#x};".format(self.character)
+
+
+class BaseAxis(Statement):
+    """An axis definition, being either a ``VertAxis.BaseTagList/BaseScriptList``
+    pair or a ``HorizAxis.BaseTagList/BaseScriptList`` pair."""
+
+    def __init__(self, bases, scripts, vertical, location=None):
+        Statement.__init__(self, location)
+        self.bases = bases  #: A list of baseline tag names as strings
+        self.scripts = scripts  #: A list of script record tuplets (script tag, default baseline tag, base coordinate)
+        self.vertical = vertical  #: Boolean; VertAxis if True, HorizAxis if False
+
+    def build(self, builder):
+        """Calls the builder object's ``set_base_axis`` callback."""
         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]
+        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))
+            direction, " ".join(self.bases), indent, direction, ", ".join(scripts)
+        )
 
 
 class OS2Field(Statement):
-    def __init__(self, location, key, value):
+    """An entry in the ``OS/2`` table. Most ``values`` should be numbers or
+    strings, apart from when the key is ``UnicodeRange``, ``CodePageRange``
+    or ``Panose``, in which case it should be an array of integers."""
+
+    def __init__(self, key, value, location=None):
         Statement.__init__(self, location)
         self.key = key
         self.value = value
 
     def build(self, builder):
+        """Calls the builder object's ``add_os2_field`` callback."""
         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")
+
+        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
+            return "{} {};".format(
+                keywords[self.key][0], keywords[self.key][1](self.value)
+            )
+        return ""  # should raise exception
 
 
 class HheaField(Statement):
-    def __init__(self, location, key, value):
+    """An entry in the ``hhea`` table."""
+
+    def __init__(self, key, value, location=None):
         Statement.__init__(self, location)
         self.key = key
         self.value = value
 
     def build(self, builder):
+        """Calls the builder object's ``add_hhea_field`` callback."""
         builder.add_hhea_field(self.key, self.value)
 
     def asFea(self, indent=""):
@@ -1184,15 +1889,147 @@
 
 
 class VheaField(Statement):
-    def __init__(self, location, key, value):
+    """An entry in the ``vhea`` table."""
+
+    def __init__(self, key, value, location=None):
         Statement.__init__(self, location)
         self.key = key
         self.value = value
 
     def build(self, builder):
+        """Calls the builder object's ``add_vhea_field`` callback."""
         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)
+
+
+class STATDesignAxisStatement(Statement):
+    """A STAT table Design Axis
+
+    Args:
+        tag (str): a 4 letter axis tag
+        axisOrder (int): an int
+        names (list): a list of :class:`STATNameStatement` objects
+    """
+
+    def __init__(self, tag, axisOrder, names, location=None):
+        Statement.__init__(self, location)
+        self.tag = tag
+        self.axisOrder = axisOrder
+        self.names = names
+        self.location = location
+
+    def build(self, builder):
+        builder.addDesignAxis(self, self.location)
+
+    def asFea(self, indent=""):
+        indent += SHIFT
+        res = f"DesignAxis {self.tag} {self.axisOrder} {{ \n"
+        res += ("\n" + indent).join([s.asFea(indent=indent) for s in self.names]) + "\n"
+        res += "};"
+        return res
+
+
+class ElidedFallbackName(Statement):
+    """STAT table ElidedFallbackName
+
+    Args:
+        names: a list of :class:`STATNameStatement` objects
+    """
+
+    def __init__(self, names, location=None):
+        Statement.__init__(self, location)
+        self.names = names
+        self.location = location
+
+    def build(self, builder):
+        builder.setElidedFallbackName(self.names, self.location)
+
+    def asFea(self, indent=""):
+        indent += SHIFT
+        res = "ElidedFallbackName { \n"
+        res += ("\n" + indent).join([s.asFea(indent=indent) for s in self.names]) + "\n"
+        res += "};"
+        return res
+
+
+class ElidedFallbackNameID(Statement):
+    """STAT table ElidedFallbackNameID
+
+    Args:
+        value: an int pointing to an existing name table name ID
+    """
+
+    def __init__(self, value, location=None):
+        Statement.__init__(self, location)
+        self.value = value
+        self.location = location
+
+    def build(self, builder):
+        builder.setElidedFallbackName(self.value, self.location)
+
+    def asFea(self, indent=""):
+        return f"ElidedFallbackNameID {self.value};"
+
+
+class STATAxisValueStatement(Statement):
+    """A STAT table Axis Value Record
+
+    Args:
+        names (list): a list of :class:`STATNameStatement` objects
+        locations (list): a list of :class:`AxisValueLocationStatement` objects
+        flags (int): an int
+    """
+
+    def __init__(self, names, locations, flags, location=None):
+        Statement.__init__(self, location)
+        self.names = names
+        self.locations = locations
+        self.flags = flags
+
+    def build(self, builder):
+        builder.addAxisValueRecord(self, self.location)
+
+    def asFea(self, indent=""):
+        res = "AxisValue {\n"
+        for location in self.locations:
+            res += location.asFea()
+
+        for nameRecord in self.names:
+            res += nameRecord.asFea()
+            res += "\n"
+
+        if self.flags:
+            flags = ["OlderSiblingFontAttribute", "ElidableAxisValueName"]
+            flagStrings = []
+            curr = 1
+            for i in range(len(flags)):
+                if self.flags & curr != 0:
+                    flagStrings.append(flags[i])
+                curr = curr << 1
+            res += f"flag {' '.join(flagStrings)};\n"
+        res += "};"
+        return res
+
+
+class AxisValueLocationStatement(Statement):
+    """
+    A STAT table Axis Value Location
+
+    Args:
+        tag (str): a 4 letter axis tag
+        values (list): a list of ints and/or floats
+    """
+
+    def __init__(self, tag, values, location=None):
+        Statement.__init__(self, location)
+        self.tag = tag
+        self.values = values
+
+    def asFea(self, res=""):
+        res += f"location {self.tag} "
+        res += f"{' '.join(str(i) for i in self.values)};\n"
+        return res
diff --git a/Lib/fontTools/feaLib/builder.py b/Lib/fontTools/feaLib/builder.py
index 4ec7ea9..989f05e 100644
--- a/Lib/fontTools/feaLib/builder.py
+++ b/Lib/fontTools/feaLib/builder.py
@@ -1,55 +1,143 @@
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import Tag, tostr
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import binary2num, safeEval
 from fontTools.feaLib.error import FeatureLibError
+from fontTools.feaLib.lookupDebugInfo import (
+    LookupDebugInfo,
+    LOOKUP_DEBUG_INFO_KEY,
+    LOOKUP_DEBUG_ENV_VAR,
+)
 from fontTools.feaLib.parser import Parser
+from fontTools.feaLib.ast import FeatureFile
 from fontTools.otlLib import builder as otl
+from fontTools.otlLib.maxContextCalc import maxCtxFont
 from fontTools.ttLib import newTable, getTableModule
 from fontTools.ttLib.tables import otBase, otTables
+from fontTools.otlLib.builder import (
+    AlternateSubstBuilder,
+    ChainContextPosBuilder,
+    ChainContextSubstBuilder,
+    LigatureSubstBuilder,
+    MultipleSubstBuilder,
+    CursivePosBuilder,
+    MarkBasePosBuilder,
+    MarkLigPosBuilder,
+    MarkMarkPosBuilder,
+    ReverseChainSingleSubstBuilder,
+    SingleSubstBuilder,
+    ClassPairPosSubtableBuilder,
+    PairPosBuilder,
+    SinglePosBuilder,
+    ChainContextualRule,
+)
+from fontTools.otlLib.error import OpenTypeLibError
+from collections import defaultdict
 import itertools
+from io import StringIO
+import logging
+import warnings
+import os
 
 
-def addOpenTypeFeatures(font, featurefile):
+log = logging.getLogger(__name__)
+
+
+def addOpenTypeFeatures(font, featurefile, tables=None, debug=False):
+    """Add features from a file to a font. Note that this replaces any features
+    currently present.
+
+    Args:
+        font (feaLib.ttLib.TTFont): The font object.
+        featurefile: Either a path or file object (in which case we
+            parse it into an AST), or a pre-parsed AST instance.
+        tables: If passed, restrict the set of affected tables to those in the
+            list.
+        debug: Whether to add source debugging information to the font in the
+            ``Debg`` table
+
+    """
     builder = Builder(font, featurefile)
-    builder.build()
+    builder.build(tables=tables, debug=debug)
 
 
-def addOpenTypeFeaturesFromString(font, features, filename=None):
-    featurefile = UnicodeIO(tounicode(features))
+def addOpenTypeFeaturesFromString(
+    font, features, filename=None, tables=None, debug=False
+):
+    """Add features from a string to a font. Note that this replaces any
+    features currently present.
+
+    Args:
+        font (feaLib.ttLib.TTFont): The font object.
+        features: A string containing feature code.
+        filename: The directory containing ``filename`` is used as the root of
+            relative ``include()`` paths; if ``None`` is provided, the current
+            directory is assumed.
+        tables: If passed, restrict the set of affected tables to those in the
+            list.
+        debug: Whether to add source debugging information to the font in the
+            ``Debg`` table
+
+    """
+
+    featurefile = StringIO(tostr(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)
+    addOpenTypeFeatures(font, featurefile, tables=tables, debug=debug)
 
 
 class Builder(object):
+
+    supportedTables = frozenset(
+        Tag(tag)
+        for tag in [
+            "BASE",
+            "GDEF",
+            "GPOS",
+            "GSUB",
+            "OS/2",
+            "head",
+            "hhea",
+            "name",
+            "vhea",
+            "STAT",
+        ]
+    )
+
     def __init__(self, font, featurefile):
         self.font = font
-        self.file = featurefile
+        # '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.seen_non_DFLT_script_ = False
         self.named_lookups_ = {}
         self.cur_lookup_ = None
         self.cur_lookup_name_ = None
         self.cur_feature_name_ = None
         self.lookups_ = []
+        self.lookup_locations = {"GSUB": {}, "GPOS": {}}
         self.features_ = {}  # ('latn', 'DEU ', 'smcp') --> [LookupBuilder*]
-        self.parseTree = None
         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_ = []
+        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'
@@ -73,35 +161,68 @@
         self.hhea_ = {}
         # for table 'vhea'
         self.vhea_ = {}
+        # for table 'STAT'
+        self.stat_ = {}
 
-    def build(self):
-        self.parseTree = Parser(self.file, self.glyphMap).parse()
+    def build(self, tables=None, debug=False):
+        if self.parseTree is None:
+            self.parseTree = Parser(self.file, self.glyphMap).parse()
         self.parseTree.build(self)
-        self.build_feature_aalt_()
-        self.build_head()
-        self.build_hhea()
-        self.build_vhea()
-        self.build_name()
-        self.build_OS_2()
-        for tag in ('GPOS', 'GSUB'):
+        # by default, build all the supported tables
+        if tables is None:
+            tables = self.supportedTables
+        else:
+            tables = frozenset(tables)
+            unsupported = tables - self.supportedTables
+            if unsupported:
+                unsupported_string = ", ".join(sorted(unsupported))
+                raise NotImplementedError(
+                    "The following tables were requested but are unsupported: "
+                    f"{unsupported_string}."
+                )
+        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()
+        if "STAT" in tables:
+            self.build_STAT()
+        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):
+            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]
-        gdef = self.buildGDEF()
-        if gdef:
-            self.font["GDEF"] = gdef
-        elif "GDEF" in self.font:
-            del self.font["GDEF"]
-        base = self.buildBASE()
-        if base:
-            self.font["BASE"] = base
-        elif "BASE" in self.font:
-            del self.font["BASE"]
+        if any(tag in self.font for tag in ("GPOS", "GSUB")) and "OS/2" in self.font:
+            self.font["OS/2"].usMaxContext = maxCtxFont(self.font)
+        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"]
+        if debug or os.environ.get(LOOKUP_DEBUG_ENV_VAR):
+            self.buildDebg()
 
     def get_chained_lookup_(self, location, builder_class):
         result = builder_class(self.font, location)
@@ -116,16 +237,19 @@
             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_):
+        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)
+                "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_
@@ -136,8 +260,7 @@
         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_)
+            self.add_lookup_to_feature_(self.cur_lookup_, self.cur_feature_name_)
         return self.cur_lookup_
 
     def build_feature_aalt_(self):
@@ -145,31 +268,40 @@
             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]
+            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)
+                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}
+                for lookuplist in lookups:
+                    if not isinstance(lookuplist, list):
+                        lookuplist = [lookuplist]
+                    for lookup in lookuplist:
+                        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}
+        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"}
+        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")
@@ -236,17 +368,43 @@
         params = None
         if tag == "size":
             params = otTables.FeatureParamsSize()
-            params.DesignSize, params.SubfamilyID, params.RangeStart, \
-                    params.RangeEnd = self.size_parameters_
+            (
+                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]
+            if not self.featureNames_ids_:
+                # name table wasn't selected among the tables to build; skip
+                pass
+            else:
+                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):
@@ -258,13 +416,20 @@
             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):
-                # A featureNames name and nameID is actually the tag
                 tag = nameID
-                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]
+                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):
@@ -280,10 +445,18 @@
             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"]
+            (
+                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"]
@@ -319,28 +492,197 @@
         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"))
+            checkattr(
+                table,
+                (
+                    "sxHeight",
+                    "sCapHeight",
+                    "usDefaultChar",
+                    "usBreakChar",
+                    "usMaxContext",
+                ),
+            )
         if version >= 5:
-            checkattr(table, ("usLowerOpticalPointSize",
-                              "usUpperOpticalPointSize"))
+            checkattr(table, ("usLowerOpticalPointSize", "usUpperOpticalPointSize"))
+
+    def setElidedFallbackName(self, value, location):
+        # ElidedFallbackName is a convenience method for setting
+        # ElidedFallbackNameID so only one can be allowed
+        for token in ("ElidedFallbackName", "ElidedFallbackNameID"):
+            if token in self.stat_:
+                raise FeatureLibError(
+                    f"{token} is already set.",
+                    location,
+                )
+        if isinstance(value, int):
+            self.stat_["ElidedFallbackNameID"] = value
+        elif isinstance(value, list):
+            self.stat_["ElidedFallbackName"] = value
+        else:
+            raise AssertionError(value)
+
+    def addDesignAxis(self, designAxis, location):
+        if "DesignAxes" not in self.stat_:
+            self.stat_["DesignAxes"] = []
+        if designAxis.tag in (r.tag for r in self.stat_["DesignAxes"]):
+            raise FeatureLibError(
+                f'DesignAxis already defined for tag "{designAxis.tag}".',
+                location,
+            )
+        if designAxis.axisOrder in (r.axisOrder for r in self.stat_["DesignAxes"]):
+            raise FeatureLibError(
+                f"DesignAxis already defined for axis number {designAxis.axisOrder}.",
+                location,
+            )
+        self.stat_["DesignAxes"].append(designAxis)
+
+    def addAxisValueRecord(self, axisValueRecord, location):
+        if "AxisValueRecords" not in self.stat_:
+            self.stat_["AxisValueRecords"] = []
+        # Check for duplicate AxisValueRecords
+        for record_ in self.stat_["AxisValueRecords"]:
+            if (
+                {n.asFea() for n in record_.names}
+                == {n.asFea() for n in axisValueRecord.names}
+                and {n.asFea() for n in record_.locations}
+                == {n.asFea() for n in axisValueRecord.locations}
+                and record_.flags == axisValueRecord.flags
+            ):
+                raise FeatureLibError(
+                    "An AxisValueRecord with these values is already defined.",
+                    location,
+                )
+        self.stat_["AxisValueRecords"].append(axisValueRecord)
+
+    def build_STAT(self):
+        if not self.stat_:
+            return
+
+        axes = self.stat_.get("DesignAxes")
+        if not axes:
+            raise FeatureLibError("DesignAxes not defined", None)
+        axisValueRecords = self.stat_.get("AxisValueRecords")
+        axisValues = {}
+        format4_locations = []
+        for tag in axes:
+            axisValues[tag.tag] = []
+        if axisValueRecords is not None:
+            for avr in axisValueRecords:
+                valuesDict = {}
+                if avr.flags > 0:
+                    valuesDict["flags"] = avr.flags
+                if len(avr.locations) == 1:
+                    location = avr.locations[0]
+                    values = location.values
+                    if len(values) == 1:  # format1
+                        valuesDict.update({"value": values[0], "name": avr.names})
+                    if len(values) == 2:  # format3
+                        valuesDict.update(
+                            {
+                                "value": values[0],
+                                "linkedValue": values[1],
+                                "name": avr.names,
+                            }
+                        )
+                    if len(values) == 3:  # format2
+                        nominal, minVal, maxVal = values
+                        valuesDict.update(
+                            {
+                                "nominalValue": nominal,
+                                "rangeMinValue": minVal,
+                                "rangeMaxValue": maxVal,
+                                "name": avr.names,
+                            }
+                        )
+                    axisValues[location.tag].append(valuesDict)
+                else:
+                    valuesDict.update(
+                        {
+                            "location": {i.tag: i.values[0] for i in avr.locations},
+                            "name": avr.names,
+                        }
+                    )
+                    format4_locations.append(valuesDict)
+
+        designAxes = [
+            {
+                "ordering": a.axisOrder,
+                "tag": a.tag,
+                "name": a.names,
+                "values": axisValues[a.tag],
+            }
+            for a in axes
+        ]
+
+        nameTable = self.font.get("name")
+        if not nameTable:  # this only happens for unit tests
+            nameTable = self.font["name"] = newTable("name")
+            nameTable.names = []
+
+        if "ElidedFallbackNameID" in self.stat_:
+            nameID = self.stat_["ElidedFallbackNameID"]
+            name = nameTable.getDebugName(nameID)
+            if not name:
+                raise FeatureLibError(
+                    f"ElidedFallbackNameID {nameID} points "
+                    "to a nameID that does not exist in the "
+                    '"name" table',
+                    None,
+                )
+        elif "ElidedFallbackName" in self.stat_:
+            nameID = self.stat_["ElidedFallbackName"]
+
+        otl.buildStatTable(
+            self.font,
+            designAxes,
+            locations=format4_locations,
+            elidedFallbackName=nameID,
+        )
 
     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,
+            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 = []
@@ -396,16 +738,22 @@
     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.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)):
+        if any(
+            (
+                gdef.GlyphClassDef,
+                gdef.AttachList,
+                gdef.LigCaretList,
+                gdef.MarkAttachClassDef,
+                gdef.MarkGlyphSetsDef,
+            )
+        ):
             result = newTable("GDEF")
             result.table = gdef
             return result
@@ -440,13 +788,20 @@
 
     def buildGDEFMarkGlyphSetsDef_(self):
         sets = []
-        for glyphs, id_ in sorted(self.markFilterSets_.items(),
-                                 key=lambda item: item[1]):
+        for glyphs, id_ in sorted(
+            self.markFilterSets_.items(), key=lambda item: item[1]
+        ):
             sets.append(glyphs)
         return otl.buildMarkGlyphSetsDef(sets, self.glyphMap)
 
+    def buildDebg(self):
+        if "Debg" not in self.font:
+            self.font["Debg"] = newTable("Debg")
+            self.font["Debg"].data = {}
+        self.font["Debg"].data[LOOKUP_DEBUG_INFO_KEY] = self.lookup_locations
+
     def buildLookups_(self, tag):
-        assert tag in ('GPOS', 'GSUB'), tag
+        assert tag in ("GPOS", "GSUB"), tag
         for lookup in self.lookups_:
             lookup.lookup_index = None
         lookups = []
@@ -454,8 +809,17 @@
             if lookup.table != tag:
                 continue
             lookup.lookup_index = len(lookups)
+            self.lookup_locations[tag][str(lookup.lookup_index)] = LookupDebugInfo(
+                location=str(lookup.location),
+                name=self.get_lookup_name_(lookup),
+                feature=None,
+            )
             lookups.append(lookup)
-        return [l.build() for l in lookups]
+        try:
+            otLookups = [l.build() for l in lookups]
+        except OpenTypeLibError as e:
+            raise FeatureLibError(str(e), e.location) from e
+        return otLookups
 
     def makeTable(self, tag):
         table = getattr(otTables, tag, None)()
@@ -473,20 +837,32 @@
         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
+        # https://github.com/fonttools/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])
+            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")
+            size_feature = tag == "GPOS" and feature_tag == "size"
             if len(lookup_indices) == 0 and not size_feature:
                 continue
 
+            for ix in lookup_indices:
+                try:
+                    self.lookup_locations[tag][str(ix)] = self.lookup_locations[tag][
+                        str(ix)
+                    ]._replace(feature=key)
+                except KeyError:
+                    warnings.warn(
+                        "feaLib.Builder subclass needs upgrading to "
+                        "stash debug information. See fonttools#2065."
+                    )
+
             feature_key = (feature_tag, lookup_indices)
             feature_index = feature_indices.get(feature_key)
             if feature_index is None:
@@ -494,14 +870,12 @@
                 frec = otTables.FeatureRecord()
                 frec.FeatureTag = feature_tag
                 frec.Feature = otTables.Feature()
-                frec.Feature.FeatureParams = self.buildFeatureParams(
-                                                feature_tag)
-                frec.Feature.LookupListIndex = lookup_indices
+                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)
+            scripts.setdefault(script, {}).setdefault(lang, []).append(feature_index)
             if self.required_features_.get((script, lang)) == feature_tag:
                 required_feature_indices[(script, lang)] = feature_index
 
@@ -517,17 +891,16 @@
                 langrec.LangSys = otTables.LangSys()
                 langrec.LangSys.LookupOrder = None
 
-                req_feature_index = \
-                    required_feature_indices.get((script, lang))
+                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)
+                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
@@ -542,17 +915,35 @@
         table.LookupList.LookupCount = len(table.LookupList.Lookup)
         return table
 
+    def get_lookup_name_(self, lookup):
+        rev = {v: k for k, v in self.named_lookups_.items()}
+        if lookup in rev:
+            return rev[lookup]
+        return None
+
     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_):
+        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)
+                "the first of the languagesystem statements",
+                location,
+            )
+        if script == "DFLT":
+            if self.seen_non_DFLT_script_:
+                raise FeatureLibError(
+                    'languagesystems using the "DFLT" script tag must '
+                    "precede all other languagesystems",
+                    location,
+                )
+        else:
+            self.seen_non_DFLT_script_ = True
         if (script, language) in self.default_language_systems_:
             raise FeatureLibError(
-                '"languagesystem %s %s" has already been specified' %
-                (script.strip(), language.strip()), location)
+                '"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):
@@ -564,11 +955,11 @@
         if self.default_language_systems_:
             return frozenset(self.default_language_systems_)
         else:
-            return frozenset({('DFLT', 'dflt')})
+            return frozenset({("DFLT", "dflt")})
 
     def start_feature(self, location, name):
         self.language_systems = self.get_default_language_systems_()
-        self.script_ = 'DFLT'
+        self.script_ = "DFLT"
         self.cur_lookup_ = None
         self.cur_feature_name_ = name
         self.lookupflag_ = 0
@@ -587,75 +978,76 @@
     def start_lookup_block(self, location, name):
         if name in self.named_lookups_:
             raise FeatureLibError(
-                'Lookup "%s" has already been defined' % name, location)
+                '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)
+                location,
+            )
         self.cur_lookup_name_ = name
         self.named_lookups_[name] = None
         self.cur_lookup_ = None
-        self.lookupflag_ = 0
-        self.lookupflag_markFilterSet_ = None
+        if self.cur_feature_name_ is 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
+        if self.cur_feature_name_ is 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_)
+        if lookup is not None:  # skip empty named lookup
+            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'):
+        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)
+                'within "feature %s"' % self.cur_feature_name_,
+                location,
+            )
+        if self.cur_feature_name_ is None:
+            raise FeatureLibError(
+                "Language statements are not allowed "
+                "within standalone lookup blocks",
+                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_())
+        lookups = self.features_.get((key[0], "dflt", key[2]))
+        if (language == "dflt" or include_default) and lookups:
+            self.features_[key] = lookups[:]
         else:
-            langsys = set()
-        langsys.add((self.script_, language))
-        self.language_systems = frozenset(langsys)
+            self.features_[key] = []
+        self.language_systems = frozenset([(self.script_, language)])
 
         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)
+                    "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):
@@ -670,9 +1062,9 @@
                 _, 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)
+                    "a MarkAttachmentType at %s" % (glyph, loc),
+                    location,
+                )
             self.markAttach_[glyph] = (id_, location)
         return id_
 
@@ -699,16 +1091,25 @@
         self.lookupflag_ = value
 
     def set_script(self, location, script):
-        if self.cur_feature_name_ in ('aalt', 'size'):
+        if self.cur_feature_name_ in ("aalt", "size"):
             raise FeatureLibError(
                 "Script statements are not allowed "
-                "within \"feature %s\"" % self.cur_feature_name_, location)
+                'within "feature %s"' % self.cur_feature_name_,
+                location,
+            )
+        if self.cur_feature_name_ is None:
+            raise FeatureLibError(
+                "Script statements are not allowed " "within standalone lookup blocks",
+                location,
+            )
+        if self.language_systems == {(script, "dflt")}:
+            # Nothing to do.
+            return
         self.cur_lookup_ = None
         self.script_ = script
         self.lookupflag_ = 0
         self.lookupflag_markFilterSet_ = None
-        self.set_language(location, "dflt",
-                          include_default=True, required=False)
+        self.set_language(location, "dflt", include_default=True, required=False)
 
     def find_lookup_builders_(self, lookups):
         """Helper for building chain contextual substitutions
@@ -717,9 +1118,11 @@
         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))
+        for lookuplist in lookups:
+            if lookuplist is not None:
+                lookup_builders.append(
+                    [self.named_lookups_.get(l.name) for l in lookuplist]
+                )
             else:
                 lookup_builders.append(None)
         return lookup_builders
@@ -730,17 +1133,21 @@
 
     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)))
+        lookup.rules.append(
+            ChainContextualRule(
+                prefix, glyphs, suffix, self.find_lookup_builders_(lookups)
+            )
+        )
 
-    def add_chain_context_subst(self, location,
-                                prefix, glyphs, suffix, 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)))
+        lookup.rules.append(
+            ChainContextualRule(
+                prefix, glyphs, suffix, self.find_lookup_builders_(lookups)
+            )
+        )
 
-    def add_alternate_subst(self, location,
-                            prefix, glyph, suffix, replacement):
+    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)
@@ -748,24 +1155,38 @@
         if prefix or suffix:
             chain = self.get_lookup_(location, ChainContextSubstBuilder)
             lookup = self.get_chained_lookup_(location, AlternateSubstBuilder)
-            chain.substitutions.append((prefix, [glyph], suffix, [lookup]))
+            chain.rules.append(ChainContextualRule(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)
+                '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)
+                'Feature references are only allowed inside "feature aalt"', location
+            )
         self.aalt_features_.append((location, featureName))
 
-    def add_featureName(self, location, tag):
-        self.featureNames_.append(tag)
+    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:
@@ -773,23 +1194,27 @@
         else:
             self.base_horiz_axis_ = (bases, scripts)
 
-    def set_size_parameters(self, location, DesignSize, SubfamilyID,
-                            RangeStart, RangeEnd):
-        if self.cur_feature_name_ != 'size':
+    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)
+                '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):
+    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]))
+            chain.rules.append(ChainContextualRule(prefix, glyphs, suffix, [lookup]))
         else:
             lookup = self.get_lookup_(location, LigatureSubstBuilder)
 
@@ -801,25 +1226,34 @@
         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:
+    def add_multiple_subst(
+        self, location, prefix, glyph, suffix, replacements, forceChain=False
+    ):
+        if prefix or suffix or forceChain:
             chain = self.get_lookup_(location, ChainContextSubstBuilder)
             sub = self.get_chained_lookup_(location, MultipleSubstBuilder)
             sub.mapping[glyph] = replacements
-            chain.substitutions.append((prefix, [{glyph}], suffix, [sub]))
+            chain.rules.append(ChainContextualRule(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)
+            if replacements == lookup.mapping[glyph]:
+                log.info(
+                    "Removing duplicate multiple substitution from glyph"
+                    ' "%s" to %s%s',
+                    glyph,
+                    replacements,
+                    f" at {location}" if location else "",
+                )
+            else:
+                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):
+    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))
+        lookup.rules.append((old_prefix, old_suffix, mapping))
 
     def add_single_subst(self, location, prefix, suffix, mapping, forceChain):
         if self.cur_feature_name_ == "aalt":
@@ -833,36 +1267,41 @@
         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)
+                if to_glyph == lookup.mapping[from_glyph]:
+                    log.info(
+                        "Removing duplicate single substitution from glyph"
+                        ' "%s" to "%s" at %s',
+                        from_glyph,
+                        to_glyph,
+                        location,
+                    )
+                else:
+                    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
+        # https://github.com/fonttools/fonttools/issues/512
         chain = self.get_lookup_(location, ChainContextSubstBuilder)
-        sub = self.find_chainable_SingleSubst_(chain, set(mapping.keys()))
+        sub = chain.find_chainable_single_subst(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]))
+        chain.rules.append(
+            ChainContextualRule(prefix, [list(mapping.keys())], suffix, [sub])
+        )
 
     def add_cursive_pos(self, location, glyphclass, entryAnchor, exitAnchor):
         lookup = self.get_lookup_(location, CursivePosBuilder)
         lookup.add_attachment(
-            location, glyphclass,
+            location,
+            glyphclass,
             makeOpenTypeAnchor(entryAnchor),
-            makeOpenTypeAnchor(exitAnchor))
+            makeOpenTypeAnchor(exitAnchor),
+        )
 
     def add_marks_(self, location, lookupBuilder, marks):
         """Helper for add_mark_{base,liga,mark}_pos."""
@@ -871,15 +1310,15 @@
                 for mark in markClassDef.glyphs.glyphSet():
                     if mark not in lookupBuilder.marks:
                         otMarkAnchor = makeOpenTypeAnchor(markClassDef.anchor)
-                        lookupBuilder.marks[mark] = (
-                            markClass.name, otMarkAnchor)
+                        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)
+                                "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)
@@ -887,8 +1326,7 @@
         for baseAnchor, markClass in marks:
             otBaseAnchor = makeOpenTypeAnchor(baseAnchor)
             for base in bases:
-                builder.bases.setdefault(base, {})[markClass.name] = (
-                    otBaseAnchor)
+                builder.bases.setdefault(base, {})[markClass.name] = otBaseAnchor
 
     def add_mark_lig_pos(self, location, ligatures, components):
         builder = self.get_lookup_(location, MarkLigPosBuilder)
@@ -908,17 +1346,24 @@
         for baseAnchor, markClass in marks:
             otBaseAnchor = makeOpenTypeAnchor(baseAnchor)
             for baseMark in baseMarks:
-                builder.baseMarks.setdefault(baseMark, {})[markClass.name] = (
-                    otBaseAnchor)
+                builder.baseMarks.setdefault(baseMark, {})[
+                    markClass.name
+                ] = otBaseAnchor
 
-    def add_class_pair_pos(self, location, glyphclass1, value1,
-                           glyphclass2, value2):
+    def add_class_pair_pos(self, location, glyphclass1, value1, glyphclass2, value2):
         lookup = self.get_lookup_(location, PairPosBuilder)
-        lookup.addClassPair(location, glyphclass1, value1, glyphclass2, value2)
+        v1 = makeOpenTypeValueRecord(value1, pairPosContext=True)
+        v2 = makeOpenTypeValueRecord(value2, pairPosContext=True)
+        lookup.addClassPair(location, glyphclass1, v1, glyphclass2, v2)
+
+    def add_subtable_break(self, location):
+        self.cur_lookup_.add_subtable_break(location)
 
     def add_specific_pair_pos(self, location, glyph1, value1, glyph2, value2):
         lookup = self.get_lookup_(location, PairPosBuilder)
-        lookup.addGlyphPair(location, glyph1, value1, glyph2, value2)
+        v1 = makeOpenTypeValueRecord(value1, pairPosContext=True)
+        v2 = makeOpenTypeValueRecord(value2, pairPosContext=True)
+        lookup.addGlyphPair(location, glyph1, v1, glyph2, v2)
 
     def add_single_pos(self, location, prefix, suffix, pos, forceChain):
         if prefix or suffix or forceChain:
@@ -926,52 +1371,50 @@
         else:
             lookup = self.get_lookup_(location, SinglePosBuilder)
             for glyphs, value in pos:
+                otValueRecord = makeOpenTypeValueRecord(value, pairPosContext=False)
                 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
+                    try:
+                        lookup.add_pos(location, glyph, otValueRecord)
+                    except OpenTypeLibError as e:
+                        raise FeatureLibError(str(e), e.location) from e
 
     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)
+            targets.extend(lookups)
         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)
+            otValue = makeOpenTypeValueRecord(value, pairPosContext=False)
+            sub = chain.find_chainable_single_pos(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)
+                sub.add_pos(location, glyph, otValue)
             subs.append(sub)
         assert len(pos) == len(subs), (pos, subs)
         chain.rules.append(
-            (prefix, [g for g, v in pos], suffix, subs))
+            ChainContextualRule(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)
+                "Glyph %s was assigned to a different class at %s"
+                % (glyph, oldLocation),
+                location,
+            )
         self.glyphClassDefs_[glyph] = (glyphClass, location)
 
-    def add_glyphClassDef(self, location, baseGlyphs, ligatureGlyphs,
-                          markGlyphs, componentGlyphs):
+    def add_glyphClassDef(
+        self, location, baseGlyphs, ligatureGlyphs, markGlyphs, componentGlyphs
+    ):
         for glyph in baseGlyphs:
             self.setGlyphClass_(location, glyph, 1)
         for glyph in ligatureGlyphs:
@@ -983,14 +1426,15 @@
 
     def add_ligatureCaretByIndex_(self, location, glyphs, carets):
         for glyph in glyphs:
-            self.ligCaretPoints_.setdefault(glyph, set()).update(carets)
+            if glyph not in self.ligCaretPoints_:
+                self.ligCaretPoints_[glyph] = carets
 
     def add_ligatureCaretByPos_(self, location, glyphs, carets):
         for glyph in glyphs:
-            self.ligCaretCoords_.setdefault(glyph, set()).update(carets)
+            if glyph not in self.ligCaretCoords_:
+                self.ligCaretCoords_[glyph] = carets
 
-    def add_name_record(self, location, nameID, platformID, platEncID,
-                        langID, string):
+    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):
@@ -1012,8 +1456,7 @@
         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)
+    return otl.buildAnchor(anchor.x, anchor.y, anchor.contourpoint, deviceX, deviceY)
 
 
 _VALUEREC_ATTRS = {
@@ -1024,9 +1467,9 @@
 
 
 def makeOpenTypeValueRecord(v, pairPosContext):
-    """ast.ValueRecord --> (otBase.ValueRecord, int ValueFormat)"""
-    if v is None:
-        return None, 0
+    """ast.ValueRecord --> otBase.ValueRecord"""
+    if not v:
+        return None
 
     vr = {}
     for astName, (otName, isDevice) in _VALUEREC_ATTRS.items():
@@ -1036,468 +1479,4 @@
     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:
-            otherLoc = self.locations[key]
-            raise FeatureLibError(
-                'Already defined position for pair %s %s at %s:%d:%d'
-                % (glyph1, glyph2, otherLoc[0], otherLoc[1], otherLoc[2]),
-                location)
-        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)
+    return valRec
diff --git a/Lib/fontTools/feaLib/error.py b/Lib/fontTools/feaLib/error.py
index e99f5f9..a2c5f9d 100644
--- a/Lib/fontTools/feaLib/error.py
+++ b/Lib/fontTools/feaLib/error.py
@@ -1,7 +1,3 @@
-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)
@@ -10,7 +6,17 @@
     def __str__(self):
         message = Exception.__str__(self)
         if self.location:
-            path, line, column = self.location
-            return "%s:%d:%d: %s" % (path, line, column, message)
+            return f"{self.location}: {message}"
         else:
             return message
+
+
+class IncludedFeaNotFound(FeatureLibError):
+    def __str__(self):
+        assert self.location is not None
+
+        message = (
+            "The following feature file should be included but cannot be found: "
+            f"{Exception.__str__(self)}"
+        )
+        return f"{self.location}: {message}"
diff --git a/Lib/fontTools/feaLib/lexer.py b/Lib/fontTools/feaLib/lexer.py
index 2e5f384..140fbd8 100644
--- a/Lib/fontTools/feaLib/lexer.py
+++ b/Lib/fontTools/feaLib/lexer.py
@@ -1,13 +1,14 @@
-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.feaLib.error import FeatureLibError, IncludedFeaNotFound
+from fontTools.feaLib.location import FeatureLibLocation
 import re
 import os
 
 
 class Lexer(object):
     NUMBER = "NUMBER"
+    HEXADECIMAL = "HEXADECIMAL"
+    OCTAL = "OCTAL"
+    NUMBERS = (NUMBER, HEXADECIMAL, OCTAL)
     FLOAT = "FLOAT"
     STRING = "STRING"
     NAME = "NAME"
@@ -28,7 +29,7 @@
     CHAR_NAME_START_ = CHAR_LETTER_ + "_+*:.^~!\\"
     CHAR_NAME_CONTINUATION_ = CHAR_LETTER_ + CHAR_DIGIT_ + "_.+*:^~!/-"
 
-    RE_GLYPHCLASS = re.compile(r"^[A-Za-z_0-9.]+$")
+    RE_GLYPHCLASS = re.compile(r"^[A-Za-z_0-9.\-]+$")
 
     MODE_NORMAL_ = "NORMAL"
     MODE_FILENAME_ = "FILENAME"
@@ -56,7 +57,7 @@
 
     def location_(self):
         column = self.pos_ - self.line_start_ + 1
-        return (self.filename_, self.line_, column)
+        return FeatureLibLocation(self.filename_ or "<features>", self.line_, column)
 
     def next_(self):
         self.scan_over_(Lexer.CHAR_WHITESPACE_)
@@ -75,72 +76,75 @@
             self.line_start_ = self.pos_
             return (Lexer.NEWLINE, None, location)
         if cur_char == "\r":
-            self.pos_ += (2 if next_char == "\n" else 1)
+            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)
+            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)
+                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)
+                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)
+            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)
+            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_]
+            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)
+                    "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)
+                    "underscore, period or hyphen",
+                    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_]
+            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)
+            return (Lexer.HEXADECIMAL, int(text[start : self.pos_], 16), location)
+        if cur_char == "0" and next_char in Lexer.CHAR_DIGIT_:
+            self.scan_over_(Lexer.CHAR_DIGIT_)
+            return (Lexer.OCTAL, int(text[start : self.pos_], 8), 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)
+                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)
+            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)
+                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)
+            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)
@@ -150,13 +154,11 @@
             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])
+                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)
+                raise FeatureLibError("Expected '\"' to terminate string", location)
+        raise FeatureLibError("Unexpected character: %r" % cur_char, location)
 
     def scan_over_(self, valid):
         p = self.pos_
@@ -175,20 +177,44 @@
         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)
+        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)
+                "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):
+    """A Lexer that follows include statements.
+
+    The OpenType feature file specification states that due to
+    historical reasons, relative imports should be resolved in this 
+    order:
+
+    1. If the source font is UFO format, then relative to the UFO's
+       font directory
+    2. relative to the top-level include file
+    3. relative to the parent include file
+
+    We only support 1 (via includeDir) and 2.
+    """
+
+    def __init__(self, featurefile, *, includeDir=None):
+        """Initializes an IncludingLexer.
+
+        Behavior:
+            If includeDir is passed, it will be used to determine the top-level
+            include directory to use for all encountered include statements. If it is
+            not passed, ``os.path.dirname(featurefile)`` will be considered the
+            include directory.
+        """
+
         self.lexers_ = [self.make_lexer_(featurefile)]
         self.featurefilepath = self.lexers_[0].filename_
+        self.includeDir = includeDir
 
     def __iter__(self):
         return self
@@ -208,35 +234,52 @@
                 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 != ";":
+                # semi_type, semi_token, semi_location = lexer.next()
+                # if semi_type is not Lexer.SYMBOL or semi_token != ";":
                 #    raise FeatureLibError("Expected ';'", semi_location)
-                curpath = os.path.dirname(self.featurefilepath)
-                path = os.path.join(curpath, fname_token)
+                if os.path.isabs(fname_token):
+                    path = fname_token
+                else:
+                    if self.includeDir is not None:
+                        curpath = self.includeDir
+                    elif 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)
-                self.lexers_.append(self.make_lexer_(path, fname_location))
-                continue
+                    raise FeatureLibError("Too many recursive includes", fname_location)
+                try:
+                    self.lexers_.append(self.make_lexer_(path))
+                except FileNotFoundError as err:
+                    raise IncludedFeaNotFound(fname_token, fname_location) from err
             else:
                 return (token_type, token, location)
         raise StopIteration()
 
     @staticmethod
-    def make_lexer_(file_or_path, location=None):
+    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
-            try:
-                fileobj = open(filename, "r", encoding="utf-8")
-            except IOError as err:
-                raise FeatureLibError(str(err), location)
+            fileobj = open(filename, "r", encoding="utf-8")
         data = fileobj.read()
-        filename = fileobj.name if hasattr(fileobj, "name") else "<features>"
+        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/location.py b/Lib/fontTools/feaLib/location.py
new file mode 100644
index 0000000..50f761d
--- /dev/null
+++ b/Lib/fontTools/feaLib/location.py
@@ -0,0 +1,12 @@
+from typing import NamedTuple
+
+
+class FeatureLibLocation(NamedTuple):
+    """A location in a feature file"""
+
+    file: str
+    line: int
+    column: int
+
+    def __str__(self):
+        return f"{self.file}:{self.line}:{self.column}"
diff --git a/Lib/fontTools/feaLib/lookupDebugInfo.py b/Lib/fontTools/feaLib/lookupDebugInfo.py
new file mode 100644
index 0000000..876cadf
--- /dev/null
+++ b/Lib/fontTools/feaLib/lookupDebugInfo.py
@@ -0,0 +1,11 @@
+from typing import NamedTuple
+
+LOOKUP_DEBUG_INFO_KEY = "com.github.fonttools.feaLib"
+LOOKUP_DEBUG_ENV_VAR  = "FONTTOOLS_LOOKUP_DEBUGGING"
+
+class LookupDebugInfo(NamedTuple):
+    """Information about where a lookup came from, to be embedded in a font"""
+
+    location: str
+    name: str
+    feature: list
diff --git a/Lib/fontTools/feaLib/parser.py b/Lib/fontTools/feaLib/parser.py
index bb33006..199d9b3 100644
--- a/Lib/fontTools/feaLib/parser.py
+++ b/Lib/fontTools/feaLib/parser.py
@@ -1,9 +1,7 @@
-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
+from fontTools.feaLib.lexer import Lexer, IncludingLexer, NonIncludingLexer
 from fontTools.misc.encodingTools import getEncoding
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytechr, tobytes, tostr
 import fontTools.feaLib.ast as ast
 import logging
 import os
@@ -14,21 +12,53 @@
 
 
 class Parser(object):
+    """Initializes a Parser object.
+
+    Example:
+
+        .. code:: python
+
+            from fontTools.feaLib.parser import Parser
+            parser = Parser(file, font.getReverseGlyphMap())
+            parsetree = parser.parse()
+
+    Note: the ``glyphNames`` iterable serves a double role to help distinguish
+    glyph names from ranges in the presence of hyphens and to ensure that glyph
+    names referenced in a feature file are actually part of a font's glyph set.
+    If the iterable is left empty, no glyph name in glyph set checking takes
+    place, and all glyph tokens containing hyphens are treated as literal glyph
+    names, not as ranges. (Adding a space around the hyphen can, in any case,
+    help to disambiguate ranges from glyph names containing hyphens.)
+
+    By default, the parser will follow ``include()`` statements in the feature
+    file. To turn this off, pass ``followIncludes=False``. Pass a directory string as
+    ``includeDir`` to explicitly declare a directory to search included feature files
+    in.
+    """
+
     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=(), **kwargs):
+    def __init__(
+        self, featurefile, glyphNames=(), followIncludes=True, includeDir=None, **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")
+                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)))
+            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()
@@ -36,21 +66,27 @@
         self.glyphclasses_ = SymbolTable()
         self.lookups_ = SymbolTable()
         self.valuerecords_ = SymbolTable()
-        self.symbol_tables_ = {
-            self.anchors_, self.valuerecords_
-        }
+        self.symbol_tables_ = {self.anchors_, self.valuerecords_}
         self.next_token_type_, self.next_token_ = (None, None)
         self.cur_comments_ = []
         self.next_token_location_ = None
-        self.lexer_ = IncludingLexer(featurefile)
+        lexerClass = IncludingLexer if followIncludes else NonIncludingLexer
+        self.lexer_ = lexerClass(featurefile, includeDir=includeDir)
         self.advance_lexer_(comments=True)
 
     def parse(self):
+        """Parse the file, and return a :class:`fontTools.feaLib.ast.FeatureFile`
+        object representing the root of the abstract syntax tree containing the
+        parsed contents of the file."""
         statements = self.doc_.statements
-        while self.next_token_type_ is not None:
+        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_))
+                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")):
@@ -68,60 +104,80 @@
             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.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_)
+                    '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):
+        # Parses an anchor in any of the four formats given in the feature
+        # file specification (2.e.vii).
         self.expect_symbol_("<")
         self.expect_keyword_("anchor")
         location = self.cur_token_location_
 
-        if self.next_token_ == "NULL":
+        if self.next_token_ == "NULL":  # Format D
             self.expect_keyword_("NULL")
             self.expect_symbol_(">")
             return None
 
-        if self.next_token_type_ == Lexer.NAME:
+        if self.next_token_type_ == Lexer.NAME:  # Format E
             name = self.expect_name_()
             anchordef = self.anchors_.resolve(name)
             if anchordef is None:
                 raise FeatureLibError(
-                    'Unknown anchor "%s"' % name,
-                    self.cur_token_location_)
+                    'Unknown anchor "%s"' % name, self.cur_token_location_
+                )
             self.expect_symbol_(">")
-            return self.ast.Anchor(location, name, anchordef.x, anchordef.y,
-                                   anchordef.contourpoint,
-                                   xDeviceTable=None, yDeviceTable=None)
+            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":
+        if self.next_token_ == "contourpoint":  # Format B
             self.expect_keyword_("contourpoint")
             contourpoint = self.expect_number_()
 
-        if self.next_token_ == "<":
+        if self.next_token_ == "<":  # Format C
             xDeviceTable = self.parse_device_()
             yDeviceTable = self.parse_device_()
         else:
             xDeviceTable, yDeviceTable = None, None
 
         self.expect_symbol_(">")
-        return self.ast.Anchor(location, None, x, y, contourpoint,
-                               xDeviceTable, yDeviceTable)
+        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]*."""
+        # Parses a sequence of ``[<anchor> mark @MARKCLASS]*.``
         anchorMarks = []  # [(self.ast.Anchor, markClassName)*]
         while self.next_token_ == "<":
             anchor = self.parse_anchor_()
@@ -133,6 +189,7 @@
         return anchorMarks
 
     def parse_anchordef_(self):
+        # Parses a named anchor definition (`section 2.e.viii <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#2.e.vii>`_).
         assert self.is_cur_keyword_("anchorDef")
         location = self.cur_token_location_
         x, y = self.expect_number_(), self.expect_number_()
@@ -142,22 +199,26 @@
             contourpoint = self.expect_number_()
         name = self.expect_name_()
         self.expect_symbol_(";")
-        anchordef = self.ast.AnchorDefinition(location, name, x, y, contourpoint)
+        anchordef = self.ast.AnchorDefinition(
+            name, x, y, contourpoint=contourpoint, location=location
+        )
         self.anchors_.define(name, anchordef)
         return anchordef
 
     def parse_anonymous_(self):
+        # Parses an anonymous data block (`section 10 <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#10>`_).
         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_('}')
+        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)
+        self.expect_symbol_(";")
+        return self.ast.AnonymousBlock(tag, content, location=location)
 
     def parse_attach_(self):
+        # Parses a GDEF Attach statement (`section 9.b <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#9.b>`_)
         assert self.is_cur_keyword_("Attach")
         location = self.cur_token_location_
         glyphs = self.parse_glyphclass_(accept_glyphname=True)
@@ -165,15 +226,16 @@
         while self.next_token_ != ";":
             contourPoints.add(self.expect_number_())
         self.expect_symbol_(";")
-        return self.ast.AttachStatement(location, glyphs, contourPoints)
+        return self.ast.AttachStatement(glyphs, contourPoints, location=location)
 
     def parse_enumerate_(self, vertical):
+        # Parse an enumerated pair positioning rule (`section 6.b.ii <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#6.b.ii>`_).
         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;'"""
+        # Parses 'GlyphClassDef @BASE, @LIGATURES, @MARKS, @COMPONENTS;'
         assert self.is_cur_keyword_("GlyphClassDef")
         location = self.cur_token_location_
         if self.next_token_ != ",":
@@ -196,16 +258,17 @@
         else:
             componentGlyphs = None
         self.expect_symbol_(";")
-        return self.ast.GlyphClassDefStatement(location, baseGlyphs, markGlyphs,
-                                               ligatureGlyphs, componentGlyphs)
+        return self.ast.GlyphClassDefStatement(
+            baseGlyphs, markGlyphs, ligatureGlyphs, componentGlyphs, location=location
+        )
 
     def parse_glyphclass_definition_(self):
-        """Parses glyph class definitions such as '@UPPERCASE = [A-Z];'"""
+        # 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(location, name, glyphs)
+        glyphclass = self.ast.GlyphClassDefinition(name, glyphs, location=location)
         self.glyphclasses_.define(name, glyphclass)
         return glyphclass
 
@@ -239,52 +302,75 @@
             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)
+                '"%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])
+            ranges = " or ".join(['"%s - %s"' % (s, l) for s, l in solutions])
             raise FeatureLibError(
-                "Ambiguous glyph range \"%s\"; "
+                'Ambiguous glyph range "%s"; '
                 "please use %s to clarify what you mean" % (name, ranges),
-                location)
+                location,
+            )
 
-    def parse_glyphclass_(self, accept_glyphname):
-        if (accept_glyphname and
-                self.next_token_type_ in (Lexer.NAME, Lexer.CID)):
+    def parse_glyphclass_(self, accept_glyphname, accept_null=False):
+        # Parses a glyph class, either named or anonymous, or (if
+        # ``bool(accept_glyphname)``) a glyph name. If ``bool(accept_null)`` then
+        # also accept the special NULL glyph.
+        if accept_glyphname and self.next_token_type_ in (Lexer.NAME, Lexer.CID):
+            if accept_null and self.next_token_ == "NULL":
+                # If you want a glyph called NULL, you should escape it.
+                self.advance_lexer_()
+                return self.ast.NullGlyph(location=self.cur_token_location_)
             glyph = self.expect_glyph_()
-            return self.ast.GlyphName(self.cur_token_location_, glyph)
+            self.check_glyph_name_in_glyph_set(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_)
+                    self.cur_token_location_,
+                )
             if isinstance(gc, self.ast.MarkClass):
-                return self.ast.MarkClassName(self.cur_token_location_, gc)
+                return self.ast.MarkClassName(gc, location=self.cur_token_location_)
             else:
-                return self.ast.GlyphClassName(self.cur_token_location_, gc)
+                return self.ast.GlyphClassName(gc, location=self.cur_token_location_)
 
         self.expect_symbol_("[")
         location = self.cur_token_location_
-        glyphs = self.ast.GlyphClass(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_:
+                if "-" in glyph and self.glyphNames_ and glyph not in self.glyphNames_:
                     start, limit = self.split_glyph_range_(glyph, location)
+                    self.check_glyph_name_in_glyph_set(start, limit)
                     glyphs.add_range(
-                        start, limit,
-                        self.make_glyph_range_(location, start, limit))
+                        start, limit, self.make_glyph_range_(location, start, limit)
+                    )
                 elif self.next_token_ == "-":
                     start = glyph
                     self.expect_symbol_("-")
                     limit = self.expect_glyph_()
+                    self.check_glyph_name_in_glyph_set(start, limit)
                     glyphs.add_range(
-                        start, limit,
-                        self.make_glyph_range_(location, start, limit))
+                        start, limit, self.make_glyph_range_(location, start, limit)
+                    )
                 else:
+                    if "-" in glyph and not self.glyphNames_:
+                        log.warning(
+                            str(
+                                FeatureLibError(
+                                    f"Ambiguous glyph name that looks like a range: {glyph!r}",
+                                    location,
+                                )
+                            )
+                        )
+                    self.check_glyph_name_in_glyph_set(glyph)
                     glyphs.append(glyph)
             elif self.next_token_type_ is Lexer.CID:
                 glyph = self.expect_glyph_()
@@ -293,44 +379,47 @@
                     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))
+                    self.check_glyph_name_in_glyph_set(
+                        f"cid{range_start:05d}",
+                        f"cid{range_end:05d}",
+                    )
+                    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_)
+                    glyph_name = f"cid{self.cur_token_:05d}"
+                    self.check_glyph_name_in_glyph_set(glyph_name)
+                    glyphs.append(glyph_name)
             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_)
+                        self.cur_token_location_,
+                    )
                 if isinstance(gc, self.ast.MarkClass):
-                    gc = self.ast.MarkClassName(self.cur_token_location_, gc)
+                    gc = self.ast.MarkClassName(gc, location=self.cur_token_location_)
                 else:
-                    gc = self.ast.GlyphClassName(self.cur_token_location_, gc)
+                    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_)
+                    f"or glyph class reference, found {self.next_token_!r}",
+                    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(self.cur_token_location_, gc)
-        else:
-            return self.ast.GlyphClassName(self.cur_token_location_, gc)
-
     def parse_glyph_pattern_(self, vertical):
+        # Parses a glyph pattern, including lookups and context, e.g.::
+        #
+        #    a b
+        #    a b c' d e
+        #    a b c' lookup ChangeC d e
         prefix, glyphs, lookups, values, suffix = ([], [], [], [], [])
         hasMarks = False
         while self.next_token_ not in {"by", "from", ";", ","}:
@@ -347,7 +436,8 @@
                     raise FeatureLibError(
                         "Unsupported contextual target sequence: at most "
                         "one run of marked (') glyph/class names allowed",
-                        self.cur_token_location_)
+                        self.cur_token_location_,
+                    )
                 glyphs.append(gc)
             elif glyphs:
                 suffix.append(gc)
@@ -359,46 +449,88 @@
             else:
                 values.append(None)
 
-            lookup = None
-            if self.next_token_ == "lookup":
+            lookuplist = None
+            while self.next_token_ == "lookup":
+                if lookuplist is None:
+                    lookuplist = []
                 self.expect_keyword_("lookup")
                 if not marked:
                     raise FeatureLibError(
                         "Lookups can only follow marked glyphs",
-                        self.cur_token_location_)
+                        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_)
+                        'Unknown lookup "%s"' % lookup_name, self.cur_token_location_
+                    )
+                lookuplist.append(lookup)
             if marked:
-                lookups.append(lookup)
+                lookups.append(lookuplist)
 
         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)]
+            if any(values[: len(prefix)]):
+                raise FeatureLibError(
+                    "Positioning cannot be applied in the bactrack glyph sequence, "
+                    "before the marked glyph sequence.",
+                    self.cur_token_location_
+                )
+            marked_values = values[len(prefix) : len(prefix) + len(glyphs)]
+            if any(marked_values):
+                if any(values[len(prefix) + len(glyphs) :]):
+                    raise FeatureLibError(
+                        "Positioning values are allowed only in the marked glyph "
+                        "sequence, or after the final glyph node when only one glyph "
+                        "node is marked.",
+                        self.cur_token_location_
+                    )
+                values = marked_values
+            elif values and values[-1]:
+                if len(glyphs) > 1 or any(values[:-1]):
+                    raise FeatureLibError(
+                        "Positioning values are allowed only in the marked glyph "
+                        "sequence, or after the final glyph node when only one glyph "
+                        "node is marked.",
+                        self.cur_token_location_
+                    )
+                values = values[-1:]
+            elif any(values):
+                raise FeatureLibError(
+                    "Positioning values are allowed only in the marked glyph "
+                    "sequence, or after the final glyph node when only one glyph "
+                    "node is marked.",
+                    self.cur_token_location_
+                )
             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)
+        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)
+            (
+                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):
+        # Parses an ignore sub/pos rule.
         assert self.is_cur_keyword_("ignore")
         location = self.cur_token_location_
         self.advance_lexer_()
@@ -406,19 +538,26 @@
             chainContext, hasLookups = self.parse_chain_context_()
             if hasLookups:
                 raise FeatureLibError(
-                    "No lookups can be specified for \"ignore sub\"",
-                    location)
-            return self.ast.IgnoreSubstStatement(location, chainContext)
+                    '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(location, chainContext)
+                    '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_)
+            '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")
@@ -426,13 +565,14 @@
         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")
+            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(location, language,
-                                          include_default, required)
+        return self.ast.LanguageStatement(
+            language, include_default, required, location=location
+        )
 
     def parse_ligatureCaretByIndex_(self):
         assert self.is_cur_keyword_("LigatureCaretByIndex")
@@ -442,7 +582,7 @@
         while self.next_token_ != ";":
             carets.append(self.expect_number_())
         self.expect_symbol_(";")
-        return self.ast.LigatureCaretByIndexStatement(location, glyphs, carets)
+        return self.ast.LigatureCaretByIndexStatement(glyphs, carets, location=location)
 
     def parse_ligatureCaretByPos_(self):
         assert self.is_cur_keyword_("LigatureCaretByPos")
@@ -452,31 +592,36 @@
         while self.next_token_ != ";":
             carets.append(self.expect_number_())
         self.expect_symbol_(";")
-        return self.ast.LigatureCaretByPosStatement(location, glyphs, carets)
+        return self.ast.LigatureCaretByPosStatement(glyphs, carets, location=location)
 
     def parse_lookup_(self, vertical):
+        # Parses a ``lookup`` - either a lookup block, or a lookup reference
+        # inside a feature.
         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_)
+                raise FeatureLibError(
+                    'Unknown lookup "%s"' % name, self.cur_token_location_
+                )
             self.expect_symbol_(";")
-            return self.ast.LookupReferenceStatement(location, lookup)
+            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(location, name, use_extension)
+        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):
+        # Parses a ``lookupflag`` statement, either specified by number or
+        # in words.
         assert self.is_cur_keyword_("lookupflag")
         location = self.cur_token_location_
 
@@ -484,36 +629,52 @@
         if self.next_token_type_ == Lexer.NUMBER:
             value = self.expect_number_()
             self.expect_symbol_(";")
-            return self.ast.LookupFlagStatement(location, value, None, None)
+            return self.ast.LookupFlagStatement(value, location=location)
 
         # format A: "lookupflag RightToLeft MarkAttachmentType @M;"
+        value_seen = False
         value, markAttachment, markFilteringSet = 0, None, None
         flags = {
-            "RightToLeft": 1, "IgnoreBaseGlyphs": 2,
-            "IgnoreLigatures": 4, "IgnoreMarks": 8
+            "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_)
+                    self.next_token_location_,
+                )
             seen.add(self.next_token_)
             if self.next_token_ == "MarkAttachmentType":
                 self.expect_keyword_("MarkAttachmentType")
-                markAttachment = self.parse_class_name_()
+                markAttachment = self.parse_glyphclass_(accept_glyphname=False)
             elif self.next_token_ == "UseMarkFilteringSet":
                 self.expect_keyword_("UseMarkFilteringSet")
-                markFilteringSet = self.parse_class_name_()
+                markFilteringSet = self.parse_glyphclass_(accept_glyphname=False)
             elif self.next_token_ in flags:
+                value_seen = True
                 value = value | flags[self.expect_name_()]
             else:
                 raise FeatureLibError(
                     '"%s" is not a recognized lookupflag' % self.next_token_,
-                    self.next_token_location_)
+                    self.next_token_location_,
+                )
         self.expect_symbol_(";")
-        return self.ast.LookupFlagStatement(location, value,
-                                            markAttachment, markFilteringSet)
+
+        if not any([value_seen, markAttachment, markFilteringSet]):
+            raise FeatureLibError(
+                "lookupflag must have a value", self.next_token_location_
+            )
+
+        return self.ast.LookupFlagStatement(
+            value,
+            markAttachment=markAttachment,
+            markFilteringSet=markFilteringSet,
+            location=location,
+        )
 
     def parse_markClass_(self):
         assert self.is_cur_keyword_("markClass")
@@ -527,7 +688,9 @@
             markClass = self.ast.MarkClass(name)
             self.doc_.markClasses[name] = markClass
             self.glyphclasses_.define(name, markClass)
-        mcdef = self.ast.MarkClassDefinition(location, markClass, anchor, glyphs)
+        mcdef = self.ast.MarkClassDefinition(
+            markClass, anchor, glyphs, location=location
+        )
         markClass.addDefinition(mcdef)
         return mcdef
 
@@ -535,26 +698,28 @@
         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
+        elif self.next_token_ == "base":  # GPOS type 4
             return self.parse_position_base_(enumerated, vertical)
-        elif self.next_token_ == "ligature":   # GPOS type 5
+        elif self.next_token_ == "ligature":  # GPOS type 5
             return self.parse_position_ligature_(enumerated, vertical)
-        elif self.next_token_ == "mark":   # GPOS type 6
+        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)
+        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)
+                    'If "lookup" is present, no values must be specified', location
+                )
             return self.ast.ChainContextPosStatement(
-                location, prefix, glyphs, suffix, lookups)
+                prefix, glyphs, suffix, lookups, location=location
+            )
 
         # Pair positioning, format A: "pos V 10 A -10;"
         # Pair positioning, format B: "pos V A -20;"
@@ -562,29 +727,41 @@
             if values[0] is None:  # Format B: "pos V A -20;"
                 values.reverse()
             return self.ast.PairPosStatement(
-                location, enumerated,
-                glyphs[0], values[0], glyphs[1], values[1])
+                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(location, list(zip(glyphs, values)),
-                                           prefix, suffix, forceChain=hasMarks)
+                '"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)
+                '"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(
-            location, glyphclass, entryAnchor, exitAnchor)
+            glyphclass, entryAnchor, exitAnchor, location=location
+        )
 
     def parse_position_base_(self, enumerated, vertical):
         location = self.cur_token_location_
@@ -592,12 +769,13 @@
         if enumerated:
             raise FeatureLibError(
                 '"enumerate" is not allowed with '
-                'mark-to-base attachment positioning',
-                location)
+                "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(location, base, marks)
+        return self.ast.MarkBasePosStatement(base, marks, location=location)
 
     def parse_position_ligature_(self, enumerated, vertical):
         location = self.cur_token_location_
@@ -605,15 +783,16 @@
         if enumerated:
             raise FeatureLibError(
                 '"enumerate" is not allowed with '
-                'mark-to-ligature attachment positioning',
-                location)
+                "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(location, ligatures, marks)
+        return self.ast.MarkLigPosStatement(ligatures, marks, location=location)
 
     def parse_position_mark_(self, enumerated, vertical):
         location = self.cur_token_location_
@@ -621,33 +800,41 @@
         if enumerated:
             raise FeatureLibError(
                 '"enumerate" is not allowed with '
-                'mark-to-mark attachment positioning',
-                location)
+                "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(location, baseMarks, marks)
+        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(location, script)
+        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)
+        (
+            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)
+                "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)
+                gc = self.parse_glyphclass_(accept_glyphname=True, accept_null=True)
                 new.append(gc)
         elif self.next_token_ == "from":
             keyword = self.expect_keyword_("from")
@@ -655,37 +842,41 @@
         else:
             keyword = None
         self.expect_symbol_(";")
-        if len(new) is 0 and not any(lookups):
+        if len(new) == 0 and not any(lookups):
             raise FeatureLibError(
                 'Expected "by", "from" or explicit lookup references',
-                self.cur_token_location_)
+                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)
+                    '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)
+                raise FeatureLibError('Expected a single glyph before "from"', location)
             if len(new) != 1:
                 raise FeatureLibError(
-                    'Expected a single glyphclass after "from"',
-                    location)
+                    'Expected a single glyphclass after "from"', location
+                )
             return self.ast.AlternateSubstStatement(
-                location, old_prefix, old[0], old_suffix, new[0])
+                old_prefix, old[0], old_suffix, new[0], location=location
+            )
 
         num_lookups = len([l for l in lookups if l is not None])
 
+        is_deletion = False
+        if len(new) == 1 and len(new[0].glyphSet()) == 0:
+            new = []  # Deletion
+            is_deletion = True
+
         # 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):
+        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:
@@ -693,33 +884,62 @@
             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)
+                    "but found a glyph class with %d elements"
+                    % (len(glyphs), len(replacements)),
+                    location,
+                )
             return self.ast.SingleSubstStatement(
-                location, old, new,
-                old_prefix, old_suffix,
-                forceChain=hasMarks
+                old, new, old_prefix, old_suffix, forceChain=hasMarks, location=location
+            )
+
+        # Glyph deletion, built as GSUB lookup type 2: Multiple substitution
+        # with empty replacement.
+        if is_deletion and len(old) == 1 and num_lookups == 0:
+            return self.ast.MultipleSubstStatement(
+                old_prefix,
+                old[0],
+                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):
+        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(
-                location, old_prefix, tuple(old[0].glyphSet())[0], old_suffix,
-                tuple([list(n.glyphSet())[0] for n in new]))
+                old_prefix,
+                tuple(old[0].glyphSet())[0],
+                old_suffix,
+                tuple([list(n.glyphSet())[0] for n in new]),
+                forceChain=hasMarks,
+                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):
+        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(
-                location, old_prefix, old, old_suffix,
-                list(new[0].glyphSet())[0], forceChain=hasMarks)
+                old_prefix,
+                old,
+                old_suffix,
+                list(new[0].glyphSet())[0],
+                forceChain=hasMarks,
+                location=location,
+            )
 
         # GSUB lookup type 8: Reverse chaining substitution.
         if reverse:
@@ -727,16 +947,19 @@
                 raise FeatureLibError(
                     "In reverse chaining single substitutions, "
                     "only a single glyph or glyph class can be replaced",
-                    location)
+                    location,
+                )
             if len(new) != 1:
                 raise FeatureLibError(
-                    'In reverse chaining single substitutions, '
+                    "In reverse chaining single substitutions, "
                     'the replacement (after "by") must be a single glyph '
-                    'or glyph class', location)
+                    "or glyph class",
+                    location,
+                )
             if num_lookups != 0:
                 raise FeatureLibError(
-                    "Reverse chaining substitutions cannot call named lookups",
-                    location)
+                    "Reverse chaining substitutions cannot call named lookups", location
+                )
             glyphs = sorted(list(old[0].glyphSet()))
             replacements = sorted(list(new[0].glyphSet()))
             if len(replacements) == 1:
@@ -744,50 +967,67 @@
             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)
+                    "but found a glyph class with %d elements"
+                    % (len(glyphs), len(replacements)),
+                    location,
+                )
             return self.ast.ReverseChainSingleSubstStatement(
-                location, old_prefix, old_suffix, old, new)
+                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,
+            )
+
+        # If there are remaining glyphs to parse, this is an invalid GSUB statement
+        if len(new) != 0 or is_deletion:
+            raise FeatureLibError("Invalid substitution statement", location)
 
         # GSUB lookup type 6: Chaining contextual substitution.
-        assert len(new) == 0, new
         rule = self.ast.ChainContextSubstStatement(
-            location, old_prefix, old, old_suffix, lookups)
+            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)
+        return self.ast.SubtableStatement(location=location)
 
     def parse_size_parameters_(self):
+        # Parses a ``parameters`` statement used in ``size`` features. See
+        # `section 8.b <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#8.b>`_.
         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 = 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(location, DesignSize, SubfamilyID,
-                                       RangeStart, RangeEnd)
+        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(location, "size", platformID,
-                                             platEncID, langID, string)
+        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(location, name)
+        table = self.ast.TableBlock(name, location=location)
         self.expect_symbol_("{")
         handler = {
             "GDEF": self.parse_table_GDEF_,
@@ -797,17 +1037,20 @@
             "name": self.parse_table_name_,
             "BASE": self.parse_table_BASE_,
             "OS/2": self.parse_table_OS_2_,
+            "STAT": self.parse_table_STAT_,
         }.get(name)
         if handler:
             handler(table)
         else:
-            raise FeatureLibError('"table %s" is not supported' % name.strip(),
-                                  location)
+            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_)
+            raise FeatureLibError(
+                'Expected "%s"' % name.strip(), self.cur_token_location_
+            )
         self.expect_symbol_(";")
         return table
 
@@ -816,7 +1059,9 @@
         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_))
+                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"):
@@ -829,23 +1074,24 @@
                 continue
             else:
                 raise FeatureLibError(
-                    "Expected Attach, LigatureCaretByIndex, "
-                    "or LigatureCaretByPos",
-                    self.cur_token_location_)
+                    "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_))
+                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_)
+                raise FeatureLibError("Expected FontRevision", self.cur_token_location_)
 
     def parse_table_hhea_(self, table):
         statements = table.statements
@@ -853,20 +1099,26 @@
         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_))
+                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(self.cur_token_location_, key, value))
+                    self.ast.HheaField(key, value, location=self.cur_token_location_)
+                )
                 if self.next_token_ != ";":
-                    raise FeatureLibError("Incomplete statement", self.next_token_location_)
+                    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_)
+                raise FeatureLibError(
+                    "Expected CaretOffset, Ascender, " "Descender or LineGap",
+                    self.cur_token_location_,
+                )
 
     def parse_table_vhea_(self, table):
         statements = table.statements
@@ -874,27 +1126,36 @@
         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_))
+                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(self.cur_token_location_, key, value))
+                    self.ast.VheaField(key, value, location=self.cur_token_location_)
+                )
                 if self.next_token_ != ";":
-                    raise FeatureLibError("Incomplete statement", self.next_token_location_)
+                    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_)
+                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_))
+                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:
@@ -902,30 +1163,30 @@
             elif self.cur_token_ == ";":
                 continue
             else:
-                raise FeatureLibError("Expected nameid",
-                                      self.cur_token_location_)
+                raise FeatureLibError("Expected nameid", self.cur_token_location_)
 
     def parse_name_(self):
+        """Parses a name record. See `section 9.e <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#9.e>`_."""
         platEncID = None
         langID = None
-        if self.next_token_type_ == Lexer.NUMBER:
-            platformID = self.expect_number_()
+        if self.next_token_type_ in Lexer.NUMBERS:
+            platformID = self.expect_any_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_()
+            if self.next_token_type_ in Lexer.NUMBERS:
+                platEncID = self.expect_any_number_()
+                langID = self.expect_any_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
+        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_(";")
@@ -936,21 +1197,54 @@
         unescaped = self.unescape_string_(string, encoding)
         return platformID, platEncID, langID, unescaped
 
+    def parse_stat_name_(self):
+        platEncID = None
+        langID = None
+        if self.next_token_type_ in Lexer.NUMBERS:
+            platformID = self.expect_any_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_ in Lexer.NUMBERS:
+                platEncID = self.expect_any_number_()
+                langID = self.expect_any_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_()
+        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_()
+        location, nameID = self.cur_token_location_, self.expect_any_number_()
         if nameID > 32767:
-            raise FeatureLibError("Name id value cannot be greater than 32767",
-                                  self.cur_token_location_)
+            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)
+            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(location, nameID, platformID, platEncID,
-                                   langID, string)
+        return self.ast.NameRecord(
+            nameID, platformID, platEncID, langID, string, location=location
+        )
 
     def unescape_string_(self, string, encoding):
         if encoding == "utf_16_be":
@@ -962,12 +1256,12 @@
         # 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")
+        return tostr(utf16, "utf_16_be")
 
     @staticmethod
     def unescape_unichr_(match):
         n = match.group(0)[1:]
-        return unichr(int(n, 16))
+        return chr(int(n, 16))
 
     @staticmethod
     def unescape_byte_(match, encoding):
@@ -979,34 +1273,59 @@
         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_))
+                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(self.cur_token_location_, horiz_bases,
-                                          horiz_scripts, False))
+                    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(self.cur_token_location_, vert_bases,
-                                          vert_scripts, True))
+                    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")
+        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_))
+                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
@@ -1019,17 +1338,213 @@
                 elif self.cur_token_ in ranges:
                     value = []
                     while self.next_token_ != ";":
-                         value.append(self.expect_number_())
+                        value.append(self.expect_number_())
                 elif self.is_cur_keyword_("Vendor"):
                     value = self.expect_string_()
                 statements.append(
-                    self.ast.OS2Field(self.cur_token_location_, key, value))
+                    self.ast.OS2Field(key, value, location=self.cur_token_location_)
+                )
+            elif self.cur_token_ == ";":
+                continue
+
+    def parse_STAT_ElidedFallbackName(self):
+        assert self.is_cur_keyword_("ElidedFallbackName")
+        self.expect_symbol_("{")
+        names = []
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_()
+            if self.is_cur_keyword_("name"):
+                platformID, platEncID, langID, string = self.parse_stat_name_()
+                nameRecord = self.ast.STATNameStatement(
+                    "stat",
+                    platformID,
+                    platEncID,
+                    langID,
+                    string,
+                    location=self.cur_token_location_,
+                )
+                names.append(nameRecord)
+            else:
+                if self.cur_token_ != ";":
+                    raise FeatureLibError(
+                        f"Unexpected token {self.cur_token_} " f"in ElidedFallbackName",
+                        self.cur_token_location_,
+                    )
+        self.expect_symbol_("}")
+        if not names:
+            raise FeatureLibError('Expected "name"', self.cur_token_location_)
+        return names
+
+    def parse_STAT_design_axis(self):
+        assert self.is_cur_keyword_("DesignAxis")
+        names = []
+        axisTag = self.expect_tag_()
+        if (
+            axisTag not in ("ital", "opsz", "slnt", "wdth", "wght")
+            and not axisTag.isupper()
+        ):
+            log.warning(f"Unregistered axis tag {axisTag} should be uppercase.")
+        axisOrder = self.expect_number_()
+        self.expect_symbol_("{")
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_()
+            if self.cur_token_type_ is Lexer.COMMENT:
+                continue
+            elif self.is_cur_keyword_("name"):
+                location = self.cur_token_location_
+                platformID, platEncID, langID, string = self.parse_stat_name_()
+                name = self.ast.STATNameStatement(
+                    "stat", platformID, platEncID, langID, string, location=location
+                )
+                names.append(name)
+            elif self.cur_token_ == ";":
+                continue
+            else:
+                raise FeatureLibError(
+                    f'Expected "name", got {self.cur_token_}', self.cur_token_location_
+                )
+
+        self.expect_symbol_("}")
+        return self.ast.STATDesignAxisStatement(
+            axisTag, axisOrder, names, self.cur_token_location_
+        )
+
+    def parse_STAT_axis_value_(self):
+        assert self.is_cur_keyword_("AxisValue")
+        self.expect_symbol_("{")
+        locations = []
+        names = []
+        flags = 0
+        while self.next_token_ != "}" or self.cur_comments_:
+            self.advance_lexer_(comments=True)
+            if self.cur_token_type_ is Lexer.COMMENT:
+                continue
+            elif self.is_cur_keyword_("name"):
+                location = self.cur_token_location_
+                platformID, platEncID, langID, string = self.parse_stat_name_()
+                name = self.ast.STATNameStatement(
+                    "stat", platformID, platEncID, langID, string, location=location
+                )
+                names.append(name)
+            elif self.is_cur_keyword_("location"):
+                location = self.parse_STAT_location()
+                locations.append(location)
+            elif self.is_cur_keyword_("flag"):
+                flags = self.expect_stat_flags()
+            elif self.cur_token_ == ";":
+                continue
+            else:
+                raise FeatureLibError(
+                    f"Unexpected token {self.cur_token_} " f"in AxisValue",
+                    self.cur_token_location_,
+                )
+        self.expect_symbol_("}")
+        if not names:
+            raise FeatureLibError('Expected "Axis Name"', self.cur_token_location_)
+        if not locations:
+            raise FeatureLibError('Expected "Axis location"', self.cur_token_location_)
+        if len(locations) > 1:
+            for location in locations:
+                if len(location.values) > 1:
+                    raise FeatureLibError(
+                        "Only one value is allowed in a "
+                        "Format 4 Axis Value Record, but "
+                        f"{len(location.values)} were found.",
+                        self.cur_token_location_,
+                    )
+            format4_tags = []
+            for location in locations:
+                tag = location.tag
+                if tag in format4_tags:
+                    raise FeatureLibError(
+                        f"Axis tag {tag} already " "defined.", self.cur_token_location_
+                    )
+                format4_tags.append(tag)
+
+        return self.ast.STATAxisValueStatement(
+            names, locations, flags, self.cur_token_location_
+        )
+
+    def parse_STAT_location(self):
+        values = []
+        tag = self.expect_tag_()
+        if len(tag.strip()) != 4:
+            raise FeatureLibError(
+                f"Axis tag {self.cur_token_} must be 4 " "characters",
+                self.cur_token_location_,
+            )
+
+        while self.next_token_ != ";":
+            if self.next_token_type_ is Lexer.FLOAT:
+                value = self.expect_float_()
+                values.append(value)
+            elif self.next_token_type_ is Lexer.NUMBER:
+                value = self.expect_number_()
+                values.append(value)
+            else:
+                raise FeatureLibError(
+                    f'Unexpected value "{self.next_token_}". '
+                    "Expected integer or float.",
+                    self.next_token_location_,
+                )
+        if len(values) == 3:
+            nominal, min_val, max_val = values
+            if nominal < min_val or nominal > max_val:
+                raise FeatureLibError(
+                    f"Default value {nominal} is outside "
+                    f"of specified range "
+                    f"{min_val}-{max_val}.",
+                    self.next_token_location_,
+                )
+        return self.ast.AxisValueLocationStatement(tag, values)
+
+    def parse_table_STAT_(self, table):
+        statements = table.statements
+        design_axes = []
+        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:
+                if self.is_cur_keyword_("ElidedFallbackName"):
+                    names = self.parse_STAT_ElidedFallbackName()
+                    statements.append(self.ast.ElidedFallbackName(names))
+                elif self.is_cur_keyword_("ElidedFallbackNameID"):
+                    value = self.expect_number_()
+                    statements.append(self.ast.ElidedFallbackNameID(value))
+                    self.expect_symbol_(";")
+                elif self.is_cur_keyword_("DesignAxis"):
+                    designAxis = self.parse_STAT_design_axis()
+                    design_axes.append(designAxis.tag)
+                    statements.append(designAxis)
+                    self.expect_symbol_(";")
+                elif self.is_cur_keyword_("AxisValue"):
+                    axisValueRecord = self.parse_STAT_axis_value_()
+                    for location in axisValueRecord.locations:
+                        if location.tag not in design_axes:
+                            # Tag must be defined in a DesignAxis before it
+                            # can be referenced
+                            raise FeatureLibError(
+                                "DesignAxis not defined for " f"{location.tag}.",
+                                self.cur_token_location_,
+                            )
+                    statements.append(axisValueRecord)
+                    self.expect_symbol_(";")
+                else:
+                    raise FeatureLibError(
+                        f"Unexpected token {self.cur_token_}", 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_
+        # Parses BASE table entries. (See `section 9.a <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#9.a>`_)
+        assert self.cur_token_ in (
+            "HorizAxis.BaseTagList",
+            "VertAxis.BaseTagList",
+        ), self.cur_token_
         bases = []
         while self.next_token_ != ";":
             bases.append(self.expect_script_tag_())
@@ -1037,8 +1552,10 @@
         return bases
 
     def parse_base_script_list_(self, count):
-        assert self.cur_token_ in ("HorizAxis.BaseScriptList",
-                                   "VertAxis.BaseScriptList"), self.cur_token_
+        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_(",")
@@ -1074,13 +1591,13 @@
         if self.next_token_type_ is Lexer.NUMBER:
             number, location = self.expect_number_(), self.cur_token_location_
             if vertical:
-                val = self.ast.ValueRecord(location, vertical,
-                                           None, None, None, number,
-                                           None, None, None, None)
+                val = self.ast.ValueRecord(
+                    yAdvance=number, vertical=vertical, location=location
+                )
             else:
-                val = self.ast.ValueRecord(location, vertical,
-                                           None, None, number, None,
-                                           None, None, None, None)
+                val = self.ast.ValueRecord(
+                    xAdvance=number, vertical=vertical, location=location
+                )
             return val
         self.expect_symbol_("<")
         location = self.cur_token_location_
@@ -1088,50 +1605,69 @@
             name = self.expect_name_()
             if name == "NULL":
                 self.expect_symbol_(">")
-                return None
+                return self.ast.ValueRecord()
             vrd = self.valuerecords_.resolve(name)
             if vrd is None:
-                raise FeatureLibError("Unknown valueRecordDef \"%s\"" % name,
-                                      self.cur_token_location_)
+                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_())
+                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 ())])
+                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_)
+                    self.cur_token_location_,
+                )
         else:
-            xPlaDevice, yPlaDevice, xAdvDevice, yAdvDevice = (
-                None, None, None, None)
+            xPlaDevice, yPlaDevice, xAdvDevice, yAdvDevice = (None, None, None, None)
 
         self.expect_symbol_(">")
         return self.ast.ValueRecord(
-            location, vertical, xPlacement, yPlacement, xAdvance, yAdvance,
-            xPlaDevice, yPlaDevice, xAdvDevice, yAdvDevice)
+            xPlacement,
+            yPlacement,
+            xAdvance,
+            yAdvance,
+            xPlaDevice,
+            yPlaDevice,
+            xAdvDevice,
+            yAdvDevice,
+            vertical=vertical,
+            location=location,
+        )
 
     def parse_valuerecord_definition_(self, vertical):
+        # Parses a named value record definition. (See section `2.e.v <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#2.e.v>`_)
         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(location, name, value)
+        vrd = self.ast.ValueRecordDefinition(name, value, location=location)
         self.valuerecords_.define(name, vrd)
         return vrd
 
@@ -1141,30 +1677,33 @@
         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(location, script, language)
+        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
-        if tag in ["ss%02d" % i for i in range(1, 20+1)]:
-            stylisticset = tag
+        vertical = tag in {"vkrn", "vpal", "vhal", "valt"}
 
-        size_feature = (tag == "size")
+        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(location, tag, use_extension)
-        self.parse_block_(block, vertical, stylisticset, size_feature)
+        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):
@@ -1172,46 +1711,49 @@
         location = self.cur_token_location_
         featureName = self.expect_tag_()
         self.expect_symbol_(";")
-        return self.ast.FeatureReferenceStatement(location, featureName)
+        return self.ast.FeatureReferenceStatement(featureName, location=location)
 
     def parse_featureNames_(self, tag):
+        """Parses a ``featureNames`` statement found in stylistic set features.
+        See section `8.c <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#8.c>`_."""
         assert self.cur_token_ == "featureNames", self.cur_token_
-        block = self.ast.FeatureNamesBlock(self.cur_token_location_)
+        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_))
+                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(location, tag, platformID,
-                                                  platEncID, langID, string))
+                    self.ast.FeatureNameStatement(
+                        tag, platformID, platEncID, langID, string, location=location
+                    )
+                )
             elif self.cur_token_ == ";":
                 continue
             else:
-                raise FeatureLibError('Expected "name"',
-                                      self.cur_token_location_)
+                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_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(location, version)
-
-    def parse_block_(self, block, vertical, stylisticset=None,
-                     size_feature=False):
+    def parse_cvParameters_(self, tag):
+        # Parses a ``cvParameters`` block found in Character Variant features.
+        # See section `8.d <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#8.d>`_.
+        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()
@@ -1220,7 +1762,108 @@
         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_))
+                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_any_number_()
+        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):
+        # Parses a ``FontRevision`` statement found in the head table. See
+        # `section 9.c <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#9.c>`_.
+        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"):
@@ -1241,11 +1884,11 @@
                 statements.append(self.parse_markClass_())
             elif self.is_cur_keyword_({"pos", "position"}):
                 statements.append(
-                    self.parse_position_(enumerated=False, vertical=vertical))
+                    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"})):
+            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_())
@@ -1253,18 +1896,26 @@
                 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:
+            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_)
+                    "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_:
@@ -1272,10 +1923,49 @@
 
         name = self.expect_name_()
         if name != block.name.strip():
-            raise FeatureLibError("Expected \"%s\"" % block.name.strip(),
-                                  self.cur_token_location_)
+            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, s.forceChain])
+
+        # Upgrade all single substitutions to multiple substitutions.
+        if has_single and has_multiple:
+            statements = []
+            for s in block.statements:
+                if isinstance(s, self.ast.SingleSubstStatement):
+                    glyphs = s.glyphs[0].glyphSet()
+                    replacements = s.replacements[0].glyphSet()
+                    if len(replacements) == 1:
+                        replacements *= len(glyphs)
+                    for i, glyph in enumerate(glyphs):
+                        statements.append(
+                            self.ast.MultipleSubstStatement(
+                                s.prefix,
+                                glyph,
+                                s.suffix,
+                                [replacements[i]],
+                                s.forceChain,
+                                location=s.location,
+                            )
+                        )
+                else:
+                    statements.append(s)
+            block.statements = statements
+
     def is_cur_keyword_(self, k):
         if self.cur_token_type_ is Lexer.NAME:
             if isinstance(k, type("")):  # basestring is gone in Python3
@@ -1296,6 +1986,12 @@
             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:
@@ -1303,22 +1999,39 @@
             if len(self.cur_token_) > 63:
                 raise FeatureLibError(
                     "Glyph names must not be longer than 63 characters",
-                    self.cur_token_location_)
+                    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_)
+        raise FeatureLibError("Expected a glyph name or CID", self.cur_token_location_)
+
+    def check_glyph_name_in_glyph_set(self, *names):
+        """Raises if glyph name (just `start`) or glyph names of a
+        range (`start` and `end`) are not in the glyph set.
+
+        If no glyph set is present, does nothing.
+        """
+        if self.glyphNames_:
+            missing = [name for name in names if name not in self.glyphNames_]
+            if missing:
+                raise FeatureLibError(
+                    "The following glyph names are referenced but are missing from the "
+                    f"glyph set: {', '.join(missing)}",
+                    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_)
+            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_)
+            raise FeatureLibError(
+                "@%s is not a markClass" % name, self.cur_token_location_
+            )
         return mc
 
     def expect_tag_(self):
@@ -1326,8 +2039,9 @@
         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_)
+            raise FeatureLibError(
+                "Tags cannot be longer than 4 characters", self.cur_token_location_
+            )
         return (self.cur_token_ + "    ")[:4]
 
     def expect_script_tag_(self):
@@ -1335,7 +2049,8 @@
         if tag == "dflt":
             raise FeatureLibError(
                 '"dflt" is not a valid script tag; use "DFLT" instead',
-                self.cur_token_location_)
+                self.cur_token_location_,
+            )
         return tag
 
     def expect_language_tag_(self):
@@ -1343,22 +2058,21 @@
         if tag == "DFLT":
             raise FeatureLibError(
                 '"DFLT" is not a valid language tag; use "dflt" instead',
-                self.cur_token_location_)
+                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_)
+        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_)
+        raise FeatureLibError('Expected "%s"' % keyword, self.cur_token_location_)
 
     def expect_name_(self):
         self.advance_lexer_()
@@ -1372,12 +2086,21 @@
             return self.cur_token_
         raise FeatureLibError("Expected a number", self.cur_token_location_)
 
+    def expect_any_number_(self):
+        self.advance_lexer_()
+        if self.cur_token_type_ in Lexer.NUMBERS:
+            return self.cur_token_
+        raise FeatureLibError(
+            "Expected a decimal, hexadecimal or octal 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_)
+        raise FeatureLibError(
+            "Expected a floating-point number", self.cur_token_location_
+        )
 
     def expect_decipoint_(self):
         if self.next_token_type_ == Lexer.FLOAT:
@@ -1385,8 +2108,35 @@
         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_)
+            raise FeatureLibError(
+                "Expected an integer or floating-point number", self.cur_token_location_
+            )
+
+    def expect_stat_flags(self):
+        value = 0
+        flags = {
+            "OlderSiblingFontAttribute": 1,
+            "ElidableAxisValueName": 2,
+        }
+        while self.next_token_ != ";":
+            if self.next_token_ in flags:
+                name = self.expect_name_()
+                value = value | flags[name]
+            else:
+                raise FeatureLibError(
+                    f"Unexpected STAT flag {self.cur_token_}", self.cur_token_location_
+                )
+        return value
+
+    def expect_stat_values_(self):
+        if self.next_token_type_ == Lexer.FLOAT:
+            return self.expect_float_()
+        elif self.next_token_type_ is Lexer.NUMBER:
+            return self.expect_number_()
+        else:
+            raise FeatureLibError(
+                "Expected an integer or floating-point number", self.cur_token_location_
+            )
 
     def expect_string_(self):
         self.advance_lexer_()
@@ -1401,12 +2151,17 @@
             return
         else:
             self.cur_token_type_, self.cur_token_, self.cur_token_location_ = (
-                self.next_token_type_, self.next_token_, self.next_token_location_)
-            self.cur_comments_ = []
+                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_)
+                (
+                    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:
@@ -1416,14 +2171,15 @@
     @staticmethod
     def reverse_string_(s):
         """'abc' --> 'cba'"""
-        return ''.join(reversed(list(s)))
+        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)
+                "Bad range: start should be less than limit", location
+            )
         for cid in range(start, limit + 1):
             result.append("cid%05d" % cid)
         return result
@@ -1433,45 +2189,45 @@
         result = list()
         if len(start) != len(limit):
             raise FeatureLibError(
-                "Bad range: \"%s\" and \"%s\" should have the same length" %
-                (start, limit), location)
+                '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)]
+            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):]
+            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)
+                "Start of range must be smaller than its end", location
+            )
 
-        uppercase = re.compile(r'^[A-Z]$')
+        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]$')
+        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}$')
+        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):]
+                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)
+        raise FeatureLibError('Bad range: "%s-%s"' % (start, limit), location)
 
 
 class SymbolTable(object):
diff --git a/Lib/fontTools/fontBuilder.py b/Lib/fontTools/fontBuilder.py
new file mode 100644
index 0000000..bf3b31b
--- /dev/null
+++ b/Lib/fontTools/fontBuilder.py
@@ -0,0 +1,959 @@
+__all__ = ["FontBuilder"]
+
+"""
+This module is *experimental*, meaning it still may evolve and change.
+
+The `FontBuilder` class is a convenient helper to construct working TTF or
+OTF fonts from scratch.
+
+Note that the various setup methods cannot be called in arbitrary order,
+due to various interdependencies between OpenType tables. Here is an order
+that works:
+
+    fb = FontBuilder(...)
+    fb.setupGlyphOrder(...)
+    fb.setupCharacterMap(...)
+    fb.setupGlyf(...) --or-- fb.setupCFF(...)
+    fb.setupHorizontalMetrics(...)
+    fb.setupHorizontalHeader()
+    fb.setupNameTable(...)
+    fb.setupOS2()
+    fb.addOpenTypeFeatures(...)
+    fb.setupPost()
+    fb.save(...)
+
+Here is how to build a minimal TTF:
+
+```python
+from fontTools.fontBuilder import FontBuilder
+from fontTools.pens.ttGlyphPen import TTGlyphPen
+
+
+def drawTestGlyph(pen):
+    pen.moveTo((100, 100))
+    pen.lineTo((100, 1000))
+    pen.qCurveTo((200, 900), (400, 900), (500, 1000))
+    pen.lineTo((500, 100))
+    pen.closePath()
+
+
+fb = FontBuilder(1024, isTTF=True)
+fb.setupGlyphOrder([".notdef", ".null", "space", "A", "a"])
+fb.setupCharacterMap({32: "space", 65: "A", 97: "a"})
+advanceWidths = {".notdef": 600, "space": 500, "A": 600, "a": 600, ".null": 0}
+
+familyName = "HelloTestFont"
+styleName = "TotallyNormal"
+version = "0.1"
+
+nameStrings = dict(
+    familyName=dict(en=familyName, nl="HalloTestFont"),
+    styleName=dict(en=styleName, nl="TotaalNormaal"),
+    uniqueFontIdentifier="fontBuilder: " + familyName + "." + styleName,
+    fullName=familyName + "-" + styleName,
+    psName=familyName + "-" + styleName,
+    version="Version " + version,
+)
+
+pen = TTGlyphPen(None)
+drawTestGlyph(pen)
+glyph = pen.glyph()
+glyphs = {".notdef": glyph, "space": glyph, "A": glyph, "a": glyph, ".null": glyph}
+fb.setupGlyf(glyphs)
+metrics = {}
+glyphTable = fb.font["glyf"]
+for gn, advanceWidth in advanceWidths.items():
+    metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
+fb.setupHorizontalMetrics(metrics)
+fb.setupHorizontalHeader(ascent=824, descent=-200)
+fb.setupNameTable(nameStrings)
+fb.setupOS2(sTypoAscender=824, usWinAscent=824, usWinDescent=200)
+fb.setupPost()
+fb.save("test.ttf")
+```
+
+And here's how to build a minimal OTF:
+
+```python
+from fontTools.fontBuilder import FontBuilder
+from fontTools.pens.t2CharStringPen import T2CharStringPen
+
+
+def drawTestGlyph(pen):
+    pen.moveTo((100, 100))
+    pen.lineTo((100, 1000))
+    pen.curveTo((200, 900), (400, 900), (500, 1000))
+    pen.lineTo((500, 100))
+    pen.closePath()
+
+
+fb = FontBuilder(1024, isTTF=False)
+fb.setupGlyphOrder([".notdef", ".null", "space", "A", "a"])
+fb.setupCharacterMap({32: "space", 65: "A", 97: "a"})
+advanceWidths = {".notdef": 600, "space": 500, "A": 600, "a": 600, ".null": 0}
+
+familyName = "HelloTestFont"
+styleName = "TotallyNormal"
+version = "0.1"
+
+nameStrings = dict(
+    familyName=dict(en=familyName, nl="HalloTestFont"),
+    styleName=dict(en=styleName, nl="TotaalNormaal"),
+    uniqueFontIdentifier="fontBuilder: " + familyName + "." + styleName,
+    fullName=familyName + "-" + styleName,
+    psName=familyName + "-" + styleName,
+    version="Version " + version,
+)
+
+pen = T2CharStringPen(600, None)
+drawTestGlyph(pen)
+charString = pen.getCharString()
+charStrings = {
+    ".notdef": charString,
+    "space": charString,
+    "A": charString,
+    "a": charString,
+    ".null": charString,
+}
+fb.setupCFF(nameStrings["psName"], {"FullName": nameStrings["psName"]}, charStrings, {})
+lsb = {gn: cs.calcBounds(None)[0] for gn, cs in charStrings.items()}
+metrics = {}
+for gn, advanceWidth in advanceWidths.items():
+    metrics[gn] = (advanceWidth, lsb[gn])
+fb.setupHorizontalMetrics(metrics)
+fb.setupHorizontalHeader(ascent=824, descent=200)
+fb.setupNameTable(nameStrings)
+fb.setupOS2(sTypoAscender=824, usWinAscent=824, usWinDescent=200)
+fb.setupPost()
+fb.save("test.otf")
+```
+"""
+
+from .ttLib import TTFont, newTable
+from .ttLib.tables._c_m_a_p import cmap_classes
+from .misc.timeTools import timestampNow
+import struct
+from collections import OrderedDict
+
+
+_headDefaults = dict(
+    tableVersion=1.0,
+    fontRevision=1.0,
+    checkSumAdjustment=0,
+    magicNumber=0x5F0F3CF5,
+    flags=0x0003,
+    unitsPerEm=1000,
+    created=0,
+    modified=0,
+    xMin=0,
+    yMin=0,
+    xMax=0,
+    yMax=0,
+    macStyle=0,
+    lowestRecPPEM=3,
+    fontDirectionHint=2,
+    indexToLocFormat=0,
+    glyphDataFormat=0,
+)
+
+_maxpDefaultsTTF = dict(
+    tableVersion=0x00010000,
+    numGlyphs=0,
+    maxPoints=0,
+    maxContours=0,
+    maxCompositePoints=0,
+    maxCompositeContours=0,
+    maxZones=2,
+    maxTwilightPoints=0,
+    maxStorage=0,
+    maxFunctionDefs=0,
+    maxInstructionDefs=0,
+    maxStackElements=0,
+    maxSizeOfInstructions=0,
+    maxComponentElements=0,
+    maxComponentDepth=0,
+)
+_maxpDefaultsOTF = dict(
+    tableVersion=0x00005000,
+    numGlyphs=0,
+)
+
+_postDefaults = dict(
+    formatType=3.0,
+    italicAngle=0,
+    underlinePosition=0,
+    underlineThickness=0,
+    isFixedPitch=0,
+    minMemType42=0,
+    maxMemType42=0,
+    minMemType1=0,
+    maxMemType1=0,
+)
+
+_hheaDefaults = dict(
+    tableVersion=0x00010000,
+    ascent=0,
+    descent=0,
+    lineGap=0,
+    advanceWidthMax=0,
+    minLeftSideBearing=0,
+    minRightSideBearing=0,
+    xMaxExtent=0,
+    caretSlopeRise=1,
+    caretSlopeRun=0,
+    caretOffset=0,
+    reserved0=0,
+    reserved1=0,
+    reserved2=0,
+    reserved3=0,
+    metricDataFormat=0,
+    numberOfHMetrics=0,
+)
+
+_vheaDefaults = dict(
+    tableVersion=0x00010000,
+    ascent=0,
+    descent=0,
+    lineGap=0,
+    advanceHeightMax=0,
+    minTopSideBearing=0,
+    minBottomSideBearing=0,
+    yMaxExtent=0,
+    caretSlopeRise=0,
+    caretSlopeRun=0,
+    reserved0=0,
+    reserved1=0,
+    reserved2=0,
+    reserved3=0,
+    reserved4=0,
+    metricDataFormat=0,
+    numberOfVMetrics=0,
+)
+
+_nameIDs = dict(
+    copyright=0,
+    familyName=1,
+    styleName=2,
+    uniqueFontIdentifier=3,
+    fullName=4,
+    version=5,
+    psName=6,
+    trademark=7,
+    manufacturer=8,
+    designer=9,
+    description=10,
+    vendorURL=11,
+    designerURL=12,
+    licenseDescription=13,
+    licenseInfoURL=14,
+    # reserved = 15,
+    typographicFamily=16,
+    typographicSubfamily=17,
+    compatibleFullName=18,
+    sampleText=19,
+    postScriptCIDFindfontName=20,
+    wwsFamilyName=21,
+    wwsSubfamilyName=22,
+    lightBackgroundPalette=23,
+    darkBackgroundPalette=24,
+    variationsPostScriptNamePrefix=25,
+)
+
+# to insert in setupNameTable doc string:
+# print("\n".join(("%s (nameID %s)" % (k, v)) for k, v in sorted(_nameIDs.items(), key=lambda x: x[1])))
+
+_panoseDefaults = dict(
+    bFamilyType=0,
+    bSerifStyle=0,
+    bWeight=0,
+    bProportion=0,
+    bContrast=0,
+    bStrokeVariation=0,
+    bArmStyle=0,
+    bLetterForm=0,
+    bMidline=0,
+    bXHeight=0,
+)
+
+_OS2Defaults = dict(
+    version=3,
+    xAvgCharWidth=0,
+    usWeightClass=400,
+    usWidthClass=5,
+    fsType=0x0004,  # default: Preview & Print embedding
+    ySubscriptXSize=0,
+    ySubscriptYSize=0,
+    ySubscriptXOffset=0,
+    ySubscriptYOffset=0,
+    ySuperscriptXSize=0,
+    ySuperscriptYSize=0,
+    ySuperscriptXOffset=0,
+    ySuperscriptYOffset=0,
+    yStrikeoutSize=0,
+    yStrikeoutPosition=0,
+    sFamilyClass=0,
+    panose=_panoseDefaults,
+    ulUnicodeRange1=0,
+    ulUnicodeRange2=0,
+    ulUnicodeRange3=0,
+    ulUnicodeRange4=0,
+    achVendID="????",
+    fsSelection=0,
+    usFirstCharIndex=0,
+    usLastCharIndex=0,
+    sTypoAscender=0,
+    sTypoDescender=0,
+    sTypoLineGap=0,
+    usWinAscent=0,
+    usWinDescent=0,
+    ulCodePageRange1=0,
+    ulCodePageRange2=0,
+    sxHeight=0,
+    sCapHeight=0,
+    usDefaultChar=0,  # .notdef
+    usBreakChar=32,  # space
+    usMaxContext=0,
+    usLowerOpticalPointSize=0,
+    usUpperOpticalPointSize=0,
+)
+
+
+class FontBuilder(object):
+    def __init__(self, unitsPerEm=None, font=None, isTTF=True):
+        """Initialize a FontBuilder instance.
+
+        If the `font` argument is not given, a new `TTFont` will be
+        constructed, and `unitsPerEm` must be given. If `isTTF` is True,
+        the font will be a glyf-based TTF; if `isTTF` is False it will be
+        a CFF-based OTF.
+
+        If `font` is given, it must be a `TTFont` instance and `unitsPerEm`
+        must _not_ be given. The `isTTF` argument will be ignored.
+        """
+        if font is None:
+            self.font = TTFont(recalcTimestamp=False)
+            self.isTTF = isTTF
+            now = timestampNow()
+            assert unitsPerEm is not None
+            self.setupHead(unitsPerEm=unitsPerEm, created=now, modified=now)
+            self.setupMaxp()
+        else:
+            assert unitsPerEm is None
+            self.font = font
+            self.isTTF = "glyf" in font
+
+    def save(self, file):
+        """Save the font. The 'file' argument can be either a pathname or a
+        writable file object.
+        """
+        self.font.save(file)
+
+    def _initTableWithValues(self, tableTag, defaults, values):
+        table = self.font[tableTag] = newTable(tableTag)
+        for k, v in defaults.items():
+            setattr(table, k, v)
+        for k, v in values.items():
+            setattr(table, k, v)
+        return table
+
+    def _updateTableWithValues(self, tableTag, values):
+        table = self.font[tableTag]
+        for k, v in values.items():
+            setattr(table, k, v)
+
+    def setupHead(self, **values):
+        """Create a new `head` table and initialize it with default values,
+        which can be overridden by keyword arguments.
+        """
+        self._initTableWithValues("head", _headDefaults, values)
+
+    def updateHead(self, **values):
+        """Update the head table with the fields and values passed as
+        keyword arguments.
+        """
+        self._updateTableWithValues("head", values)
+
+    def setupGlyphOrder(self, glyphOrder):
+        """Set the glyph order for the font."""
+        self.font.setGlyphOrder(glyphOrder)
+
+    def setupCharacterMap(self, cmapping, uvs=None, allowFallback=False):
+        """Build the `cmap` table for the font. The `cmapping` argument should
+        be a dict mapping unicode code points as integers to glyph names.
+
+        The `uvs` argument, when passed, must be a list of tuples, describing
+        Unicode Variation Sequences. These tuples have three elements:
+            (unicodeValue, variationSelector, glyphName)
+        `unicodeValue` and `variationSelector` are integer code points.
+        `glyphName` may be None, to indicate this is the default variation.
+        Text processors will then use the cmap to find the glyph name.
+        Each Unicode Variation Sequence should be an officially supported
+        sequence, but this is not policed.
+        """
+        subTables = []
+        highestUnicode = max(cmapping)
+        if highestUnicode > 0xFFFF:
+            cmapping_3_1 = dict((k, v) for k, v in cmapping.items() if k < 0x10000)
+            subTable_3_10 = buildCmapSubTable(cmapping, 12, 3, 10)
+            subTables.append(subTable_3_10)
+        else:
+            cmapping_3_1 = cmapping
+        format = 4
+        subTable_3_1 = buildCmapSubTable(cmapping_3_1, format, 3, 1)
+        try:
+            subTable_3_1.compile(self.font)
+        except struct.error:
+            # format 4 overflowed, fall back to format 12
+            if not allowFallback:
+                raise ValueError(
+                    "cmap format 4 subtable overflowed; sort glyph order by unicode to fix."
+                )
+            format = 12
+            subTable_3_1 = buildCmapSubTable(cmapping_3_1, format, 3, 1)
+        subTables.append(subTable_3_1)
+        subTable_0_3 = buildCmapSubTable(cmapping_3_1, format, 0, 3)
+        subTables.append(subTable_0_3)
+
+        if uvs is not None:
+            uvsDict = {}
+            for unicodeValue, variationSelector, glyphName in uvs:
+                if cmapping.get(unicodeValue) == glyphName:
+                    # this is a default variation
+                    glyphName = None
+                if variationSelector not in uvsDict:
+                    uvsDict[variationSelector] = []
+                uvsDict[variationSelector].append((unicodeValue, glyphName))
+            uvsSubTable = buildCmapSubTable({}, 14, 0, 5)
+            uvsSubTable.uvsDict = uvsDict
+            subTables.append(uvsSubTable)
+
+        self.font["cmap"] = newTable("cmap")
+        self.font["cmap"].tableVersion = 0
+        self.font["cmap"].tables = subTables
+
+    def setupNameTable(self, nameStrings, windows=True, mac=True):
+        """Create the `name` table for the font. The `nameStrings` argument must
+        be a dict, mapping nameIDs or descriptive names for the nameIDs to name
+        record values. A value is either a string, or a dict, mapping language codes
+        to strings, to allow localized name table entries.
+
+        By default, both Windows (platformID=3) and Macintosh (platformID=1) name
+        records are added, unless any of `windows` or `mac` arguments is False.
+
+        The following descriptive names are available for nameIDs:
+
+            copyright (nameID 0)
+            familyName (nameID 1)
+            styleName (nameID 2)
+            uniqueFontIdentifier (nameID 3)
+            fullName (nameID 4)
+            version (nameID 5)
+            psName (nameID 6)
+            trademark (nameID 7)
+            manufacturer (nameID 8)
+            designer (nameID 9)
+            description (nameID 10)
+            vendorURL (nameID 11)
+            designerURL (nameID 12)
+            licenseDescription (nameID 13)
+            licenseInfoURL (nameID 14)
+            typographicFamily (nameID 16)
+            typographicSubfamily (nameID 17)
+            compatibleFullName (nameID 18)
+            sampleText (nameID 19)
+            postScriptCIDFindfontName (nameID 20)
+            wwsFamilyName (nameID 21)
+            wwsSubfamilyName (nameID 22)
+            lightBackgroundPalette (nameID 23)
+            darkBackgroundPalette (nameID 24)
+            variationsPostScriptNamePrefix (nameID 25)
+        """
+        nameTable = self.font["name"] = newTable("name")
+        nameTable.names = []
+
+        for nameName, nameValue in nameStrings.items():
+            if isinstance(nameName, int):
+                nameID = nameName
+            else:
+                nameID = _nameIDs[nameName]
+            if isinstance(nameValue, str):
+                nameValue = dict(en=nameValue)
+            nameTable.addMultilingualName(
+                nameValue, ttFont=self.font, nameID=nameID, windows=windows, mac=mac
+            )
+
+    def setupOS2(self, **values):
+        """Create a new `OS/2` table and initialize it with default values,
+        which can be overridden by keyword arguments.
+        """
+        if "xAvgCharWidth" not in values:
+            gs = self.font.getGlyphSet()
+            widths = [
+                gs[glyphName].width
+                for glyphName in gs.keys()
+                if gs[glyphName].width > 0
+            ]
+            values["xAvgCharWidth"] = int(round(sum(widths) / float(len(widths))))
+        self._initTableWithValues("OS/2", _OS2Defaults, values)
+        if not (
+            "ulUnicodeRange1" in values
+            or "ulUnicodeRange2" in values
+            or "ulUnicodeRange3" in values
+            or "ulUnicodeRange3" in values
+        ):
+            assert (
+                "cmap" in self.font
+            ), "the 'cmap' table must be setup before the 'OS/2' table"
+            self.font["OS/2"].recalcUnicodeRanges(self.font)
+
+    def setupCFF(self, psName, fontInfo, charStringsDict, privateDict):
+        from .cffLib import (
+            CFFFontSet,
+            TopDictIndex,
+            TopDict,
+            CharStrings,
+            GlobalSubrsIndex,
+            PrivateDict,
+        )
+
+        assert not self.isTTF
+        self.font.sfntVersion = "OTTO"
+        fontSet = CFFFontSet()
+        fontSet.major = 1
+        fontSet.minor = 0
+        fontSet.otFont = self.font
+        fontSet.fontNames = [psName]
+        fontSet.topDictIndex = TopDictIndex()
+
+        globalSubrs = GlobalSubrsIndex()
+        fontSet.GlobalSubrs = globalSubrs
+        private = PrivateDict()
+        for key, value in privateDict.items():
+            setattr(private, key, value)
+        fdSelect = None
+        fdArray = None
+
+        topDict = TopDict()
+        topDict.charset = self.font.getGlyphOrder()
+        topDict.Private = private
+        topDict.GlobalSubrs = fontSet.GlobalSubrs
+        for key, value in fontInfo.items():
+            setattr(topDict, key, value)
+        if "FontMatrix" not in fontInfo:
+            scale = 1 / self.font["head"].unitsPerEm
+            topDict.FontMatrix = [scale, 0, 0, scale, 0, 0]
+
+        charStrings = CharStrings(
+            None, topDict.charset, globalSubrs, private, fdSelect, fdArray
+        )
+        for glyphName, charString in charStringsDict.items():
+            charString.private = private
+            charString.globalSubrs = globalSubrs
+            charStrings[glyphName] = charString
+        topDict.CharStrings = charStrings
+
+        fontSet.topDictIndex.append(topDict)
+
+        self.font["CFF "] = newTable("CFF ")
+        self.font["CFF "].cff = fontSet
+
+    def setupCFF2(self, charStringsDict, fdArrayList=None, regions=None):
+        from .cffLib import (
+            CFFFontSet,
+            TopDictIndex,
+            TopDict,
+            CharStrings,
+            GlobalSubrsIndex,
+            PrivateDict,
+            FDArrayIndex,
+            FontDict,
+        )
+
+        assert not self.isTTF
+        self.font.sfntVersion = "OTTO"
+        fontSet = CFFFontSet()
+        fontSet.major = 2
+        fontSet.minor = 0
+
+        cff2GetGlyphOrder = self.font.getGlyphOrder
+        fontSet.topDictIndex = TopDictIndex(None, cff2GetGlyphOrder, None)
+
+        globalSubrs = GlobalSubrsIndex()
+        fontSet.GlobalSubrs = globalSubrs
+
+        if fdArrayList is None:
+            fdArrayList = [{}]
+        fdSelect = None
+        fdArray = FDArrayIndex()
+        fdArray.strings = None
+        fdArray.GlobalSubrs = globalSubrs
+        for privateDict in fdArrayList:
+            fontDict = FontDict()
+            fontDict.setCFF2(True)
+            private = PrivateDict()
+            for key, value in privateDict.items():
+                setattr(private, key, value)
+            fontDict.Private = private
+            fdArray.append(fontDict)
+
+        topDict = TopDict()
+        topDict.cff2GetGlyphOrder = cff2GetGlyphOrder
+        topDict.FDArray = fdArray
+        scale = 1 / self.font["head"].unitsPerEm
+        topDict.FontMatrix = [scale, 0, 0, scale, 0, 0]
+
+        private = fdArray[0].Private
+        charStrings = CharStrings(None, None, globalSubrs, private, fdSelect, fdArray)
+        for glyphName, charString in charStringsDict.items():
+            charString.private = private
+            charString.globalSubrs = globalSubrs
+            charStrings[glyphName] = charString
+        topDict.CharStrings = charStrings
+
+        fontSet.topDictIndex.append(topDict)
+
+        self.font["CFF2"] = newTable("CFF2")
+        self.font["CFF2"].cff = fontSet
+
+        if regions:
+            self.setupCFF2Regions(regions)
+
+    def setupCFF2Regions(self, regions):
+        from .varLib.builder import buildVarRegionList, buildVarData, buildVarStore
+        from .cffLib import VarStoreData
+
+        assert "fvar" in self.font, "fvar must to be set up first"
+        assert "CFF2" in self.font, "CFF2 must to be set up first"
+        axisTags = [a.axisTag for a in self.font["fvar"].axes]
+        varRegionList = buildVarRegionList(regions, axisTags)
+        varData = buildVarData(list(range(len(regions))), None, optimize=False)
+        varStore = buildVarStore(varRegionList, [varData])
+        vstore = VarStoreData(otVarStore=varStore)
+        topDict = self.font["CFF2"].cff.topDictIndex[0]
+        topDict.VarStore = vstore
+        for fontDict in topDict.FDArray:
+            fontDict.Private.vstore = vstore
+
+    def setupGlyf(self, glyphs, calcGlyphBounds=True):
+        """Create the `glyf` table from a dict, that maps glyph names
+        to `fontTools.ttLib.tables._g_l_y_f.Glyph` objects, for example
+        as made by `fontTools.pens.ttGlyphPen.TTGlyphPen`.
+
+        If `calcGlyphBounds` is True, the bounds of all glyphs will be
+        calculated. Only pass False if your glyph objects already have
+        their bounding box values set.
+        """
+        assert self.isTTF
+        self.font["loca"] = newTable("loca")
+        self.font["glyf"] = newTable("glyf")
+        self.font["glyf"].glyphs = glyphs
+        if hasattr(self.font, "glyphOrder"):
+            self.font["glyf"].glyphOrder = self.font.glyphOrder
+        if calcGlyphBounds:
+            self.calcGlyphBounds()
+
+    def setupFvar(self, axes, instances):
+        """Adds an font variations table to the font.
+
+        Args:
+            axes (list): See below.
+            instances (list): See below.
+
+        ``axes`` should be a list of axes, with each axis either supplied as
+        a py:class:`.designspaceLib.AxisDescriptor` object, or a tuple in the
+        format ```tupletag, minValue, defaultValue, maxValue, name``.
+        The ``name`` is either a string, or a dict, mapping language codes
+        to strings, to allow localized name table entries.
+
+        ```instances`` should be a list of instances, with each instance either
+        supplied as a py:class:`.designspaceLib.InstanceDescriptor` object, or a
+        dict with keys ``location`` (mapping of axis tags to float values),
+        ``stylename`` and (optionally) ``postscriptfontname``.
+        The ``stylename`` is either a string, or a dict, mapping language codes
+        to strings, to allow localized name table entries.
+        """
+
+        addFvar(self.font, axes, instances)
+
+    def setupAvar(self, axes):
+        """Adds an axis variations table to the font.
+
+        Args:
+            axes (list): A list of py:class:`.designspaceLib.AxisDescriptor` objects.
+        """
+        from .varLib import _add_avar
+
+        _add_avar(self.font, OrderedDict(enumerate(axes)))  # Only values are used
+
+    def setupGvar(self, variations):
+        gvar = self.font["gvar"] = newTable("gvar")
+        gvar.version = 1
+        gvar.reserved = 0
+        gvar.variations = variations
+
+    def calcGlyphBounds(self):
+        """Calculate the bounding boxes of all glyphs in the `glyf` table.
+        This is usually not called explicitly by client code.
+        """
+        glyphTable = self.font["glyf"]
+        for glyph in glyphTable.glyphs.values():
+            glyph.recalcBounds(glyphTable)
+
+    def setupHorizontalMetrics(self, metrics):
+        """Create a new `hmtx` table, for horizontal metrics.
+
+        The `metrics` argument must be a dict, mapping glyph names to
+        `(width, leftSidebearing)` tuples.
+        """
+        self.setupMetrics("hmtx", metrics)
+
+    def setupVerticalMetrics(self, metrics):
+        """Create a new `vmtx` table, for horizontal metrics.
+
+        The `metrics` argument must be a dict, mapping glyph names to
+        `(height, topSidebearing)` tuples.
+        """
+        self.setupMetrics("vmtx", metrics)
+
+    def setupMetrics(self, tableTag, metrics):
+        """See `setupHorizontalMetrics()` and `setupVerticalMetrics()`."""
+        assert tableTag in ("hmtx", "vmtx")
+        mtxTable = self.font[tableTag] = newTable(tableTag)
+        roundedMetrics = {}
+        for gn in metrics:
+            w, lsb = metrics[gn]
+            roundedMetrics[gn] = int(round(w)), int(round(lsb))
+        mtxTable.metrics = roundedMetrics
+
+    def setupHorizontalHeader(self, **values):
+        """Create a new `hhea` table initialize it with default values,
+        which can be overridden by keyword arguments.
+        """
+        self._initTableWithValues("hhea", _hheaDefaults, values)
+
+    def setupVerticalHeader(self, **values):
+        """Create a new `vhea` table initialize it with default values,
+        which can be overridden by keyword arguments.
+        """
+        self._initTableWithValues("vhea", _vheaDefaults, values)
+
+    def setupVerticalOrigins(self, verticalOrigins, defaultVerticalOrigin=None):
+        """Create a new `VORG` table. The `verticalOrigins` argument must be
+        a dict, mapping glyph names to vertical origin values.
+
+        The `defaultVerticalOrigin` argument should be the most common vertical
+        origin value. If omitted, this value will be derived from the actual
+        values in the `verticalOrigins` argument.
+        """
+        if defaultVerticalOrigin is None:
+            # find the most frequent vorg value
+            bag = {}
+            for gn in verticalOrigins:
+                vorg = verticalOrigins[gn]
+                if vorg not in bag:
+                    bag[vorg] = 1
+                else:
+                    bag[vorg] += 1
+            defaultVerticalOrigin = sorted(
+                bag, key=lambda vorg: bag[vorg], reverse=True
+            )[0]
+        self._initTableWithValues(
+            "VORG",
+            {},
+            dict(VOriginRecords={}, defaultVertOriginY=defaultVerticalOrigin),
+        )
+        vorgTable = self.font["VORG"]
+        vorgTable.majorVersion = 1
+        vorgTable.minorVersion = 0
+        for gn in verticalOrigins:
+            vorgTable[gn] = verticalOrigins[gn]
+
+    def setupPost(self, keepGlyphNames=True, **values):
+        """Create a new `post` table and initialize it with default values,
+        which can be overridden by keyword arguments.
+        """
+        isCFF2 = "CFF2" in self.font
+        postTable = self._initTableWithValues("post", _postDefaults, values)
+        if (self.isTTF or isCFF2) and keepGlyphNames:
+            postTable.formatType = 2.0
+            postTable.extraNames = []
+            postTable.mapping = {}
+        else:
+            postTable.formatType = 3.0
+
+    def setupMaxp(self):
+        """Create a new `maxp` table. This is called implicitly by FontBuilder
+        itself and is usually not called by client code.
+        """
+        if self.isTTF:
+            defaults = _maxpDefaultsTTF
+        else:
+            defaults = _maxpDefaultsOTF
+        self._initTableWithValues("maxp", defaults, {})
+
+    def setupDummyDSIG(self):
+        """This adds an empty DSIG table to the font to make some MS applications
+        happy. This does not properly sign the font.
+        """
+        values = dict(
+            ulVersion=1,
+            usFlag=0,
+            usNumSigs=0,
+            signatureRecords=[],
+        )
+        self._initTableWithValues("DSIG", {}, values)
+
+    def addOpenTypeFeatures(self, features, filename=None, tables=None):
+        """Add OpenType features to the font from a string containing
+        Feature File syntax.
+
+        The `filename` argument is used in error messages and to determine
+        where to look for "include" files.
+
+        The optional `tables` argument can be a list of OTL tables tags to
+        build, allowing the caller to only build selected OTL tables. See
+        `fontTools.feaLib` for details.
+        """
+        from .feaLib.builder import addOpenTypeFeaturesFromString
+
+        addOpenTypeFeaturesFromString(
+            self.font, features, filename=filename, tables=tables
+        )
+
+    def addFeatureVariations(self, conditionalSubstitutions, featureTag="rvrn"):
+        """Add conditional substitutions to a Variable Font.
+
+        See `fontTools.varLib.featureVars.addFeatureVariations`.
+        """
+        from .varLib import featureVars
+
+        if "fvar" not in self.font:
+            raise KeyError("'fvar' table is missing; can't add FeatureVariations.")
+
+        featureVars.addFeatureVariations(
+            self.font, conditionalSubstitutions, featureTag=featureTag
+        )
+
+    def setupCOLR(
+        self,
+        colorLayers,
+        version=None,
+        varStore=None,
+        varIndexMap=None,
+        clipBoxes=None,
+    ):
+        """Build new COLR table using color layers dictionary.
+
+        Cf. `fontTools.colorLib.builder.buildCOLR`.
+        """
+        from fontTools.colorLib.builder import buildCOLR
+
+        glyphMap = self.font.getReverseGlyphMap()
+        self.font["COLR"] = buildCOLR(
+            colorLayers,
+            version=version,
+            glyphMap=glyphMap,
+            varStore=varStore,
+            varIndexMap=varIndexMap,
+            clipBoxes=clipBoxes,
+        )
+
+    def setupCPAL(
+        self,
+        palettes,
+        paletteTypes=None,
+        paletteLabels=None,
+        paletteEntryLabels=None,
+    ):
+        """Build new CPAL table using list of palettes.
+
+        Optionally build CPAL v1 table using paletteTypes, paletteLabels and
+        paletteEntryLabels.
+
+        Cf. `fontTools.colorLib.builder.buildCPAL`.
+        """
+        from fontTools.colorLib.builder import buildCPAL
+
+        self.font["CPAL"] = buildCPAL(
+            palettes,
+            paletteTypes=paletteTypes,
+            paletteLabels=paletteLabels,
+            paletteEntryLabels=paletteEntryLabels,
+            nameTable=self.font.get("name"),
+        )
+
+    def setupStat(self, axes, locations=None, elidedFallbackName=2):
+        """Build a new 'STAT' table.
+
+        See `fontTools.otlLib.builder.buildStatTable` for details about
+        the arguments.
+        """
+        from .otlLib.builder import buildStatTable
+
+        buildStatTable(self.font, axes, locations, elidedFallbackName)
+
+
+def buildCmapSubTable(cmapping, format, platformID, platEncID):
+    subTable = cmap_classes[format](format)
+    subTable.cmap = cmapping
+    subTable.platformID = platformID
+    subTable.platEncID = platEncID
+    subTable.language = 0
+    return subTable
+
+
+def addFvar(font, axes, instances):
+    from .ttLib.tables._f_v_a_r import Axis, NamedInstance
+
+    assert axes
+
+    fvar = newTable("fvar")
+    nameTable = font["name"]
+
+    for axis_def in axes:
+        axis = Axis()
+
+        if isinstance(axis_def, tuple):
+            (
+                axis.axisTag,
+                axis.minValue,
+                axis.defaultValue,
+                axis.maxValue,
+                name,
+            ) = axis_def
+        else:
+            (axis.axisTag, axis.minValue, axis.defaultValue, axis.maxValue, name) = (
+                axis_def.tag,
+                axis_def.minimum,
+                axis_def.default,
+                axis_def.maximum,
+                axis_def.name,
+            )
+
+        if isinstance(name, str):
+            name = dict(en=name)
+
+        axis.axisNameID = nameTable.addMultilingualName(name, ttFont=font)
+        fvar.axes.append(axis)
+
+    for instance in instances:
+        if isinstance(instance, dict):
+            coordinates = instance["location"]
+            name = instance["stylename"]
+            psname = instance.get("postscriptfontname")
+        else:
+            coordinates = instance.location
+            name = instance.localisedStyleName or instance.styleName
+            psname = instance.postScriptFontName
+
+        if isinstance(name, str):
+            name = dict(en=name)
+
+        inst = NamedInstance()
+        inst.subfamilyNameID = nameTable.addMultilingualName(name, ttFont=font)
+        if psname is not None:
+            inst.postscriptNameID = nameTable.addName(psname)
+        inst.coordinates = coordinates
+        fvar.instances.append(inst)
+
+    font["fvar"] = fvar
diff --git a/Lib/fontTools/help.py b/Lib/fontTools/help.py
new file mode 100644
index 0000000..ff8048d
--- /dev/null
+++ b/Lib/fontTools/help.py
@@ -0,0 +1,34 @@
+import pkgutil
+import sys
+import fontTools
+import importlib
+import os
+from pathlib import Path
+
+
+def main():
+    """Show this help"""
+    path = fontTools.__path__
+    descriptions = {}
+    for pkg in sorted(
+        mod.name
+        for mod in pkgutil.walk_packages([fontTools.__path__[0]], prefix="fontTools.")
+    ):
+        try:
+            imports = __import__(pkg, globals(), locals(), ["main"])
+        except ImportError as e:
+            continue
+        try:
+            description = imports.main.__doc__
+            if description:
+                pkg = pkg.replace("fontTools.", "").replace(".__main__", "")
+                descriptions[pkg] = description
+        except AttributeError as e:
+            pass
+    for pkg, description in descriptions.items():
+        print("fonttools %-12s %s" % (pkg, description), file=sys.stderr)
+
+
+if __name__ == "__main__":
+    print("fonttools v%s\n" % fontTools.__version__, file=sys.stderr)
+    main()
diff --git a/Lib/fontTools/inspect.py b/Lib/fontTools/inspect.py
deleted file mode 100644
index bdacadf..0000000
--- a/Lib/fontTools/inspect.py
+++ /dev/null
@@ -1,271 +0,0 @@
-# Copyright 2013 Google, Inc. All Rights Reserved.
-#
-# Google Author(s): Behdad Esfahbod
-
-"""GUI font inspector.
-"""
-
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools import misc, ttLib, cffLib
-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
-		self._index = index
-		self._key = key
-		self._value = value
-		self._font = font
-
-		if isinstance(value, ttLib.TTFont):
-			self._add_font(value)
-			return
-
-		if not isinstance(value, basestring):
-			# Try sequences
-			is_sequence = True
-			try:
-				len(value)
-				iter(value)
-				# It's hard to differentiate list-type sequences
-				# from dict-type ones.  Try fetching item 0.
-				value[0]
-			except (TypeError, AttributeError, KeyError, IndexError):
-				is_sequence = False
-			if is_sequence:
-				self._add_list(key, value)
-				return
-		if hasattr(value, '__dict__'):
-			self._add_object(key, value)
-			return
-		if hasattr(value, 'items'):
-			self._add_dict(key, value)
-			return
-
-		if isinstance(value, basestring):
-			self._value_str = '"'+value+'"'
-			self._children = []
-			return
-
-		# Everything else
-		self._children = []
-
-	def _filter_items(self):
-		items = []
-		for k,v in self._items:
-			if isinstance(v, ttLib.TTFont):
-				continue
-			if k in ['reader', 'file', 'tableTag', 'compileStatus', 'recurse']:
-				continue
-			if isinstance(k, basestring) and k[0] == '_':
-				continue
-			items.append((k,v))
-		self._items = items
-
-	def _add_font(self, font):
-		self._items = [(tag,font[tag]) for tag in font.keys()]
-
-	def _add_object(self, key, value):
-		# Make sure item is decompiled
-		try:
-			value.asdf # Any better way?!
-		except (AttributeError, KeyError, TypeError, ttLib.TTLibError):
-			pass
-		if isinstance(value, ttLib.getTableModule('glyf').Glyph):
-			# Glyph type needs explicit expanding to be useful
-			value.expand(self._font['glyf'])
-		if isinstance(value, misc.psCharStrings.T2CharString):
-			try:
-				value.decompile()
-			except TypeError:  # Subroutines can't be decompiled
-				pass
-		if isinstance(value, cffLib.BaseDict):
-			for k in value.rawDict.keys():
-				getattr(value, k)
-		if isinstance(value, cffLib.Index):
-			# Load all items
-			for i in range(len(value)):
-				value[i]
-			# Discard offsets as should not be needed anymore
-			if hasattr(value, 'offsets'):
-				del value.offsets
-
-		self._value_str = value.__class__.__name__
-		if isinstance(value, ttLib.tables.DefaultTable.DefaultTable):
-			self._value_str += ' (%d Bytes)' % self._font.reader.tables[key].length
-		self._items = sorted(value.__dict__.items())
-		self._filter_items()
-
-	def _add_dict(self, key, value):
-		self._value_str = '%s of %d items' % (value.__class__.__name__, len(value))
-		self._items = sorted(value.items())
-
-	def _add_list(self, key, value):
-		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._items = list(enumerate(value))
-
-	def __len__(self):
-		if hasattr(self, '_children'):
-			return len(self._children)
-		if hasattr(self, '_items'):
-			return len(self._items)
-		assert False
-
-	def _ensure_children(self):
-		if hasattr(self, '_children'):
-			return
-		children = []
-		for i,(k,v) in enumerate(self._items):
-			children.append(Row(self, i, k, v, self._font))
-		self._children = children
-		del self._items
-
-	def __getitem__(self, n):
-		if n >= len(self):
-			return None
-		if not hasattr(self, '_children'):
-			self._children = [None] * len(self)
-		c = self._children[n]
-		if c is None:
-			k,v = self._items[n]
-			c = self._children[n] = Row(self, n, k, v, self._font)
-			self._items[n] = None
-		return c
-
-	def get_parent(self):
-		return self._parent
-
-	def get_index(self):
-		return self._index
-
-	def get_key(self):
-		return self._key
-
-	def get_value(self):
-		return self._value
-
-	def get_value_str(self):
-		if hasattr(self,'_value_str'):
-			return self._value_str
-		return str(self._value)
-
-class FontTreeModel(gtk.GenericTreeModel):
-
-	__gtype_name__ = 'FontTreeModel'
-
-	def __init__(self, font):
-		super(FontTreeModel, self).__init__()
-		self._columns = (str, str)
-		self.font = font
-		self._root = Row(None, 0, "font", font, font)
-
-	def on_get_flags(self):
-		return 0
-
-	def on_get_n_columns(self):
-		return len(self._columns)
-
-	def on_get_column_type(self, index):
-		return self._columns[index]
-
-	def on_get_iter(self, path):
-		rowref = self._root
-		while path:
-			rowref = rowref[path[0]]
-			path = path[1:]
-		return rowref
-
-	def on_get_path(self, rowref):
-		path = []
-		while rowref != self._root:
-			path.append(rowref.get_index())
-			rowref = rowref.get_parent()
-		path.reverse()
-		return tuple(path)
-
-	def on_get_value(self, rowref, column):
-		if column == 0:
-			return rowref.get_key()
-		else:
-			return rowref.get_value_str()
-
-	def on_iter_next(self, rowref):
-		return rowref.get_parent()[rowref.get_index() + 1]
-
-	def on_iter_children(self, rowref):
-		return rowref[0]
-
-	def on_iter_has_child(self, rowref):
-		return bool(len(rowref))
-
-	def on_iter_n_children(self, rowref):
-		return len(rowref)
-
-	def on_iter_nth_child(self, rowref, n):
-		if not rowref: rowref = self._root
-		return rowref[n]
-
-	def on_iter_parent(self, rowref):
-		return rowref.get_parent()
-
-class Inspect(object):
-
-	def _delete_event(self, widget, event, data=None):
-		gtk.main_quit()
-		return False
-
-	def __init__(self, fontfile):
-
-		self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
-		self.window.set_title("%s - pyftinspect" % fontfile)
-		self.window.connect("delete_event", self._delete_event)
-		self.window.set_size_request(400, 600)
-
-		self.scrolled_window = gtk.ScrolledWindow()
-		self.window.add(self.scrolled_window)
-
-		self.font = ttLib.TTFont(fontfile, lazy=True)
-		self.treemodel = FontTreeModel(self.font)
-		self.treeview = gtk.TreeView(self.treemodel)
-		#self.treeview.set_reorderable(True)
-
-		for i in range(2):
-			col_name = ('Key', 'Value')[i]
-			col = gtk.TreeViewColumn(col_name)
-			col.set_sort_column_id(-1)
-			self.treeview.append_column(col)
-
-			cell = gtk.CellRendererText()
-			col.pack_start(cell, True)
-			col.add_attribute(cell, 'text', i)
-
-		self.treeview.set_search_column(1)
-		self.scrolled_window.add(self.treeview)
-		self.window.show_all()
-
-def main(args=None):
-	if args is None:
-		args = sys.argv[1:]
-	if len(args) < 1:
-		print("usage: pyftinspect font...", file=sys.stderr)
-		return 1
-	for arg in args:
-		Inspect(arg)
-	gtk.main()
-
-if __name__ == "__main__":
-	sys.exit(main())
diff --git a/Lib/fontTools/merge.py b/Lib/fontTools/merge.py
index f3173eb..2df22a8 100644
--- a/Lib/fontTools/merge.py
+++ b/Lib/fontTools/merge.py
@@ -2,16 +2,12 @@
 #
 # Google Author(s): Behdad Esfahbod, Roozbeh Pournader
 
-"""Font merger.
-"""
-
-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
@@ -225,6 +221,23 @@
 	'numberOfHMetrics': recalculate,
 }
 
+ttLib.getTableClass('vhea').mergeMap = {
+	'*': equal,
+	'tableTag': equal,
+	'tableVersion': max,
+	'ascent': max,
+	'descent': min,
+	'lineGap': max,
+	'advanceHeightMax': max,
+	'minTopSideBearing': min,
+	'minBottomSideBearing': min,
+	'yMaxExtent': max,
+	'caretSlopeRise': first,
+	'caretSlopeRun': first,
+	'caretOffset': first,
+	'numberOfVMetrics': recalculate,
+}
+
 os2FsTypeMergeBitMap = {
 	'size': 16,
 	'*': lambda bit: 0,
@@ -277,11 +290,18 @@
 	'sTypoLineGap': max,
 	'usWinAscent': max,
 	'usWinDescent': max,
-	# Version 2,3,4
+	# Version 1
 	'ulCodePageRange1': onlyExisting(bitwise_or),
 	'ulCodePageRange2': onlyExisting(bitwise_or),
-	'usMaxContex': onlyExisting(max),
-	# TODO version 5
+	# Version 2, 3, 4
+	'sxHeight': onlyExisting(max),
+	'sCapHeight': onlyExisting(max),
+	'usDefaultChar': onlyExisting(first),
+	'usBreakChar': onlyExisting(first),
+	'usMaxContext': onlyExisting(max),
+	# version 5
+	'usLowerOpticalPointSize': onlyExisting(min),
+	'usUpperOpticalPointSize': onlyExisting(max),
 }
 
 @_add_method(ttLib.getTableClass('OS/2'))
@@ -351,10 +371,29 @@
 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))
+
+# Valid (format, platformID, platEncID) triplets for cmap subtables containing
+# Unicode BMP-only and Unicode Full Repertoire semantics.
+# Cf. OpenType spec for "Platform specific encodings":
+# https://docs.microsoft.com/en-us/typography/opentype/spec/name
+class CmapUnicodePlatEncodings:
+	BMP = {(4, 3, 1), (4, 0, 3), (4, 0, 4), (4, 0, 6)}
+	FullRepertoire = {(12, 3, 10), (12, 0, 4), (12, 0, 6)}
+
 @_add_method(ttLib.getTableClass('cmap'))
 def merge(self, m, tables):
 	# TODO Handle format=14.
-	# Only merges 4/3/1 and 12/3/10 subtables, ignores all other subtables
+	# Only merge format 4 and 12 Unicode 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):
@@ -362,10 +401,16 @@
 		format12 = None
 		for subtable in table.tables:
 			properties = (subtable.format, subtable.platformID, subtable.platEncID)
-			if properties == (4,3,1):
+			if properties in CmapUnicodePlatEncodings.BMP:
 				format4 = subtable
-			elif properties == (12,3,10):
+			elif properties in CmapUnicodePlatEncodings.FullRepertoire:
 				format12 = subtable
+			else:
+				log.warning(
+					"Dropped cmap subtable from font [%s]:\t"
+					"format %2s, platformID %2s, platEncID %2s",
+					fontIdx, subtable.format, subtable.platformID, subtable.platEncID
+				)
 		if format12 is not None:
 			cmapTables.append((format12, fontIdx))
 		elif format4 is not None:
@@ -373,19 +418,30 @@
 
 	# 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:
 		# 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.
-				if m.duplicateGlyphsPerFont[fontIdx].get(oldgid, gid) == 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
-				else:
-					# Char previously mapped to oldgid but already remapped to a different 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)
 
@@ -459,13 +515,27 @@
 
 	if len(lst) == 1:
 		return lst[0]
-	# TODO Support merging LangSysRecords
-	assert all(not s.LangSysRecord for s in lst)
+	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 = []
-	self.LangSysCount = 0
-	self.DefaultLangSys = mergeLangSyses([s.DefaultLangSys for s in lst if s.DefaultLangSys])
+	self.LangSysRecord = lsrecords
+	self.LangSysCount = len(lsrecords)
+	dfltLangSyses = [s.DefaultLangSys for s in lst if s.DefaultLangSys]
+	if dfltLangSyses:
+		self.DefaultLangSys = mergeLangSyses(dfltLangSyses)
+	else:
+		self.DefaultLangSys = None
 	return self
 
 def mergeScriptRecords(lst):
@@ -604,6 +674,13 @@
 					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
 
@@ -664,12 +741,12 @@
 
 	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:
+	if not hasattr(self.__class__, "_merge__ContextHelpers"):
+		self.__class__._merge__ContextHelpers = {}
+	if self.Format not in self.__class__._merge__ContextHelpers:
 		helper = ContextHelper(self.__class__, self.Format)
-		self.__class__.__ContextHelpers[self.Format] = helper
-	return self.__class__.__ContextHelpers[self.Format]
+		self.__class__._merge__ContextHelpers[self.Format] = helper
+	return self.__class__._merge__ContextHelpers[self.Format]
 
 
 @_add_method(otTables.ContextSubst,
@@ -824,9 +901,9 @@
 
 		return ret
 
-class _AttendanceRecordingIdentityDict(dict):
+class _AttendanceRecordingIdentityDict(object):
 	"""A dictionary-like object that records indices of items actually accessed
-		from a list."""
+	from a list."""
 
 	def __init__(self, lst):
 		self.l = lst
@@ -837,7 +914,7 @@
 		self.s.add(self.d[id(v)])
 		return v
 
-class _GregariousDict(dict):
+class _GregariousIdentityDict(object):
 	"""A dictionary-like object that welcomes guests without reservations and
 	adds them to the end of the guest list."""
 
@@ -851,16 +928,53 @@
 			self.l.append(v)
 		return v
 
-class _NonhashableDict(dict):
-	"""A dictionary-like object mapping objects to their index within a list."""
+class _NonhashableDict(object):
+	"""A dictionary-like object mapping objects to values."""
 
-	def __init__(self, lst):
-		self.d = {id(v):i for i,v in enumerate(lst)}
+	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, v):
-		return self.d[id(v)]
+	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):
+	"""Font merger.
+
+	This class merges multiple files into a single OpenType font, taking into
+	account complexities such as OpenType layout (``GSUB``/``GPOS``) tables and
+	cross-font metrics (e.g. ``hhea.ascent`` is set to the maximum value across
+	all the fonts).
+
+	If multiple glyphs map to the same Unicode value, and the glyphs are considered
+	sufficiently different (that is, they differ in any of paths, widths, or
+	height), then subsequent glyphs are renamed and a lookup in the ``locl``
+	feature will be created to disambiguate them. For example, if the arguments
+	are an Arabic font and a Latin font and both contain a set of parentheses,
+	the Latin glyphs will be renamed to ``parenleft#1`` and ``parenright#1``,
+	and a lookup will be inserted into the to ``locl`` feature (creating it if
+	necessary) under the ``latn`` script to substitute ``parenleft`` with
+	``parenleft#1`` etc.
+
+	Restrictions:
+
+	- All fonts must currently have TrueType outlines (``glyf`` table).
+		Merging fonts with CFF outlines is not supported.
+	- All fonts must have the same units per em.
+	- If duplicate glyph disambiguation takes place as described above then the
+		fonts must have a ``GSUB`` table.
+
+	Attributes:
+		options: Currently unused.
+	"""
 
 	def __init__(self, options=None):
 
@@ -870,7 +984,15 @@
 		self.options = options
 
 	def merge(self, fontfiles):
+		"""Merges fonts together.
 
+		Args:
+			fontfiles: A list of file names to be merged
+
+		Returns:
+			A :class:`fontTools.ttLib.TTFont` object. Call the ``save`` method on
+			this to write it out to an OTF file.
+		"""
 		mega = ttLib.TTFont()
 
 		#
@@ -890,7 +1012,8 @@
 		for font in fonts:
 			self._preMerge(font)
 
-		self.duplicateGlyphsPerFont = [{} for f in fonts]
+		self.fonts = fonts
+		self.duplicateGlyphsPerFont = [{} for _ in fonts]
 
 		allTags = reduce(set.union, (list(font.keys()) for font in fonts), set())
 		allTags.remove('GlyphOrder')
@@ -919,6 +1042,7 @@
 					log.info("Dropped '%s'.", tag)
 
 		del self.duplicateGlyphsPerFont
+		del self.fonts
 
 		self._postMerge(mega)
 
@@ -927,16 +1051,18 @@
 	def _mergeGlyphOrders(self, glyphOrders):
 		"""Modifies passed-in glyphOrders to reflect new glyph names.
 		Returns glyphOrder for the merged font."""
-		# Simply append font index to the glyph name for now.
-		# TODO Even this simplistic numbering can result in conflicts.
-		# But then again, we have to improve this soon anyway.
-		mega = []
-		for n,glyphOrder in enumerate(glyphOrders):
+		mega = {}
+		for glyphOrder in glyphOrders:
 			for i,glyphName in enumerate(glyphOrder):
-				glyphName += "#" + repr(n)
-				glyphOrder[i] = glyphName
-				mega.append(glyphName)
-		return mega
+				if glyphName in mega:
+					n = mega[glyphName]
+					while (glyphName + "#" + repr(n)) in mega:
+						n += 1
+					mega[glyphName] = n
+					glyphName += "#" + repr(n)
+					glyphOrder[i] = glyphName
+				mega[glyphName] = 1
+		return list(mega.keys())
 
 	def mergeObjects(self, returnTable, logic, tables):
 		# Right now we don't use self at all.  Will use in the future
@@ -997,7 +1123,7 @@
 			if t.table.FeatureList and t.table.ScriptList:
 
 				# Collect unregistered (new) features.
-				featureMap = _GregariousDict(t.table.FeatureList.FeatureRecord)
+				featureMap = _GregariousIdentityDict(t.table.FeatureList.FeatureRecord)
 				t.table.ScriptList.mapFeatures(featureMap)
 
 				# Record used features.
@@ -1017,7 +1143,7 @@
 			if t.table.LookupList:
 
 				# Collect unregistered (new) lookups.
-				lookupMap = _GregariousDict(t.table.LookupList.Lookup)
+				lookupMap = _GregariousIdentityDict(t.table.LookupList.Lookup)
 				t.table.FeatureList.mapLookups(lookupMap)
 				t.table.LookupList.mapLookups(lookupMap)
 
@@ -1049,6 +1175,7 @@
 
 @timer("make one with everything (TOTAL TIME)")
 def main(args=None):
+	"""Merge multiple fonts into one"""
 	from fontTools import configLogger
 
 	if args is None:
diff --git a/Lib/fontTools/misc/__init__.py b/Lib/fontTools/misc/__init__.py
index 3f9abc9..156cb23 100644
--- a/Lib/fontTools/misc/__init__.py
+++ b/Lib/fontTools/misc/__init__.py
@@ -1,4 +1 @@
 """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 f2cfac8..01ccbe8 100644
--- a/Lib/fontTools/misc/arrayTools.py
+++ b/Lib/fontTools/misc/arrayTools.py
@@ -1,48 +1,87 @@
-#
-# Various array and rectangle tools, but mostly rectangles, hence the
-# name of this module (not).
-#
+"""Routines for calculating bounding boxes, point in rectangle calculations and
+so on.
+"""
 
-
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from numbers import Number
+from fontTools.misc.roundTools import otRound
+from fontTools.misc.vector import Vector as _Vector
 import math
-import operator
+import warnings
+
 
 def calcBounds(array):
-    """Return the bounding rectangle of a 2D points array as a tuple:
-    (xMin, yMin, xMax, yMax)
+    """Calculate the bounding rectangle of a 2D points array.
+
+    Args:
+        array: A sequence of 2D tuples.
+
+    Returns:
+        A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.
     """
-    if len(array) == 0:
+    if not array:
         return 0, 0, 0, 0
     xs = [x for x, y in array]
     ys = [y for x, y in array]
     return min(xs), min(ys), max(xs), max(ys)
 
-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.
+def calcIntBounds(array, round=otRound):
+    """Calculate the integer bounding rectangle of a 2D points array.
+
+    Values are rounded to closest integer towards ``+Infinity`` using the
+    :func:`fontTools.misc.fixedTools.otRound` function by default, unless
+    an optional ``round`` function is passed.
+
+    Args:
+        array: A sequence of 2D tuples.
+        round: A rounding function of type ``f(x: float) -> int``.
+
+    Returns:
+        A four-item tuple of integers representing the bounding rectangle:
+        ``(xMin, yMin, xMax, yMax)``.
     """
     return tuple(round(v) for v in calcBounds(array))
 
 
 def updateBounds(bounds, p, min=min, max=max):
-    """Return the bounding recangle of rectangle bounds and point (x, y)."""
+    """Add a point to a bounding rectangle.
+
+    Args:
+        bounds: A bounding rectangle expressed as a tuple
+            ``(xMin, yMin, xMax, yMax)``.
+        p: A 2D tuple representing a point.
+        min,max: functions to compute the minimum and maximum.
+
+    Returns:
+        The updated bounding rectangle ``(xMin, yMin, xMax, yMax)``.
+    """
     (x, y) = p
     xMin, yMin, xMax, yMax = bounds
     return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
 
 def pointInRect(p, rect):
-    """Return True when point (x, y) is inside rect."""
+    """Test if a point is inside a bounding rectangle.
+
+    Args:
+        p: A 2D tuple representing a point.
+        rect: A bounding rectangle expressed as a tuple
+            ``(xMin, yMin, xMax, yMax)``.
+
+    Returns:
+        ``True`` if the point is inside the rectangle, ``False`` otherwise.
+    """
     (x, y) = p
     xMin, yMin, xMax, yMax = rect
     return (xMin <= x <= xMax) and (yMin <= y <= yMax)
 
 def pointsInRect(array, rect):
-    """Find out which points or array are inside rect.
-    Returns an array with a boolean for each point.
+    """Determine which points are inside a bounding rectangle.
+
+    Args:
+        array: A sequence of 2D tuples.
+        rect: A bounding rectangle expressed as a tuple
+            ``(xMin, yMin, xMax, yMax)``.
+
+    Returns:
+        A list containing the points inside the rectangle.
     """
     if len(array) < 1:
         return []
@@ -50,41 +89,105 @@
     return [(xMin <= x <= xMax) and (yMin <= y <= yMax) for x, y in array]
 
 def vectorLength(vector):
-    """Return the length of the given vector."""
+    """Calculate the length of the given vector.
+
+    Args:
+        vector: A 2D tuple.
+
+    Returns:
+        The Euclidean length of the vector.
+    """
     x, y = vector
     return math.sqrt(x**2 + y**2)
 
 def asInt16(array):
-    """Round and cast to 16 bit integer."""
+    """Round a list of floats to 16-bit signed integers.
+
+    Args:
+        array: List of float values.
+
+    Returns:
+        A list of rounded integers.
+    """
     return [int(math.floor(i+0.5)) for i in array]
 
 
 def normRect(rect):
-    """Normalize the rectangle so that the following holds:
+    """Normalize a bounding box rectangle.
+
+    This function "turns the rectangle the right way up", so that the following
+    holds::
+
         xMin <= xMax and yMin <= yMax
+
+    Args:
+        rect: A bounding rectangle expressed as a tuple
+            ``(xMin, yMin, xMax, yMax)``.
+
+    Returns:
+        A normalized bounding rectangle.
     """
     (xMin, yMin, xMax, yMax) = rect
     return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
 
 def scaleRect(rect, x, y):
-    """Scale the rectangle by x, y."""
+    """Scale a bounding box rectangle.
+
+    Args:
+        rect: A bounding rectangle expressed as a tuple
+            ``(xMin, yMin, xMax, yMax)``.
+        x: Factor to scale the rectangle along the X axis.
+        Y: Factor to scale the rectangle along the Y axis.
+
+    Returns:
+        A scaled bounding rectangle.
+    """
     (xMin, yMin, xMax, yMax) = rect
     return xMin * x, yMin * y, xMax * x, yMax * y
 
 def offsetRect(rect, dx, dy):
-    """Offset the rectangle by dx, dy."""
+    """Offset a bounding box rectangle.
+
+    Args:
+        rect: A bounding rectangle expressed as a tuple
+            ``(xMin, yMin, xMax, yMax)``.
+        dx: Amount to offset the rectangle along the X axis.
+        dY: Amount to offset the rectangle along the Y axis.
+
+    Returns:
+        An offset bounding rectangle.
+    """
     (xMin, yMin, xMax, yMax) = rect
     return xMin+dx, yMin+dy, xMax+dx, yMax+dy
 
 def insetRect(rect, dx, dy):
-    """Inset the rectangle by dx, dy on all sides."""
+    """Inset a bounding box rectangle on all sides.
+
+    Args:
+        rect: A bounding rectangle expressed as a tuple
+            ``(xMin, yMin, xMax, yMax)``.
+        dx: Amount to inset the rectangle along the X axis.
+        dY: Amount to inset the rectangle along the Y axis.
+
+    Returns:
+        An inset bounding rectangle.
+    """
     (xMin, yMin, xMax, yMax) = rect
     return xMin+dx, yMin+dy, xMax-dx, yMax-dy
 
 def sectRect(rect1, rect2):
-    """Return a boolean and a rectangle. If the input rectangles intersect, return
-    True and the intersecting rectangle. Return False and (0, 0, 0, 0) if the input
-    rectangles don't intersect.
+    """Test for rectangle-rectangle intersection.
+
+    Args:
+        rect1: First bounding rectangle, expressed as tuples
+            ``(xMin, yMin, xMax, yMax)``.
+        rect2: Second bounding rectangle.
+
+    Returns:
+        A boolean and a rectangle.
+        If the input rectangles intersect, returns ``True`` and the intersecting
+        rectangle. Returns ``False`` and ``(0, 0, 0, 0)`` if the input
+        rectangles don't intersect.
     """
     (xMin1, yMin1, xMax1, yMax1) = rect1
     (xMin2, yMin2, xMax2, yMax2) = rect2
@@ -95,9 +198,16 @@
     return True, (xMin, yMin, xMax, yMax)
 
 def unionRect(rect1, rect2):
-    """Return the smallest rectangle in which both input rectangles are fully
-    enclosed. In other words, return the total bounding rectangle of both input
-    rectangles.
+    """Determine union of bounding rectangles.
+
+    Args:
+        rect1: First bounding rectangle, expressed as tuples
+            ``(xMin, yMin, xMax, yMax)``.
+        rect2: Second bounding rectangle.
+
+    Returns:
+        The smallest rectangle in which both input rectangles are fully
+        enclosed.
     """
     (xMin1, yMin1, xMax1, yMax1) = rect1
     (xMin2, yMin2, xMax2, yMax2) = rect2
@@ -105,16 +215,45 @@
                               max(xMax1, xMax2), max(yMax1, yMax2))
     return (xMin, yMin, xMax, yMax)
 
-def rectCenter(rect0):
-    """Return the center of the rectangle as an (x, y) coordinate."""
-    (xMin, yMin, xMax, yMax) = rect0
+def rectCenter(rect):
+    """Determine rectangle center.
+
+    Args:
+        rect: Bounding rectangle, expressed as tuples
+            ``(xMin, yMin, xMax, yMax)``.
+
+    Returns:
+        A 2D tuple representing the point at the center of the rectangle.
+    """
+    (xMin, yMin, xMax, yMax) = rect
     return (xMin+xMax)/2, (yMin+yMax)/2
 
-def intRect(rect1):
-    """Return the rectangle, rounded off to integer values, but guaranteeing that
-    the resulting rectangle is NOT smaller than the original.
+def rectArea(rect):
+    """Determine rectangle area.
+
+    Args:
+        rect: Bounding rectangle, expressed as tuples
+            ``(xMin, yMin, xMax, yMax)``.
+
+    Returns:
+        The area of the rectangle.
     """
-    (xMin, yMin, xMax, yMax) = rect1
+    (xMin, yMin, xMax, yMax) = rect
+    return (yMax - yMin) * (xMax - xMin)
+
+def intRect(rect):
+    """Round a rectangle to integer values.
+
+    Guarantees that the resulting rectangle is NOT smaller than the original.
+
+    Args:
+        rect: Bounding rectangle, expressed as tuples
+            ``(xMin, yMin, xMax, yMax)``.
+
+    Returns:
+        A rounded bounding rectangle.
+    """
+    (xMin, yMin, xMax, yMax) = rect
     xMin = int(math.floor(xMin))
     yMin = int(math.floor(yMin))
     xMax = int(math.ceil(xMax))
@@ -122,121 +261,48 @@
     return (xMin, yMin, xMax, yMax)
 
 
-class Vector(object):
-    """A math-like vector."""
+class Vector(_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 __init__(self, *args, **kwargs):
+        warnings.warn(
+            "fontTools.misc.arrayTools.Vector has been deprecated, please use "
+            "fontTools.misc.vector.Vector instead.",
+            DeprecationWarning,
+        )
 
 
 def pairwise(iterable, reverse=False):
-    """Iterate over current and next items in iterable, optionally in
-    reverse order.
+    """Iterate over current and next items in iterable.
 
-    >>> 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'))
+    Args:
+        iterable: An iterable
+        reverse: If true, iterate in reverse order.
+
+    Returns:
+        A iterable yielding two elements per iteration.
+
+    Example:
+
+        >>> 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
diff --git a/Lib/fontTools/misc/bezierTools.py b/Lib/fontTools/misc/bezierTools.py
index ef9e6cd..2cf2640 100644
--- a/Lib/fontTools/misc/bezierTools.py
+++ b/Lib/fontTools/misc/bezierTools.py
@@ -1,11 +1,13 @@
 # -*- coding: utf-8 -*-
-"""fontTools.misc.bezierTools.py -- tools for working with bezier path segments.
+"""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 *
+from fontTools.misc.arrayTools import calcBounds, sectRect, rectArea
+from fontTools.misc.transform import Identity
 import math
+from collections import namedtuple
+
+Intersection = namedtuple("Intersection", ["pt", "t1", "t2"])
 
 
 __all__ = [
@@ -13,10 +15,12 @@
     "approximateCubicArcLengthC",
     "approximateQuadraticArcLength",
     "approximateQuadraticArcLengthC",
+    "calcCubicArcLength",
+    "calcCubicArcLengthC",
     "calcQuadraticArcLength",
     "calcQuadraticArcLengthC",
-    "calcQuadraticBounds",
     "calcCubicBounds",
+    "calcQuadraticBounds",
     "splitLine",
     "splitQuadratic",
     "splitCubic",
@@ -24,9 +28,71 @@
     "splitCubicAtT",
     "solveQuadratic",
     "solveCubic",
+    "quadraticPointAtT",
+    "cubicPointAtT",
+    "linePointAtT",
+    "segmentPointAtT",
+    "lineLineIntersections",
+    "curveLineIntersections",
+    "curveCurveIntersections",
+    "segmentSegmentIntersections",
 ]
 
 
+def calcCubicArcLength(pt1, pt2, pt3, pt4, tolerance=0.005):
+    """Calculates the arc length for a cubic Bezier segment.
+
+    Whereas :func:`approximateCubicArcLength` approximates the length, this
+    function calculates it by "measuring", recursively dividing the curve
+    until the divided segments are shorter than ``tolerance``.
+
+    Args:
+        pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
+        tolerance: Controls the precision of the calcuation.
+
+    Returns:
+        Arc length value.
+    """
+    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) * 0.125
+    deriv3 = (p3 + p2 - p1 - p0) * 0.125
+    return (
+        (p0, (p0 + p1) * 0.5, mid - deriv3, mid),
+        (mid, mid + deriv3, (p2 + p3) * 0.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) * 0.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):
+    """Calculates the arc length for a cubic Bezier segment.
+
+    Args:
+        pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.
+        tolerance: Controls the precision of the calcuation.
+
+    Returns:
+        Arc length value.
+    """
+    mult = 1.0 + 1.5 * tolerance  # The 1.5 is a empirical hack; no math
+    return _calcCubicArcLengthCRecurse(mult, pt1, pt2, pt3, pt4)
+
+
 epsilonDigits = 6
 epsilon = 1e-10
 
@@ -38,12 +104,21 @@
 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
+    return x * math.sqrt(x ** 2 + 1) / 2 + math.asinh(x) / 2
 
 
-def calcQuadraticArcLength(pt1, pt2, pt3, approximate_fallback=False):
-    """Return the arc length for a qudratic bezier segment.
-    pt1 and pt3 are the "anchor" points, pt2 is the "handle".
+def calcQuadraticArcLength(pt1, pt2, pt3):
+    """Calculates the arc length for a quadratic Bezier segment.
+
+    Args:
+        pt1: Start point of the Bezier as 2D tuple.
+        pt2: Handle point of the Bezier as 2D tuple.
+        pt3: End point of the Bezier as 2D tuple.
+
+    Returns:
+        Arc length value.
+
+    Example::
 
         >>> calcQuadraticArcLength((0, 0), (0, 0), (0, 0)) # empty segment
         0.0
@@ -59,18 +134,25 @@
         120.21581243984076
         >>> calcQuadraticArcLength((0, 0), (50, -10), (80, 50))
         102.53273816445825
-        >>> calcQuadraticArcLength((0, 0), (40, 0), (-40, 0), True) # collinear points, control point outside, exact result should be 66.6666666666667
-        69.41755572720999
-        >>> calcQuadraticArcLength((0, 0), (40, 0), (0, 0), True) # collinear points, looping back, exact result should be 40
-        34.4265186329548
+        >>> 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), approximate_fallback)
+    return calcQuadraticArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3))
 
 
-def calcQuadraticArcLengthC(pt1, pt2, pt3, approximate_fallback=False):
-    """Return the arc length for a qudratic bezier segment using complex points.
-    pt1 and pt3 are the "anchor" points, pt2 is the "handle"."""
-    
+def calcQuadraticArcLengthC(pt1, pt2, pt3):
+    """Calculates the arc length for a quadratic Bezier segment.
+
+    Args:
+        pt1: Start point of the Bezier as a complex number.
+        pt2: Handle point of the Bezier as a complex number.
+        pt3: End point of the Bezier as a complex number.
+
+    Returns:
+        Arc length value.
+    """
     # Analytical solution to the length of a quadratic bezier.
     # I'll explain how I arrived at this later.
     d0 = pt2 - pt1
@@ -78,49 +160,82 @@
     d = d1 - d0
     n = d * 1j
     scale = abs(n)
-    if scale == 0.:
-        return abs(pt3-pt1)
-    origDist = _dot(n,d0)
-    if origDist == 0.:
-        if _dot(d0,d1) >= 0:
-            return abs(pt3-pt1)
-        if approximate_fallback:
-            return approximateQuadraticArcLengthC(pt1, pt2, pt3)
-        assert 0 # TODO handle cusps
-    x0 = _dot(d,d0) / origDist
-    x1 = _dot(d,d1) / origDist
+    if scale == 0.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.
+    """Calculates the arc length for a quadratic Bezier segment.
+
+    Uses Gauss-Legendre quadrature for a branch-free approximation.
+    See :func:`calcQuadraticArcLength` for a slower but more accurate result.
+
+    Args:
+        pt1: Start point of the Bezier as 2D tuple.
+        pt2: Handle point of the Bezier as 2D tuple.
+        pt3: End point of the Bezier as 2D tuple.
+
+    Returns:
+        Approximate arc length value.
+    """
     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.
-    #
+    """Calculates the arc length for a quadratic Bezier segment.
+
+    Uses Gauss-Legendre quadrature for a branch-free approximation.
+    See :func:`calcQuadraticArcLength` for a slower but more accurate result.
+
+    Args:
+        pt1: Start point of the Bezier as a complex number.
+        pt2: Handle point of the Bezier as a complex number.
+        pt3: End point of the Bezier as a complex number.
+
+    Returns:
+        Approximate arc length value.
+    """
     # 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
+    # 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)
+    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):
-    """Return the bounding rectangle for a qudratic bezier segment.
-    pt1 and pt3 are the "anchor" points, pt2 is the "handle".
+    """Calculates the bounding rectangle for a quadratic Bezier segment.
+
+    Args:
+        pt1: Start point of the Bezier as a 2D tuple.
+        pt2: Handle point of the Bezier as a 2D tuple.
+        pt3: End point of the Bezier as a 2D tuple.
+
+    Returns:
+        A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.
+
+    Example::
 
         >>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))
         (0, 0, 100, 50.0)
@@ -128,20 +243,34 @@
         (0.0, 0.0, 100, 100)
     """
     (ax, ay), (bx, by), (cx, cy) = calcQuadraticParameters(pt1, pt2, pt3)
-    ax2 = ax*2.0
-    ay2 = ay*2.0
+    ax2 = ax * 2.0
+    ay2 = ay * 2.0
     roots = []
     if ax2 != 0:
-        roots.append(-bx/ax2)
+        roots.append(-bx / ax2)
     if ay2 != 0:
-        roots.append(-by/ay2)
-    points = [(ax*t*t + bx*t + cx, ay*t*t + by*t + cy) for t in roots if 0 <= t < 1] + [pt1, pt3]
+        roots.append(-by / ay2)
+    points = [
+        (ax * t * t + bx * t + cx, ay * t * t + by * t + cy)
+        for t in roots
+        if 0 <= t < 1
+    ] + [pt1, pt3]
     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".
+    """Approximates the arc length for a cubic Bezier segment.
+
+    Uses Gauss-Lobatto quadrature with n=5 points to approximate arc length.
+    See :func:`calcCubicArcLength` for a slower but more accurate result.
+
+    Args:
+        pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
+
+    Returns:
+        Arc length value.
+
+    Example::
 
         >>> approximateCubicArcLength((0, 0), (25, 100), (75, 100), (100, 0))
         190.04332968932817
@@ -154,18 +283,20 @@
         >>> 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))
+    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"."""
+    """Approximates the arc length for a cubic Bezier segment.
 
-    # Approximate length of cubic Bezier curve using Gauss-Lobatto quadrature
-    # with n=5 points for complex points.
-    #
+    Args:
+        pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.
+
+    Returns:
+        Arc length value.
+    """
     # This, essentially, approximates the length-of-derivative function
     # to be integrated with the best-matching seventh-degree polynomial
     # approximation of it.
@@ -174,18 +305,35 @@
 
     # 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
+    v0 = abs(pt2 - pt1) * 0.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) * 0.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".
+    """Calculates the bounding rectangle for a quadratic Bezier segment.
+
+    Args:
+        pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
+
+    Returns:
+        A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.
+
+    Example::
 
         >>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))
         (0, 0, 100, 75.0)
@@ -204,16 +352,33 @@
     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]
+    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)
 
 
 def splitLine(pt1, pt2, where, isHorizontal):
-    """Split the line between pt1 and pt2 at position 'where', which
-    is an x coordinate if isHorizontal is False, a y coordinate if
-    isHorizontal is True. Return a list of two line segments if the
-    line was successfully split, or a list containing the original
-    line.
+    """Split a line at a given coordinate.
+
+    Args:
+        pt1: Start point of line as 2D tuple.
+        pt2: End point of line as 2D tuple.
+        where: Position at which to split the line.
+        isHorizontal: Direction of the ray splitting the line. If true,
+            ``where`` is interpreted as a Y coordinate; if false, then
+            ``where`` is interpreted as an X coordinate.
+
+    Returns:
+        A list of two line segments (each line segment being two 2D tuples)
+        if the line was successfully split, or a list containing the original
+        line.
+
+    Example::
 
         >>> printSegments(splitLine((0, 0), (100, 100), 50, True))
         ((0, 0), (50, 50))
@@ -236,8 +401,8 @@
     pt1x, pt1y = pt1
     pt2x, pt2y = pt2
 
-    ax = (pt2x - pt1x)
-    ay = (pt2y - pt1y)
+    ax = pt2x - pt1x
+    ay = pt2y - pt1y
 
     bx = pt1x
     by = pt1y
@@ -255,9 +420,21 @@
 
 
 def splitQuadratic(pt1, pt2, pt3, where, isHorizontal):
-    """Split the quadratic curve between pt1, pt2 and pt3 at position 'where',
-    which is an x coordinate if isHorizontal is False, a y coordinate if
-    isHorizontal is True. Return a list of curve segments.
+    """Split a quadratic Bezier curve at a given coordinate.
+
+    Args:
+        pt1,pt2,pt3: Control points of the Bezier as 2D tuples.
+        where: Position at which to split the curve.
+        isHorizontal: Direction of the ray splitting the curve. If true,
+            ``where`` is interpreted as a Y coordinate; if false, then
+            ``where`` is interpreted as an X coordinate.
+
+    Returns:
+        A list of two curve segments (each curve segment being three 2D tuples)
+        if the curve was successfully split, or a list containing the original
+        curve.
+
+    Example::
 
         >>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 150, False))
         ((0, 0), (50, 100), (100, 0))
@@ -278,18 +455,31 @@
         ((50, 50), (75, 50), (100, 0))
     """
     a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
-    solutions = solveQuadratic(a[isHorizontal], b[isHorizontal],
-        c[isHorizontal] - where)
-    solutions = sorted([t for t in solutions if 0 <= t < 1])
+    solutions = solveQuadratic(
+        a[isHorizontal], b[isHorizontal], c[isHorizontal] - where
+    )
+    solutions = sorted(t for t in solutions if 0 <= t < 1)
     if not solutions:
         return [(pt1, pt2, pt3)]
     return _splitQuadraticAtT(a, b, c, *solutions)
 
 
 def splitCubic(pt1, pt2, pt3, pt4, where, isHorizontal):
-    """Split the cubic curve between pt1, pt2, pt3 and pt4 at position 'where',
-    which is an x coordinate if isHorizontal is False, a y coordinate if
-    isHorizontal is True. Return a list of curve segments.
+    """Split a cubic Bezier curve at a given coordinate.
+
+    Args:
+        pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
+        where: Position at which to split the curve.
+        isHorizontal: Direction of the ray splitting the curve. If true,
+            ``where`` is interpreted as a Y coordinate; if false, then
+            ``where`` is interpreted as an X coordinate.
+
+    Returns:
+        A list of two curve segments (each curve segment being four 2D tuples)
+        if the curve was successfully split, or a list containing the original
+        curve.
+
+    Example::
 
         >>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 150, False))
         ((0, 0), (25, 100), (75, 100), (100, 0))
@@ -302,17 +492,26 @@
         ((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],
-        d[isHorizontal] - where)
-    solutions = sorted([t for t in solutions if 0 <= t < 1])
+    solutions = solveCubic(
+        a[isHorizontal], b[isHorizontal], c[isHorizontal], d[isHorizontal] - where
+    )
+    solutions = sorted(t for t in solutions if 0 <= t < 1)
     if not solutions:
         return [(pt1, pt2, pt3, pt4)]
     return _splitCubicAtT(a, b, c, d, *solutions)
 
 
 def splitQuadraticAtT(pt1, pt2, pt3, *ts):
-    """Split the quadratic curve between pt1, pt2 and pt3 at one or more
-    values of t. Return a list of curve segments.
+    """Split a quadratic Bezier curve at one or more values of t.
+
+    Args:
+        pt1,pt2,pt3: Control points of the Bezier as 2D tuples.
+        *ts: Positions at which to split the curve.
+
+    Returns:
+        A list of curve segments (each curve segment being three 2D tuples).
+
+    Examples::
 
         >>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5))
         ((0, 0), (25, 50), (50, 50))
@@ -327,8 +526,16 @@
 
 
 def splitCubicAtT(pt1, pt2, pt3, pt4, *ts):
-    """Split the cubic curve between pt1, pt2, pt3 and pt4 at one or more
-    values of t. Return a list of curve segments.
+    """Split a cubic Bezier curve at one or more values of t.
+
+    Args:
+        pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
+        *ts: Positions at which to split the curve.
+
+    Returns:
+        A list of curve segments (each curve segment being four 2D tuples).
+
+    Examples::
 
         >>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5))
         ((0, 0), (12.5, 50), (31.25, 75), (50, 75))
@@ -352,17 +559,17 @@
     cx, cy = c
     for i in range(len(ts) - 1):
         t1 = ts[i]
-        t2 = ts[i+1]
-        delta = (t2 - t1)
+        t2 = ts[i + 1]
+        delta = t2 - t1
         # calc new a, b and c
-        delta_2 = delta*delta
+        delta_2 = delta * delta
         a1x = ax * delta_2
         a1y = ay * delta_2
-        b1x = (2*ax*t1 + bx) * delta
-        b1y = (2*ay*t1 + by) * delta
-        t1_2 = t1*t1
-        c1x = ax*t1_2 + bx*t1 + cx
-        c1y = ay*t1_2 + by*t1 + cy
+        b1x = (2 * ax * t1 + bx) * delta
+        b1y = (2 * ay * t1 + by) * delta
+        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))
@@ -380,24 +587,26 @@
     dx, dy = d
     for i in range(len(ts) - 1):
         t1 = ts[i]
-        t2 = ts[i+1]
-        delta = (t2 - t1)
+        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
+        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
-        pt1, pt2, pt3, pt4 = calcCubicPoints((a1x, a1y), (b1x, b1y), (c1x, c1y), (d1x, d1y))
+        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
 
@@ -409,12 +618,19 @@
 from math import sqrt, acos, cos, pi
 
 
-def solveQuadratic(a, b, c,
-        sqrt=sqrt):
-    """Solve a quadratic equation where a, b and c are real.
-        a*x*x + b*x + c = 0
-    This function returns a list of roots. Note that the returned list
-    is neither guaranteed to be sorted nor to contain unique values!
+def solveQuadratic(a, b, c, sqrt=sqrt):
+    """Solve a quadratic equation.
+
+    Solves *a*x*x + b*x + c = 0* where a, b and c are real.
+
+    Args:
+        a: coefficient of *x²*
+        b: coefficient of *x*
+        c: constant term
+
+    Returns:
+        A list of roots. Note that the returned list is neither guaranteed to
+        be sorted nor to contain unique values!
     """
     if abs(a) < epsilon:
         if abs(b) < epsilon:
@@ -422,13 +638,13 @@
             roots = []
         else:
             # We have a linear equation with 1 root.
-            roots = [-c/b]
+            roots = [-c / b]
     else:
         # We have a true quadratic equation.  Apply the quadratic formula to find two roots.
-        DD = b*b - 4.0*a*c
+        DD = b * b - 4.0 * a * c
         if DD >= 0.0:
             rDD = sqrt(DD)
-            roots = [(-b+rDD)/2.0/a, (-b-rDD)/2.0/a]
+            roots = [(-b + rDD) / 2.0 / a, (-b - rDD) / 2.0 / a]
         else:
             # complex roots, ignore
             roots = []
@@ -436,25 +652,36 @@
 
 
 def solveCubic(a, b, c, d):
-    """Solve a cubic equation where a, b, c and d are real.
-        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!
+    """Solve a cubic equation.
 
-    >>> 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
+    Solves *a*x*x*x + b*x*x + c*x + d = 0* where a, b, c and d are real.
+
+    Args:
+        a: coefficient of *x³*
+        b: coefficient of *x²*
+        c: coefficient of *x*
+        d: constant term
+
+    Returns:
+        A list of roots. Note that the returned list is neither guaranteed to
+        be sorted nor to contain unique values!
+
+    Examples::
+
+        >>> 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:
@@ -467,52 +694,52 @@
         # returns unreliable results, so we fall back to quad.
         return solveQuadratic(b, c, d)
     a = float(a)
-    a1 = b/a
-    a2 = c/a
-    a3 = d/a
+    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
+    Q = (a1 * a1 - 3.0 * a2) / 9.0
+    R = (2.0 * a1 * a1 * a1 - 9.0 * a1 * a2 + 27.0 * a3) / 54.0
 
-    R2 = R*R
-    Q3 = 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)
+    if R2 == 0.0 and Q3 == 0.0:
+        x = round(-a1 / 3.0, epsilonDigits)
         return [x, x, x]
-    elif R2_Q3 <= epsilon * .5:
+    elif R2_Q3 <= epsilon * 0.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)
-        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
+        theta = acos(max(min(R / sqrt(Q3), 1.0), -1.0))
+        rQ2 = -2.0 * sqrt(Q)
+        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)
+            x0 = x1 = x2 = round((x0 + x1 + x2) / 3.0, epsilonDigits)
         elif x1 - x0 < epsilon:
-            x0 = x1 = round((x0 + x1) / 2., epsilonDigits)
+            x0 = x1 = round((x0 + x1) / 2.0, epsilonDigits)
             x2 = round(x2, epsilonDigits)
         elif x2 - x1 < epsilon:
             x0 = round(x0, epsilonDigits)
-            x1 = x2 = round((x1 + x2) / 2., epsilonDigits)
+            x1 = x2 = round((x1 + x2) / 2.0, epsilonDigits)
         else:
             x0 = round(x0, epsilonDigits)
             x1 = round(x1, epsilonDigits)
             x2 = round(x2, epsilonDigits)
         return [x0, x1, x2]
     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 = round(x - a1/3.0, epsilonDigits)
+        x = round(x - a1 / 3.0, epsilonDigits)
         return [x]
 
 
@@ -520,6 +747,7 @@
 # Conversion routines for points to parameters and vice versa
 #
 
+
 def calcQuadraticParameters(pt1, pt2, pt3):
     x2, y2 = pt2
     x3, y3 = pt3
@@ -536,8 +764,8 @@
     x3, y3 = pt3
     x4, y4 = pt4
     dx, dy = pt1
-    cx = (x2 -dx) * 3.0
-    cy = (y2 -dy) * 3.0
+    cx = (x2 - dx) * 3.0
+    cy = (y2 - dy) * 3.0
     bx = (x3 - x2) * 3.0 - cx
     by = (y3 - y2) * 3.0 - cy
     ax = x4 - dx - cx - bx
@@ -574,17 +802,406 @@
     return (x1, y1), (x2, y2), (x3, y3), (x4, y4)
 
 
+#
+# Point at time
+#
+
+
+def linePointAtT(pt1, pt2, t):
+    """Finds the point at time `t` on a line.
+
+    Args:
+        pt1, pt2: Coordinates of the line as 2D tuples.
+        t: The time along the line.
+
+    Returns:
+        A 2D tuple with the coordinates of the point.
+    """
+    return ((pt1[0] * (1 - t) + pt2[0] * t), (pt1[1] * (1 - t) + pt2[1] * t))
+
+
+def quadraticPointAtT(pt1, pt2, pt3, t):
+    """Finds the point at time `t` on a quadratic curve.
+
+    Args:
+        pt1, pt2, pt3: Coordinates of the curve as 2D tuples.
+        t: The time along the curve.
+
+    Returns:
+        A 2D tuple with the coordinates of the point.
+    """
+    x = (1 - t) * (1 - t) * pt1[0] + 2 * (1 - t) * t * pt2[0] + t * t * pt3[0]
+    y = (1 - t) * (1 - t) * pt1[1] + 2 * (1 - t) * t * pt2[1] + t * t * pt3[1]
+    return (x, y)
+
+
+def cubicPointAtT(pt1, pt2, pt3, pt4, t):
+    """Finds the point at time `t` on a cubic curve.
+
+    Args:
+        pt1, pt2, pt3, pt4: Coordinates of the curve as 2D tuples.
+        t: The time along the curve.
+
+    Returns:
+        A 2D tuple with the coordinates of the point.
+    """
+    x = (
+        (1 - t) * (1 - t) * (1 - t) * pt1[0]
+        + 3 * (1 - t) * (1 - t) * t * pt2[0]
+        + 3 * (1 - t) * t * t * pt3[0]
+        + t * t * t * pt4[0]
+    )
+    y = (
+        (1 - t) * (1 - t) * (1 - t) * pt1[1]
+        + 3 * (1 - t) * (1 - t) * t * pt2[1]
+        + 3 * (1 - t) * t * t * pt3[1]
+        + t * t * t * pt4[1]
+    )
+    return (x, y)
+
+
+def segmentPointAtT(seg, t):
+    if len(seg) == 2:
+        return linePointAtT(*seg, t)
+    elif len(seg) == 3:
+        return quadraticPointAtT(*seg, t)
+    elif len(seg) == 4:
+        return cubicPointAtT(*seg, t)
+    raise ValueError("Unknown curve degree")
+
+
+#
+# Intersection finders
+#
+
+
+def _line_t_of_pt(s, e, pt):
+    sx, sy = s
+    ex, ey = e
+    px, py = pt
+    if not math.isclose(sx, ex):
+        return (px - sx) / (ex - sx)
+    if not math.isclose(sy, ey):
+        return (py - sy) / (ey - sy)
+    # Line is a point!
+    return -1
+
+
+def _both_points_are_on_same_side_of_origin(a, b, origin):
+    xDiff = (a[0] - origin[0]) * (b[0] - origin[0])
+    yDiff = (a[1] - origin[1]) * (b[1] - origin[1])
+    return not (xDiff <= 0.0 and yDiff <= 0.0)
+
+
+def lineLineIntersections(s1, e1, s2, e2):
+    """Finds intersections between two line segments.
+
+    Args:
+        s1, e1: Coordinates of the first line as 2D tuples.
+        s2, e2: Coordinates of the second line as 2D tuples.
+
+    Returns:
+        A list of ``Intersection`` objects, each object having ``pt``, ``t1``
+        and ``t2`` attributes containing the intersection point, time on first
+        segment and time on second segment respectively.
+
+    Examples::
+
+        >>> a = lineLineIntersections( (310,389), (453, 222), (289, 251), (447, 367))
+        >>> len(a)
+        1
+        >>> intersection = a[0]
+        >>> intersection.pt
+        (374.44882952482897, 313.73458370177315)
+        >>> (intersection.t1, intersection.t2)
+        (0.45069111555824454, 0.5408153767394238)
+    """
+    s1x, s1y = s1
+    e1x, e1y = e1
+    s2x, s2y = s2
+    e2x, e2y = e2
+    if (
+        math.isclose(s2x, e2x) and math.isclose(s1x, e1x) and not math.isclose(s1x, s2x)
+    ):  # Parallel vertical
+        return []
+    if (
+        math.isclose(s2y, e2y) and math.isclose(s1y, e1y) and not math.isclose(s1y, s2y)
+    ):  # Parallel horizontal
+        return []
+    if math.isclose(s2x, e2x) and math.isclose(s2y, e2y):  # Line segment is tiny
+        return []
+    if math.isclose(s1x, e1x) and math.isclose(s1y, e1y):  # Line segment is tiny
+        return []
+    if math.isclose(e1x, s1x):
+        x = s1x
+        slope34 = (e2y - s2y) / (e2x - s2x)
+        y = slope34 * (x - s2x) + s2y
+        pt = (x, y)
+        return [
+            Intersection(
+                pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+            )
+        ]
+    if math.isclose(s2x, e2x):
+        x = s2x
+        slope12 = (e1y - s1y) / (e1x - s1x)
+        y = slope12 * (x - s1x) + s1y
+        pt = (x, y)
+        return [
+            Intersection(
+                pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+            )
+        ]
+
+    slope12 = (e1y - s1y) / (e1x - s1x)
+    slope34 = (e2y - s2y) / (e2x - s2x)
+    if math.isclose(slope12, slope34):
+        return []
+    x = (slope12 * s1x - s1y - slope34 * s2x + s2y) / (slope12 - slope34)
+    y = slope12 * (x - s1x) + s1y
+    pt = (x, y)
+    if _both_points_are_on_same_side_of_origin(
+        pt, e1, s1
+    ) and _both_points_are_on_same_side_of_origin(pt, s2, e2):
+        return [
+            Intersection(
+                pt=pt, t1=_line_t_of_pt(s1, e1, pt), t2=_line_t_of_pt(s2, e2, pt)
+            )
+        ]
+    return []
+
+
+def _alignment_transformation(segment):
+    # Returns a transformation which aligns a segment horizontally at the
+    # origin. Apply this transformation to curves and root-find to find
+    # intersections with the segment.
+    start = segment[0]
+    end = segment[-1]
+    angle = math.atan2(end[1] - start[1], end[0] - start[0])
+    return Identity.rotate(-angle).translate(-start[0], -start[1])
+
+
+def _curve_line_intersections_t(curve, line):
+    aligned_curve = _alignment_transformation(line).transformPoints(curve)
+    if len(curve) == 3:
+        a, b, c = calcQuadraticParameters(*aligned_curve)
+        intersections = solveQuadratic(a[1], b[1], c[1])
+    elif len(curve) == 4:
+        a, b, c, d = calcCubicParameters(*aligned_curve)
+        intersections = solveCubic(a[1], b[1], c[1], d[1])
+    else:
+        raise ValueError("Unknown curve degree")
+    return sorted(i for i in intersections if 0.0 <= i <= 1)
+
+
+def curveLineIntersections(curve, line):
+    """Finds intersections between a curve and a line.
+
+    Args:
+        curve: List of coordinates of the curve segment as 2D tuples.
+        line: List of coordinates of the line segment as 2D tuples.
+
+    Returns:
+        A list of ``Intersection`` objects, each object having ``pt``, ``t1``
+        and ``t2`` attributes containing the intersection point, time on first
+        segment and time on second segment respectively.
+
+    Examples::
+        >>> curve = [ (100, 240), (30, 60), (210, 230), (160, 30) ]
+        >>> line  = [ (25, 260), (230, 20) ]
+        >>> intersections = curveLineIntersections(curve, line)
+        >>> len(intersections)
+        3
+        >>> intersections[0].pt
+        (84.90010344084885, 189.87306176459828)
+    """
+    if len(curve) == 3:
+        pointFinder = quadraticPointAtT
+    elif len(curve) == 4:
+        pointFinder = cubicPointAtT
+    else:
+        raise ValueError("Unknown curve degree")
+    intersections = []
+    for t in _curve_line_intersections_t(curve, line):
+        pt = pointFinder(*curve, t)
+        intersections.append(Intersection(pt=pt, t1=t, t2=_line_t_of_pt(*line, pt)))
+    return intersections
+
+
+def _curve_bounds(c):
+    if len(c) == 3:
+        return calcQuadraticBounds(*c)
+    elif len(c) == 4:
+        return calcCubicBounds(*c)
+    raise ValueError("Unknown curve degree")
+
+
+def _split_segment_at_t(c, t):
+    if len(c) == 2:
+        s, e = c
+        midpoint = linePointAtT(s, e, t)
+        return [(s, midpoint), (midpoint, e)]
+    if len(c) == 3:
+        return splitQuadraticAtT(*c, t)
+    elif len(c) == 4:
+        return splitCubicAtT(*c, t)
+    raise ValueError("Unknown curve degree")
+
+
+def _curve_curve_intersections_t(
+    curve1, curve2, precision=1e-3, range1=None, range2=None
+):
+    bounds1 = _curve_bounds(curve1)
+    bounds2 = _curve_bounds(curve2)
+
+    if not range1:
+        range1 = (0.0, 1.0)
+    if not range2:
+        range2 = (0.0, 1.0)
+
+    # If bounds don't intersect, go home
+    intersects, _ = sectRect(bounds1, bounds2)
+    if not intersects:
+        return []
+
+    def midpoint(r):
+        return 0.5 * (r[0] + r[1])
+
+    # If they do overlap but they're tiny, approximate
+    if rectArea(bounds1) < precision and rectArea(bounds2) < precision:
+        return [(midpoint(range1), midpoint(range2))]
+
+    c11, c12 = _split_segment_at_t(curve1, 0.5)
+    c11_range = (range1[0], midpoint(range1))
+    c12_range = (midpoint(range1), range1[1])
+
+    c21, c22 = _split_segment_at_t(curve2, 0.5)
+    c21_range = (range2[0], midpoint(range2))
+    c22_range = (midpoint(range2), range2[1])
+
+    found = []
+    found.extend(
+        _curve_curve_intersections_t(
+            c11, c21, precision, range1=c11_range, range2=c21_range
+        )
+    )
+    found.extend(
+        _curve_curve_intersections_t(
+            c12, c21, precision, range1=c12_range, range2=c21_range
+        )
+    )
+    found.extend(
+        _curve_curve_intersections_t(
+            c11, c22, precision, range1=c11_range, range2=c22_range
+        )
+    )
+    found.extend(
+        _curve_curve_intersections_t(
+            c12, c22, precision, range1=c12_range, range2=c22_range
+        )
+    )
+
+    unique_key = lambda ts: (int(ts[0] / precision), int(ts[1] / precision))
+    seen = set()
+    unique_values = []
+
+    for ts in found:
+        key = unique_key(ts)
+        if key in seen:
+            continue
+        seen.add(key)
+        unique_values.append(ts)
+
+    return unique_values
+
+
+def curveCurveIntersections(curve1, curve2):
+    """Finds intersections between a curve and a curve.
+
+    Args:
+        curve1: List of coordinates of the first curve segment as 2D tuples.
+        curve2: List of coordinates of the second curve segment as 2D tuples.
+
+    Returns:
+        A list of ``Intersection`` objects, each object having ``pt``, ``t1``
+        and ``t2`` attributes containing the intersection point, time on first
+        segment and time on second segment respectively.
+
+    Examples::
+        >>> curve1 = [ (10,100), (90,30), (40,140), (220,220) ]
+        >>> curve2 = [ (5,150), (180,20), (80,250), (210,190) ]
+        >>> intersections = curveCurveIntersections(curve1, curve2)
+        >>> len(intersections)
+        3
+        >>> intersections[0].pt
+        (81.7831487395506, 109.88904552375288)
+    """
+    intersection_ts = _curve_curve_intersections_t(curve1, curve2)
+    return [
+        Intersection(pt=segmentPointAtT(curve1, ts[0]), t1=ts[0], t2=ts[1])
+        for ts in intersection_ts
+    ]
+
+
+def segmentSegmentIntersections(seg1, seg2):
+    """Finds intersections between two segments.
+
+    Args:
+        seg1: List of coordinates of the first segment as 2D tuples.
+        seg2: List of coordinates of the second segment as 2D tuples.
+
+    Returns:
+        A list of ``Intersection`` objects, each object having ``pt``, ``t1``
+        and ``t2`` attributes containing the intersection point, time on first
+        segment and time on second segment respectively.
+
+    Examples::
+        >>> curve1 = [ (10,100), (90,30), (40,140), (220,220) ]
+        >>> curve2 = [ (5,150), (180,20), (80,250), (210,190) ]
+        >>> intersections = segmentSegmentIntersections(curve1, curve2)
+        >>> len(intersections)
+        3
+        >>> intersections[0].pt
+        (81.7831487395506, 109.88904552375288)
+        >>> curve3 = [ (100, 240), (30, 60), (210, 230), (160, 30) ]
+        >>> line  = [ (25, 260), (230, 20) ]
+        >>> intersections = segmentSegmentIntersections(curve3, line)
+        >>> len(intersections)
+        3
+        >>> intersections[0].pt
+        (84.90010344084885, 189.87306176459828)
+
+    """
+    # Arrange by degree
+    swapped = False
+    if len(seg2) > len(seg1):
+        seg2, seg1 = seg1, seg2
+        swapped = True
+    if len(seg1) > 2:
+        if len(seg2) > 2:
+            intersections = curveCurveIntersections(seg1, seg2)
+        else:
+            intersections = curveLineIntersections(seg1, seg2)
+    elif len(seg1) == 2 and len(seg2) == 2:
+        intersections = lineLineIntersections(*seg1, *seg2)
+    else:
+        raise ValueError("Couldn't work out which intersection function to use")
+    if not swapped:
+        return intersections
+    return [Intersection(pt=i.pt, t1=i.t2, t2=i.t1) for i in intersections]
+
+
 def _segmentrepr(obj):
     """
-        >>> _segmentrepr([1, [2, 3], [], [[2, [3, 4], [0.1, 2.2]]]])
-        '(1, (2, 3), (), ((2, (3, 4), (0.1, 2.2))))'
+    >>> _segmentrepr([1, [2, 3], [], [[2, [3, 4], [0.1, 2.2]]]])
+    '(1, (2, 3), (), ((2, (3, 4), (0.1, 2.2))))'
     """
     try:
         it = iter(obj)
     except TypeError:
         return "%g" % obj
     else:
-        return "(%s)" % ", ".join([_segmentrepr(x) for x in it])
+        return "(%s)" % ", ".join(_segmentrepr(x) for x in it)
 
 
 def printSegments(segments):
@@ -594,7 +1211,9 @@
     for segment in segments:
         print(_segmentrepr(segment))
 
+
 if __name__ == "__main__":
     import sys
     import doctest
+
     sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/misc/classifyTools.py b/Lib/fontTools/misc/classifyTools.py
index 64bcdc8..ae88a8f 100644
--- a/Lib/fontTools/misc/classifyTools.py
+++ b/Lib/fontTools/misc/classifyTools.py
@@ -1,8 +1,6 @@
 """ fontTools.misc.classifyTools.py -- tools for classifying things.
 """
 
-from __future__ import print_function, absolute_import
-from fontTools.misc.py23 import *
 
 class Classifier(object):
 
diff --git a/Lib/fontTools/misc/cliTools.py b/Lib/fontTools/misc/cliTools.py
index 59ac3be..e8c1767 100644
--- a/Lib/fontTools/misc/cliTools.py
+++ b/Lib/fontTools/misc/cliTools.py
@@ -1,14 +1,34 @@
 """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+$")
+numberAddedRE = re.compile(r"#\d+$")
 
 
 def makeOutputFileName(input, outputDir=None, extension=None, overWrite=False):
+    """Generates a suitable file name for writing output.
+
+    Often tools will want to take a file, do some kind of transformation to it,
+    and write it out again. This function determines an appropriate name for the
+    output file, through one or more of the following steps:
+
+    - changing the output directory
+    - replacing the file extension
+    - suffixing the filename with a number (``#1``, ``#2``, etc.) to avoid
+      overwriting an existing file.
+
+    Args:
+        input: Name of input file.
+        outputDir: Optionally, a new directory to write the file into.
+        extension: Optionally, a replacement for the current file extension.
+        overWrite: Overwriting an existing file is permitted if true; if false
+            and the proposed filename exists, a new name will be generated by
+            adding an appropriate number suffix.
+
+    Returns:
+        str: Suitable output filename
+    """
     dirName, fileName = os.path.split(input)
     fileName, ext = os.path.splitext(fileName)
     if outputDir:
diff --git a/Lib/fontTools/misc/cython.py b/Lib/fontTools/misc/cython.py
new file mode 100644
index 0000000..0ba659f
--- /dev/null
+++ b/Lib/fontTools/misc/cython.py
@@ -0,0 +1,25 @@
+""" Exports a no-op 'cython' namespace similar to
+https://github.com/cython/cython/blob/master/Cython/Shadow.py
+
+This allows to optionally compile @cython decorated functions
+(when cython is available at built time), or run the same code
+as pure-python, without runtime dependency on cython module.
+
+We only define the symbols that we use. E.g. see fontTools.cu2qu
+"""
+
+from types import SimpleNamespace
+
+def _empty_decorator(x):
+    return x
+
+compiled = False
+
+for name in ("double", "complex", "int"):
+    globals()[name] = None
+
+for name in ("cfunc", "inline"):
+    globals()[name] = _empty_decorator
+
+locals = lambda **_: _empty_decorator
+returns = lambda _: _empty_decorator
diff --git a/Lib/fontTools/misc/dictTools.py b/Lib/fontTools/misc/dictTools.py
new file mode 100644
index 0000000..ae7932c
--- /dev/null
+++ b/Lib/fontTools/misc/dictTools.py
@@ -0,0 +1,66 @@
+"""Misc dict tools."""
+
+
+__all__ = ['hashdict']
+
+# https://stackoverflow.com/questions/1151658/python-hashable-dicts
+class hashdict(dict):
+    """
+    hashable dict implementation, suitable for use as a key into
+    other dicts.
+
+        >>> h1 = hashdict({"apples": 1, "bananas":2})
+        >>> h2 = hashdict({"bananas": 3, "mangoes": 5})
+        >>> h1+h2
+        hashdict(apples=1, bananas=3, mangoes=5)
+        >>> d1 = {}
+        >>> d1[h1] = "salad"
+        >>> d1[h1]
+        'salad'
+        >>> d1[h2]
+        Traceback (most recent call last):
+        ...
+        KeyError: hashdict(bananas=3, mangoes=5)
+
+    based on answers from
+       http://stackoverflow.com/questions/1151658/python-hashable-dicts
+
+    """
+    def __key(self):
+        return tuple(sorted(self.items()))
+    def __repr__(self):
+        return "{0}({1})".format(self.__class__.__name__,
+            ", ".join("{0}={1}".format(
+                    str(i[0]),repr(i[1])) for i in self.__key()))
+
+    def __hash__(self):
+        return hash(self.__key())
+    def __setitem__(self, key, value):
+        raise TypeError("{0} does not support item assignment"
+                         .format(self.__class__.__name__))
+    def __delitem__(self, key):
+        raise TypeError("{0} does not support item assignment"
+                         .format(self.__class__.__name__))
+    def clear(self):
+        raise TypeError("{0} does not support item assignment"
+                         .format(self.__class__.__name__))
+    def pop(self, *args, **kwargs):
+        raise TypeError("{0} does not support item assignment"
+                         .format(self.__class__.__name__))
+    def popitem(self, *args, **kwargs):
+        raise TypeError("{0} does not support item assignment"
+                         .format(self.__class__.__name__))
+    def setdefault(self, *args, **kwargs):
+        raise TypeError("{0} does not support item assignment"
+                         .format(self.__class__.__name__))
+    def update(self, *args, **kwargs):
+        raise TypeError("{0} does not support item assignment"
+                         .format(self.__class__.__name__))
+    # update is not ok because it mutates the object
+    # __add__ is ok because it creates a new object
+    # while the new object is under construction, it's ok to mutate it
+    def __add__(self, right):
+        result = hashdict(self)
+        dict.update(result, right)
+        return result
+
diff --git a/Lib/fontTools/misc/eexec.py b/Lib/fontTools/misc/eexec.py
index 0efa6f5..71f733c 100644
--- a/Lib/fontTools/misc/eexec.py
+++ b/Lib/fontTools/misc/eexec.py
@@ -1,9 +1,19 @@
-"""fontTools.misc.eexec.py -- Module implementing the eexec and
-charstring encryption algorithm as used by PostScript Type 1 fonts.
+"""
+PostScript Type 1 fonts make use of two types of encryption: charstring
+encryption and ``eexec`` encryption. Charstring encryption is used for
+the charstrings themselves, while ``eexec`` is used to encrypt larger
+sections of the font program, such as the ``Private`` and ``CharStrings``
+dictionaries. Despite the different names, the algorithm is the same,
+although ``eexec`` encryption uses a fixed initial key R=55665.
+
+The algorithm uses cipher feedback, meaning that the ciphertext is used
+to modify the key. Because of this, the routines in this module return
+the new key at the end of the operation.
+
 """
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytechr, bytesjoin, byteord
+
 
 def _decryptChar(cipher, R):
 	cipher = byteord(cipher)
@@ -20,12 +30,24 @@
 
 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
+	Decrypts a string using the Type 1 encryption algorithm.
+
+	Args:
+		cipherstring: String of ciphertext.
+		R: Initial key.
+
+	Returns:
+		decryptedStr: Plaintext string.
+		R: Output key for subsequent decryptions.
+
+	Examples::
+
+		>>> 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:
@@ -36,6 +58,30 @@
 
 def encrypt(plainstring, R):
 	r"""
+	Encrypts a string using the Type 1 encryption algorithm.
+
+	Note that the algorithm as described in the Type 1 specification requires the
+	plaintext to be prefixed with a number of random bytes. (For ``eexec`` the
+	number of random bytes is set to 4.) This routine does *not* add the random
+	prefix to its input.
+
+	Args:
+		plainstring: String of plaintext.
+		R: Initial key.
+
+	Returns:
+		cipherstring: Ciphertext string.
+		R: Output key for subsequent encryptions.
+
+	Examples::
+
+		>>> 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
+
 	>>> 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"
diff --git a/Lib/fontTools/misc/encodingTools.py b/Lib/fontTools/misc/encodingTools.py
index 275ae9f..eccf951 100644
--- a/Lib/fontTools/misc/encodingTools.py
+++ b/Lib/fontTools/misc/encodingTools.py
@@ -1,8 +1,6 @@
 """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
@@ -18,7 +16,7 @@
 	},
 	1: { # Macintosh
 		# See
-		# https://github.com/behdad/fonttools/issues/236
+		# https://github.com/fonttools/fonttools/issues/236
 		0: { # Macintosh, platEncID==0, keyed by langID
 			15: "mac_iceland",
 			17: "mac_turkish",
diff --git a/Lib/fontTools/misc/etree.py b/Lib/fontTools/misc/etree.py
new file mode 100644
index 0000000..6e943e4
--- /dev/null
+++ b/Lib/fontTools/misc/etree.py
@@ -0,0 +1,481 @@
+"""Shim module exporting the same ElementTree API for lxml and
+xml.etree backends.
+
+When lxml is installed, it is automatically preferred over the built-in
+xml.etree module.
+On Python 2.7, the cElementTree module is preferred over the pure-python
+ElementTree module.
+
+Besides exporting a unified interface, this also defines extra functions
+or subclasses built-in ElementTree classes to add features that are
+only availble in lxml, like OrderedDict for attributes, pretty_print and
+iterwalk.
+"""
+from fontTools.misc.py23 import unicode, tostr
+
+
+XML_DECLARATION = """<?xml version='1.0' encoding='%s'?>"""
+
+__all__ = [
+    # public symbols
+    "Comment",
+    "dump",
+    "Element",
+    "ElementTree",
+    "fromstring",
+    "fromstringlist",
+    "iselement",
+    "iterparse",
+    "parse",
+    "ParseError",
+    "PI",
+    "ProcessingInstruction",
+    "QName",
+    "SubElement",
+    "tostring",
+    "tostringlist",
+    "TreeBuilder",
+    "XML",
+    "XMLParser",
+    "register_namespace",
+]
+
+try:
+    from lxml.etree import *
+
+    _have_lxml = True
+except ImportError:
+    try:
+        from xml.etree.cElementTree import *
+
+        # the cElementTree version of XML function doesn't support
+        # the optional 'parser' keyword argument
+        from xml.etree.ElementTree import XML
+    except ImportError:  # pragma: no cover
+        from xml.etree.ElementTree import *
+    _have_lxml = False
+
+    import sys
+
+    # dict is always ordered in python >= 3.6 and on pypy
+    PY36 = sys.version_info >= (3, 6)
+    try:
+        import __pypy__
+    except ImportError:
+        __pypy__ = None
+    _dict_is_ordered = bool(PY36 or __pypy__)
+    del PY36, __pypy__
+
+    if _dict_is_ordered:
+        _Attrib = dict
+    else:
+        from collections import OrderedDict as _Attrib
+
+    if isinstance(Element, type):
+        _Element = Element
+    else:
+        # in py27, cElementTree.Element cannot be subclassed, so
+        # we need to import the pure-python class
+        from xml.etree.ElementTree import Element as _Element
+
+    class Element(_Element):
+        """Element subclass that keeps the order of attributes."""
+
+        def __init__(self, tag, attrib=_Attrib(), **extra):
+            super(Element, self).__init__(tag)
+            self.attrib = _Attrib()
+            if attrib:
+                self.attrib.update(attrib)
+            if extra:
+                self.attrib.update(extra)
+
+    def SubElement(parent, tag, attrib=_Attrib(), **extra):
+        """Must override SubElement as well otherwise _elementtree.SubElement
+        fails if 'parent' is a subclass of Element object.
+        """
+        element = parent.__class__(tag, attrib, **extra)
+        parent.append(element)
+        return element
+
+    def _iterwalk(element, events, tag):
+        include = tag is None or element.tag == tag
+        if include and "start" in events:
+            yield ("start", element)
+        for e in element:
+            for item in _iterwalk(e, events, tag):
+                yield item
+        if include:
+            yield ("end", element)
+
+    def iterwalk(element_or_tree, events=("end",), tag=None):
+        """A tree walker that generates events from an existing tree as
+        if it was parsing XML data with iterparse().
+        Drop-in replacement for lxml.etree.iterwalk.
+        """
+        if iselement(element_or_tree):
+            element = element_or_tree
+        else:
+            element = element_or_tree.getroot()
+        if tag == "*":
+            tag = None
+        for item in _iterwalk(element, events, tag):
+            yield item
+
+    _ElementTree = ElementTree
+
+    class ElementTree(_ElementTree):
+        """ElementTree subclass that adds 'pretty_print' and 'doctype'
+        arguments to the 'write' method.
+        Currently these are only supported for the default XML serialization
+        'method', and not also for "html" or "text", for these are delegated
+        to the base class.
+        """
+
+        def write(
+            self,
+            file_or_filename,
+            encoding=None,
+            xml_declaration=False,
+            method=None,
+            doctype=None,
+            pretty_print=False,
+        ):
+            if method and method != "xml":
+                # delegate to super-class
+                super(ElementTree, self).write(
+                    file_or_filename,
+                    encoding=encoding,
+                    xml_declaration=xml_declaration,
+                    method=method,
+                )
+                return
+
+            if encoding is unicode or (
+                encoding is not None and encoding.lower() == "unicode"
+            ):
+                if xml_declaration:
+                    raise ValueError(
+                        "Serialisation to unicode must not request an XML declaration"
+                    )
+                write_declaration = False
+                encoding = "unicode"
+            elif xml_declaration is None:
+                # by default, write an XML declaration only for non-standard encodings
+                write_declaration = encoding is not None and encoding.upper() not in (
+                    "ASCII",
+                    "UTF-8",
+                    "UTF8",
+                    "US-ASCII",
+                )
+            else:
+                write_declaration = xml_declaration
+
+            if encoding is None:
+                encoding = "ASCII"
+
+            if pretty_print:
+                # NOTE this will modify the tree in-place
+                _indent(self._root)
+
+            with _get_writer(file_or_filename, encoding) as write:
+                if write_declaration:
+                    write(XML_DECLARATION % encoding.upper())
+                    if pretty_print:
+                        write("\n")
+                if doctype:
+                    write(_tounicode(doctype))
+                    if pretty_print:
+                        write("\n")
+
+                qnames, namespaces = _namespaces(self._root)
+                _serialize_xml(write, self._root, qnames, namespaces)
+
+    import io
+
+    def tostring(
+        element,
+        encoding=None,
+        xml_declaration=None,
+        method=None,
+        doctype=None,
+        pretty_print=False,
+    ):
+        """Custom 'tostring' function that uses our ElementTree subclass, with
+        pretty_print support.
+        """
+        stream = io.StringIO() if encoding == "unicode" else io.BytesIO()
+        ElementTree(element).write(
+            stream,
+            encoding=encoding,
+            xml_declaration=xml_declaration,
+            method=method,
+            doctype=doctype,
+            pretty_print=pretty_print,
+        )
+        return stream.getvalue()
+
+    # serialization support
+
+    import re
+
+    # Valid XML strings can include any Unicode character, excluding control
+    # characters, the surrogate blocks, FFFE, and FFFF:
+    #   Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+    # Here we reversed the pattern to match only the invalid characters.
+    # For the 'narrow' python builds supporting only UCS-2, which represent
+    # characters beyond BMP as UTF-16 surrogate pairs, we need to pass through
+    # the surrogate block. I haven't found a more elegant solution...
+    UCS2 = sys.maxunicode < 0x10FFFF
+    if UCS2:
+        _invalid_xml_string = re.compile(
+            "[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uFFFE-\uFFFF]"
+        )
+    else:
+        _invalid_xml_string = re.compile(
+            "[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE-\uFFFF]"
+        )
+
+    def _tounicode(s):
+        """Test if a string is valid user input and decode it to unicode string
+        using ASCII encoding if it's a bytes string.
+        Reject all bytes/unicode input that contains non-XML characters.
+        Reject all bytes input that contains non-ASCII characters.
+        """
+        try:
+            s = tostr(s, encoding="ascii", errors="strict")
+        except UnicodeDecodeError:
+            raise ValueError(
+                "Bytes strings can only contain ASCII characters. "
+                "Use unicode strings for non-ASCII characters.")
+        except AttributeError:
+            _raise_serialization_error(s)
+        if s and _invalid_xml_string.search(s):
+            raise ValueError(
+                "All strings must be XML compatible: Unicode or ASCII, "
+                "no NULL bytes or control characters"
+            )
+        return s
+
+    import contextlib
+
+    @contextlib.contextmanager
+    def _get_writer(file_or_filename, encoding):
+        # returns text write method and release all resources after using
+        try:
+            write = file_or_filename.write
+        except AttributeError:
+            # file_or_filename is a file name
+            f = open(
+                file_or_filename,
+                "w",
+                encoding="utf-8" if encoding == "unicode" else encoding,
+                errors="xmlcharrefreplace",
+            )
+            with f:
+                yield f.write
+        else:
+            # file_or_filename is a file-like object
+            # encoding determines if it is a text or binary writer
+            if encoding == "unicode":
+                # use a text writer as is
+                yield write
+            else:
+                # wrap a binary writer with TextIOWrapper
+                detach_buffer = False
+                if isinstance(file_or_filename, io.BufferedIOBase):
+                    buf = file_or_filename
+                elif isinstance(file_or_filename, io.RawIOBase):
+                    buf = io.BufferedWriter(file_or_filename)
+                    detach_buffer = True
+                else:
+                    # This is to handle passed objects that aren't in the
+                    # IOBase hierarchy, but just have a write method
+                    buf = io.BufferedIOBase()
+                    buf.writable = lambda: True
+                    buf.write = write
+                    try:
+                        # TextIOWrapper uses this methods to determine
+                        # if BOM (for UTF-16, etc) should be added
+                        buf.seekable = file_or_filename.seekable
+                        buf.tell = file_or_filename.tell
+                    except AttributeError:
+                        pass
+                wrapper = io.TextIOWrapper(
+                    buf,
+                    encoding=encoding,
+                    errors="xmlcharrefreplace",
+                    newline="\n",
+                )
+                try:
+                    yield wrapper.write
+                finally:
+                    # Keep the original file open when the TextIOWrapper and
+                    # the BufferedWriter are destroyed
+                    wrapper.detach()
+                    if detach_buffer:
+                        buf.detach()
+
+    from xml.etree.ElementTree import _namespace_map
+
+    def _namespaces(elem):
+        # identify namespaces used in this tree
+
+        # maps qnames to *encoded* prefix:local names
+        qnames = {None: None}
+
+        # maps uri:s to prefixes
+        namespaces = {}
+
+        def add_qname(qname):
+            # calculate serialized qname representation
+            try:
+                qname = _tounicode(qname)
+                if qname[:1] == "{":
+                    uri, tag = qname[1:].rsplit("}", 1)
+                    prefix = namespaces.get(uri)
+                    if prefix is None:
+                        prefix = _namespace_map.get(uri)
+                        if prefix is None:
+                            prefix = "ns%d" % len(namespaces)
+                        else:
+                            prefix = _tounicode(prefix)
+                        if prefix != "xml":
+                            namespaces[uri] = prefix
+                    if prefix:
+                        qnames[qname] = "%s:%s" % (prefix, tag)
+                    else:
+                        qnames[qname] = tag  # default element
+                else:
+                    qnames[qname] = qname
+            except TypeError:
+                _raise_serialization_error(qname)
+
+        # populate qname and namespaces table
+        for elem in elem.iter():
+            tag = elem.tag
+            if isinstance(tag, QName):
+                if tag.text not in qnames:
+                    add_qname(tag.text)
+            elif isinstance(tag, str):
+                if tag not in qnames:
+                    add_qname(tag)
+            elif tag is not None and tag is not Comment and tag is not PI:
+                _raise_serialization_error(tag)
+            for key, value in elem.items():
+                if isinstance(key, QName):
+                    key = key.text
+                if key not in qnames:
+                    add_qname(key)
+                if isinstance(value, QName) and value.text not in qnames:
+                    add_qname(value.text)
+            text = elem.text
+            if isinstance(text, QName) and text.text not in qnames:
+                add_qname(text.text)
+        return qnames, namespaces
+
+    def _serialize_xml(write, elem, qnames, namespaces, **kwargs):
+        tag = elem.tag
+        text = elem.text
+        if tag is Comment:
+            write("<!--%s-->" % _tounicode(text))
+        elif tag is ProcessingInstruction:
+            write("<?%s?>" % _tounicode(text))
+        else:
+            tag = qnames[_tounicode(tag) if tag is not None else None]
+            if tag is None:
+                if text:
+                    write(_escape_cdata(text))
+                for e in elem:
+                    _serialize_xml(write, e, qnames, None)
+            else:
+                write("<" + tag)
+                if namespaces:
+                    for uri, prefix in sorted(
+                        namespaces.items(), key=lambda x: x[1]
+                    ):  # sort on prefix
+                        if prefix:
+                            prefix = ":" + prefix
+                        write(' xmlns%s="%s"' % (prefix, _escape_attrib(uri)))
+                attrs = elem.attrib
+                if attrs:
+                    # try to keep existing attrib order
+                    if len(attrs) <= 1 or type(attrs) is _Attrib:
+                        items = attrs.items()
+                    else:
+                        # if plain dict, use lexical order
+                        items = sorted(attrs.items())
+                    for k, v in items:
+                        if isinstance(k, QName):
+                            k = _tounicode(k.text)
+                        else:
+                            k = _tounicode(k)
+                        if isinstance(v, QName):
+                            v = qnames[_tounicode(v.text)]
+                        else:
+                            v = _escape_attrib(v)
+                        write(' %s="%s"' % (qnames[k], v))
+                if text is not None or len(elem):
+                    write(">")
+                    if text:
+                        write(_escape_cdata(text))
+                    for e in elem:
+                        _serialize_xml(write, e, qnames, None)
+                    write("</" + tag + ">")
+                else:
+                    write("/>")
+        if elem.tail:
+            write(_escape_cdata(elem.tail))
+
+    def _raise_serialization_error(text):
+        raise TypeError(
+            "cannot serialize %r (type %s)" % (text, type(text).__name__)
+        )
+
+    def _escape_cdata(text):
+        # escape character data
+        try:
+            text = _tounicode(text)
+            # it's worth avoiding do-nothing calls for short strings
+            if "&" in text:
+                text = text.replace("&", "&amp;")
+            if "<" in text:
+                text = text.replace("<", "&lt;")
+            if ">" in text:
+                text = text.replace(">", "&gt;")
+            return text
+        except (TypeError, AttributeError):
+            _raise_serialization_error(text)
+
+    def _escape_attrib(text):
+        # escape attribute value
+        try:
+            text = _tounicode(text)
+            if "&" in text:
+                text = text.replace("&", "&amp;")
+            if "<" in text:
+                text = text.replace("<", "&lt;")
+            if ">" in text:
+                text = text.replace(">", "&gt;")
+            if '"' in text:
+                text = text.replace('"', "&quot;")
+            if "\n" in text:
+                text = text.replace("\n", "&#10;")
+            return text
+        except (TypeError, AttributeError):
+            _raise_serialization_error(text)
+
+    def _indent(elem, level=0):
+        # From http://effbot.org/zone/element-lib.htm#prettyprint
+        i = "\n" + level * "  "
+        if len(elem):
+            if not elem.text or not elem.text.strip():
+                elem.text = i + "  "
+            if not elem.tail or not elem.tail.strip():
+                elem.tail = i
+            for elem in elem:
+                _indent(elem, 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
diff --git a/Lib/fontTools/misc/filenames.py b/Lib/fontTools/misc/filenames.py
new file mode 100644
index 0000000..0f01000
--- /dev/null
+++ b/Lib/fontTools/misc/filenames.py
@@ -0,0 +1,242 @@
+"""
+This module implements the algorithm for converting between a "user name" -
+something that a user can choose arbitrarily inside a font editor - and a file
+name suitable for use in a wide range of operating systems and filesystems.
+
+The `UFO 3 specification <http://unifiedfontobject.org/versions/ufo3/conventions/>`_
+provides an example of an algorithm for such conversion, which avoids illegal
+characters, reserved file names, ambiguity between upper- and lower-case
+characters, and clashes with existing files.
+
+This code was originally copied from
+`ufoLib <https://github.com/unified-font-object/ufoLib/blob/8747da7/Lib/ufoLib/filenames.py>`_
+by Tal Leming and is copyright (c) 2005-2016, The RoboFab Developers:
+
+-	Erik van Blokland
+-	Tal Leming
+-	Just van Rossum
+"""
+
+
+illegalCharacters = r"\" * + / : < > ? [ \ ] | \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=""):
+	"""Converts from a user name to a file name.
+
+	Takes care to avoid illegal characters, reserved file names, ambiguity between
+	upper- and lower-case characters, and clashes with existing files.
+
+	Args:
+		userName (str): The input file name.
+		existing: A case-insensitive list of all existing file names.
+		prefix: Prefix to be prepended to the file name.
+		suffix: Suffix to be appended to the file name.
+
+	Returns:
+		A suitable filename.
+
+	Raises:
+		NameTranslationError: If no suitable name could be generated.
+
+	Examples::
+
+		>>> 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 str
+	if not isinstance(userName, str):
+		raise ValueError("The value for userName must be a 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 7d11eb5..f87e332 100644
--- a/Lib/fontTools/misc/fixedTools.py
+++ b/Lib/fontTools/misc/fixedTools.py
@@ -1,70 +1,233 @@
-"""fontTools.misc.fixedTools.py -- tools for working with fixed numbers.
+"""
+The `OpenType specification <https://docs.microsoft.com/en-us/typography/opentype/spec/otff#data-types>`_
+defines two fixed-point data types:
+
+``Fixed``
+	A 32-bit signed fixed-point number with a 16 bit twos-complement
+	magnitude component and 16 fractional bits.
+``F2DOT14``
+	A 16-bit signed fixed-point number with a 2 bit twos-complement
+	magnitude component and 14 fractional bits.
+
+To support reading and writing data with these data types, this module provides
+functions for converting between fixed-point, float and string representations.
+
+.. data:: MAX_F2DOT14
+
+	The maximum value that can still fit in an F2Dot14. (1.99993896484375)
 """
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from .roundTools import otRound, nearestMultipleShortestRepr
 import logging
 
 log = logging.getLogger(__name__)
 
 __all__ = [
+	"MAX_F2DOT14",
 	"fixedToFloat",
 	"floatToFixed",
-    "floatToFixedToFloat",
+	"floatToFixedToFloat",
+	"floatToFixedToStr",
+	"fixedToStr",
+	"strToFixed",
+	"strToFixedToFloat",
 	"ensureVersionIsLong",
 	"versionToFixed",
 ]
 
+
+MAX_F2DOT14 = 0x7FFF / (1 << 14)
+
+
 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.
+	"""Converts a fixed-point number to a float given the number of
+	precision bits.
 
-	precisionBits is only supported up to 16.
+	Args:
+		value (int): Number in fixed-point format.
+		precisionBits (int): Number of precision bits.
+
+	Returns:
+		Floating point value.
+
+	Examples::
+
+		>>> import math
+		>>> f = fixedToFloat(-10139, precisionBits=14)
+		>>> math.isclose(f, -0.61883544921875)
+		True
 	"""
-	if not value: return 0.0
+	return value / (1 << precisionBits)
 
-	scale = 1 << precisionBits
-	value /= scale
-	eps = .5 / scale
-	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
-	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. round(value * (1<<precisionBits)).
+	precision bits.
+
+	Args:
+		value (float): Floating point value.
+		precisionBits (int): Number of precision bits.
+
+	Returns:
+		int: Fixed-point representation.
+
+	Examples::
+
+		>>> floatToFixed(-0.61883544921875, precisionBits=14)
+		-10139
+		>>> floatToFixed(-0.61884, precisionBits=14)
+		-10139
 	"""
-	return round(value * (1<<precisionBits))
+	return otRound(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.
+	"""Converts a float to a fixed-point number and back again.
+
+	By converting the float to fixed, rounding it, and converting it back
+	to float again, this returns a floating point values which is exactly
+	representable in fixed-point format.
+
+	Note: this **is** equivalent to ``fixedToFloat(floatToFixed(value))``.
+
+	Args:
+		value (float): The input floating point value.
+		precisionBits (int): Number of precision bits.
+
+	Returns:
+		float: The transformed and rounded value.
+
+	Examples::
+		>>> import math
+		>>> f1 = -0.61884
+		>>> f2 = floatToFixedToFloat(-0.61884, precisionBits=14)
+		>>> f1 != f2
+		True
+		>>> math.isclose(f2, -0.61883544921875)
+		True
 	"""
-	scale = 1<<precisionBits
-	return round(value * scale) / scale
+	scale = 1 << precisionBits
+	return otRound(value * scale) / scale
+
+
+def fixedToStr(value, precisionBits):
+	"""Converts a fixed-point number to a string representing a decimal float.
+
+	This chooses the float that has the shortest decimal representation (the least
+	number of fractional decimal digits).
+
+	For example, to convert a fixed-point number in a 2.14 format, use
+	``precisionBits=14``::
+
+		>>> fixedToStr(-10139, precisionBits=14)
+		'-0.61884'
+
+	This is pretty slow compared to the simple division used in ``fixedToFloat``.
+	Use sporadically when you need to serialize or print the fixed-point number in
+	a human-readable form.
+	It uses nearestMultipleShortestRepr under the hood.
+
+	Args:
+		value (int): The fixed-point value to convert.
+		precisionBits (int): Number of precision bits, *up to a maximum of 16*.
+
+	Returns:
+		str: A string representation of the value.
+	"""
+	scale = 1 << precisionBits
+	return nearestMultipleShortestRepr(value/scale, factor=1.0/scale)
+
+
+def strToFixed(string, precisionBits):
+	"""Converts a string representing a decimal float to a fixed-point number.
+
+	Args:
+		string (str): A string representing a decimal float.
+		precisionBits (int): Number of precision bits, *up to a maximum of 16*.
+
+	Returns:
+		int: Fixed-point representation.
+
+	Examples::
+
+	>>> ## to convert a float string to a 2.14 fixed-point number:
+	>>> strToFixed('-0.61884', precisionBits=14)
+	-10139
+	"""
+	value = float(string)
+	return otRound(value * (1 << precisionBits))
+
+
+def strToFixedToFloat(string, precisionBits):
+	"""Convert a string to a decimal float with fixed-point rounding.
+
+	This first converts string to a float, then turns it into a fixed-point
+	number with ``precisionBits`` fractional binary digits, then back to a
+	float again.
+
+	This is simply a shorthand for fixedToFloat(floatToFixed(float(s))).
+
+	Args:
+		string (str): A string representing a decimal float.
+		precisionBits (int): Number of precision bits.
+
+	Returns:
+		float: The transformed and rounded value.
+
+	Examples::
+
+		>>> import math
+		>>> s = '-0.61884'
+		>>> bits = 14
+		>>> f = strToFixedToFloat(s, precisionBits=bits)
+		>>> math.isclose(f, -0.61883544921875)
+		True
+		>>> f == fixedToFloat(floatToFixed(float(s), precisionBits=bits), precisionBits=bits)
+		True
+	"""
+	value = float(string)
+	scale = 1 << precisionBits
+	return otRound(value * scale) / scale
+
+
+def floatToFixedToStr(value, precisionBits):
+	"""Convert float to string with fixed-point rounding.
+
+	This uses the shortest decimal representation (ie. the least
+	number of fractional decimal digits) to represent the equivalent
+	fixed-point number with ``precisionBits`` fractional binary digits.
+	It uses nearestMultipleShortestRepr under the hood.
+
+	>>> floatToFixedToStr(-0.61883544921875, precisionBits=14)
+	'-0.61884'
+
+	Args:
+		value (float): The float value to convert.
+		precisionBits (int): Number of precision bits, *up to a maximum of 16*.
+
+	Returns:
+		str: A string representation of the value.
+
+	"""
+	scale = 1 << precisionBits
+	return nearestMultipleShortestRepr(value, factor=1.0/scale)
+
 
 def ensureVersionIsLong(value):
-	"""Ensure a table version is an unsigned long (unsigned short major,
-	unsigned short minor) instead of a float."""
+	"""Ensure a table version is an unsigned long.
+
+	OpenType table version numbers are expressed as a single unsigned long
+	comprising of an unsigned short major version and unsigned short minor
+	version. This function detects if the value to be used as a version number
+	looks too small (i.e. is less than ``0x10000``), and converts it to
+	fixed-point using :func:`floatToFixed` if so.
+
+	Args:
+		value (Number): a candidate table version number.
+
+	Returns:
+		int: A table version number, possibly corrected to fixed-point.
+	"""
 	if value < 0x10000:
 		newValue = floatToFixed(value, 16)
 		log.warning(
@@ -75,7 +238,14 @@
 
 
 def versionToFixed(value):
-	"""Converts a table version to a fixed"""
+	"""Ensure a table version number is fixed-point.
+
+	Args:
+		value (str): a candidate table version number.
+
+	Returns:
+		int: A table version number, possibly corrected to fixed-point.
+	"""
 	value = int(value, 0) if value.startswith("0") else float(value)
 	value = ensureVersionIsLong(value)
 	return value
diff --git a/Lib/fontTools/misc/intTools.py b/Lib/fontTools/misc/intTools.py
new file mode 100644
index 0000000..6ba03e1
--- /dev/null
+++ b/Lib/fontTools/misc/intTools.py
@@ -0,0 +1,25 @@
+__all__ = ["popCount"]
+
+
+try:
+    bit_count = int.bit_count
+except AttributeError:
+
+    def bit_count(v):
+        return bin(v).count("1")
+
+
+"""Return number of 1 bits (population count) of the absolute value of an integer.
+
+See https://docs.python.org/3.10/library/stdtypes.html#int.bit_count
+"""
+popCount = bit_count
+
+
+def bit_indices(v):
+    """Return list of indices where bits are set, 0 being the index of the least significant bit.
+
+    >>> bit_indices(0b101)
+    [0, 2]
+    """
+    return [i for i, b in enumerate(bin(v)[::-1]) if b == "1"]
diff --git a/Lib/fontTools/misc/loggingTools.py b/Lib/fontTools/misc/loggingTools.py
index 4ed788e..d1baa83 100644
--- a/Lib/fontTools/misc/loggingTools.py
+++ b/Lib/fontTools/misc/loggingTools.py
@@ -1,20 +1,10 @@
-""" 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
+from collections.abc import Mapping, Callable
 import warnings
-
-try:
-	from logging import PercentStyle
-except ImportError:
-	PercentStyle = None
+from logging import PercentStyle
 
 
 # default logging level used by Timer class
@@ -30,10 +20,18 @@
 
 
 class LevelFormatter(logging.Formatter):
-	""" Formatter class which optionally takes a dict of logging levels to
+	"""Log formatter with level-specific formatting.
+
+	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.
+
+
+	Attributes:
+		fmt: A dictionary mapping logging levels to format strings.
+			The ``*`` key identifies the default format string.
+		datefmt: As per py:class:`logging.Formatter`
+		style: As per py:class:`logging.Formatter`
 
 	>>> import sys
 	>>> handler = logging.StreamHandler(sys.stdout)
@@ -61,10 +59,10 @@
 				"only '%' percent style is supported in both python 2 and 3")
 		if fmt is None:
 			fmt = DEFAULT_FORMATS
-		if isinstance(fmt, basestring):
+		if isinstance(fmt, str):
 			default_format = fmt
 			custom_formats = {}
-		elif isinstance(fmt, collections.Mapping):
+		elif isinstance(fmt, Mapping):
 			custom_formats = dict(fmt)
 			default_format = custom_formats.pop("*", None)
 		else:
@@ -88,46 +86,48 @@
 
 
 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.
+	"""A more sophisticated logging system configuation manager.
 
-	The default behaviour is to create a StreamHandler which writes to
-	sys.stderr, set a formatter using the DEFAULT_FORMATS strings, and add
+	This is more or less the same as :py:func:`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.
+	Args:
+
+		logger: Specifies the logger name or a Logger instance to be
+			configured. (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. If the ``handlers`` do not already have filters assigned,
+			these filters will be added to them.
+		propagate: All loggers have a ``propagate`` attribute which determines
+			whether to continue searching for handlers up the logging hierarchy.
+			If not provided, the "propagate" attribute will be set to ``False``.
 	"""
 	# using kwargs to enforce keyword-only arguments in py2.
 	handlers = kwargs.pop("handlers", None)
@@ -150,7 +150,7 @@
 		handlers = [h]
 	# By default, the top-level library logger is configured.
 	logger = kwargs.pop("logger", "fontTools")
-	if not logger or isinstance(logger, basestring):
+	if not logger or isinstance(logger, str):
 		# empty "" or None means the 'root' logger
 		logger = logging.getLogger(logger)
 	# before (re)configuring, reset named logger and its children (if exist)
@@ -247,8 +247,8 @@
 	upon exiting the with-statement.
 
 	>>> import logging
-	>>> log = logging.getLogger("fontTools")
-	>>> configLogger(level="DEBUG", format="%(message)s", stream=sys.stdout)
+	>>> log = logging.getLogger("my-fancy-timer-logger")
+	>>> configLogger(logger=log, level="DEBUG", format="%(message)s", stream=sys.stdout)
 	>>> with Timer(log, 'do something'):
 	...     time.sleep(0.01)
 	Took ... to do something
@@ -360,7 +360,7 @@
 		Timer instance, referencing the same logger.
 		A 'level' keyword can also be passed to override self.level.
 		"""
-		if isinstance(func_or_msg, collections.Callable):
+		if isinstance(func_or_msg, Callable):
 			func = func_or_msg
 			# use the function name when no explicit 'msg' is provided
 			if not self.msg:
@@ -387,9 +387,11 @@
 
 
 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.
+	"""Provides a hierarchical filter for log entries based on channel names.
+
+	Filters out records emitted from a list of enabled channel names,
+	including their children. It works the same as the ``logging.Filter``
+	class, but allows the user to specify multiple channel names.
 
 	>>> import sys
 	>>> handler = logging.StreamHandler(sys.stdout)
@@ -414,13 +416,13 @@
 	def __init__(self, *names):
 		self.names = names
 		self.num = len(names)
-		self.lenghts = {n: len(n) for n in names}
+		self.lengths = {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]
+			nlen = self.lengths[name]
 			if name == record.name:
 				return True
 			elif (record.name.find(name, 0, nlen) == 0
@@ -431,9 +433,9 @@
 
 class CapturingLogHandler(logging.Handler):
 	def __init__(self, logger, level):
+		super(CapturingLogHandler, self).__init__(level=level)
 		self.records = []
-		self.level = logging._checkLevel(level)
-		if isinstance(logger, basestring):
+		if isinstance(logger, str):
 			self.logger = logging.getLogger(logger)
 		else:
 			self.logger = logger
@@ -441,43 +443,45 @@
 	def __enter__(self):
 		self.original_disabled = self.logger.disabled
 		self.original_level = self.logger.level
+		self.original_propagate = self.logger.propagate
 
 		self.logger.addHandler(self)
-		self.logger.level = self.level
+		self.logger.setLevel(self.level)
 		self.logger.disabled = False
+		self.logger.propagate = 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
+		self.logger.setLevel(self.original_level)
+		self.logger.disabled = self.original_disabled
+		self.logger.propagate = self.original_propagate
+
 		return self
 
-	def handle(self, record):
+	def emit(self, record):
 		self.records.append(record)
 
-	def emit(self, record):
-		pass
-
-	def createLock(self):
-		self.lock = None
-
-	def assertRegex(self, regexp):
+	def assertRegex(self, regexp, msg=None):
 		import re
 		pattern = re.compile(regexp)
 		for r in self.records:
-			if pattern.search(r.msg):
+			if pattern.search(r.getMessage()):
 				return True
-		assert 0, "Pattern '%s' not found in logger records" % regexp
+		if msg is None:
+			msg = "Pattern '%s' not found in logger records" % regexp
+		assert 0, msg
 
 
 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
+
+	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>.
+	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):
@@ -500,8 +504,12 @@
 
 	@property
 	def log(self):
-		name = ".".join([self.__class__.__module__, self.__class__.__name__])
-		return logging.getLogger(name)
+		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):
diff --git a/Lib/fontTools/misc/macCreatorType.py b/Lib/fontTools/misc/macCreatorType.py
index 2b33e89..fb23720 100644
--- a/Lib/fontTools/misc/macCreatorType.py
+++ b/Lib/fontTools/misc/macCreatorType.py
@@ -1,14 +1,8 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-import sys
+from fontTools.misc.py23 import Tag, bytesjoin, strjoin
 try:
 	import xattr
 except ImportError:
 	xattr = None
-try:
-	import MacOS
-except ImportError:
-	MacOS = None
 
 
 def _reverseString(s):
@@ -18,6 +12,16 @@
 
 
 def getMacCreatorAndType(path):
+	"""Returns file creator and file type codes for a path.
+
+	Args:
+		path (str): A file path.
+
+	Returns:
+		A tuple of two :py:class:`fontTools.py23.Tag` objects, the first
+		representing the file creator and the second representing the
+		file type.
+	"""
 	if xattr is not None:
 		try:
 			finderInfo = xattr.getxattr(path, 'com.apple.FinderInfo')
@@ -27,25 +31,24 @@
 			fileType = Tag(finderInfo[:4])
 			fileCreator = Tag(finderInfo[4:8])
 			return fileCreator, fileType
-	if MacOS is not None:
-		fileCreator, fileType = MacOS.GetCreatorAndType(path)
-		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
-	else:
-		return None, None
+	return None, None
 
 
 def setMacCreatorAndType(path, fileCreator, fileType):
+	"""Set file creator and file type codes for a path.
+
+	Note that if the ``xattr`` module is not installed, no action is
+	taken but no error is raised.
+
+	Args:
+		path (str): A file path.
+		fileCreator: A four-character file creator tag.
+		fileType: A four-character file type tag.
+
+	"""
 	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:
-		MacOS.SetCreatorAndType(path, fileCreator, fileType)
diff --git a/Lib/fontTools/misc/macRes.py b/Lib/fontTools/misc/macRes.py
index 20bfa71..2c15b34 100644
--- a/Lib/fontTools/misc/macRes.py
+++ b/Lib/fontTools/misc/macRes.py
@@ -1,13 +1,9 @@
-""" Tools for reading Mac resource forks. """
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin, tostr
+from io import BytesIO
 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
+from collections.abc import MutableMapping
 
 
 class ResourceError(Exception):
@@ -15,8 +11,25 @@
 
 
 class ResourceReader(MutableMapping):
+	"""Reader for Mac OS resource forks.
 
+	Parses a resource fork and returns resources according to their type.
+	If run on OS X, this will open the resource fork in the filesystem.
+	Otherwise, it will open the file itself and attempt to read it as
+	though it were a resource fork.
+
+	The returned object can be indexed by type and iterated over,
+	returning in each case a list of py:class:`Resource` objects
+	representing all the resources of a certain type.
+
+	"""
 	def __init__(self, fileOrPath):
+		"""Open a file
+
+		Args:
+			fileOrPath: Either an object supporting a ``read`` method, an
+				``os.PathLike`` object, or a string.
+		"""
 		self._resources = OrderedDict()
 		if hasattr(fileOrPath, 'read'):
 			self.file = fileOrPath
@@ -33,6 +46,8 @@
 
 	@staticmethod
 	def openResourceFork(path):
+		if hasattr(path, "__fspath__"):  # support os.PathLike objects
+			path = path.__fspath__()
 		with open(path + '/..namedfork/rsrc', 'rb') as resfork:
 			data = resfork.read()
 		infile = BytesIO(data)
@@ -123,6 +138,7 @@
 
 	@property
 	def types(self):
+		"""A list of the types of resources in the resource fork."""
 		return list(self._resources.keys())
 
 	def countResources(self, resType):
@@ -133,6 +149,7 @@
 			return 0
 
 	def getIndices(self, resType):
+		"""Returns a list of indices of resources of a given type."""
 		numRes = self.countResources(resType)
 		if numRes:
 			return list(range(1, numRes+1))
@@ -169,6 +186,15 @@
 
 
 class Resource(object):
+	"""Represents a resource stored within a resource fork.
+
+	Attributes:
+		type: resource type.
+		data: resource data.
+		id: ID.
+		name: resource name.
+		attr: attributes.
+	"""
 
 	def __init__(self, resType=None, resData=None, resID=None, resName=None,
 			     resAttr=None):
diff --git a/Lib/fontTools/misc/plistlib/__init__.py b/Lib/fontTools/misc/plistlib/__init__.py
new file mode 100644
index 0000000..84dc418
--- /dev/null
+++ b/Lib/fontTools/misc/plistlib/__init__.py
@@ -0,0 +1,677 @@
+import collections.abc
+import re
+from typing import (
+    Any,
+    Callable,
+    Dict,
+    List,
+    Mapping,
+    MutableMapping,
+    Optional,
+    Sequence,
+    Type,
+    Union,
+    IO,
+)
+import warnings
+from io import BytesIO
+from datetime import datetime
+from base64 import b64encode, b64decode
+from numbers import Integral
+from types import SimpleNamespace
+from functools import singledispatch
+
+from fontTools.misc import etree
+
+from fontTools.misc.py23 import tostr
+
+
+# By default, we
+#  - deserialize <data> elements as bytes and
+#  - serialize bytes as <data> elements.
+# Before, on Python 2, we
+#  - deserialized <data> elements as plistlib.Data objects, in order to
+#    distinguish them from the built-in str type (which is bytes on python2)
+#  - serialized bytes as <string> elements (they must have only contained
+#    ASCII characters in this case)
+# You can pass use_builtin_types=[True|False] to the load/dump etc. functions
+# to enforce a specific treatment.
+# NOTE that unicode type always maps to <string> element, and plistlib.Data
+# always maps to <data> element, regardless of use_builtin_types.
+USE_BUILTIN_TYPES = True
+
+XML_DECLARATION = b"""<?xml version='1.0' encoding='UTF-8'?>"""
+
+PLIST_DOCTYPE = (
+    b'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" '
+    b'"http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
+)
+
+
+# Date should conform to a subset of ISO 8601:
+# YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'
+_date_parser = re.compile(
+    r"(?P<year>\d\d\d\d)"
+    r"(?:-(?P<month>\d\d)"
+    r"(?:-(?P<day>\d\d)"
+    r"(?:T(?P<hour>\d\d)"
+    r"(?::(?P<minute>\d\d)"
+    r"(?::(?P<second>\d\d))"
+    r"?)?)?)?)?Z",
+    re.ASCII,
+)
+
+
+def _date_from_string(s: str) -> datetime:
+    order = ("year", "month", "day", "hour", "minute", "second")
+    m = _date_parser.match(s)
+    if m is None:
+        raise ValueError(f"Expected ISO 8601 date string, but got '{s:r}'.")
+    gd = m.groupdict()
+    lst = []
+    for key in order:
+        val = gd[key]
+        if val is None:
+            break
+        lst.append(int(val))
+    # NOTE: mypy doesn't know that lst is 6 elements long.
+    return datetime(*lst)  # type:ignore
+
+
+def _date_to_string(d: datetime) -> str:
+    return "%04d-%02d-%02dT%02d:%02d:%02dZ" % (
+        d.year,
+        d.month,
+        d.day,
+        d.hour,
+        d.minute,
+        d.second,
+    )
+
+
+class Data:
+    """Represents binary data when ``use_builtin_types=False.``
+
+    This class wraps binary data loaded from a plist file when the
+    ``use_builtin_types`` argument to the loading function (:py:func:`fromtree`,
+    :py:func:`load`, :py:func:`loads`) is false.
+
+    The actual binary data is retrieved using the ``data`` attribute.
+    """
+
+    def __init__(self, data: bytes) -> None:
+        if not isinstance(data, bytes):
+            raise TypeError("Expected bytes, found %s" % type(data).__name__)
+        self.data = data
+
+    @classmethod
+    def fromBase64(cls, data: Union[bytes, str]) -> "Data":
+        return cls(b64decode(data))
+
+    def asBase64(self, maxlinelength: int = 76, indent_level: int = 1) -> bytes:
+        return _encode_base64(
+            self.data, maxlinelength=maxlinelength, indent_level=indent_level
+        )
+
+    def __eq__(self, other: Any) -> bool:
+        if isinstance(other, self.__class__):
+            return self.data == other.data
+        elif isinstance(other, bytes):
+            return self.data == other
+        else:
+            return NotImplemented
+
+    def __repr__(self) -> str:
+        return "%s(%s)" % (self.__class__.__name__, repr(self.data))
+
+
+def _encode_base64(
+    data: bytes, maxlinelength: Optional[int] = 76, indent_level: int = 1
+) -> bytes:
+    data = b64encode(data)
+    if data and maxlinelength:
+        # split into multiple lines right-justified to 'maxlinelength' chars
+        indent = b"\n" + b"  " * indent_level
+        max_length = max(16, maxlinelength - len(indent))
+        chunks = []
+        for i in range(0, len(data), max_length):
+            chunks.append(indent)
+            chunks.append(data[i : i + max_length])
+        chunks.append(indent)
+        data = b"".join(chunks)
+    return data
+
+
+# Mypy does not support recursive type aliases as of 0.782, Pylance does.
+# https://github.com/python/mypy/issues/731
+# https://devblogs.microsoft.com/python/pylance-introduces-five-new-features-that-enable-type-magic-for-python-developers/#1-support-for-recursive-type-aliases
+PlistEncodable = Union[
+    bool,
+    bytes,
+    Data,
+    datetime,
+    float,
+    int,
+    Mapping[str, Any],
+    Sequence[Any],
+    str,
+]
+
+
+class PlistTarget:
+    """Event handler using the ElementTree Target API that can be
+    passed to a XMLParser to produce property list objects from XML.
+    It is based on the CPython plistlib module's _PlistParser class,
+    but does not use the expat parser.
+
+    >>> from fontTools.misc import etree
+    >>> parser = etree.XMLParser(target=PlistTarget())
+    >>> result = etree.XML(
+    ...     "<dict>"
+    ...     "    <key>something</key>"
+    ...     "    <string>blah</string>"
+    ...     "</dict>",
+    ...     parser=parser)
+    >>> result == {"something": "blah"}
+    True
+
+    Links:
+    https://github.com/python/cpython/blob/master/Lib/plistlib.py
+    http://lxml.de/parsing.html#the-target-parser-interface
+    """
+
+    def __init__(
+        self,
+        use_builtin_types: Optional[bool] = None,
+        dict_type: Type[MutableMapping[str, Any]] = dict,
+    ) -> None:
+        self.stack: List[PlistEncodable] = []
+        self.current_key: Optional[str] = None
+        self.root: Optional[PlistEncodable] = None
+        if use_builtin_types is None:
+            self._use_builtin_types = USE_BUILTIN_TYPES
+        else:
+            if use_builtin_types is False:
+                warnings.warn(
+                    "Setting use_builtin_types to False is deprecated and will be "
+                    "removed soon.",
+                    DeprecationWarning,
+                )
+            self._use_builtin_types = use_builtin_types
+        self._dict_type = dict_type
+
+    def start(self, tag: str, attrib: Mapping[str, str]) -> None:
+        self._data: List[str] = []
+        handler = _TARGET_START_HANDLERS.get(tag)
+        if handler is not None:
+            handler(self)
+
+    def end(self, tag: str) -> None:
+        handler = _TARGET_END_HANDLERS.get(tag)
+        if handler is not None:
+            handler(self)
+
+    def data(self, data: str) -> None:
+        self._data.append(data)
+
+    def close(self) -> PlistEncodable:
+        if self.root is None:
+            raise ValueError("No root set.")
+        return self.root
+
+    # helpers
+
+    def add_object(self, value: PlistEncodable) -> None:
+        if self.current_key is not None:
+            stack_top = self.stack[-1]
+            if not isinstance(stack_top, collections.abc.MutableMapping):
+                raise ValueError("unexpected element: %r" % stack_top)
+            stack_top[self.current_key] = value
+            self.current_key = None
+        elif not self.stack:
+            # this is the root object
+            self.root = value
+        else:
+            stack_top = self.stack[-1]
+            if not isinstance(stack_top, list):
+                raise ValueError("unexpected element: %r" % stack_top)
+            stack_top.append(value)
+
+    def get_data(self) -> str:
+        data = "".join(self._data)
+        self._data = []
+        return data
+
+
+# event handlers
+
+
+def start_dict(self: PlistTarget) -> None:
+    d = self._dict_type()
+    self.add_object(d)
+    self.stack.append(d)
+
+
+def end_dict(self: PlistTarget) -> None:
+    if self.current_key:
+        raise ValueError("missing value for key '%s'" % self.current_key)
+    self.stack.pop()
+
+
+def end_key(self: PlistTarget) -> None:
+    if self.current_key or not isinstance(self.stack[-1], collections.abc.Mapping):
+        raise ValueError("unexpected key")
+    self.current_key = self.get_data()
+
+
+def start_array(self: PlistTarget) -> None:
+    a: List[PlistEncodable] = []
+    self.add_object(a)
+    self.stack.append(a)
+
+
+def end_array(self: PlistTarget) -> None:
+    self.stack.pop()
+
+
+def end_true(self: PlistTarget) -> None:
+    self.add_object(True)
+
+
+def end_false(self: PlistTarget) -> None:
+    self.add_object(False)
+
+
+def end_integer(self: PlistTarget) -> None:
+    self.add_object(int(self.get_data()))
+
+
+def end_real(self: PlistTarget) -> None:
+    self.add_object(float(self.get_data()))
+
+
+def end_string(self: PlistTarget) -> None:
+    self.add_object(self.get_data())
+
+
+def end_data(self: PlistTarget) -> None:
+    if self._use_builtin_types:
+        self.add_object(b64decode(self.get_data()))
+    else:
+        self.add_object(Data.fromBase64(self.get_data()))
+
+
+def end_date(self: PlistTarget) -> None:
+    self.add_object(_date_from_string(self.get_data()))
+
+
+_TARGET_START_HANDLERS: Dict[str, Callable[[PlistTarget], None]] = {
+    "dict": start_dict,
+    "array": start_array,
+}
+
+_TARGET_END_HANDLERS: Dict[str, Callable[[PlistTarget], None]] = {
+    "dict": end_dict,
+    "array": end_array,
+    "key": end_key,
+    "true": end_true,
+    "false": end_false,
+    "integer": end_integer,
+    "real": end_real,
+    "string": end_string,
+    "data": end_data,
+    "date": end_date,
+}
+
+
+# functions to build element tree from plist data
+
+
+def _string_element(value: str, ctx: SimpleNamespace) -> etree.Element:
+    el = etree.Element("string")
+    el.text = value
+    return el
+
+
+def _bool_element(value: bool, ctx: SimpleNamespace) -> etree.Element:
+    if value:
+        return etree.Element("true")
+    return etree.Element("false")
+
+
+def _integer_element(value: int, ctx: SimpleNamespace) -> etree.Element:
+    if -1 << 63 <= value < 1 << 64:
+        el = etree.Element("integer")
+        el.text = "%d" % value
+        return el
+    raise OverflowError(value)
+
+
+def _real_element(value: float, ctx: SimpleNamespace) -> etree.Element:
+    el = etree.Element("real")
+    el.text = repr(value)
+    return el
+
+
+def _dict_element(d: Mapping[str, PlistEncodable], ctx: SimpleNamespace) -> etree.Element:
+    el = etree.Element("dict")
+    items = d.items()
+    if ctx.sort_keys:
+        items = sorted(items)  # type: ignore
+    ctx.indent_level += 1
+    for key, value in items:
+        if not isinstance(key, str):
+            if ctx.skipkeys:
+                continue
+            raise TypeError("keys must be strings")
+        k = etree.SubElement(el, "key")
+        k.text = tostr(key, "utf-8")
+        el.append(_make_element(value, ctx))
+    ctx.indent_level -= 1
+    return el
+
+
+def _array_element(array: Sequence[PlistEncodable], ctx: SimpleNamespace) -> etree.Element:
+    el = etree.Element("array")
+    if len(array) == 0:
+        return el
+    ctx.indent_level += 1
+    for value in array:
+        el.append(_make_element(value, ctx))
+    ctx.indent_level -= 1
+    return el
+
+
+def _date_element(date: datetime, ctx: SimpleNamespace) -> etree.Element:
+    el = etree.Element("date")
+    el.text = _date_to_string(date)
+    return el
+
+
+def _data_element(data: bytes, ctx: SimpleNamespace) -> etree.Element:
+    el = etree.Element("data")
+    # NOTE: mypy is confused about whether el.text should be str or bytes.
+    el.text = _encode_base64(  # type: ignore
+        data,
+        maxlinelength=(76 if ctx.pretty_print else None),
+        indent_level=ctx.indent_level,
+    )
+    return el
+
+
+def _string_or_data_element(raw_bytes: bytes, ctx: SimpleNamespace) -> etree.Element:
+    if ctx.use_builtin_types:
+        return _data_element(raw_bytes, ctx)
+    else:
+        try:
+            string = raw_bytes.decode(encoding="ascii", errors="strict")
+        except UnicodeDecodeError:
+            raise ValueError(
+                "invalid non-ASCII bytes; use unicode string instead: %r" % raw_bytes
+            )
+        return _string_element(string, ctx)
+
+
+# The following is probably not entirely correct. The signature should take `Any`
+# and return `NoReturn`. At the time of this writing, neither mypy nor Pyright
+# can deal with singledispatch properly and will apply the signature of the base
+# function to all others. Being slightly dishonest makes it type-check and return
+# usable typing information for the optimistic case.
+@singledispatch
+def _make_element(value: PlistEncodable, ctx: SimpleNamespace) -> etree.Element:
+    raise TypeError("unsupported type: %s" % type(value))
+
+
+_make_element.register(str)(_string_element)
+_make_element.register(bool)(_bool_element)
+_make_element.register(Integral)(_integer_element)
+_make_element.register(float)(_real_element)
+_make_element.register(collections.abc.Mapping)(_dict_element)
+_make_element.register(list)(_array_element)
+_make_element.register(tuple)(_array_element)
+_make_element.register(datetime)(_date_element)
+_make_element.register(bytes)(_string_or_data_element)
+_make_element.register(bytearray)(_data_element)
+_make_element.register(Data)(lambda v, ctx: _data_element(v.data, ctx))
+
+
+# Public functions to create element tree from plist-compatible python
+# data structures and viceversa, for use when (de)serializing GLIF xml.
+
+
+def totree(
+    value: PlistEncodable,
+    sort_keys: bool = True,
+    skipkeys: bool = False,
+    use_builtin_types: Optional[bool] = None,
+    pretty_print: bool = True,
+    indent_level: int = 1,
+) -> etree.Element:
+    """Convert a value derived from a plist into an XML tree.
+
+    Args:
+        value: Any kind of value to be serialized to XML.
+        sort_keys: Whether keys of dictionaries should be sorted.
+        skipkeys (bool): Whether to silently skip non-string dictionary
+            keys.
+        use_builtin_types (bool): If true, byte strings will be
+            encoded in Base-64 and wrapped in a ``data`` tag; if
+            false, they will be either stored as ASCII strings or an
+            exception raised if they cannot be decoded as such. Defaults
+            to ``True`` if not present. Deprecated.
+        pretty_print (bool): Whether to indent the output.
+        indent_level (int): Level of indentation when serializing.
+
+    Returns: an ``etree`` ``Element`` object.
+
+    Raises:
+        ``TypeError``
+            if non-string dictionary keys are serialized
+            and ``skipkeys`` is false.
+        ``ValueError``
+            if non-ASCII binary data is present
+            and `use_builtin_types` is false.
+    """
+    if use_builtin_types is None:
+        use_builtin_types = USE_BUILTIN_TYPES
+    else:
+        use_builtin_types = use_builtin_types
+    context = SimpleNamespace(
+        sort_keys=sort_keys,
+        skipkeys=skipkeys,
+        use_builtin_types=use_builtin_types,
+        pretty_print=pretty_print,
+        indent_level=indent_level,
+    )
+    return _make_element(value, context)
+
+
+def fromtree(
+    tree: etree.Element,
+    use_builtin_types: Optional[bool] = None,
+    dict_type: Type[MutableMapping[str, Any]] = dict,
+) -> Any:
+    """Convert an XML tree to a plist structure.
+
+    Args:
+        tree: An ``etree`` ``Element``.
+        use_builtin_types: If True, binary data is deserialized to
+            bytes strings. If False, it is wrapped in :py:class:`Data`
+            objects. Defaults to True if not provided. Deprecated.
+        dict_type: What type to use for dictionaries.
+
+    Returns: An object (usually a dictionary).
+    """
+    target = PlistTarget(use_builtin_types=use_builtin_types, dict_type=dict_type)
+    for action, element in etree.iterwalk(tree, events=("start", "end")):
+        if action == "start":
+            target.start(element.tag, element.attrib)
+        elif action == "end":
+            # if there are no children, parse the leaf's data
+            if not len(element):
+                # always pass str, not None
+                target.data(element.text or "")
+            target.end(element.tag)
+    return target.close()
+
+
+# python3 plistlib API
+
+
+def load(
+    fp: IO[bytes],
+    use_builtin_types: Optional[bool] = None,
+    dict_type: Type[MutableMapping[str, Any]] = dict,
+) -> Any:
+    """Load a plist file into an object.
+
+    Args:
+        fp: An opened file.
+        use_builtin_types: If True, binary data is deserialized to
+            bytes strings. If False, it is wrapped in :py:class:`Data`
+            objects. Defaults to True if not provided. Deprecated.
+        dict_type: What type to use for dictionaries.
+
+    Returns:
+        An object (usually a dictionary) representing the top level of
+        the plist file.
+    """
+
+    if not hasattr(fp, "read"):
+        raise AttributeError("'%s' object has no attribute 'read'" % type(fp).__name__)
+    target = PlistTarget(use_builtin_types=use_builtin_types, dict_type=dict_type)
+    parser = etree.XMLParser(target=target)
+    result = etree.parse(fp, parser=parser)
+    # lxml returns the target object directly, while ElementTree wraps
+    # it as the root of an ElementTree object
+    try:
+        return result.getroot()
+    except AttributeError:
+        return result
+
+
+def loads(
+    value: bytes,
+    use_builtin_types: Optional[bool] = None,
+    dict_type: Type[MutableMapping[str, Any]] = dict,
+) -> Any:
+    """Load a plist file from a string into an object.
+
+    Args:
+        value: A bytes string containing a plist.
+        use_builtin_types: If True, binary data is deserialized to
+            bytes strings. If False, it is wrapped in :py:class:`Data`
+            objects. Defaults to True if not provided. Deprecated.
+        dict_type: What type to use for dictionaries.
+
+    Returns:
+        An object (usually a dictionary) representing the top level of
+        the plist file.
+    """
+
+    fp = BytesIO(value)
+    return load(fp, use_builtin_types=use_builtin_types, dict_type=dict_type)
+
+
+def dump(
+    value: PlistEncodable,
+    fp: IO[bytes],
+    sort_keys: bool = True,
+    skipkeys: bool = False,
+    use_builtin_types: Optional[bool] = None,
+    pretty_print: bool = True,
+) -> None:
+    """Write a Python object to a plist file.
+
+    Args:
+        value: An object to write.
+        fp: A file opened for writing.
+        sort_keys (bool): Whether keys of dictionaries should be sorted.
+        skipkeys (bool): Whether to silently skip non-string dictionary
+            keys.
+        use_builtin_types (bool): If true, byte strings will be
+            encoded in Base-64 and wrapped in a ``data`` tag; if
+            false, they will be either stored as ASCII strings or an
+            exception raised if they cannot be represented. Defaults
+        pretty_print (bool): Whether to indent the output.
+        indent_level (int): Level of indentation when serializing.
+
+    Raises:
+        ``TypeError``
+            if non-string dictionary keys are serialized
+            and ``skipkeys`` is false.
+        ``ValueError``
+            if non-representable binary data is present
+            and `use_builtin_types` is false.
+    """
+
+    if not hasattr(fp, "write"):
+        raise AttributeError("'%s' object has no attribute 'write'" % type(fp).__name__)
+    root = etree.Element("plist", version="1.0")
+    el = totree(
+        value,
+        sort_keys=sort_keys,
+        skipkeys=skipkeys,
+        use_builtin_types=use_builtin_types,
+        pretty_print=pretty_print,
+    )
+    root.append(el)
+    tree = etree.ElementTree(root)
+    # we write the doctype ourselves instead of using the 'doctype' argument
+    # of 'write' method, becuse lxml will force adding a '\n' even when
+    # pretty_print is False.
+    if pretty_print:
+        header = b"\n".join((XML_DECLARATION, PLIST_DOCTYPE, b""))
+    else:
+        header = XML_DECLARATION + PLIST_DOCTYPE
+    fp.write(header)
+    tree.write(  # type: ignore
+        fp,
+        encoding="utf-8",
+        pretty_print=pretty_print,
+        xml_declaration=False,
+    )
+
+
+def dumps(
+    value: PlistEncodable,
+    sort_keys: bool = True,
+    skipkeys: bool = False,
+    use_builtin_types: Optional[bool] = None,
+    pretty_print: bool = True,
+) -> bytes:
+    """Write a Python object to a string in plist format.
+
+    Args:
+        value: An object to write.
+        sort_keys (bool): Whether keys of dictionaries should be sorted.
+        skipkeys (bool): Whether to silently skip non-string dictionary
+            keys.
+        use_builtin_types (bool): If true, byte strings will be
+            encoded in Base-64 and wrapped in a ``data`` tag; if
+            false, they will be either stored as strings or an
+            exception raised if they cannot be represented. Defaults
+        pretty_print (bool): Whether to indent the output.
+        indent_level (int): Level of indentation when serializing.
+
+    Returns:
+        string: A plist representation of the Python object.
+
+    Raises:
+        ``TypeError``
+            if non-string dictionary keys are serialized
+            and ``skipkeys`` is false.
+        ``ValueError``
+            if non-representable binary data is present
+            and `use_builtin_types` is false.
+    """
+    fp = BytesIO()
+    dump(
+        value,
+        fp,
+        sort_keys=sort_keys,
+        skipkeys=skipkeys,
+        use_builtin_types=use_builtin_types,
+        pretty_print=pretty_print,
+    )
+    return fp.getvalue()
diff --git a/Lib/fontTools/misc/plistlib/py.typed b/Lib/fontTools/misc/plistlib/py.typed
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/fontTools/misc/plistlib/py.typed
diff --git a/Lib/fontTools/misc/psCharStrings.py b/Lib/fontTools/misc/psCharStrings.py
index 0bb4a79..cb67505 100644
--- a/Lib/fontTools/misc/psCharStrings.py
+++ b/Lib/fontTools/misc/psCharStrings.py
@@ -2,9 +2,10 @@
 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
+from fontTools.misc.py23 import bytechr, byteord, bytesjoin, strjoin
+from fontTools.misc.fixedTools import (
+	fixedToFloat, floatToFixed, floatToFixedToStr, strToFixedToFloat,
+)
 from fontTools.pens.boundsPen import BoundsPen
 import struct
 import logging
@@ -199,8 +200,7 @@
 				# distinguish anymore between small ints that were supposed to
 				# be small fixed numbers and small ints that were just small
 				# ints. Hence the warning.
-				import sys
-				sys.stderr.write("Warning: 4-byte T2 number got passed to the "
+				log.warning("4-byte T2 number got passed to the "
 					"IntType handler. This should happen only when reading in "
 					"old XML files.\n")
 				code = bytechr(255) + pack(">l", value)
@@ -216,12 +216,23 @@
 encodeIntT2 = getIntEncoder("t2")
 
 def encodeFixed(f, pack=struct.pack):
-	# For T2 only
-	return b"\xff" + pack(">l", round(f * 65536))
+	"""For T2 only"""
+	value = floatToFixed(f, precisionBits=16)
+	if value & 0xFFFF == 0:  # check if the fractional part is zero
+		return encodeIntT2(value >> 16)  # encode only the integer part
+	else:
+		return b"\xff" + pack(">l", value)  # encode the entire fixed point value
+
+
+realZeroBytes = bytechr(30) + bytechr(0xf)
 
 def encodeFloat(f):
 	# For CFF only, used in cffLib
-	s = str(f).upper()
+	if f == 0.0: # 0.0 == +0.0 == -0.0
+		return realZeroBytes
+	# Note: 14 decimal digits seems to be the limitation for CFF real numbers
+	# in macOS. However, we use 8 here to match the implementation of AFDKO.
+	s = "%.8G" % f
 	if s[:2] == "0.":
 		s = s[1:]
 	elif s[:3] == "-0.":
@@ -230,9 +241,13 @@
 	while s:
 		c = s[0]
 		s = s[1:]
-		if c == "E" and s[:1] == "-":
-			s = s[1:]
-			c = "E-"
+		if c == "E":
+			c2 = s[:1]
+			if c2 == "-":
+				s = s[1:]
+				c = "E-"
+			elif c2 == "+":
+				s = s[1:]
 		nibbles.append(realNibblesDict[c])
 	nibbles.append(0xf)
 	if len(nibbles) % 2:
@@ -263,22 +278,6 @@
 		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()
@@ -307,7 +306,6 @@
 			else:
 				pushToStack(token)
 		if needsDecompilation:
-			self.check_program(program)
 			charString.setProgram(program)
 		del self.callingStack[-1]
 
@@ -419,8 +417,7 @@
 			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.
+		del self.operandStack[-(numOps-numBlends):] # Leave the default operands on the stack.
 
 	def op_vsindex(self, index):
 		vi = self.pop()
@@ -459,8 +456,8 @@
 
 class T2WidthExtractor(SimpleT2Decompiler):
 
-	def __init__(self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX):
-		SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs)
+	def __init__(self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private=None):
+		SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs, private)
 		self.nominalWidthX = nominalWidthX
 		self.defaultWidthX = defaultWidthX
 
@@ -473,6 +470,8 @@
 		args = self.popall()
 		if not self.gotWidth:
 			if evenOdd ^ (len(args) % 2):
+				# For CFF2 charstrings, this should never happen
+				assert self.defaultWidthX is not None, "CFF2 CharStrings must not have an initial width value"
 				self.width = self.nominalWidthX + args[0]
 				args = args[1:]
 			else:
@@ -499,9 +498,9 @@
 
 class T2OutlineExtractor(T2WidthExtractor):
 
-	def __init__(self, pen, localSubrs, globalSubrs, nominalWidthX, defaultWidthX):
+	def __init__(self, pen, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private=None):
 		T2WidthExtractor.__init__(
-			self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX)
+			self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private)
 		self.pen = pen
 
 	def reset(self):
@@ -701,12 +700,6 @@
 		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
@@ -943,8 +936,7 @@
 	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 = []
@@ -952,6 +944,16 @@
 		self.program = program
 		self.private = private
 		self.globalSubrs = globalSubrs if globalSubrs is not None else []
+		self._cur_vsindex = None
+
+	def getNumRegions(self, vsindex=None):
+		pd = self.private
+		assert(pd is not None)
+		if vsindex is not None:
+			self._cur_vsindex = vsindex
+		elif self._cur_vsindex is None:
+			self._cur_vsindex = pd.vsindex if hasattr(pd, 'vsindex') else 0
+		return pd.getNumRegions(self._cur_vsindex)
 
 	def __repr__(self):
 		if self.bytecode is None:
@@ -975,29 +977,31 @@
 	def draw(self, pen):
 		subrs = getattr(self.private, "Subrs", [])
 		extractor = self.outlineExtractor(pen, subrs, self.globalSubrs,
-				self.private.nominalWidthX, self.private.defaultWidthX)
+				self.private.nominalWidthX, self.private.defaultWidthX,
+				self.private)
 		extractor.execute(self)
 		self.width = extractor.width
 
-	def calcBounds(self):
-		boundsPen = BoundsPen(None)
+	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)
+
+		if isCFF2:
+			# If present, remove return and endchar operators.
+			if program and program[-1] in ("return", "endchar"):
+				program = program[:-1]
+		elif program and not isinstance(program[-1], str):
+			raise CharStringCompileError(
+				"T2CharString or Subr has items on the stack after last operator."
+			)
+
 		bytecode = []
 		encodeInt = self.getIntEncoder()
 		encodeFixed = self.getFixedEncoder()
@@ -1006,8 +1010,7 @@
 		while i < end:
 			token = program[i]
 			i = i + 1
-			tp = type(token)
-			if issubclass(tp, basestring):
+			if isinstance(token, str):
 				try:
 					bytecode.extend(bytechr(b) for b in opcodes[token])
 				except KeyError:
@@ -1015,12 +1018,12 @@
 				if token in ('hintmask', 'cntrmask'):
 					bytecode.append(program[i])  # hint mask
 					i = i + 1
-			elif tp == int:
+			elif isinstance(token, int):
 				bytecode.append(encodeInt(token))
-			elif tp == float:
+			elif isinstance(token, float):
 				bytecode.append(encodeFixed(token))
 			else:
-				assert 0, "unsupported type: %s" % tp
+				assert 0, "unsupported type: %s" % type(token)
 		try:
 			bytecode = bytesjoin(bytecode)
 		except TypeError:
@@ -1028,11 +1031,6 @@
 			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
 
@@ -1045,8 +1043,7 @@
 		self.program = None
 
 	def getToken(self, index,
-			len=len, byteord=byteord, basestring=basestring,
-			isinstance=isinstance):
+			len=len, byteord=byteord, isinstance=isinstance):
 		if self.bytecode is not None:
 			if index >= len(self.bytecode):
 				return None, 0, 0
@@ -1059,7 +1056,7 @@
 				return None, 0, 0
 			token = self.program[index]
 			index = index + 1
-		isOperator = isinstance(token, basestring)
+		isOperator = isinstance(token, str)
 		return token, isOperator, index
 
 	def getBytes(self, index, nBytes):
@@ -1076,7 +1073,7 @@
 	def handle_operator(self, operator):
 		return operator
 
-	def toXML(self, xmlWriter):
+	def toXML(self, xmlWriter, ttFont=None):
 		from fontTools.misc.textTools import num2binary
 		if self.bytecode is not None:
 			xmlWriter.dumphex(self.bytecode)
@@ -1088,7 +1085,6 @@
 				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 = []
@@ -1102,15 +1098,17 @@
 					xmlWriter.newline()
 					args = []
 				else:
+					if isinstance(token, float):
+						token = floatToFixedToStr(token, precisionBits=16)
+					else:
+						token = str(token)
 					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."
+				# NOTE: only CFF2 charstrings/subrs can have numeric arguments on
+				# the stack after the last operator. Compiling this would fail if
+				# this is part of CFF 1.0 table.
+				line = ' '.join(args)
+				xmlWriter.write(line)
 
 	def fromXML(self, name, attrs, content):
 		from fontTools.misc.textTools import binary2num, readHex
@@ -1129,7 +1127,7 @@
 				token = int(token)
 			except ValueError:
 				try:
-					token = float(token)
+					token = strToFixedToFloat(token, precisionBits=16)
 				except ValueError:
 					program.append(token)
 					if token in ('hintmask', 'cntrmask'):
@@ -1145,19 +1143,13 @@
 				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
+		super().__init__(bytecode, program)
 		self.subrs = subrs
 
 	def getIntEncoder(self):
@@ -1259,12 +1251,12 @@
 		"""
 		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. 
+		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 
+
+		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)
diff --git a/Lib/fontTools/misc/psLib.py b/Lib/fontTools/misc/psLib.py
index dffe561..916755c 100644
--- a/Lib/fontTools/misc/psLib.py
+++ b/Lib/fontTools/misc/psLib.py
@@ -1,9 +1,23 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytechr, byteord, bytesjoin, tobytes, tostr
 from fontTools.misc import eexec
-from .psOperators import *
+from .psOperators import (
+	PSOperators,
+	ps_StandardEncoding,
+	ps_array,
+	ps_boolean,
+	ps_dict,
+	ps_integer,
+	ps_literal,
+	ps_mark,
+	ps_name,
+	ps_operator,
+	ps_procedure,
+	ps_procmark,
+	ps_real,
+	ps_string,
+)
 import re
-import collections
+from collections.abc import Callable
 from string import whitespace
 import logging
 
@@ -43,13 +57,14 @@
 
 class PSTokenizer(object):
 
-	def __init__(self, buf=b''):
+	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
@@ -122,7 +137,7 @@
 			_, nextpos = m.span()
 			token = buf[pos:nextpos]
 		self.pos = pos + len(token)
-		token = tostr(token, encoding='ascii')
+		token = tostr(token, encoding=self.encoding)
 		return tokentype, token
 
 	def skipwhite(self, whitematch=skipwhiteRE.match):
@@ -145,9 +160,10 @@
 
 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
@@ -167,14 +183,14 @@
 	def suckoperators(self, systemdict, klass):
 		for name in dir(klass):
 			attr = getattr(self, name)
-			if isinstance(attr, collections.Callable) and name[:3] == 'ps_':
+			if isinstance(attr, Callable) and name[:3] == 'ps_':
 				name = name[3:]
 				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)
+		tokenizer = self.tokenizer = PSTokenizer(data, self.encoding)
 		getnexttoken = tokenizer.getnexttoken
 		do_token = self.do_token
 		handle_object = self.handle_object
@@ -345,13 +361,13 @@
 		newitem = item.value
 	return newitem
 
-def suckfont(data):
+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
diff --git a/Lib/fontTools/misc/psOperators.py b/Lib/fontTools/misc/psOperators.py
index 4aa0281..3b378f5 100644
--- a/Lib/fontTools/misc/psOperators.py
+++ b/Lib/fontTools/misc/psOperators.py
@@ -1,6 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-
 _accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}
 
 
diff --git a/Lib/fontTools/misc/py23.py b/Lib/fontTools/misc/py23.py
index 1801a39..25a4cdb 100644
--- a/Lib/fontTools/misc/py23.py
+++ b/Lib/fontTools/misc/py23.py
@@ -1,548 +1,148 @@
-"""Python 2/3 compat layer."""
+"""Python 2/3 compat layer leftovers."""
 
-from __future__ import print_function, division, absolute_import
-import sys
+import decimal as _decimal
+import math as _math
+import warnings
+from contextlib import redirect_stderr, redirect_stdout
+from io import BytesIO
+from io import StringIO as UnicodeIO
+from types import SimpleNamespace
 
+warnings.warn(
+    "The py23 module has been deprecated and will be removed in a future release. "
+    "Please update your code.",
+    DeprecationWarning,
+)
 
-__all__ = ['basestring', 'unicode', 'unichr', 'byteord', 'bytechr', 'BytesIO',
-		'StringIO', 'UnicodeIO', 'strjoin', 'bytesjoin', 'tobytes', 'tostr',
-		'tounicode', 'Tag', 'open', 'range', 'xrange', 'round', 'Py23Error']
+__all__ = [
+    "basestring",
+    "bytechr",
+    "byteord",
+    "BytesIO",
+    "bytesjoin",
+    "open",
+    "Py23Error",
+    "range",
+    "RecursionError",
+    "round",
+    "SimpleNamespace",
+    "StringIO",
+    "strjoin",
+    "Tag",
+    "tobytes",
+    "tostr",
+    "tounicode",
+    "unichr",
+    "unicode",
+    "UnicodeIO",
+    "xrange",
+    "zip",
+]
 
 
 class Py23Error(NotImplementedError):
-	pass
+    pass
 
 
-PY3 = sys.version_info[0] == 3
-PY2 = sys.version_info[0] == 2
+RecursionError = RecursionError
+StringIO = UnicodeIO
+
+basestring = str
+isclose = _math.isclose
+isfinite = _math.isfinite
+open = open
+range = range
+round = round3 = round
+unichr = chr
+unicode = str
+zip = zip
 
 
-try:
-	basestring = basestring
-except NameError:
-	basestring = str
-
-try:
-	unicode = unicode
-except NameError:
-	unicode = str
-
-try:
-	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
-
-except NameError:
-	unichr = chr
-	def bytechr(n):
-		return bytes([n])
-	def byteord(c):
-		return c if isinstance(c, int) else ord(c)
+def bytechr(n):
+    return bytes([n])
 
 
-# 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:
-	# in Python 3, we mean instead a stream of *unicode* strings
-	StringIO = UnicodeIO
+def byteord(c):
+    return c if isinstance(c, int) else ord(c)
 
 
-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):
-			if isinstance(self, bytes):
-				return self
-			else:
-				return self.encode('latin1')
-
-	tostr = tobytes
-
-	bytesjoin = strjoin
-else:
-	class Tag(str):
-
-		@staticmethod
-		def transcode(blob):
-			if not isinstance(blob, str):
-				blob = blob.decode('latin-1')
-			return blob
-
-		def __new__(self, content):
-			return str.__new__(self, self.transcode(content))
-		def __ne__(self, other):
-			return not self.__eq__(other)
-		def __eq__(self, other):
-			return str.__eq__(self, self.transcode(other))
-
-		def __hash__(self):
-			return str.__hash__(self)
-
-		def tobytes(self):
-			return self.encode('latin-1')
-
-	tostr = tounicode
-
-	def bytesjoin(iterable, joiner=b''):
-		return tobytes(joiner).join(tobytes(item) for item in iterable)
+def strjoin(iterable, joiner=""):
+    return tostr(joiner).join(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 tobytes(s, encoding="ascii", errors="strict"):
+    if isinstance(s, str):
+        return s.encode(encoding, errors)
+    else:
+        return bytes(s)
 
 
-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)
+def tounicode(s, encoding="ascii", errors="strict"):
+    if not isinstance(s, unicode):
+        return s.decode(encoding, errors)
+    else:
+        return s
 
 
-# always use iterator for 'range' on both py 2 and 3
-try:
-	range = xrange
-except NameError:
-	range = range
+tostr = tounicode
+
+
+class Tag(str):
+    @staticmethod
+    def transcode(blob):
+        if isinstance(blob, bytes):
+            blob = blob.decode("latin-1")
+        return blob
+
+    def __new__(self, content):
+        return str.__new__(self, self.transcode(content))
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __eq__(self, other):
+        return str.__eq__(self, self.transcode(other))
+
+    def __hash__(self):
+        return str.__hash__(self)
+
+    def tobytes(self):
+        return self.encode("latin-1")
+
+
+def bytesjoin(iterable, joiner=b""):
+    return tobytes(joiner).join(tobytes(item) for item in iterable)
+
 
 def xrange(*args, **kwargs):
-	raise Py23Error("'xrange' is not defined. Use 'range' instead.")
+    raise Py23Error("'xrange' is not defined. Use 'range' instead.")
 
 
-import math as _math
+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
 
-try:
-	isclose = _math.isclose
-except AttributeError:
-	# math.isclose() was only added in Python 3.5
+    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)
 
-	_isinf = _math.isinf
-	_fabs = _math.fabs
+        d = _decimal.Decimal.from_float(number).quantize(
+            exponent, rounding=_decimal.ROUND_HALF_UP
+        )
 
-	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
-
-
-import logging
-
-
-class _Logger(logging.Logger):
-	""" Add support for 'lastResort' handler introduced in Python 3.2. """
-
-	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
-	whatever 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 not hasattr(logging, 'lastResort'):
-	# for Python pre-3.2, we need to define the "last resort" handler used when
-	# clients don't explicitly configure logging (in Python 3.2 and above this is
-	# already defined). The handler prints the bare message to sys.stderr, only
-	# for events of severity WARNING or greater.
-	# To obtain the pre-3.2 behaviour, you can set logging.lastResort to None.
-	# https://docs.python.org/3.5/howto/logging.html#what-happens-if-no-configuration-is-provided
-	logging.lastResort = _StderrHandler(logging.WARNING)
-	# Also, we need to set the Logger class to one which supports the last resort
-	# handler. All new loggers instantiated after this call will use the custom
-	# logger class (the already existing ones, like the 'root' logger, will not)
-	logging.setLoggerClass(_Logger)
-
-
-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)
+        return float(d)
diff --git a/Lib/fontTools/misc/roundTools.py b/Lib/fontTools/misc/roundTools.py
new file mode 100644
index 0000000..6f4aa63
--- /dev/null
+++ b/Lib/fontTools/misc/roundTools.py
@@ -0,0 +1,105 @@
+"""
+Various round-to-integer helpers.
+"""
+
+import math
+import functools
+import logging
+
+log = logging.getLogger(__name__)
+
+__all__ = [
+	"noRound",
+	"otRound",
+	"maybeRound",
+	"roundFunc",
+]
+
+def noRound(value):
+	return value
+
+def otRound(value):
+	"""Round float value to nearest integer towards ``+Infinity``.
+
+	The OpenType spec (in the section on `"normalization" of OpenType Font Variations <https://docs.microsoft.com/en-us/typography/opentype/spec/otvaroverview#coordinate-scales-and-normalization>`_)
+	defines the required method for converting floating point values to
+	fixed-point. In particular it specifies the following rounding strategy:
+
+		for fractional values of 0.5 and higher, take the next higher integer;
+		for other fractional values, truncate.
+
+	This function rounds the floating-point value according to this strategy
+	in preparation for conversion to fixed-point.
+
+	Args:
+		value (float): The input floating-point value.
+
+	Returns
+		float: The rounded value.
+	"""
+	# See this thread for how we ended up with this implementation:
+	# https://github.com/fonttools/fonttools/issues/1248#issuecomment-383198166
+	return int(math.floor(value + 0.5))
+
+def maybeRound(v, tolerance, round=otRound):
+	rounded = round(v)
+	return rounded if abs(rounded - v) <= tolerance else v
+
+def roundFunc(tolerance, round=otRound):
+    if tolerance < 0:
+        raise ValueError("Rounding tolerance must be positive")
+
+    if tolerance == 0:
+        return noRound
+
+    if tolerance >= .5:
+        return round
+
+    return functools.partial(maybeRound, tolerance=tolerance, round=round)
+
+
+def nearestMultipleShortestRepr(value: float, factor: float) -> str:
+    """Round to nearest multiple of factor and return shortest decimal representation.
+
+    This chooses the float that is closer to a multiple of the given factor while
+    having the shortest decimal representation (the least number of fractional decimal
+    digits).
+
+    For example, given the following:
+
+    >>> nearestMultipleShortestRepr(-0.61883544921875, 1.0/(1<<14))
+    '-0.61884'
+
+    Useful when you need to serialize or print a fixed-point number (or multiples
+    thereof, such as F2Dot14 fractions of 180 degrees in COLRv1 PaintRotate) in
+    a human-readable form.
+
+    Args:
+        value (value): The value to be rounded and serialized.
+        factor (float): The value which the result is a close multiple of.
+
+    Returns:
+        str: A compact string representation of the value.
+    """
+    if not value:
+        return "0.0"
+
+    value = otRound(value / factor) * factor
+    eps = .5 * factor
+    lo = value - eps
+    hi = value + eps
+    # If the range of valid choices spans an integer, return the integer.
+    if int(lo) != int(hi):
+        return str(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
+    period = lo.find('.')
+    assert period < i
+    fmt = "%%.%df" % (i - period)
+    return fmt % value
diff --git a/Lib/fontTools/misc/sstruct.py b/Lib/fontTools/misc/sstruct.py
index 528b5e0..9f1de8b 100644
--- a/Lib/fontTools/misc/sstruct.py
+++ b/Lib/fontTools/misc/sstruct.py
@@ -46,8 +46,7 @@
 	it returns the size of the data in bytes.
 """
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import tobytes, tostr
 from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
 import struct
 import re
@@ -60,7 +59,7 @@
 	pass
 
 def pack(fmt, obj):
-	formatstring, names, fixes = getformat(fmt)
+	formatstring, names, fixes = getformat(fmt, keep_pad_byte=True)
 	elements = []
 	if not isinstance(obj, dict):
 		obj = obj.__dict__
@@ -69,7 +68,7 @@
 		if name in fixes:
 			# fixed point conversion
 			value = fl2fi(value, fixes[name])
-		elif isinstance(value, basestring):
+		elif isinstance(value, str):
 			value = tobytes(value)
 		elements.append(value)
 	data = struct.pack(*(formatstring,) + tuple(elements))
@@ -110,20 +109,21 @@
 
 # matches "name:formatchar" (whitespace is allowed)
 _elementRE = re.compile(
-		"\s*"							# whitespace
-		"([A-Za-z_][A-Za-z_0-9]*)"		# name (python identifier)
-		"\s*:\s*"						# whitespace : whitespace
-		"([cbBhHiIlLqQfd]|[0-9]+[ps]|"	# formatchar...
-			"([0-9]+)\.([0-9]+)(F))"	# ...formatchar
-		"\s*"							# whitespace
-		"(#.*)?$"						# [comment] + end of string
+		r"\s*"							# whitespace
+		r"([A-Za-z_][A-Za-z_0-9]*)"		# name (python identifier)
+		r"\s*:\s*"						# whitespace : whitespace
+		r"([xcbB?hHiIlLqQfd]|"			# formatchar...
+			r"[0-9]+[ps]|"				# ...formatchar...
+			r"([0-9]+)\.([0-9]+)(F))"	# ...formatchar
+		r"\s*"							# whitespace
+		r"(#.*)?$"						# [comment] + end of string
 	)
 
 # matches the special struct fmt chars and 'x' (pad byte)
-_extraRE = re.compile("\s*([x@=<>!])\s*(#.*)?$")
+_extraRE = re.compile(r"\s*([x@=<>!])\s*(#.*)?$")
 
 # matches an "empty" string, possibly containing whitespace and/or a comment
-_emptyRE = re.compile("\s*(#.*)?$")
+_emptyRE = re.compile(r"\s*(#.*)?$")
 
 _fixedpointmappings = {
 		8: "b",
@@ -132,7 +132,7 @@
 
 _formatcache = {}
 
-def getformat(fmt):
+def getformat(fmt, keep_pad_byte=False):
 	fmt = tostr(fmt, encoding="ascii")
 	try:
 		formatstring, names, fixes = _formatcache[fmt]
@@ -154,8 +154,9 @@
 				if not m:
 					raise Error("syntax error in fmt: '%s'" % line)
 				name = m.group(1)
-				names.append(name)
 				formatchar = m.group(2)
+				if keep_pad_byte or formatchar != "x":
+					names.append(name)
 				if m.group(3):
 					# fixed point
 					before = int(m.group(3))
@@ -183,6 +184,8 @@
 		astr: 5s
 		afloat: f; adouble: d	# multiple "statements" are allowed
 		afixed: 16.16F
+		abool: ?
+		apad: x
 	"""
 
 	print('size:', calcsize(fmt))
@@ -200,6 +203,7 @@
 	i.afloat = 0.5
 	i.adouble = 0.5
 	i.afixed = 1.5
+	i.abool = True
 
 	data = pack(fmt, i)
 	print('data:', repr(data))
diff --git a/Lib/fontTools/misc/symfont.py b/Lib/fontTools/misc/symfont.py
index d09efac..a1a8730 100644
--- a/Lib/fontTools/misc/symfont.py
+++ b/Lib/fontTools/misc/symfont.py
@@ -1,5 +1,3 @@
-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
@@ -113,9 +111,7 @@
 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
+'''from fontTools.pens.basePen import BasePen
 
 class %s(BasePen):
 
diff --git a/Lib/fontTools/misc/testTools.py b/Lib/fontTools/misc/testTools.py
index b32e57a..1b258e3 100644
--- a/Lib/fontTools/misc/testTools.py
+++ b/Lib/fontTools/misc/testTools.py
@@ -1,9 +1,13 @@
 """Helpers for writing unit tests."""
 
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
-import collections
-from fontTools.misc.py23 import *
+from collections.abc import Iterable
+from io import BytesIO
+import os
+import shutil
+import sys
+import tempfile
+from unittest import TestCase as _TestCase
+from fontTools.misc.py23 import tobytes
 from fontTools.misc.xmlWriter import XMLWriter
 
 
@@ -22,9 +26,9 @@
     xml = b"<root>"
     if isinstance(xmlSnippet, bytes):
         xml += xmlSnippet
-    elif isinstance(xmlSnippet, unicode):
+    elif isinstance(xmlSnippet, str):
         xml += tobytes(xmlSnippet, 'utf-8')
-    elif isinstance(xmlSnippet, collections.Iterable):
+    elif isinstance(xmlSnippet, Iterable):
         xml += b"".join(tobytes(s, 'utf-8') for s in xmlSnippet)
     else:
         raise TypeError("expected string or sequence of strings; found %r"
@@ -37,7 +41,7 @@
 class FakeFont:
     def __init__(self, glyphs):
         self.glyphOrder_ = glyphs
-        self.reverseGlyphOrderDict_ = {g:i for i,g in enumerate(glyphs)}
+        self.reverseGlyphOrderDict_ = {g: i for i, g in enumerate(glyphs)}
         self.lazy = False
         self.tables = {}
 
@@ -65,6 +69,9 @@
     def getReverseGlyphMap(self):
         return self.reverseGlyphOrderDict_
 
+    def getGlyphNames(self):
+        return sorted(self.getGlyphOrder())
+
 
 class TestXMLReader_(object):
     def __init__(self):
@@ -114,29 +121,65 @@
 
 
 class MockFont(object):
-	"""A font-like object that automatically adds any looked up glyphname
-	to its glyphOrder."""
+    """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 __init__(self):
+        self._glyphOrder = ['.notdef']
 
-	def getGlyphID(self, glyph, requireReal=None):
-		gid = self._reverseGlyphOrder[glyph]
-		return gid
+        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 getReverseGlyphMap(self):
-		return self._reverseGlyphOrder
+    def getGlyphID(self, glyph, requireReal=None):
+        gid = self._reverseGlyphOrder[glyph]
+        return gid
 
-	def getGlyphName(self, gid):
-		return self._glyphOrder[gid]
+    def getReverseGlyphMap(self):
+        return self._reverseGlyphOrder
 
-	def getGlyphOrder(self):
-		return self._glyphOrder
+    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 a324042..072976a 100644
--- a/Lib/fontTools/misc/textTools.py
+++ b/Lib/fontTools/misc/textTools.py
@@ -1,19 +1,19 @@
 """fontTools.misc.textTools.py -- miscellaneous routines."""
 
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytechr, byteord, bytesjoin, strjoin, tobytes
+import ast
 import string
 
 
-def safeEval(data, eval=eval):
-	"""A (kindof) safe replacement for eval."""
-	return eval(data, {"__builtins__":{"True":True,"False":False}})
+# 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)))
+	return deHexStr(strjoin(chunk for chunk in content if isinstance(chunk, str)))
+
 
 def deHexStr(hexdata):
 	"""Convert a hex string to binary data."""
diff --git a/Lib/fontTools/misc/timeTools.py b/Lib/fontTools/misc/timeTools.py
index c30a377..f4b84f6 100644
--- a/Lib/fontTools/misc/timeTools.py
+++ b/Lib/fontTools/misc/timeTools.py
@@ -1,10 +1,9 @@
 """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
+from datetime import datetime, timezone
 import calendar
 
 
@@ -31,7 +30,7 @@
 	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
+	See https://github.com/fonttools/fonttools/issues/455
 	"""
 	if t is None:
 		t = time.localtime()
@@ -45,7 +44,12 @@
 	return asctime(time.gmtime(max(0, value + epoch_diff)))
 
 def timestampFromString(value):
-	return calendar.timegm(time.strptime(value)) - epoch_diff
+	wkday, mnth = value[:7].split()
+	t = datetime.strptime(value[7:], ' %d %H:%M:%S %Y')
+	t = t.replace(month=MONTHNAMES.index(mnth), tzinfo=timezone.utc)
+	wkday_idx = DAYNAMES.index(wkday)
+	assert t.weekday() == wkday_idx, '"' + value + '" has inconsistent weekday'
+	return int(t.timestamp()) - epoch_diff
 
 def timestampNow():
 	# https://reproducible-builds.org/specs/source-date-epoch/
diff --git a/Lib/fontTools/misc/transform.py b/Lib/fontTools/misc/transform.py
index 1296571..22ad776 100644
--- a/Lib/fontTools/misc/transform.py
+++ b/Lib/fontTools/misc/transform.py
@@ -45,8 +45,8 @@
 	>>>
 """
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from typing import NamedTuple
+
 
 __all__ = ["Transform", "Identity", "Offset", "Scale"]
 
@@ -66,7 +66,7 @@
 	return v
 
 
-class Transform(object):
+class Transform(NamedTuple):
 
 	"""2x2 transformation matrix plus offset, a.k.a. Affine transform.
 	Transform instances are immutable: all transforming methods, eg.
@@ -83,20 +83,68 @@
 		>>>
 		>>> t.scale(2, 3).transformPoint((100, 100))
 		(200, 300)
+
+	Transform's constructor takes six arguments, all of which are
+	optional, and can be used as keyword arguments:
+		>>> Transform(12)
+		<Transform [12 0 0 1 0 0]>
+		>>> Transform(dx=12)
+		<Transform [1 0 0 1 12 0]>
+		>>> Transform(yx=12)
+		<Transform [1 0 12 1 0 0]>
+
+	Transform instances also behave like sequences of length 6:
+		>>> len(Identity)
+		6
+		>>> list(Identity)
+		[1, 0, 0, 1, 0, 0]
+		>>> tuple(Identity)
+		(1, 0, 0, 1, 0, 0)
+
+	Transform instances are comparable:
+		>>> t1 = Identity.scale(2, 3).translate(4, 6)
+		>>> t2 = Identity.translate(8, 18).scale(2, 3)
+		>>> t1 == t2
+		1
+
+	But beware of floating point rounding errors:
+		>>> 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.3 0.08 0.18]>
+		>>> t2
+		<Transform [0.2 0 0 0.3 0.08 0.18]>
+		>>> t1 == t2
+		0
+
+	Transform instances are hashable, meaning you can use them as
+	keys in dictionaries:
+		>>> d = {Scale(12, 13): None}
+		>>> d
+		{<Transform [12 0 0 13 0 0]>: None}
+
+	But again, beware of floating point rounding errors:
+		>>> 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.3 0.08 0.18]>
+		>>> t2
+		<Transform [0.2 0 0 0.3 0.08 0.18]>
+		>>> d = {t1: None}
+		>>> d
+		{<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.3 0.08 0.18]>
 	"""
 
-	def __init__(self, xx=1, xy=0, yx=0, yy=1, dx=0, dy=0):
-		"""Transform's constructor takes six arguments, all of which are
-		optional, and can be used as keyword arguments:
-			>>> Transform(12)
-			<Transform [12 0 0 1 0 0]>
-			>>> Transform(dx=12)
-			<Transform [1 0 0 1 12 0]>
-			>>> Transform(yx=12)
-			<Transform [1 0 12 1 0 0]>
-			>>>
-		"""
-		self.__affine = xx, xy, yx, yy, dx, dy
+	xx: float = 1
+	xy: float = 0
+	yx: float = 0
+	yy: float = 1
+	dx: float = 0
+	dy: float = 0
 
 	def transformPoint(self, p):
 		"""Transform a point.
@@ -108,7 +156,7 @@
 			(250.0, 550.0)
 		"""
 		(x, y) = p
-		xx, xy, yx, yy, dx, dy = self.__affine
+		xx, xy, yx, yy, dx, dy = self
 		return (xx*x + yx*y + dx, xy*x + yy*y + dy)
 
 	def transformPoints(self, points):
@@ -120,9 +168,34 @@
 			[(0, 0), (0, 300), (200, 300), (200, 0)]
 			>>>
 		"""
-		xx, xy, yx, yy, dx, dy = self.__affine
+		xx, xy, yx, yy, dx, dy = self
 		return [(xx*x + yx*y + dx, xy*x + yy*y + dy) for x, y in points]
 
+	def transformVector(self, v):
+		"""Transform an (dx, dy) vector, treating translation as zero.
+
+		Example:
+			>>> t = Transform(2, 0, 0, 2, 10, 20)
+			>>> t.transformVector((3, -4))
+			(6, -8)
+			>>>
+		"""
+		(dx, dy) = v
+		xx, xy, yx, yy = self[:4]
+		return (xx*dx + yx*dy, xy*dx + yy*dy)
+
+	def transformVectors(self, vectors):
+		"""Transform a list of (dx, dy) vector, treating translation as zero.
+
+		Example:
+			>>> t = Transform(2, 0, 0, 2, 10, 20)
+			>>> t.transformVectors([(3, -4), (5, -6)])
+			[(6, -8), (10, -12)]
+			>>>
+		"""
+		xx, xy, yx, yy = self[:4]
+		return [(xx*dx + yx*dy, xy*dx + yy*dy) for dx, dy in vectors]
+
 	def translate(self, x=0, y=0):
 		"""Return a new transformation, translated (offset) by x, y.
 
@@ -189,7 +262,7 @@
 			>>>
 		"""
 		xx1, xy1, yx1, yy1, dx1, dy1 = other
-		xx2, xy2, yx2, yy2, dx2, dy2 = self.__affine
+		xx2, xy2, yx2, yy2, dx2, dy2 = self
 		return self.__class__(
 				xx1*xx2 + xy1*yx2,
 				xx1*xy2 + xy1*yy2,
@@ -211,7 +284,7 @@
 			<Transform [8 6 6 3 21 15]>
 			>>>
 		"""
-		xx1, xy1, yx1, yy1, dx1, dy1 = self.__affine
+		xx1, xy1, yx1, yy1, dx1, dy1 = self
 		xx2, xy2, yx2, yy2, dx2, dy2 = other
 		return self.__class__(
 				xx1*xx2 + xy1*yx2,
@@ -233,9 +306,9 @@
 			(10.0, 20.0)
 			>>>
 		"""
-		if self.__affine == (1, 0, 0, 1, 0, 0):
+		if self == Identity:
 			return self
-		xx, xy, yx, yy, dx, dy = self.__affine
+		xx, xy, yx, yy, dx, dy = self
 		det = xx*yy - yx*xy
 		xx, xy, yx, yy = yy/det, -xy/det, -yx/det, xx/det
 		dx, dy = -xx*dx - yx*dy, -xy*dx - yy*dy
@@ -248,77 +321,7 @@
 			'[2 0 0 3 8 15]'
 			>>>
 		"""
-		return "[%s %s %s %s %s %s]" % self.__affine
-
-	def __len__(self):
-		"""Transform instances also behave like sequences of length 6:
-			>>> len(Identity)
-			6
-			>>>
-		"""
-		return 6
-
-	def __getitem__(self, index):
-		"""Transform instances also behave like sequences of length 6:
-			>>> list(Identity)
-			[1, 0, 0, 1, 0, 0]
-			>>> tuple(Identity)
-			(1, 0, 0, 1, 0, 0)
-			>>>
-		"""
-		return self.__affine[index]
-
-	def __ne__(self, other):
-		return not self.__eq__(other)
-	def __eq__(self, other):
-		"""Transform instances are comparable:
-			>>> t1 = Identity.scale(2, 3).translate(4, 6)
-			>>> t2 = Identity.translate(8, 18).scale(2, 3)
-			>>> t1 == t2
-			1
-			>>>
-
-		But beware of floating point rounding errors:
-			>>> 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.3 0.08 0.18]>
-			>>> t2
-			<Transform [0.2 0 0 0.3 0.08 0.18]>
-			>>> t1 == t2
-			0
-			>>>
-		"""
-		xx1, xy1, yx1, yy1, dx1, dy1 = self.__affine
-		xx2, xy2, yx2, yy2, dx2, dy2 = other
-		return (xx1, xy1, yx1, yy1, dx1, dy1) == \
-				(xx2, xy2, yx2, yy2, dx2, dy2)
-
-	def __hash__(self):
-		"""Transform instances are hashable, meaning you can use them as
-		keys in dictionaries:
-			>>> d = {Scale(12, 13): None}
-			>>> d
-			{<Transform [12 0 0 13 0 0]>: None}
-			>>>
-
-		But again, beware of floating point rounding errors:
-			>>> 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.3 0.08 0.18]>
-			>>> t2
-			<Transform [0.2 0 0 0.3 0.08 0.18]>
-			>>> d = {t1: None}
-			>>> d
-			{<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.3 0.08 0.18]>
-			>>>
-		"""
-		return hash(self.__affine)
+		return "[%s %s %s %s %s %s]" % self
 
 	def __bool__(self):
 		"""Returns True if transform is not identity, False otherwise.
@@ -337,13 +340,10 @@
 			>>> bool(Offset(2))
 			True
 		"""
-		return self.__affine != Identity.__affine
-
-	__nonzero__ = __bool__
+		return self != Identity
 
 	def __repr__(self):
-		return "<%s [%g %g %g %g %g %g]>" % ((self.__class__.__name__,) \
-				+ self.__affine)
+		return "<%s [%g %g %g %g %g %g]>" % ((self.__class__.__name__,) + self)
 
 
 Identity = Transform()
diff --git a/Lib/fontTools/misc/vector.py b/Lib/fontTools/misc/vector.py
new file mode 100644
index 0000000..81c1484
--- /dev/null
+++ b/Lib/fontTools/misc/vector.py
@@ -0,0 +1,143 @@
+from numbers import Number
+import math
+import operator
+import warnings
+
+
+__all__ = ["Vector"]
+
+
+class Vector(tuple):
+
+    """A math-like vector.
+
+    Represents an n-dimensional numeric vector. ``Vector`` objects support
+    vector addition and subtraction, scalar multiplication and division,
+    negation, rounding, and comparison tests.
+    """
+
+    __slots__ = ()
+
+    def __new__(cls, values, keep=False):
+        if keep is not False:
+            warnings.warn(
+                "the 'keep' argument has been deprecated",
+                DeprecationWarning,
+            )
+        if type(values) == Vector:
+            # No need to create a new object
+            return values
+        return super().__new__(cls, values)
+
+    def __repr__(self):
+        return f"{self.__class__.__name__}({super().__repr__()})"
+
+    def _vectorOp(self, other, op):
+        if isinstance(other, Vector):
+            assert len(self) == len(other)
+            return self.__class__(op(a, b) for a, b in zip(self, other))
+        if isinstance(other, Number):
+            return self.__class__(op(v, other) for v in self)
+        raise NotImplementedError()
+
+    def _scalarOp(self, other, op):
+        if isinstance(other, Number):
+            return self.__class__(op(v, other) for v in self)
+        raise NotImplementedError()
+
+    def _unaryOp(self, op):
+        return self.__class__(op(v) for v in self)
+
+    def __add__(self, other):
+        return self._vectorOp(other, operator.add)
+
+    __radd__ = __add__
+
+    def __sub__(self, other):
+        return self._vectorOp(other, operator.sub)
+
+    def __rsub__(self, other):
+        return self._vectorOp(other, _operator_rsub)
+
+    def __mul__(self, other):
+        return self._scalarOp(other, operator.mul)
+
+    __rmul__ = __mul__
+
+    def __truediv__(self, other):
+        return self._scalarOp(other, operator.truediv)
+
+    def __rtruediv__(self, other):
+        return self._scalarOp(other, _operator_rtruediv)
+
+    def __pos__(self):
+        return self._unaryOp(operator.pos)
+
+    def __neg__(self):
+        return self._unaryOp(operator.neg)
+
+    def __round__(self, *, round=round):
+        return self._unaryOp(round)
+
+    def __eq__(self, other):
+        if isinstance(other, list):
+            # bw compat Vector([1, 2, 3]) == [1, 2, 3]
+            other = tuple(other)
+        return super().__eq__(other)
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __bool__(self):
+        return any(self)
+
+    __nonzero__ = __bool__
+
+    def __abs__(self):
+        return math.sqrt(sum(x * x for x in self))
+
+    def length(self):
+        """Return the length of the vector. Equivalent to abs(vector)."""
+        return abs(self)
+
+    def normalized(self):
+        """Return the normalized vector of the vector."""
+        return self / abs(self)
+
+    def dot(self, other):
+        """Performs vector dot product, returning the sum of
+        ``a[0] * b[0], a[1] * b[1], ...``"""
+        assert len(self) == len(other)
+        return sum(a * b for a, b in zip(self, other))
+
+    # Deprecated methods/properties
+
+    def toInt(self):
+        warnings.warn(
+            "the 'toInt' method has been deprecated, use round(vector) instead",
+            DeprecationWarning,
+        )
+        return self.__round__()
+
+    @property
+    def values(self):
+        warnings.warn(
+            "the 'values' attribute has been deprecated, use "
+            "the vector object itself instead",
+            DeprecationWarning,
+        )
+        return list(self)
+
+    @values.setter
+    def values(self, values):
+        raise AttributeError(
+            "can't set attribute, the 'values' attribute has been deprecated",
+        )
+
+
+def _operator_rsub(a, b):
+    return operator.sub(b, a)
+
+
+def _operator_rtruediv(a, b):
+    return operator.truediv(b, a)
diff --git a/Lib/fontTools/misc/xmlReader.py b/Lib/fontTools/misc/xmlReader.py
index 438549d..6ec50de 100644
--- a/Lib/fontTools/misc/xmlReader.py
+++ b/Lib/fontTools/misc/xmlReader.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools import ttLib
 from fontTools.misc.textTools import safeEval
 from fontTools.ttLib.tables.DefaultTable import DefaultTable
@@ -18,7 +16,6 @@
 class XMLReader(object):
 
 	def __init__(self, fileOrPath, ttFont, progress=None, quiet=None, contentOnly=False):
-  
 		if fileOrPath == '-':
 			fileOrPath = sys.stdin
 		if not hasattr(fileOrPath, "read"):
@@ -96,11 +93,12 @@
 		if not stackSize:
 			if name != "ttFont":
 				raise TTXParseError("illegal root tag: %s" % name)
-			sfntVersion = attrs.get("sfntVersion")
-			if sfntVersion is not None:
-				if len(sfntVersion) != 4:
-					sfntVersion = safeEval('"' + sfntVersion + '"')
-				self.ttFont.sfntVersion = sfntVersion
+			if self.ttFont.reader is None and not self.ttFont.tables:
+				sfntVersion = attrs.get("sfntVersion")
+				if sfntVersion is not None:
+					if len(sfntVersion) != 4:
+						sfntVersion = safeEval('"' + sfntVersion + '"')
+					self.ttFont.sfntVersion = sfntVersion
 			self.contentStack.append([])
 		elif stackSize == 1:
 			if subFile is not None:
diff --git a/Lib/fontTools/misc/xmlWriter.py b/Lib/fontTools/misc/xmlWriter.py
index fe67f6e..ddc2fdb 100644
--- a/Lib/fontTools/misc/xmlWriter.py
+++ b/Lib/fontTools/misc/xmlWriter.py
@@ -1,7 +1,6 @@
 """xmlWriter.py -- Simple XML authoring class"""
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import byteord, strjoin, tobytes, tostr
 import sys
 import os
 import string
@@ -12,27 +11,31 @@
 class XMLWriter(object):
 
 	def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None, encoding="utf_8",
-			newlinestr=None):
+			newlinestr="\n"):
 		if encoding.lower().replace('-','').replace('_','') != 'utf8':
 			raise Exception('Only UTF-8 encoding is supported.')
 		if fileOrPath == '-':
 			fileOrPath = sys.stdout
 		if not hasattr(fileOrPath, "write"):
+			self.filename = fileOrPath
 			self.file = open(fileOrPath, "wb")
+			self._closeStream = True
 		else:
+			self.filename = None
 			# assume writable file object
 			self.file = fileOrPath
+			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
+			# https://github.com/fonttools/fonttools/pull/233
 			self.file.write(b'')
 			self.totype = tobytes
 		except TypeError:
 			# This better not fail.
-			self.file.write(tounicode(''))
-			self.totype = tounicode
+			self.file.write('')
+			self.totype = tostr
 		self.indentwhite = self.totype(indentwhite)
 		if newlinestr is None:
 			self.newlinestr = self.totype(os.linesep)
@@ -46,8 +49,15 @@
 		self._writeraw('<?xml version="1.0" encoding="UTF-8"?>')
 		self.newline()
 
+	def __enter__(self):
+		return self
+
+	def __exit__(self, exception_type, exception_value, traceback):
+		self.close()
+
 	def close(self):
-		self.file.close()
+		if self._closeStream:
+			self.file.close()
 
 	def write(self, string, indent=True):
 		"""Writes text."""
@@ -146,7 +156,7 @@
 			return ""
 		data = ""
 		for attr, value in attributes:
-			if not isinstance(value, (bytes, unicode)):
+			if not isinstance(value, (bytes, str)):
 				value = str(value)
 			data = data + ' %s="%s"' % (attr, escapeattr(value))
 		return data
diff --git a/Lib/fontTools/mtiLib/__init__.py b/Lib/fontTools/mtiLib/__init__.py
index d4f4880..667a216 100644
--- a/Lib/fontTools/mtiLib/__init__.py
+++ b/Lib/fontTools/mtiLib/__init__.py
@@ -6,9 +6,6 @@
 # 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
@@ -856,7 +853,7 @@
 
 	lookup.SubTable = subtables
 	lookup.SubTableCount = len(lookup.SubTable)
-	if lookup.SubTableCount is 0:
+	if lookup.SubTableCount == 0:
 		# Remove this return when following is fixed:
 		# https://github.com/fonttools/fonttools/issues/789
 		return None
@@ -1148,11 +1145,33 @@
 		return line
 
 def build(f, font, tableTag=None):
+	"""Convert a Monotype font layout file to an OpenType layout object
+
+	A font object must be passed, but this may be a "dummy" font; it is only
+	used for sorting glyph sets when making coverage tables and to hold the
+	OpenType layout table while it is being built.
+
+	Args:
+		f: A file object.
+		font (TTFont): A font object.
+		tableTag (string): If provided, asserts that the file contains data for the
+			given OpenType table.
+
+	Returns:
+		An object representing the table. (e.g. ``table_G_S_U_B_``)
+	"""
 	lines = Tokenizer(f)
 	return parseTable(lines, font, tableTag=tableTag)
 
 
 def main(args=None, font=None):
+	"""Convert a FontDame OTL file to TTX XML.
+
+	Writes XML output to stdout.
+
+	Args:
+		args: Command line arguments (``--font``, ``--table``, input files).
+	"""
 	import sys
 	from fontTools import configLogger
 	from fontTools.misc.testTools import MockFont
@@ -1165,16 +1184,31 @@
 	# comment this out to enable debug messages from mtiLib's logger
 	# log.setLevel(logging.DEBUG)
 
-	if font is None:
-		font = MockFont()
+	import argparse
+	parser = argparse.ArgumentParser(
+		"fonttools mtiLib",
+		description=main.__doc__,
+	)
 
-	tableTag = None
-	if args[0].startswith('-t'):
-		tableTag = args[0][2:]
-		del args[0]
-	for f in args:
+	parser.add_argument('--font', '-f', metavar='FILE', dest="font",
+		help="Input TTF files (used for glyph classes and sorting coverage tables)")
+	parser.add_argument('--table', '-t', metavar='TABLE', dest="tableTag",
+		help="Table to fill (sniffed from input file if not provided)")
+	parser.add_argument('inputs', metavar='FILE', type=str, nargs='+',
+		help="Input FontDame .txt files")
+
+	args = parser.parse_args(args)
+
+	if font is None:
+		if args.font:
+			font = ttLib.TTFont(args.font)
+		else:
+			font = MockFont()
+
+	for f in args.inputs:
 		log.debug("Processing %s", f)
-		table = build(open(f, 'rt', encoding="utf-8"), font, tableTag=tableTag)
+		with open(f, 'rt', encoding="utf-8") as f:
+			table = build(f, font, tableTag=args.tableTag)
 		blob = table.compile(font) # Make sure it compiles
 		decompiled = table.__class__()
 		decompiled.decompile(blob, font) # Make sure it decompiles!
diff --git a/Lib/fontTools/mtiLib/__main__.py b/Lib/fontTools/mtiLib/__main__.py
index 0741237..fe6b638 100644
--- a/Lib/fontTools/mtiLib/__main__.py
+++ b/Lib/fontTools/mtiLib/__main__.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 import sys
 from fontTools.mtiLib import main
 
diff --git a/Lib/fontTools/otlLib/builder.py b/Lib/fontTools/otlLib/builder.py
index efe66d5..bfb9d41 100644
--- a/Lib/fontTools/otlLib/builder.py
+++ b/Lib/fontTools/otlLib/builder.py
@@ -1,10 +1,54 @@
-from __future__ import print_function, division, absolute_import
+from collections import namedtuple, OrderedDict
+import os
+from fontTools.misc.fixedTools import fixedToFloat
 from fontTools import ttLib
 from fontTools.ttLib.tables import otTables as ot
-from fontTools.ttLib.tables.otBase import ValueRecord, valueRecordFormatDict
+from fontTools.ttLib.tables.otBase import (
+    ValueRecord,
+    valueRecordFormatDict,
+    OTTableWriter,
+    CountReference,
+)
+from fontTools.ttLib.tables import otBase
+from fontTools.feaLib.ast import STATNameStatement
+from fontTools.otlLib.optimize.gpos import GPOS_COMPACT_MODE_DEFAULT, GPOS_COMPACT_MODE_ENV_KEY, compact_lookup
+from fontTools.otlLib.error import OpenTypeLibError
+from functools import reduce
+import logging
+import copy
+
+
+log = logging.getLogger(__name__)
 
 
 def buildCoverage(glyphs, glyphMap):
+    """Builds a coverage table.
+
+    Coverage tables (as defined in the `OpenType spec <https://docs.microsoft.com/en-gb/typography/opentype/spec/chapter2#coverage-table>`_)
+    are used in all OpenType Layout lookups apart from the Extension type, and
+    define the glyphs involved in a layout subtable. This allows shaping engines
+    to compare the glyph stream with the coverage table and quickly determine
+    whether a subtable should be involved in a shaping operation.
+
+    This function takes a list of glyphs and a glyphname-to-ID map, and
+    returns a ``Coverage`` object representing the coverage table.
+
+    Example::
+
+        glyphMap = font.getReverseGlyphMap()
+        glyphs = [ "A", "B", "C" ]
+        coverage = buildCoverage(glyphs, glyphMap)
+
+    Args:
+        glyphs: a sequence of glyph names.
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        An ``otTables.Coverage`` object or ``None`` if there are no glyphs
+        supplied.
+    """
+
     if not glyphs:
         return None
     self = ot.Coverage()
@@ -20,36 +64,1409 @@
 
 
 def buildLookup(subtables, flags=0, markFilterSet=None):
+    """Turns a collection of rules into a lookup.
+
+    A Lookup (as defined in the `OpenType Spec <https://docs.microsoft.com/en-gb/typography/opentype/spec/chapter2#lookupTbl>`_)
+    wraps the individual rules in a layout operation (substitution or
+    positioning) in a data structure expressing their overall lookup type -
+    for example, single substitution, mark-to-base attachment, and so on -
+    as well as the lookup flags and any mark filtering sets. You may import
+    the following constants to express lookup flags:
+
+    - ``LOOKUP_FLAG_RIGHT_TO_LEFT``
+    - ``LOOKUP_FLAG_IGNORE_BASE_GLYPHS``
+    - ``LOOKUP_FLAG_IGNORE_LIGATURES``
+    - ``LOOKUP_FLAG_IGNORE_MARKS``
+    - ``LOOKUP_FLAG_USE_MARK_FILTERING_SET``
+
+    Args:
+        subtables: A list of layout subtable objects (e.g.
+            ``MultipleSubst``, ``PairPos``, etc.) or ``None``.
+        flags (int): This lookup's flags.
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+
+    Returns:
+        An ``otTables.Lookup`` object or ``None`` if there are no subtables
+        supplied.
+    """
     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]))
+    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)
+        self.LookupFlag |= LOOKUP_FLAG_USE_MARK_FILTERING_SET
         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)
+        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
 
 
+class LookupBuilder(object):
+    SUBTABLE_BREAK_ = "SUBTABLE_BREAK"
+
+    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 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 = buildCoverage(p, self.glyphMap)
+            subtable.BacktrackCoverage.append(coverage)
+
+    def setLookAheadCoverage_(self, suffix, subtable):
+        subtable.LookAheadGlyphCount = len(suffix)
+        subtable.LookAheadCoverage = []
+        for s in suffix:
+            coverage = buildCoverage(s, self.glyphMap)
+            subtable.LookAheadCoverage.append(coverage)
+
+    def setInputCoverage_(self, glyphs, subtable):
+        subtable.InputGlyphCount = len(glyphs)
+        subtable.InputCoverage = []
+        for g in glyphs:
+            coverage = buildCoverage(g, self.glyphMap)
+            subtable.InputCoverage.append(coverage)
+
+    def setCoverage_(self, glyphs, subtable):
+        subtable.GlyphCount = len(glyphs)
+        subtable.Coverage = []
+        for g in glyphs:
+            coverage = buildCoverage(g, self.glyphMap)
+            subtable.Coverage.append(coverage)
+
+    def build_subst_subtables(self, mapping, klass):
+        substitutions = [{}]
+        for key in mapping:
+            if key[0] == self.SUBTABLE_BREAK_:
+                substitutions.append({})
+            else:
+                substitutions[-1][key] = mapping[key]
+        subtables = [klass(s) for s in substitutions]
+        return subtables
+
+    def add_subtable_break(self, location):
+        """Add an explicit subtable break.
+
+        Args:
+            location: A string or tuple representing the location in the
+                original source which produced this break, or ``None`` if
+                no location is provided.
+        """
+        log.warning(
+            OpenTypeLibError(
+                'unsupported "subtable" statement for lookup type', location
+            )
+        )
+
+
+class AlternateSubstBuilder(LookupBuilder):
+    """Builds an Alternate Substitution (GSUB3) lookup.
+
+    Users are expected to manually add alternate glyph substitutions to
+    the ``alternates`` attribute after the object has been initialized,
+    e.g.::
+
+        builder.alternates["A"] = ["A.alt1", "A.alt2"]
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        alternates: An ordered dictionary of alternates, mapping glyph names
+            to a list of names of alternates.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, "GSUB", 3)
+        self.alternates = OrderedDict()
+
+    def equals(self, other):
+        return LookupBuilder.equals(self, other) and self.alternates == other.alternates
+
+    def build(self):
+        """Build the lookup.
+
+        Returns:
+            An ``otTables.Lookup`` object representing the alternate
+            substitution lookup.
+        """
+        subtables = self.build_subst_subtables(
+            self.alternates, buildAlternateSubstSubtable
+        )
+        return self.buildLookup_(subtables)
+
+    def getAlternateGlyphs(self):
+        return self.alternates
+
+    def add_subtable_break(self, location):
+        self.alternates[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
+
+
+class ChainContextualRule(
+    namedtuple("ChainContextualRule", ["prefix", "glyphs", "suffix", "lookups"])
+):
+    @property
+    def is_subtable_break(self):
+        return self.prefix == LookupBuilder.SUBTABLE_BREAK_
+
+
+class ChainContextualRuleset:
+    def __init__(self):
+        self.rules = []
+
+    def addRule(self, rule):
+        self.rules.append(rule)
+
+    @property
+    def hasPrefixOrSuffix(self):
+        # Do we have any prefixes/suffixes? If this is False for all
+        # rulesets, we can express the whole lookup as GPOS5/GSUB7.
+        for rule in self.rules:
+            if len(rule.prefix) > 0 or len(rule.suffix) > 0:
+                return True
+        return False
+
+    @property
+    def hasAnyGlyphClasses(self):
+        # Do we use glyph classes anywhere in the rules? If this is False
+        # we can express this subtable as a Format 1.
+        for rule in self.rules:
+            for coverage in (rule.prefix, rule.glyphs, rule.suffix):
+                if any(len(x) > 1 for x in coverage):
+                    return True
+        return False
+
+    def format2ClassDefs(self):
+        PREFIX, GLYPHS, SUFFIX = 0, 1, 2
+        classDefBuilders = []
+        for ix in [PREFIX, GLYPHS, SUFFIX]:
+            context = []
+            for r in self.rules:
+                context.append(r[ix])
+            classes = self._classBuilderForContext(context)
+            if not classes:
+                return None
+            classDefBuilders.append(classes)
+        return classDefBuilders
+
+    def _classBuilderForContext(self, context):
+        classdefbuilder = ClassDefBuilder(useClass0=False)
+        for position in context:
+            for glyphset in position:
+                glyphs = set(glyphset)
+                if not classdefbuilder.canAdd(glyphs):
+                    return None
+                classdefbuilder.add(glyphs)
+        return classdefbuilder
+
+
+class ChainContextualBuilder(LookupBuilder):
+    def equals(self, other):
+        return LookupBuilder.equals(self, other) and self.rules == other.rules
+
+    def rulesets(self):
+        # Return a list of ChainContextRuleset objects, taking explicit
+        # subtable breaks into account
+        ruleset = [ChainContextualRuleset()]
+        for rule in self.rules:
+            if rule.is_subtable_break:
+                ruleset.append(ChainContextualRuleset())
+                continue
+            ruleset[-1].addRule(rule)
+        # Squish any empty subtables
+        return [x for x in ruleset if len(x.rules) > 0]
+
+    def getCompiledSize_(self, subtables):
+        size = 0
+        for st in subtables:
+            w = OTTableWriter()
+            w["LookupType"] = CountReference(
+                {"LookupType": st.LookupType}, "LookupType"
+            )
+            # We need to make a copy here because compiling
+            # modifies the subtable (finalizing formats etc.)
+            copy.deepcopy(st).compile(w, self.font)
+            size += len(w.getAllData())
+        return size
+
+    def build(self):
+        """Build the lookup.
+
+        Returns:
+            An ``otTables.Lookup`` object representing the chained
+            contextual positioning lookup.
+        """
+        subtables = []
+        chaining = False
+        rulesets = self.rulesets()
+        chaining = any(ruleset.hasPrefixOrSuffix for ruleset in rulesets)
+        for ruleset in rulesets:
+            # Determine format strategy. We try to build formats 1, 2 and 3
+            # subtables and then work out which is best. candidates list holds
+            # the subtables in each format for this ruleset (including a dummy
+            # "format 0" to make the addressing match the format numbers).
+
+            # We can always build a format 3 lookup by accumulating each of
+            # the rules into a list, so start with that.
+            candidates = [None, None, None, []]
+            for rule in ruleset.rules:
+                candidates[3].append(self.buildFormat3Subtable(rule, chaining))
+
+            # Can we express the whole ruleset as a format 2 subtable?
+            classdefs = ruleset.format2ClassDefs()
+            if classdefs:
+                candidates[2] = [
+                    self.buildFormat2Subtable(ruleset, classdefs, chaining)
+                ]
+
+            if not ruleset.hasAnyGlyphClasses:
+                candidates[1] = [self.buildFormat1Subtable(ruleset, chaining)]
+
+            candidates = [x for x in candidates if x is not None]
+            winner = min(candidates, key=self.getCompiledSize_)
+            subtables.extend(winner)
+
+        # If we are not chaining, lookup type will be automatically fixed by
+        # buildLookup_
+        return self.buildLookup_(subtables)
+
+    def buildFormat1Subtable(self, ruleset, chaining=True):
+        st = self.newSubtable_(chaining=chaining)
+        st.Format = 1
+        st.populateDefaults()
+        coverage = set()
+        rulesetsByFirstGlyph = {}
+        ruleAttr = self.ruleAttr_(format=1, chaining=chaining)
+
+        for rule in ruleset.rules:
+            ruleAsSubtable = self.newRule_(format=1, chaining=chaining)
+
+            if chaining:
+                ruleAsSubtable.BacktrackGlyphCount = len(rule.prefix)
+                ruleAsSubtable.LookAheadGlyphCount = len(rule.suffix)
+                ruleAsSubtable.Backtrack = [list(x)[0] for x in reversed(rule.prefix)]
+                ruleAsSubtable.LookAhead = [list(x)[0] for x in rule.suffix]
+
+                ruleAsSubtable.InputGlyphCount = len(rule.glyphs)
+            else:
+                ruleAsSubtable.GlyphCount = len(rule.glyphs)
+
+            ruleAsSubtable.Input = [list(x)[0] for x in rule.glyphs[1:]]
+
+            self.buildLookupList(rule, ruleAsSubtable)
+
+            firstGlyph = list(rule.glyphs[0])[0]
+            if firstGlyph not in rulesetsByFirstGlyph:
+                coverage.add(firstGlyph)
+                rulesetsByFirstGlyph[firstGlyph] = []
+            rulesetsByFirstGlyph[firstGlyph].append(ruleAsSubtable)
+
+        st.Coverage = buildCoverage(coverage, self.glyphMap)
+        ruleSets = []
+        for g in st.Coverage.glyphs:
+            ruleSet = self.newRuleSet_(format=1, chaining=chaining)
+            setattr(ruleSet, ruleAttr, rulesetsByFirstGlyph[g])
+            setattr(ruleSet, f"{ruleAttr}Count", len(rulesetsByFirstGlyph[g]))
+            ruleSets.append(ruleSet)
+
+        setattr(st, self.ruleSetAttr_(format=1, chaining=chaining), ruleSets)
+        setattr(
+            st, self.ruleSetAttr_(format=1, chaining=chaining) + "Count", len(ruleSets)
+        )
+
+        return st
+
+    def buildFormat2Subtable(self, ruleset, classdefs, chaining=True):
+        st = self.newSubtable_(chaining=chaining)
+        st.Format = 2
+        st.populateDefaults()
+
+        if chaining:
+            (
+                st.BacktrackClassDef,
+                st.InputClassDef,
+                st.LookAheadClassDef,
+            ) = [c.build() for c in classdefs]
+        else:
+            st.ClassDef = classdefs[1].build()
+
+        inClasses = classdefs[1].classes()
+
+        classSets = []
+        for _ in inClasses:
+            classSet = self.newRuleSet_(format=2, chaining=chaining)
+            classSets.append(classSet)
+
+        coverage = set()
+        classRuleAttr = self.ruleAttr_(format=2, chaining=chaining)
+
+        for rule in ruleset.rules:
+            ruleAsSubtable = self.newRule_(format=2, chaining=chaining)
+            if chaining:
+                ruleAsSubtable.BacktrackGlyphCount = len(rule.prefix)
+                ruleAsSubtable.LookAheadGlyphCount = len(rule.suffix)
+                # The glyphs in the rule may be list, tuple, odict_keys...
+                # Order is not important anyway because they are guaranteed
+                # to be members of the same class.
+                ruleAsSubtable.Backtrack = [
+                    st.BacktrackClassDef.classDefs[list(x)[0]]
+                    for x in reversed(rule.prefix)
+                ]
+                ruleAsSubtable.LookAhead = [
+                    st.LookAheadClassDef.classDefs[list(x)[0]] for x in rule.suffix
+                ]
+
+                ruleAsSubtable.InputGlyphCount = len(rule.glyphs)
+                ruleAsSubtable.Input = [
+                    st.InputClassDef.classDefs[list(x)[0]] for x in rule.glyphs[1:]
+                ]
+                setForThisRule = classSets[
+                    st.InputClassDef.classDefs[list(rule.glyphs[0])[0]]
+                ]
+            else:
+                ruleAsSubtable.GlyphCount = len(rule.glyphs)
+                ruleAsSubtable.Class = [  # The spec calls this InputSequence
+                    st.ClassDef.classDefs[list(x)[0]] for x in rule.glyphs[1:]
+                ]
+                setForThisRule = classSets[
+                    st.ClassDef.classDefs[list(rule.glyphs[0])[0]]
+                ]
+
+            self.buildLookupList(rule, ruleAsSubtable)
+            coverage |= set(rule.glyphs[0])
+
+            getattr(setForThisRule, classRuleAttr).append(ruleAsSubtable)
+            setattr(
+                setForThisRule,
+                f"{classRuleAttr}Count",
+                getattr(setForThisRule, f"{classRuleAttr}Count") + 1,
+            )
+        setattr(st, self.ruleSetAttr_(format=2, chaining=chaining), classSets)
+        setattr(
+            st, self.ruleSetAttr_(format=2, chaining=chaining) + "Count", len(classSets)
+        )
+        st.Coverage = buildCoverage(coverage, self.glyphMap)
+        return st
+
+    def buildFormat3Subtable(self, rule, chaining=True):
+        st = self.newSubtable_(chaining=chaining)
+        st.Format = 3
+        if chaining:
+            self.setBacktrackCoverage_(rule.prefix, st)
+            self.setLookAheadCoverage_(rule.suffix, st)
+            self.setInputCoverage_(rule.glyphs, st)
+        else:
+            self.setCoverage_(rule.glyphs, st)
+        self.buildLookupList(rule, st)
+        return st
+
+    def buildLookupList(self, rule, st):
+        for sequenceIndex, lookupList in enumerate(rule.lookups):
+            if lookupList is not None:
+                if not isinstance(lookupList, list):
+                    # Can happen with synthesised lookups
+                    lookupList = [lookupList]
+                for l in lookupList:
+                    if l.lookup_index is None:
+                        if isinstance(self, ChainContextPosBuilder):
+                            other = "substitution"
+                        else:
+                            other = "positioning"
+                        raise OpenTypeLibError(
+                            "Missing index of the specified "
+                            f"lookup, might be a {other} lookup",
+                            self.location,
+                        )
+                    rec = self.newLookupRecord_(st)
+                    rec.SequenceIndex = sequenceIndex
+                    rec.LookupListIndex = l.lookup_index
+
+    def add_subtable_break(self, location):
+        self.rules.append(
+            ChainContextualRule(
+                self.SUBTABLE_BREAK_,
+                self.SUBTABLE_BREAK_,
+                self.SUBTABLE_BREAK_,
+                [self.SUBTABLE_BREAK_],
+            )
+        )
+
+    def newSubtable_(self, chaining=True):
+        subtablename = f"Context{self.subtable_type}"
+        if chaining:
+            subtablename = "Chain" + subtablename
+        st = getattr(ot, subtablename)()  # ot.ChainContextPos()/ot.ChainSubst()/etc.
+        setattr(st, f"{self.subtable_type}Count", 0)
+        setattr(st, f"{self.subtable_type}LookupRecord", [])
+        return st
+
+    # Format 1 and format 2 GSUB5/GSUB6/GPOS7/GPOS8 rulesets and rules form a family:
+    #
+    #       format 1 ruleset      format 1 rule      format 2 ruleset      format 2 rule
+    # GSUB5 SubRuleSet            SubRule            SubClassSet           SubClassRule
+    # GSUB6 ChainSubRuleSet       ChainSubRule       ChainSubClassSet      ChainSubClassRule
+    # GPOS7 PosRuleSet            PosRule            PosClassSet           PosClassRule
+    # GPOS8 ChainPosRuleSet       ChainPosRule       ChainPosClassSet      ChainPosClassRule
+    #
+    # The following functions generate the attribute names and subtables according
+    # to this naming convention.
+    def ruleSetAttr_(self, format=1, chaining=True):
+        if format == 1:
+            formatType = "Rule"
+        elif format == 2:
+            formatType = "Class"
+        else:
+            raise AssertionError(formatType)
+        subtablename = f"{self.subtable_type[0:3]}{formatType}Set"  # Sub, not Subst.
+        if chaining:
+            subtablename = "Chain" + subtablename
+        return subtablename
+
+    def ruleAttr_(self, format=1, chaining=True):
+        if format == 1:
+            formatType = ""
+        elif format == 2:
+            formatType = "Class"
+        else:
+            raise AssertionError(formatType)
+        subtablename = f"{self.subtable_type[0:3]}{formatType}Rule"  # Sub, not Subst.
+        if chaining:
+            subtablename = "Chain" + subtablename
+        return subtablename
+
+    def newRuleSet_(self, format=1, chaining=True):
+        st = getattr(
+            ot, self.ruleSetAttr_(format, chaining)
+        )()  # ot.ChainPosRuleSet()/ot.SubRuleSet()/etc.
+        st.populateDefaults()
+        return st
+
+    def newRule_(self, format=1, chaining=True):
+        st = getattr(
+            ot, self.ruleAttr_(format, chaining)
+        )()  # ot.ChainPosClassRule()/ot.SubClassRule()/etc.
+        st.populateDefaults()
+        return st
+
+    def attachSubtableWithCount_(
+        self, st, subtable_name, count_name, existing=None, index=None, chaining=False
+    ):
+        if chaining:
+            subtable_name = "Chain" + subtable_name
+            count_name = "Chain" + count_name
+
+        if not hasattr(st, count_name):
+            setattr(st, count_name, 0)
+            setattr(st, subtable_name, [])
+
+        if existing:
+            new_subtable = existing
+        else:
+            # Create a new, empty subtable from otTables
+            new_subtable = getattr(ot, subtable_name)()
+
+        setattr(st, count_name, getattr(st, count_name) + 1)
+
+        if index:
+            getattr(st, subtable_name).insert(index, new_subtable)
+        else:
+            getattr(st, subtable_name).append(new_subtable)
+
+        return new_subtable
+
+    def newLookupRecord_(self, st):
+        return self.attachSubtableWithCount_(
+            st,
+            f"{self.subtable_type}LookupRecord",
+            f"{self.subtable_type}Count",
+            chaining=False,
+        )  # Oddly, it isn't ChainSubstLookupRecord
+
+
+class ChainContextPosBuilder(ChainContextualBuilder):
+    """Builds a Chained Contextual Positioning (GPOS8) lookup.
+
+    Users are expected to manually add rules to the ``rules`` attribute after
+    the object has been initialized, e.g.::
+
+        # pos [A B] [C D] x' lookup lu1 y' z' lookup lu2 E;
+
+        prefix  = [ ["A", "B"], ["C", "D"] ]
+        suffix  = [ ["E"] ]
+        glyphs  = [ ["x"], ["y"], ["z"] ]
+        lookups = [ [lu1], None,  [lu2] ]
+        builder.rules.append( (prefix, glyphs, suffix, lookups) )
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        rules: A list of tuples representing the rules in this lookup.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, "GPOS", 8)
+        self.rules = []
+        self.subtable_type = "Pos"
+
+    def find_chainable_single_pos(self, lookups, glyphs, value):
+        """Helper for add_single_pos_chained_()"""
+        res = None
+        for lookup in lookups[::-1]:
+            if lookup == self.SUBTABLE_BREAK_:
+                return res
+            if isinstance(lookup, SinglePosBuilder) and all(
+                lookup.can_add(glyph, value) for glyph in glyphs
+            ):
+                res = lookup
+        return res
+
+
+class ChainContextSubstBuilder(ChainContextualBuilder):
+    """Builds a Chained Contextual Substitution (GSUB6) lookup.
+
+    Users are expected to manually add rules to the ``rules`` attribute after
+    the object has been initialized, e.g.::
+
+        # sub [A B] [C D] x' lookup lu1 y' z' lookup lu2 E;
+
+        prefix  = [ ["A", "B"], ["C", "D"] ]
+        suffix  = [ ["E"] ]
+        glyphs  = [ ["x"], ["y"], ["z"] ]
+        lookups = [ [lu1], None,  [lu2] ]
+        builder.rules.append( (prefix, glyphs, suffix, lookups) )
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        rules: A list of tuples representing the rules in this lookup.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, "GSUB", 6)
+        self.rules = []  # (prefix, input, suffix, lookups)
+        self.subtable_type = "Subst"
+
+    def getAlternateGlyphs(self):
+        result = {}
+        for rule in self.rules:
+            if rule.is_subtable_break:
+                continue
+            for lookups in rule.lookups:
+                if not isinstance(lookups, list):
+                    lookups = [lookups]
+                for lookup in lookups:
+                    if lookup is not None:
+                        alts = lookup.getAlternateGlyphs()
+                        for glyph, replacements in alts.items():
+                            result.setdefault(glyph, set()).update(replacements)
+        return result
+
+    def find_chainable_single_subst(self, glyphs):
+        """Helper for add_single_subst_chained_()"""
+        res = None
+        for rule in self.rules[::-1]:
+            if rule.is_subtable_break:
+                return res
+            for sub in rule.lookups:
+                if isinstance(sub, SingleSubstBuilder) and not any(
+                    g in glyphs for g in sub.mapping.keys()
+                ):
+                    res = sub
+        return res
+
+
+class LigatureSubstBuilder(LookupBuilder):
+    """Builds a Ligature Substitution (GSUB4) lookup.
+
+    Users are expected to manually add ligatures to the ``ligatures``
+    attribute after the object has been initialized, e.g.::
+
+        # sub f i by f_i;
+        builder.ligatures[("f","f","i")] = "f_f_i"
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        ligatures: An ordered dictionary mapping a tuple of glyph names to the
+            ligature glyphname.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, "GSUB", 4)
+        self.ligatures = OrderedDict()  # {('f','f','i'): 'f_f_i'}
+
+    def equals(self, other):
+        return LookupBuilder.equals(self, other) and self.ligatures == other.ligatures
+
+    def build(self):
+        """Build the lookup.
+
+        Returns:
+            An ``otTables.Lookup`` object representing the ligature
+            substitution lookup.
+        """
+        subtables = self.build_subst_subtables(
+            self.ligatures, buildLigatureSubstSubtable
+        )
+        return self.buildLookup_(subtables)
+
+    def add_subtable_break(self, location):
+        self.ligatures[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
+
+
+class MultipleSubstBuilder(LookupBuilder):
+    """Builds a Multiple Substitution (GSUB2) lookup.
+
+    Users are expected to manually add substitutions to the ``mapping``
+    attribute after the object has been initialized, e.g.::
+
+        # sub uni06C0 by uni06D5.fina hamza.above;
+        builder.mapping["uni06C0"] = [ "uni06D5.fina", "hamza.above"]
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        mapping: An ordered dictionary mapping a glyph name to a list of
+            substituted glyph names.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, "GSUB", 2)
+        self.mapping = OrderedDict()
+
+    def equals(self, other):
+        return LookupBuilder.equals(self, other) and self.mapping == other.mapping
+
+    def build(self):
+        subtables = self.build_subst_subtables(self.mapping, buildMultipleSubstSubtable)
+        return self.buildLookup_(subtables)
+
+    def add_subtable_break(self, location):
+        self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
+
+
+class CursivePosBuilder(LookupBuilder):
+    """Builds a Cursive Positioning (GPOS3) lookup.
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        attachments: An ordered dictionary mapping a glyph name to a two-element
+            tuple of ``otTables.Anchor`` objects.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    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):
+        """Adds attachment information to the cursive positioning lookup.
+
+        Args:
+            location: A string or tuple representing the location in the
+                original source which produced this lookup. (Unused.)
+            glyphs: A list of glyph names sharing these entry and exit
+                anchor locations.
+            entryAnchor: A ``otTables.Anchor`` object representing the
+                entry anchor, or ``None`` if no entry anchor is present.
+            exitAnchor: A ``otTables.Anchor`` object representing the
+                exit anchor, or ``None`` if no exit anchor is present.
+        """
+        for glyph in glyphs:
+            self.attachments[glyph] = (entryAnchor, exitAnchor)
+
+    def build(self):
+        """Build the lookup.
+
+        Returns:
+            An ``otTables.Lookup`` object representing the cursive
+            positioning lookup.
+        """
+        st = buildCursivePosSubtable(self.attachments, self.glyphMap)
+        return self.buildLookup_([st])
+
+
+class MarkBasePosBuilder(LookupBuilder):
+    """Builds a Mark-To-Base Positioning (GPOS4) lookup.
+
+    Users are expected to manually add marks and bases to the ``marks``
+    and ``bases`` attributes after the object has been initialized, e.g.::
+
+        builder.marks["acute"]   = (0, a1)
+        builder.marks["grave"]   = (0, a1)
+        builder.marks["cedilla"] = (1, a2)
+        builder.bases["a"] = {0: a3, 1: a5}
+        builder.bases["b"] = {0: a4, 1: a5}
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        marks: An dictionary mapping a glyph name to a two-element
+            tuple containing a mark class ID and ``otTables.Anchor`` object.
+        bases: An dictionary mapping a glyph name to a dictionary of
+            mark class IDs and ``otTables.Anchor`` object.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    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):
+        """Build the lookup.
+
+        Returns:
+            An ``otTables.Lookup`` object representing the mark-to-base
+            positioning lookup.
+        """
+        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 = buildMarkBasePos(marks, bases, self.glyphMap)
+        return self.buildLookup_(subtables)
+
+
+class MarkLigPosBuilder(LookupBuilder):
+    """Builds a Mark-To-Ligature Positioning (GPOS5) lookup.
+
+    Users are expected to manually add marks and bases to the ``marks``
+    and ``ligatures`` attributes after the object has been initialized, e.g.::
+
+        builder.marks["acute"]   = (0, a1)
+        builder.marks["grave"]   = (0, a1)
+        builder.marks["cedilla"] = (1, a2)
+        builder.ligatures["f_i"] = [
+            { 0: a3, 1: a5 }, # f
+            { 0: a4, 1: a5 }  # i
+        ]
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        marks: An dictionary mapping a glyph name to a two-element
+            tuple containing a mark class ID and ``otTables.Anchor`` object.
+        ligatures: An dictionary mapping a glyph name to an array with one
+            element for each ligature component. Each array element should be
+            a dictionary mapping mark class IDs to ``otTables.Anchor`` objects.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    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):
+        """Build the lookup.
+
+        Returns:
+            An ``otTables.Lookup`` object representing the mark-to-ligature
+            positioning lookup.
+        """
+        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 = buildMarkLigPos(marks, ligs, self.glyphMap)
+        return self.buildLookup_(subtables)
+
+
+class MarkMarkPosBuilder(LookupBuilder):
+    """Builds a Mark-To-Mark Positioning (GPOS6) lookup.
+
+    Users are expected to manually add marks and bases to the ``marks``
+    and ``baseMarks`` attributes after the object has been initialized, e.g.::
+
+        builder.marks["acute"]     = (0, a1)
+        builder.marks["grave"]     = (0, a1)
+        builder.marks["cedilla"]   = (1, a2)
+        builder.baseMarks["acute"] = {0: a3}
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        marks: An dictionary mapping a glyph name to a two-element
+            tuple containing a mark class ID and ``otTables.Anchor`` object.
+        baseMarks: An dictionary mapping a glyph name to a dictionary
+            containing one item: a mark class ID and a ``otTables.Anchor`` object.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    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):
+        """Build the lookup.
+
+        Returns:
+            An ``otTables.Lookup`` object representing the mark-to-mark
+            positioning lookup.
+        """
+        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 = ot.MarkMarkPos()
+        st.Format = 1
+        st.ClassCount = len(markClasses)
+        st.Mark1Coverage = buildCoverage(marks, self.glyphMap)
+        st.Mark2Coverage = buildCoverage(self.baseMarks, self.glyphMap)
+        st.Mark1Array = buildMarkArray(marks, self.glyphMap)
+        st.Mark2Array = ot.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(buildMark2Record(anchors))
+        return self.buildLookup_([st])
+
+
+class ReverseChainSingleSubstBuilder(LookupBuilder):
+    """Builds a Reverse Chaining Contextual Single Substitution (GSUB8) lookup.
+
+    Users are expected to manually add substitutions to the ``substitutions``
+    attribute after the object has been initialized, e.g.::
+
+        # reversesub [a e n] d' by d.alt;
+        prefix = [ ["a", "e", "n"] ]
+        suffix = []
+        mapping = { "d": "d.alt" }
+        builder.substitutions.append( (prefix, suffix, mapping) )
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        substitutions: A three-element tuple consisting of a prefix sequence,
+            a suffix sequence, and a dictionary of single substitutions.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, "GSUB", 8)
+        self.rules = []  # (prefix, suffix, mapping)
+
+    def equals(self, other):
+        return LookupBuilder.equals(self, other) and self.rules == other.rules
+
+    def build(self):
+        """Build the lookup.
+
+        Returns:
+            An ``otTables.Lookup`` object representing the chained
+            contextual substitution lookup.
+        """
+        subtables = []
+        for prefix, suffix, mapping in self.rules:
+            st = ot.ReverseChainSingleSubst()
+            st.Format = 1
+            self.setBacktrackCoverage_(prefix, st)
+            self.setLookAheadCoverage_(suffix, st)
+            st.Coverage = 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)
+
+    def add_subtable_break(self, location):
+        # Nothing to do here, each substitution is in its own subtable.
+        pass
+
+
+class SingleSubstBuilder(LookupBuilder):
+    """Builds a Single Substitution (GSUB1) lookup.
+
+    Users are expected to manually add substitutions to the ``mapping``
+    attribute after the object has been initialized, e.g.::
+
+        # sub x by y;
+        builder.mapping["x"] = "y"
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        mapping: A dictionary mapping a single glyph name to another glyph name.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, "GSUB", 1)
+        self.mapping = OrderedDict()
+
+    def equals(self, other):
+        return LookupBuilder.equals(self, other) and self.mapping == other.mapping
+
+    def build(self):
+        """Build the lookup.
+
+        Returns:
+            An ``otTables.Lookup`` object representing the multiple
+            substitution lookup.
+        """
+        subtables = self.build_subst_subtables(self.mapping, buildSingleSubstSubtable)
+        return self.buildLookup_(subtables)
+
+    def getAlternateGlyphs(self):
+        return {glyph: set([repl]) for glyph, repl in self.mapping.items()}
+
+    def add_subtable_break(self, location):
+        self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
+
+
+class ClassPairPosSubtableBuilder(object):
+    """Builds class-based Pair Positioning (GPOS2 format 2) subtables.
+
+    Note that this does *not* build a GPOS2 ``otTables.Lookup`` directly,
+    but builds a list of ``otTables.PairPos`` subtables. It is used by the
+    :class:`PairPosBuilder` below.
+
+    Attributes:
+        builder (PairPosBuilder): A pair positioning lookup builder.
+    """
+
+    def __init__(self, builder):
+        self.builder_ = builder
+        self.classDef1_, self.classDef2_ = None, None
+        self.values_ = {}  # (glyphclass1, glyphclass2) --> (value1, value2)
+        self.forceSubtableBreak_ = False
+        self.subtables_ = []
+
+    def addPair(self, gc1, value1, gc2, value2):
+        """Add a pair positioning rule.
+
+        Args:
+            gc1: A set of glyph names for the "left" glyph
+            value1: An ``otTables.ValueRecord`` object for the left glyph's
+                positioning.
+            gc2: A set of glyph names for the "right" glyph
+            value2: An ``otTables.ValueRecord`` object for the right glyph's
+                positioning.
+        """
+        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_ = ClassDefBuilder(useClass0=True)
+            self.classDef2_ = ClassDefBuilder(useClass0=False)
+            self.values_ = {}
+        self.classDef1_.add(gc1)
+        self.classDef2_.add(gc2)
+        self.values_[(gc1, gc2)] = (value1, value2)
+
+    def addSubtableBreak(self):
+        """Add an explicit subtable break at this point."""
+        self.forceSubtableBreak_ = True
+
+    def subtables(self):
+        """Return the list of ``otTables.PairPos`` subtables constructed."""
+        self.flush_()
+        return self.subtables_
+
+    def flush_(self):
+        if self.classDef1_ is None or self.classDef2_ is None:
+            return
+        st = buildPairPosClassesSubtable(self.values_, self.builder_.glyphMap)
+        if st.Coverage is None:
+            return
+        self.subtables_.append(st)
+        self.forceSubtableBreak_ = False
+
+
+class PairPosBuilder(LookupBuilder):
+    """Builds a Pair Positioning (GPOS2) lookup.
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        pairs: An array of class-based pair positioning tuples. Usually
+            manipulated with the :meth:`addClassPair` method below.
+        glyphPairs: A dictionary mapping a tuple of glyph names to a tuple
+            of ``otTables.ValueRecord`` objects. Usually manipulated with the
+            :meth:`addGlyphPair` method below.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    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):
+        """Add a class pair positioning rule to the current lookup.
+
+        Args:
+            location: A string or tuple representing the location in the
+                original source which produced this rule. Unused.
+            glyphclass1: A set of glyph names for the "left" glyph in the pair.
+            value1: A ``otTables.ValueRecord`` for positioning the left glyph.
+            glyphclass2: A set of glyph names for the "right" glyph in the pair.
+            value2: A ``otTables.ValueRecord`` for positioning the right glyph.
+        """
+        self.pairs.append((glyphclass1, value1, glyphclass2, value2))
+
+    def addGlyphPair(self, location, glyph1, value1, glyph2, value2):
+        """Add a glyph pair positioning rule to the current lookup.
+
+        Args:
+            location: A string or tuple representing the location in the
+                original source which produced this rule.
+            glyph1: A glyph name for the "left" glyph in the pair.
+            value1: A ``otTables.ValueRecord`` for positioning the left glyph.
+            glyph2: A glyph name for the "right" glyph in the pair.
+            value2: A ``otTables.ValueRecord`` for positioning the right glyph.
+        """
+        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
+            otherLoc = self.locations[key]
+            log.debug(
+                "Already defined position for pair %s %s at %s; "
+                "choosing the first value",
+                glyph1,
+                glyph2,
+                otherLoc,
+            )
+        else:
+            self.glyphPairs[key] = (value1, value2)
+            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):
+        """Build the lookup.
+
+        Returns:
+            An ``otTables.Lookup`` object representing the pair positioning
+            lookup.
+        """
+        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
+            valFormat1, valFormat2 = 0, 0
+            if value1:
+                valFormat1 = value1.getFormat()
+            if value2:
+                valFormat2 = value2.getFormat()
+            builder = builders.get((valFormat1, valFormat2))
+            if builder is None:
+                builder = ClassPairPosSubtableBuilder(self)
+                builders[(valFormat1, valFormat2)] = builder
+            builder.addPair(glyphclass1, value1, glyphclass2, value2)
+        subtables = []
+        if self.glyphPairs:
+            subtables.extend(buildPairPosGlyphs(self.glyphPairs, self.glyphMap))
+        for key in sorted(builders.keys()):
+            subtables.extend(builders[key].subtables())
+        lookup = self.buildLookup_(subtables)
+
+        # Compact the lookup
+        # This is a good moment to do it because the compaction should create
+        # smaller subtables, which may prevent overflows from happening.
+        mode = os.environ.get(GPOS_COMPACT_MODE_ENV_KEY, GPOS_COMPACT_MODE_DEFAULT)
+        if mode and mode != "0":
+            log.info("Compacting GPOS...")
+            compact_lookup(self.font, mode, lookup)
+
+        return lookup
+
+
+class SinglePosBuilder(LookupBuilder):
+    """Builds a Single Positioning (GPOS1) lookup.
+
+    Attributes:
+        font (``fontTools.TTLib.TTFont``): A font object.
+        location: A string or tuple representing the location in the original
+            source which produced this lookup.
+        mapping: A dictionary mapping a glyph name to a ``otTables.ValueRecord``
+            objects. Usually manipulated with the :meth:`add_pos` method below.
+        lookupflag (int): The lookup's flag
+        markFilterSet: Either ``None`` if no mark filtering set is used, or
+            an integer representing the filtering set to be used for this
+            lookup. If a mark filtering set is provided,
+            `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
+            flags.
+    """
+
+    def __init__(self, font, location):
+        LookupBuilder.__init__(self, font, location, "GPOS", 1)
+        self.locations = {}  # glyph -> (filename, line, column)
+        self.mapping = {}  # glyph -> ot.ValueRecord
+
+    def add_pos(self, location, glyph, otValueRecord):
+        """Add a single positioning rule.
+
+        Args:
+            location: A string or tuple representing the location in the
+                original source which produced this lookup.
+            glyph: A glyph name.
+            otValueRection: A ``otTables.ValueRecord`` used to position the
+                glyph.
+        """
+        if not self.can_add(glyph, otValueRecord):
+            otherLoc = self.locations[glyph]
+            raise OpenTypeLibError(
+                'Already defined different position for glyph "%s" at %s'
+                % (glyph, otherLoc),
+                location,
+            )
+        if otValueRecord:
+            self.mapping[glyph] = otValueRecord
+        self.locations[glyph] = location
+
+    def can_add(self, glyph, value):
+        assert isinstance(value, 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):
+        """Build the lookup.
+
+        Returns:
+            An ``otTables.Lookup`` object representing the single positioning
+            lookup.
+        """
+        subtables = buildSinglePos(self.mapping, self.glyphMap)
+        return self.buildLookup_(subtables)
+
+
 # GSUB
 
 
 def buildSingleSubstSubtable(mapping):
+    """Builds a single substitution (GSUB1) subtable.
+
+    Note that if you are implementing a layout compiler, you may find it more
+    flexible to use
+    :py:class:`fontTools.otlLib.lookupBuilders.SingleSubstBuilder` instead.
+
+    Args:
+        mapping: A dictionary mapping input glyph names to output glyph names.
+
+    Returns:
+        An ``otTables.SingleSubst`` object, or ``None`` if the mapping dictionary
+        is empty.
+    """
     if not mapping:
         return None
     self = ot.SingleSubst()
@@ -58,6 +1475,30 @@
 
 
 def buildMultipleSubstSubtable(mapping):
+    """Builds a multiple substitution (GSUB2) subtable.
+
+    Note that if you are implementing a layout compiler, you may find it more
+    flexible to use
+    :py:class:`fontTools.otlLib.lookupBuilders.MultipleSubstBuilder` instead.
+
+    Example::
+
+        # sub uni06C0 by uni06D5.fina hamza.above
+        # sub uni06C2 by uni06C1.fina hamza.above;
+
+        subtable = buildMultipleSubstSubtable({
+            "uni06C0": [ "uni06D5.fina", "hamza.above"],
+            "uni06C2": [ "uni06D1.fina", "hamza.above"]
+        })
+
+    Args:
+        mapping: A dictionary mapping input glyph names to a list of output
+            glyph names.
+
+    Returns:
+        An ``otTables.MultipleSubst`` object or ``None`` if the mapping dictionary
+        is empty.
+    """
     if not mapping:
         return None
     self = ot.MultipleSubst()
@@ -66,6 +1507,20 @@
 
 
 def buildAlternateSubstSubtable(mapping):
+    """Builds an alternate substitution (GSUB3) subtable.
+
+    Note that if you are implementing a layout compiler, you may find it more
+    flexible to use
+    :py:class:`fontTools.otlLib.lookupBuilders.AlternateSubstBuilder` instead.
+
+    Args:
+        mapping: A dictionary mapping input glyph names to a list of output
+            glyph names.
+
+    Returns:
+        An ``otTables.AlternateSubst`` object or ``None`` if the mapping dictionary
+        is empty.
+    """
     if not mapping:
         return None
     self = ot.AlternateSubst()
@@ -74,20 +1529,44 @@
 
 
 def _getLigatureKey(components):
-    """Computes a key for ordering ligatures in a GSUB Type-4 lookup.
+    # 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.
+    # 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).
-    """
+    # For example, this will sort (f,f,f) < (f,f,i) < (f,f) < (f,i) < (f,l).
     return (-len(components), components)
 
 
 def buildLigatureSubstSubtable(mapping):
+    """Builds a ligature substitution (GSUB4) subtable.
+
+    Note that if you are implementing a layout compiler, you may find it more
+    flexible to use
+    :py:class:`fontTools.otlLib.lookupBuilders.LigatureSubstBuilder` instead.
+
+    Example::
+
+        # sub f f i by f_f_i;
+        # sub f i by f_i;
+
+        subtable = buildLigatureSubstSubtable({
+            ("f", "f", "i"): "f_f_i",
+            ("f", "i"): "f_i",
+        })
+
+    Args:
+        mapping: A dictionary mapping tuples of glyph names to output
+            glyph names.
+
+    Returns:
+        An ``otTables.LigatureSubst`` object or ``None`` if the mapping dictionary
+        is empty.
+    """
+
     if not mapping:
         return None
     self = ot.LigatureSubst()
@@ -109,6 +1588,20 @@
 
 
 def buildAnchor(x, y, point=None, deviceX=None, deviceY=None):
+    """Builds an Anchor table.
+
+    This determines the appropriate anchor format based on the passed parameters.
+
+    Args:
+        x (int): X coordinate.
+        y (int): Y coordinate.
+        point (int): Index of glyph contour point, if provided.
+        deviceX (``otTables.Device``): X coordinate device table, if provided.
+        deviceY (``otTables.Device``): Y coordinate device table, if provided.
+
+    Returns:
+        An ``otTables.Anchor`` object.
+    """
     self = ot.Anchor()
     self.XCoordinate, self.YCoordinate = x, y
     self.Format = 1
@@ -116,8 +1609,9 @@
         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."
+        assert (
+            self.Format == 1
+        ), "Either point, or both of deviceX/deviceY, must be None."
         self.XDeviceTable = deviceX
         self.YDeviceTable = deviceY
         self.Format = 3
@@ -125,6 +1619,31 @@
 
 
 def buildBaseArray(bases, numMarkClasses, glyphMap):
+    """Builds a base array record.
+
+    As part of building mark-to-base positioning rules, you will need to define
+    a ``BaseArray`` record, which "defines for each base glyph an array of
+    anchors, one for each mark class." This function builds the base array
+    subtable.
+
+    Example::
+
+        bases = {"a": {0: a3, 1: a5}, "b": {0: a4, 1: a5}}
+        basearray = buildBaseArray(bases, 2, font.getReverseGlyphMap())
+
+    Args:
+        bases (dict): A dictionary mapping anchors to glyphs; the keys being
+            glyph names, and the values being dictionaries mapping mark class ID
+            to the appropriate ``otTables.Anchor`` object used for attaching marks
+            of that class.
+        numMarkClasses (int): The total number of mark classes for which anchors
+            are defined.
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        An ``otTables.BaseArray`` object.
+    """
     self = ot.BaseArray()
     self.BaseRecord = []
     for base in sorted(bases, key=glyphMap.__getitem__):
@@ -136,14 +1655,27 @@
 
 
 def buildBaseRecord(anchors):
-    """[otTables.Anchor, otTables.Anchor, ...] --> otTables.BaseRecord"""
+    # [otTables.Anchor, otTables.Anchor, ...] --> otTables.BaseRecord
     self = ot.BaseRecord()
     self.BaseAnchor = anchors
     return self
 
 
 def buildComponentRecord(anchors):
-    """[otTables.Anchor, otTables.Anchor, ...] --> otTables.ComponentRecord"""
+    """Builds a component record.
+
+    As part of building mark-to-ligature positioning rules, you will need to
+    define ``ComponentRecord`` objects, which contain "an array of offsets...
+    to the Anchor tables that define all the attachment points used to attach
+    marks to the component." This function builds the component record.
+
+    Args:
+        anchors: A list of ``otTables.Anchor`` objects or ``None``.
+
+    Returns:
+        A ``otTables.ComponentRecord`` object or ``None`` if no anchors are
+        supplied.
+    """
     if not anchors:
         return None
     self = ot.ComponentRecord()
@@ -152,7 +1684,30 @@
 
 
 def buildCursivePosSubtable(attach, glyphMap):
-    """{"alef": (entry, exit)} --> otTables.CursivePos"""
+    """Builds a cursive positioning (GPOS3) subtable.
+
+    Cursive positioning lookups are made up of a coverage table of glyphs,
+    and a set of ``EntryExitRecord`` records containing the anchors for
+    each glyph. This function builds the cursive positioning subtable.
+
+    Example::
+
+        subtable = buildCursivePosSubtable({
+            "AlifIni": (None, buildAnchor(0, 50)),
+            "BehMed": (buildAnchor(500,250), buildAnchor(0,50)),
+            # ...
+        }, font.getReverseGlyphMap())
+
+    Args:
+        attach (dict): A mapping between glyph names and a tuple of two
+            ``otTables.Anchor`` objects representing entry and exit anchors.
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        An ``otTables.CursivePos`` object, or ``None`` if the attachment
+        dictionary was empty.
+    """
     if not attach:
         return None
     self = ot.CursivePos()
@@ -170,7 +1725,22 @@
 
 
 def buildDevice(deltas):
-    """{8:+1, 10:-3, ...} --> otTables.Device"""
+    """Builds a Device record as part of a ValueRecord or Anchor.
+
+    Device tables specify size-specific adjustments to value records
+    and anchors to reflect changes based on the resolution of the output.
+    For example, one could specify that an anchor's Y position should be
+    increased by 1 pixel when displayed at 8 pixels per em. This routine
+    builds device records.
+
+    Args:
+        deltas: A dictionary mapping pixels-per-em sizes to the delta
+            adjustment in pixels when the font is displayed at that size.
+
+    Returns:
+        An ``otTables.Device`` object if any deltas were supplied, or
+        ``None`` otherwise.
+    """
     if not deltas:
         return None
     self = ot.Device()
@@ -179,8 +1749,8 @@
     self.EndSize = endSize = max(keys)
     assert 0 <= startSize <= endSize
     self.DeltaValue = deltaValues = [
-        deltas.get(size, 0)
-        for size in range(startSize, endSize + 1)]
+        deltas.get(size, 0) for size in range(startSize, endSize + 1)
+    ]
     maxDelta = max(deltaValues)
     minDelta = min(deltaValues)
     assert minDelta > -129 and maxDelta < 128
@@ -194,6 +1764,36 @@
 
 
 def buildLigatureArray(ligs, numMarkClasses, glyphMap):
+    """Builds a LigatureArray subtable.
+
+    As part of building a mark-to-ligature lookup, you will need to define
+    the set of anchors (for each mark class) on each component of the ligature
+    where marks can be attached. For example, for an Arabic divine name ligature
+    (lam lam heh), you may want to specify mark attachment positioning for
+    superior marks (fatha, etc.) and inferior marks (kasra, etc.) on each glyph
+    of the ligature. This routine builds the ligature array record.
+
+    Example::
+
+        buildLigatureArray({
+            "lam-lam-heh": [
+                { 0: superiorAnchor1, 1: inferiorAnchor1 }, # attach points for lam1
+                { 0: superiorAnchor2, 1: inferiorAnchor2 }, # attach points for lam2
+                { 0: superiorAnchor3, 1: inferiorAnchor3 }, # attach points for heh
+            ]
+        }, 2, font.getReverseGlyphMap())
+
+    Args:
+        ligs (dict): A mapping of ligature names to an array of dictionaries:
+            for each component glyph in the ligature, an dictionary mapping
+            mark class IDs to anchors.
+        numMarkClasses (int): The number of mark classes.
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        An ``otTables.LigatureArray`` object if deltas were supplied.
+    """
     self = ot.LigatureArray()
     self.LigatureAttach = []
     for lig in sorted(ligs, key=glyphMap.__getitem__):
@@ -206,7 +1806,7 @@
 
 
 def buildLigatureAttach(components):
-    """[[Anchor, Anchor], [Anchor, Anchor, Anchor]] --> LigatureAttach"""
+    # [[Anchor, Anchor], [Anchor, Anchor, Anchor]] --> LigatureAttach
     self = ot.LigatureAttach()
     self.ComponentRecord = [buildComponentRecord(c) for c in components]
     self.ComponentCount = len(self.ComponentRecord)
@@ -214,7 +1814,31 @@
 
 
 def buildMarkArray(marks, glyphMap):
-    """{"acute": (markClass, otTables.Anchor)} --> otTables.MarkArray"""
+    """Builds a mark array subtable.
+
+    As part of building mark-to-* positioning rules, you will need to define
+    a MarkArray subtable, which "defines the class and the anchor point
+    for a mark glyph." This function builds the mark array subtable.
+
+    Example::
+
+        mark = {
+            "acute": (0, buildAnchor(300,712)),
+            # ...
+        }
+        markarray = buildMarkArray(marks, font.getReverseGlyphMap())
+
+    Args:
+        marks (dict): A dictionary mapping anchors to glyphs; the keys being
+            glyph names, and the values being a tuple of mark class number and
+            an ``otTables.Anchor`` object representing the mark's attachment
+            point.
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        An ``otTables.MarkArray`` object.
+    """
     self = ot.MarkArray()
     self.MarkRecord = []
     for mark in sorted(marks.keys(), key=glyphMap.__getitem__):
@@ -226,11 +1850,40 @@
 
 
 def buildMarkBasePos(marks, bases, glyphMap):
-    """Build a list of MarkBasePos subtables.
+    """Build a list of MarkBasePos (GPOS4) 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}}
+    This routine turns a set of marks and bases into a list of mark-to-base
+    positioning subtables. Currently the list will contain a single subtable
+    containing all marks and bases, although at a later date it may return the
+    optimal list of subtables subsetting the marks and bases into groups which
+    save space. See :func:`buildMarkBasePosSubtable` below.
+
+    Note that if you are implementing a layout compiler, you may find it more
+    flexible to use
+    :py:class:`fontTools.otlLib.lookupBuilders.MarkBasePosBuilder` instead.
+
+    Example::
+
+        # 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}}
+        markbaseposes = buildMarkBasePos(marks, bases, font.getReverseGlyphMap())
+
+    Args:
+        marks (dict): A dictionary mapping anchors to glyphs; the keys being
+            glyph names, and the values being a tuple of mark class number and
+            an ``otTables.Anchor`` object representing the mark's attachment
+            point. (See :func:`buildMarkArray`.)
+        bases (dict): A dictionary mapping anchors to glyphs; the keys being
+            glyph names, and the values being dictionaries mapping mark class ID
+            to the appropriate ``otTables.Anchor`` object used for attaching marks
+            of that class. (See :func:`buildBaseArray`.)
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        A list of ``otTables.MarkBasePos`` objects.
     """
     # TODO: Consider emitting multiple subtables to save space.
     # Partition the marks and bases into disjoint subsets, so that
@@ -247,11 +1900,25 @@
 
 
 def buildMarkBasePosSubtable(marks, bases, glyphMap):
-    """Build a single MarkBasePos subtable.
+    """Build a single MarkBasePos (GPOS4) 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}}
+    This builds a mark-to-base lookup subtable containing all of the referenced
+    marks and bases. See :func:`buildMarkBasePos`.
+
+    Args:
+        marks (dict): A dictionary mapping anchors to glyphs; the keys being
+            glyph names, and the values being a tuple of mark class number and
+            an ``otTables.Anchor`` object representing the mark's attachment
+            point. (See :func:`buildMarkArray`.)
+        bases (dict): A dictionary mapping anchors to glyphs; the keys being
+            glyph names, and the values being dictionaries mapping mark class ID
+            to the appropriate ``otTables.Anchor`` object used for attaching marks
+            of that class. (See :func:`buildBaseArray`.)
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        A ``otTables.MarkBasePos`` object.
     """
     self = ot.MarkBasePos()
     self.Format = 1
@@ -264,11 +1931,50 @@
 
 
 def buildMarkLigPos(marks, ligs, glyphMap):
-    """Build a list of MarkLigPos subtables.
+    """Build a list of MarkLigPos (GPOS5) 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": [{...}, {...}]}
+    This routine turns a set of marks and ligatures into a list of mark-to-ligature
+    positioning subtables. Currently the list will contain a single subtable
+    containing all marks and ligatures, although at a later date it may return
+    the optimal list of subtables subsetting the marks and ligatures into groups
+    which save space. See :func:`buildMarkLigPosSubtable` below.
+
+    Note that if you are implementing a layout compiler, you may find it more
+    flexible to use
+    :py:class:`fontTools.otlLib.lookupBuilders.MarkLigPosBuilder` instead.
+
+    Example::
+
+        # 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 }, # f
+                { 0: a4, 1: a5 }  # i
+                ],
+        #   "c_t": [{...}, {...}]
+        }
+        markligposes = buildMarkLigPos(marks, ligs,
+            font.getReverseGlyphMap())
+
+    Args:
+        marks (dict): A dictionary mapping anchors to glyphs; the keys being
+            glyph names, and the values being a tuple of mark class number and
+            an ``otTables.Anchor`` object representing the mark's attachment
+            point. (See :func:`buildMarkArray`.)
+        ligs (dict): A mapping of ligature names to an array of dictionaries:
+            for each component glyph in the ligature, an dictionary mapping
+            mark class IDs to anchors. (See :func:`buildLigatureArray`.)
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        A list of ``otTables.MarkLigPos`` objects.
+
     """
     # TODO: Consider splitting into multiple subtables to save space,
     # as with MarkBasePos, this would be a trade-off that would need
@@ -278,11 +1984,24 @@
 
 
 def buildMarkLigPosSubtable(marks, ligs, glyphMap):
-    """Build a single MarkLigPos subtable.
+    """Build a single MarkLigPos (GPOS5) 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": [{...}, {...}]}
+    This builds a mark-to-base lookup subtable containing all of the referenced
+    marks and bases. See :func:`buildMarkLigPos`.
+
+    Args:
+        marks (dict): A dictionary mapping anchors to glyphs; the keys being
+            glyph names, and the values being a tuple of mark class number and
+            an ``otTables.Anchor`` object representing the mark's attachment
+            point. (See :func:`buildMarkArray`.)
+        ligs (dict): A mapping of ligature names to an array of dictionaries:
+            for each component glyph in the ligature, an dictionary mapping
+            mark class IDs to anchors. (See :func:`buildLigatureArray`.)
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        A ``otTables.MarkLigPos`` object.
     """
     self = ot.MarkLigPos()
     self.Format = 1
@@ -304,14 +2023,14 @@
 
 
 def buildMark2Record(anchors):
-    """[otTables.Anchor, otTables.Anchor, ...] --> otTables.Mark2Record"""
+    # [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."""
+    # Helper for buildPairPos{Glyphs|Classes}Subtable.
     if f is not None:
         return f
     mask = 0
@@ -321,8 +2040,45 @@
     return mask
 
 
-def buildPairPosClassesSubtable(pairs, glyphMap,
-                                valueFormat1=None, valueFormat2=None):
+def buildPairPosClassesSubtable(pairs, glyphMap, valueFormat1=None, valueFormat2=None):
+    """Builds a class pair adjustment (GPOS2 format 2) subtable.
+
+    Kerning tables are generally expressed as pair positioning tables using
+    class-based pair adjustments. This routine builds format 2 PairPos
+    subtables.
+
+    Note that if you are implementing a layout compiler, you may find it more
+    flexible to use
+    :py:class:`fontTools.otlLib.lookupBuilders.ClassPairPosSubtableBuilder`
+    instead, as this takes care of ensuring that the supplied pairs can be
+    formed into non-overlapping classes and emitting individual subtables
+    whenever the non-overlapping requirement means that a new subtable is
+    required.
+
+    Example::
+
+        pairs = {}
+
+        pairs[(
+            [ "K", "X" ],
+            [ "W", "V" ]
+        )] = ( buildValue(xAdvance=+5), buildValue() )
+        # pairs[(... , ...)] = (..., ...)
+
+        pairpos = buildPairPosClassesSubtable(pairs, font.getReverseGlyphMap())
+
+    Args:
+        pairs (dict): Pair positioning data; the keys being a two-element
+            tuple of lists of glyphnames, and the values being a two-element
+            tuple of ``otTables.ValueRecord`` objects.
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+        valueFormat1: Force the "left" value records to the given format.
+        valueFormat2: Force the "right" value records to the given format.
+
+    Returns:
+        A ``otTables.PairPos`` object.
+    """
     coverage = set()
     classDef1 = ClassDefBuilder(useClass0=True)
     classDef2 = ClassDefBuilder(useClass0=False)
@@ -332,8 +2088,8 @@
         classDef2.add(gc2)
     self = ot.PairPos()
     self.Format = 2
-    self.ValueFormat1 = _getValueFormat(valueFormat1, pairs.values(), 0)
-    self.ValueFormat2 = _getValueFormat(valueFormat2, pairs.values(), 1)
+    valueFormat1 = self.ValueFormat1 = _getValueFormat(valueFormat1, pairs.values(), 0)
+    valueFormat2 = self.ValueFormat2 = _getValueFormat(valueFormat2, pairs.values(), 1)
     self.Coverage = buildCoverage(coverage, glyphMap)
     self.ClassDef1 = classDef1.build()
     self.ClassDef2 = classDef2.build()
@@ -346,7 +2102,9 @@
         self.Class1Record.append(rec1)
         for c2 in classes2:
             rec2 = ot.Class2Record()
-            rec2.Value1, rec2.Value2 = pairs.get((c1, c2), (None, None))
+            val1, val2 = pairs.get((c1, c2), (None, None))
+            rec2.Value1 = ValueRecord(src=val1, valueFormat=valueFormat1) if valueFormat1 else None
+            rec2.Value2 = ValueRecord(src=val2, valueFormat=valueFormat2) if valueFormat2 else None
             rec1.Class2Record.append(rec2)
     self.Class1Count = len(self.Class1Record)
     self.Class2Count = len(classes2)
@@ -354,6 +2112,37 @@
 
 
 def buildPairPosGlyphs(pairs, glyphMap):
+    """Builds a list of glyph-based pair adjustment (GPOS2 format 1) subtables.
+
+    This organises a list of pair positioning adjustments into subtables based
+    on common value record formats.
+
+    Note that if you are implementing a layout compiler, you may find it more
+    flexible to use
+    :py:class:`fontTools.otlLib.lookupBuilders.PairPosBuilder`
+    instead.
+
+    Example::
+
+        pairs = {
+            ("K", "W"): ( buildValue(xAdvance=+5), buildValue() ),
+            ("K", "V"): ( buildValue(xAdvance=+5), buildValue() ),
+            # ...
+        }
+
+        subtables = buildPairPosGlyphs(pairs, font.getReverseGlyphMap())
+
+    Args:
+        pairs (dict): Pair positioning data; the keys being a two-element
+            tuple of glyphnames, and the values being a two-element
+            tuple of ``otTables.ValueRecord`` objects.
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        A list of ``otTables.PairPos`` objects.
+    """
+
     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
@@ -362,15 +2151,46 @@
         pos[(glyphA, glyphB)] = (valA, valB)
     return [
         buildPairPosGlyphsSubtable(pos, glyphMap, formatA, formatB)
-        for ((formatA, formatB), pos) in sorted(p.items())]
+        for ((formatA, formatB), pos) in sorted(p.items())
+    ]
 
 
-def buildPairPosGlyphsSubtable(pairs, glyphMap,
-                               valueFormat1=None, valueFormat2=None):
+def buildPairPosGlyphsSubtable(pairs, glyphMap, valueFormat1=None, valueFormat2=None):
+    """Builds a single glyph-based pair adjustment (GPOS2 format 1) subtable.
+
+    This builds a PairPos subtable from a dictionary of glyph pairs and
+    their positioning adjustments. See also :func:`buildPairPosGlyphs`.
+
+    Note that if you are implementing a layout compiler, you may find it more
+    flexible to use
+    :py:class:`fontTools.otlLib.lookupBuilders.PairPosBuilder` instead.
+
+    Example::
+
+        pairs = {
+            ("K", "W"): ( buildValue(xAdvance=+5), buildValue() ),
+            ("K", "V"): ( buildValue(xAdvance=+5), buildValue() ),
+            # ...
+        }
+
+        pairpos = buildPairPosGlyphsSubtable(pairs, font.getReverseGlyphMap())
+
+    Args:
+        pairs (dict): Pair positioning data; the keys being a two-element
+            tuple of glyphnames, and the values being a two-element
+            tuple of ``otTables.ValueRecord`` objects.
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+        valueFormat1: Force the "left" value records to the given format.
+        valueFormat2: Force the "right" value records to the given format.
+
+    Returns:
+        A ``otTables.PairPos`` object.
+    """
     self = ot.PairPos()
     self.Format = 1
-    self.ValueFormat1 = _getValueFormat(valueFormat1, pairs.values(), 0)
-    self.ValueFormat2 = _getValueFormat(valueFormat2, pairs.values(), 1)
+    valueFormat1 = self.ValueFormat1 = _getValueFormat(valueFormat1, pairs.values(), 0)
+    valueFormat2 = self.ValueFormat2 = _getValueFormat(valueFormat2, pairs.values(), 1)
     p = {}
     for (glyphA, glyphB), (valA, valB) in pairs.items():
         p.setdefault(glyphA, []).append((glyphB, valA, valB))
@@ -380,12 +2200,11 @@
         ps = ot.PairSet()
         ps.PairValueRecord = []
         self.PairSet.append(ps)
-        for glyph2, val1, val2 in \
-                sorted(p[glyph], key=lambda x: glyphMap[x[0]]):
+        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
+            pvr.Value1 = ValueRecord(src=val1, valueFormat=valueFormat1) if valueFormat1 else None
+            pvr.Value2 = ValueRecord(src=val2, valueFormat=valueFormat2) if valueFormat2 else None
             ps.PairValueRecord.append(pvr)
         ps.PairValueCount = len(ps.PairValueRecord)
     self.PairSetCount = len(self.PairSet)
@@ -393,7 +2212,35 @@
 
 
 def buildSinglePos(mapping, glyphMap):
-    """{"glyph": ValueRecord} --> [otTables.SinglePos*]"""
+    """Builds a list of single adjustment (GPOS1) subtables.
+
+    This builds a list of SinglePos subtables from a dictionary of glyph
+    names and their positioning adjustments. The format of the subtables are
+    determined to optimize the size of the resulting subtables.
+    See also :func:`buildSinglePosSubtable`.
+
+    Note that if you are implementing a layout compiler, you may find it more
+    flexible to use
+    :py:class:`fontTools.otlLib.lookupBuilders.SinglePosBuilder` instead.
+
+    Example::
+
+        mapping = {
+            "V": buildValue({ "xAdvance" : +5 }),
+            # ...
+        }
+
+        subtables = buildSinglePos(pairs, font.getReverseGlyphMap())
+
+    Args:
+        mapping (dict): A mapping between glyphnames and
+            ``otTables.ValueRecord`` objects.
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        A list of ``otTables.SinglePos`` objects.
+    """
     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
@@ -408,7 +2255,8 @@
     # 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:
+        # 5 ushorts is the length of introducing another sublookup
+        if len(glyphs) * _getSinglePosValueSize(key) > 5:
             format1Mapping = {g: values[key] for g in glyphs}
             result.append(buildSinglePosSubtable(format1Mapping, glyphMap))
             handled.add(key)
@@ -419,17 +2267,18 @@
     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}
+            format2Mapping = {}
+            for k in f2:
+                format2Mapping.update((g, values[k]) for g in coverages[k])
             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.
+    # The remaining ValueRecords are only used by a few glyphs, normally
+    # one. 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)
+            for g in glyphs:
+                st = buildSinglePosSubtable({g: values[key]}, glyphMap)
             result.append(st)
 
     # When the OpenType layout engine traverses the subtables, it will
@@ -445,13 +2294,39 @@
 
 
 def buildSinglePosSubtable(values, glyphMap):
-    """{glyphName: otBase.ValueRecord} --> otTables.SinglePos"""
+    """Builds a single adjustment (GPOS1) subtable.
+
+    This builds a list of SinglePos subtables from a dictionary of glyph
+    names and their positioning adjustments. The format of the subtable is
+    determined to optimize the size of the output.
+    See also :func:`buildSinglePos`.
+
+    Note that if you are implementing a layout compiler, you may find it more
+    flexible to use
+    :py:class:`fontTools.otlLib.lookupBuilders.SinglePosBuilder` instead.
+
+    Example::
+
+        mapping = {
+            "V": buildValue({ "xAdvance" : +5 }),
+            # ...
+        }
+
+        subtable = buildSinglePos(pairs, font.getReverseGlyphMap())
+
+    Args:
+        mapping (dict): A mapping between glyphnames and
+            ``otTables.ValueRecord`` objects.
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        A ``otTables.SinglePos`` object.
+    """
     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()
+    valueFormat = self.ValueFormat = reduce(int.__or__, [v.getFormat() for v in values.values()], 0)
+    valueRecords = [ValueRecord(src=values[g], valueFormat=valueFormat) for g in self.Coverage.glyphs]
     if all(v == valueRecords[0] for v in valueRecords):
         self.Format = 1
         if self.ValueFormat != 0:
@@ -472,7 +2347,7 @@
 
 
 def _getSinglePosValueKey(valueRecord):
-    """otBase.ValueRecord --> (2, ("YPlacement": 12))"""
+    # otBase.ValueRecord --> (2, ("YPlacement": 12))
     assert isinstance(valueRecord, ValueRecord), valueRecord
     valueFormat, result = 0, []
     for name, value in valueRecord.__dict__.items():
@@ -486,13 +2361,52 @@
     return tuple(result)
 
 
+_DeviceTuple = namedtuple("_DeviceTuple", "DeltaFormat StartSize EndSize DeltaValue")
+
+
 def _makeDeviceTuple(device):
-    """otTables.Device --> tuple, for making device tables unique"""
-    return (device.DeltaFormat, device.StartSize, device.EndSize,
-            tuple(device.DeltaValue))
+    # otTables.Device --> tuple, for making device tables unique
+    return _DeviceTuple(
+        device.DeltaFormat,
+        device.StartSize,
+        device.EndSize,
+        () if device.DeltaFormat & 0x8000 else tuple(device.DeltaValue),
+    )
+
+
+def _getSinglePosValueSize(valueKey):
+    # Returns how many ushorts this valueKey (short form of ValueRecord) takes up
+    count = 0
+    for _, v in valueKey[1:]:
+        if isinstance(v, _DeviceTuple):
+            count += len(v.DeltaValue) + 3
+        else:
+            count += 1
+    return count
 
 
 def buildValue(value):
+    """Builds a positioning value record.
+
+    Value records are used to specify coordinates and adjustments for
+    positioning and attaching glyphs. Many of the positioning functions
+    in this library take ``otTables.ValueRecord`` objects as arguments.
+    This function builds value records from dictionaries.
+
+    Args:
+        value (dict): A dictionary with zero or more of the following keys:
+            - ``xPlacement``
+            - ``yPlacement``
+            - ``xAdvance``
+            - ``yAdvance``
+            - ``xPlaDevice``
+            - ``yPlaDevice``
+            - ``xAdvDevice``
+            - ``yAdvDevice``
+
+    Returns:
+        An ``otTables.ValueRecord`` object.
+    """
     self = ValueRecord()
     for k, v in value.items():
         setattr(self, k, v)
@@ -501,20 +2415,34 @@
 
 # GDEF
 
+
 def buildAttachList(attachPoints, glyphMap):
-    """{"glyphName": [4, 23]} --> otTables.AttachList, or None"""
+    """Builds an AttachList subtable.
+
+    A GDEF table may contain an Attachment Point List table (AttachList)
+    which stores the contour indices of attachment points for glyphs with
+    attachment points. This routine builds AttachList subtables.
+
+    Args:
+        attachPoints (dict): A mapping between glyph names and a list of
+            contour indices.
+
+    Returns:
+        An ``otTables.AttachList`` object if attachment points are supplied,
+            or ``None`` otherwise.
+    """
     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.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"""
+    # [4, 23, 41] --> otTables.AttachPoint
+    # Only used by above.
     if not points:
         return None
     self = ot.AttachPoint()
@@ -524,7 +2452,7 @@
 
 
 def buildCaretValueForCoord(coord):
-    """500 --> otTables.CaretValue, format 1"""
+    # 500 --> otTables.CaretValue, format 1
     self = ot.CaretValue()
     self.Format = 1
     self.Coordinate = coord
@@ -532,7 +2460,7 @@
 
 
 def buildCaretValueForPoint(point):
-    """4 --> otTables.CaretValue, format 2"""
+    # 4 --> otTables.CaretValue, format 2
     self = ot.CaretValue()
     self.Format = 2
     self.CaretValuePoint = point
@@ -540,7 +2468,37 @@
 
 
 def buildLigCaretList(coords, points, glyphMap):
-    """{"f_f_i":[300,600]}, {"c_t":[28]} --> otTables.LigCaretList, or None"""
+    """Builds a ligature caret list table.
+
+    Ligatures appear as a single glyph representing multiple characters; however
+    when, for example, editing text containing a ``f_i`` ligature, the user may
+    want to place the cursor between the ``f`` and the ``i``. The ligature caret
+    list in the GDEF table specifies the position to display the "caret" (the
+    character insertion indicator, typically a flashing vertical bar) "inside"
+    the ligature to represent an insertion point. The insertion positions may
+    be specified either by coordinate or by contour point.
+
+    Example::
+
+        coords = {
+            "f_f_i": [300, 600] # f|fi cursor at 300 units, ff|i cursor at 600.
+        }
+        points = {
+            "c_t": [28] # c|t cursor appears at coordinate of contour point 28.
+        }
+        ligcaretlist = buildLigCaretList(coords, points, font.getReverseGlyphMap())
+
+    Args:
+        coords: A mapping between glyph names and a list of coordinates for
+            the insertion point of each ligature component after the first one.
+        points: A mapping between glyph names and a list of contour points for
+            the insertion point of each ligature component after the first one.
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns:
+        A ``otTables.LigCaretList`` object if any carets are present, or
+            ``None`` otherwise."""
     glyphs = set(coords.keys()) if coords else set()
     if points:
         glyphs.update(points.keys())
@@ -556,7 +2514,7 @@
 
 
 def buildLigGlyph(coords, points):
-    """([500], [4]) --> otTables.LigGlyph; None for empty coords/points"""
+    # ([500], [4]) --> otTables.LigGlyph; None for empty coords/points
     carets = []
     if coords:
         carets.extend([buildCaretValueForCoord(c) for c in sorted(coords)])
@@ -571,7 +2529,30 @@
 
 
 def buildMarkGlyphSetsDef(markSets, glyphMap):
-    """[{"acute","grave"}, {"caron","grave"}] --> otTables.MarkGlyphSetsDef"""
+    """Builds a mark glyph sets definition table.
+
+    OpenType Layout lookups may choose to use mark filtering sets to consider
+    or ignore particular combinations of marks. These sets are specified by
+    setting a flag on the lookup, but the mark filtering sets are defined in
+    the ``GDEF`` table. This routine builds the subtable containing the mark
+    glyph set definitions.
+
+    Example::
+
+        set0 = set("acute", "grave")
+        set1 = set("caron", "grave")
+
+        markglyphsets = buildMarkGlyphSetsDef([set0, set1], font.getReverseGlyphMap())
+
+    Args:
+
+        markSets: A list of sets of glyphnames.
+        glyphMap: a glyph name to ID map, typically returned from
+            ``font.getReverseGlyphMap()``.
+
+    Returns
+        An ``otTables.MarkGlyphSetsDef`` object.
+    """
     if not markSets:
         return None
     self = ot.MarkGlyphSetsDef()
@@ -583,6 +2564,7 @@
 
 class ClassDefBuilder(object):
     """Helper for building ClassDef tables."""
+
     def __init__(self, useClass0):
         self.classes_ = set()
         self.glyphs_ = {}
@@ -607,7 +2589,10 @@
             return
         self.classes_.add(glyphs)
         for glyph in glyphs:
-            assert glyph not in self.glyphs_
+            if glyph in self.glyphs_:
+                raise OpenTypeLibError(
+                    f"Glyph {glyph} is already present in class.", None
+                )
             self.glyphs_[glyph] = glyphs
 
     def classes(self):
@@ -638,3 +2623,211 @@
         classDef = ot.ClassDef()
         classDef.classDefs = glyphClasses
         return classDef
+
+
+AXIS_VALUE_NEGATIVE_INFINITY = fixedToFloat(-0x80000000, 16)
+AXIS_VALUE_POSITIVE_INFINITY = fixedToFloat(0x7FFFFFFF, 16)
+
+
+def buildStatTable(ttFont, axes, locations=None, elidedFallbackName=2):
+    """Add a 'STAT' table to 'ttFont'.
+
+    'axes' is a list of dictionaries describing axes and their
+    values.
+
+    Example::
+
+        axes = [
+            dict(
+                tag="wght",
+                name="Weight",
+                ordering=0,  # optional
+                values=[
+                    dict(value=100, name='Thin'),
+                    dict(value=300, name='Light'),
+                    dict(value=400, name='Regular', flags=0x2),
+                    dict(value=900, name='Black'),
+                ],
+            )
+        ]
+
+    Each axis dict must have 'tag' and 'name' items. 'tag' maps
+    to the 'AxisTag' field. 'name' can be a name ID (int), a string,
+    or a dictionary containing multilingual names (see the
+    addMultilingualName() name table method), and will translate to
+    the AxisNameID field.
+
+    An axis dict may contain an 'ordering' item that maps to the
+    AxisOrdering field. If omitted, the order of the axes list is
+    used to calculate AxisOrdering fields.
+
+    The axis dict may contain a 'values' item, which is a list of
+    dictionaries describing AxisValue records belonging to this axis.
+
+    Each value dict must have a 'name' item, which can be a name ID
+    (int), a string, or a dictionary containing multilingual names,
+    like the axis name. It translates to the ValueNameID field.
+
+    Optionally the value dict can contain a 'flags' item. It maps to
+    the AxisValue Flags field, and will be 0 when omitted.
+
+    The format of the AxisValue is determined by the remaining contents
+    of the value dictionary:
+
+    If the value dict contains a 'value' item, an AxisValue record
+    Format 1 is created. If in addition to the 'value' item it contains
+    a 'linkedValue' item, an AxisValue record Format 3 is built.
+
+    If the value dict contains a 'nominalValue' item, an AxisValue
+    record Format 2 is built. Optionally it may contain 'rangeMinValue'
+    and 'rangeMaxValue' items. These map to -Infinity and +Infinity
+    respectively if omitted.
+
+    You cannot specify Format 4 AxisValue tables this way, as they are
+    not tied to a single axis, and specify a name for a location that
+    is defined by multiple axes values. Instead, you need to supply the
+    'locations' argument.
+
+    The optional 'locations' argument specifies AxisValue Format 4
+    tables. It should be a list of dicts, where each dict has a 'name'
+    item, which works just like the value dicts above, an optional
+    'flags' item (defaulting to 0x0), and a 'location' dict. A
+    location dict key is an axis tag, and the associated value is the
+    location on the specified axis. They map to the AxisIndex and Value
+    fields of the AxisValueRecord.
+
+    Example::
+
+        locations = [
+            dict(name='Regular ABCD', location=dict(wght=300, ABCD=100)),
+            dict(name='Bold ABCD XYZ', location=dict(wght=600, ABCD=200)),
+        ]
+
+    The optional 'elidedFallbackName' argument can be a name ID (int),
+    a string, a dictionary containing multilingual names, or a list of
+    STATNameStatements. It translates to the ElidedFallbackNameID field.
+
+    The 'ttFont' argument must be a TTFont instance that already has a
+    'name' table. If a 'STAT' table already exists, it will be
+    overwritten by the newly created one.
+    """
+    ttFont["STAT"] = ttLib.newTable("STAT")
+    statTable = ttFont["STAT"].table = ot.STAT()
+    nameTable = ttFont["name"]
+    statTable.ElidedFallbackNameID = _addName(nameTable, elidedFallbackName)
+
+    # 'locations' contains data for AxisValue Format 4
+    axisRecords, axisValues = _buildAxisRecords(axes, nameTable)
+    if not locations:
+        statTable.Version = 0x00010001
+    else:
+        # We'll be adding Format 4 AxisValue records, which
+        # requires a higher table version
+        statTable.Version = 0x00010002
+        multiAxisValues = _buildAxisValuesFormat4(locations, axes, nameTable)
+        axisValues = multiAxisValues + axisValues
+
+    # Store AxisRecords
+    axisRecordArray = ot.AxisRecordArray()
+    axisRecordArray.Axis = axisRecords
+    # XXX these should not be hard-coded but computed automatically
+    statTable.DesignAxisRecordSize = 8
+    statTable.DesignAxisRecord = axisRecordArray
+    statTable.DesignAxisCount = len(axisRecords)
+
+    if axisValues:
+        # Store AxisValueRecords
+        axisValueArray = ot.AxisValueArray()
+        axisValueArray.AxisValue = axisValues
+        statTable.AxisValueArray = axisValueArray
+        statTable.AxisValueCount = len(axisValues)
+
+
+def _buildAxisRecords(axes, nameTable):
+    axisRecords = []
+    axisValues = []
+    for axisRecordIndex, axisDict in enumerate(axes):
+        axis = ot.AxisRecord()
+        axis.AxisTag = axisDict["tag"]
+        axis.AxisNameID = _addName(nameTable, axisDict["name"], 256)
+        axis.AxisOrdering = axisDict.get("ordering", axisRecordIndex)
+        axisRecords.append(axis)
+
+        for axisVal in axisDict.get("values", ()):
+            axisValRec = ot.AxisValue()
+            axisValRec.AxisIndex = axisRecordIndex
+            axisValRec.Flags = axisVal.get("flags", 0)
+            axisValRec.ValueNameID = _addName(nameTable, axisVal["name"])
+
+            if "value" in axisVal:
+                axisValRec.Value = axisVal["value"]
+                if "linkedValue" in axisVal:
+                    axisValRec.Format = 3
+                    axisValRec.LinkedValue = axisVal["linkedValue"]
+                else:
+                    axisValRec.Format = 1
+            elif "nominalValue" in axisVal:
+                axisValRec.Format = 2
+                axisValRec.NominalValue = axisVal["nominalValue"]
+                axisValRec.RangeMinValue = axisVal.get(
+                    "rangeMinValue", AXIS_VALUE_NEGATIVE_INFINITY
+                )
+                axisValRec.RangeMaxValue = axisVal.get(
+                    "rangeMaxValue", AXIS_VALUE_POSITIVE_INFINITY
+                )
+            else:
+                raise ValueError("Can't determine format for AxisValue")
+
+            axisValues.append(axisValRec)
+    return axisRecords, axisValues
+
+
+def _buildAxisValuesFormat4(locations, axes, nameTable):
+    axisTagToIndex = {}
+    for axisRecordIndex, axisDict in enumerate(axes):
+        axisTagToIndex[axisDict["tag"]] = axisRecordIndex
+
+    axisValues = []
+    for axisLocationDict in locations:
+        axisValRec = ot.AxisValue()
+        axisValRec.Format = 4
+        axisValRec.ValueNameID = _addName(nameTable, axisLocationDict["name"])
+        axisValRec.Flags = axisLocationDict.get("flags", 0)
+        axisValueRecords = []
+        for tag, value in axisLocationDict["location"].items():
+            avr = ot.AxisValueRecord()
+            avr.AxisIndex = axisTagToIndex[tag]
+            avr.Value = value
+            axisValueRecords.append(avr)
+        axisValueRecords.sort(key=lambda avr: avr.AxisIndex)
+        axisValRec.AxisCount = len(axisValueRecords)
+        axisValRec.AxisValueRecord = axisValueRecords
+        axisValues.append(axisValRec)
+    return axisValues
+
+
+def _addName(nameTable, value, minNameID=0):
+    if isinstance(value, int):
+        # Already a nameID
+        return value
+    if isinstance(value, str):
+        names = dict(en=value)
+    elif isinstance(value, dict):
+        names = value
+    elif isinstance(value, list):
+        nameID = nameTable._findUnusedNameID()
+        for nameRecord in value:
+            if isinstance(nameRecord, STATNameStatement):
+                nameTable.setName(
+                    nameRecord.string,
+                    nameID,
+                    nameRecord.platformID,
+                    nameRecord.platEncID,
+                    nameRecord.langID,
+                )
+            else:
+                raise TypeError("value must be a list of STATNameStatements")
+        return nameID
+    else:
+        raise TypeError("value must be int, str, dict or list")
+    return nameTable.addMultilingualName(names, minNameID=minNameID)
diff --git a/Lib/fontTools/otlLib/error.py b/Lib/fontTools/otlLib/error.py
new file mode 100644
index 0000000..1cbef57
--- /dev/null
+++ b/Lib/fontTools/otlLib/error.py
@@ -0,0 +1,11 @@
+class OpenTypeLibError(Exception):
+    def __init__(self, message, location):
+        Exception.__init__(self, message)
+        self.location = location
+
+    def __str__(self):
+        message = Exception.__str__(self)
+        if self.location:
+            return f"{self.location}: {message}"
+        else:
+            return message
diff --git a/Lib/fontTools/otlLib/maxContextCalc.py b/Lib/fontTools/otlLib/maxContextCalc.py
new file mode 100644
index 0000000..03e7561
--- /dev/null
+++ b/Lib/fontTools/otlLib/maxContextCalc.py
@@ -0,0 +1,96 @@
+__all__ = ["maxCtxFont"]
+
+
+def maxCtxFont(font):
+    """Calculate the usMaxContext value for an entire font."""
+
+    maxCtx = 0
+    for tag in ("GSUB", "GPOS"):
+        if tag not in font:
+            continue
+        table = font[tag].table
+        if not table.LookupList:
+            continue
+        for lookup in table.LookupList.Lookup:
+            for st in lookup.SubTable:
+                maxCtx = maxCtxSubtable(maxCtx, tag, lookup.LookupType, st)
+    return maxCtx
+
+
+def maxCtxSubtable(maxCtx, tag, lookupType, st):
+    """Calculate usMaxContext based on a single lookup table (and an existing
+    max value).
+    """
+
+    # single positioning, single / multiple substitution
+    if (tag == "GPOS" and lookupType == 1) or (
+        tag == "GSUB" and lookupType in (1, 2, 3)
+    ):
+        maxCtx = max(maxCtx, 1)
+
+    # pair positioning
+    elif tag == "GPOS" and lookupType == 2:
+        maxCtx = max(maxCtx, 2)
+
+    # ligatures
+    elif tag == "GSUB" and lookupType == 4:
+        for ligatures in st.ligatures.values():
+            for ligature in ligatures:
+                maxCtx = max(maxCtx, ligature.CompCount)
+
+    # context
+    elif (tag == "GPOS" and lookupType == 7) or (tag == "GSUB" and lookupType == 5):
+        maxCtx = maxCtxContextualSubtable(maxCtx, st, "Pos" if tag == "GPOS" else "Sub")
+
+    # chained context
+    elif (tag == "GPOS" and lookupType == 8) or (tag == "GSUB" and lookupType == 6):
+        maxCtx = maxCtxContextualSubtable(
+            maxCtx, st, "Pos" if tag == "GPOS" else "Sub", "Chain"
+        )
+
+    # extensions
+    elif (tag == "GPOS" and lookupType == 9) or (tag == "GSUB" and lookupType == 7):
+        maxCtx = maxCtxSubtable(maxCtx, tag, st.ExtensionLookupType, st.ExtSubTable)
+
+    # reverse-chained context
+    elif tag == "GSUB" and lookupType == 8:
+        maxCtx = maxCtxContextualRule(maxCtx, st, "Reverse")
+
+    return maxCtx
+
+
+def maxCtxContextualSubtable(maxCtx, st, ruleType, chain=""):
+    """Calculate usMaxContext based on a contextual feature subtable."""
+
+    if st.Format == 1:
+        for ruleset in getattr(st, "%s%sRuleSet" % (chain, ruleType)):
+            if ruleset is None:
+                continue
+            for rule in getattr(ruleset, "%s%sRule" % (chain, ruleType)):
+                if rule is None:
+                    continue
+                maxCtx = maxCtxContextualRule(maxCtx, rule, chain)
+
+    elif st.Format == 2:
+        for ruleset in getattr(st, "%s%sClassSet" % (chain, ruleType)):
+            if ruleset is None:
+                continue
+            for rule in getattr(ruleset, "%s%sClassRule" % (chain, ruleType)):
+                if rule is None:
+                    continue
+                maxCtx = maxCtxContextualRule(maxCtx, rule, chain)
+
+    elif st.Format == 3:
+        maxCtx = maxCtxContextualRule(maxCtx, st, chain)
+
+    return maxCtx
+
+
+def maxCtxContextualRule(maxCtx, st, chain):
+    """Calculate usMaxContext based on a contextual feature rule."""
+
+    if not chain:
+        return max(maxCtx, st.GlyphCount)
+    elif chain == "Reverse":
+        return max(maxCtx, st.GlyphCount + st.LookAheadGlyphCount)
+    return max(maxCtx, st.InputGlyphCount + st.LookAheadGlyphCount)
diff --git a/Lib/fontTools/otlLib/optimize/__init__.py b/Lib/fontTools/otlLib/optimize/__init__.py
new file mode 100644
index 0000000..5c007e8
--- /dev/null
+++ b/Lib/fontTools/otlLib/optimize/__init__.py
@@ -0,0 +1,68 @@
+from argparse import RawTextHelpFormatter
+from textwrap import dedent
+
+from fontTools.ttLib import TTFont
+from fontTools.otlLib.optimize.gpos import compact, GPOS_COMPACT_MODE_DEFAULT
+
+def main(args=None):
+    """Optimize the layout tables of an existing font."""
+    from argparse import ArgumentParser
+    from fontTools import configLogger
+
+    parser = ArgumentParser(prog="otlLib.optimize", description=main.__doc__, formatter_class=RawTextHelpFormatter)
+    parser.add_argument("font")
+    parser.add_argument(
+        "-o", metavar="OUTPUTFILE", dest="outfile", default=None, help="output file"
+    )
+    parser.add_argument(
+        "--gpos-compact-mode",
+        help=dedent(
+            f"""\
+            GPOS Lookup type 2 (PairPos) compaction mode:
+                0 = do not attempt to compact PairPos lookups;
+                1 to 8 = create at most 1 to 8 new subtables for each existing
+                    subtable, provided that it would yield a 50%% file size saving;
+                9 = create as many new subtables as needed to yield a file size saving.
+            Default: {GPOS_COMPACT_MODE_DEFAULT}.
+
+            This compaction aims to save file size, by splitting large class
+            kerning subtables (Format 2) that contain many zero values into
+            smaller and denser subtables. It's a trade-off between the overhead
+            of several subtables versus the sparseness of one big subtable.
+
+            See the pull request: https://github.com/fonttools/fonttools/pull/2326
+            """
+        ),
+        default=int(GPOS_COMPACT_MODE_DEFAULT),
+        choices=list(range(10)),
+        type=int,
+    )
+    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)
+
+    configLogger(
+        level=("DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO")
+    )
+
+    font = TTFont(options.font)
+    # TODO: switch everything to have type(mode) = int when using the Config class
+    compact(font, str(options.gpos_compact_mode))
+    font.save(options.outfile or options.font)
+
+
+
+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/otlLib/optimize/__main__.py b/Lib/fontTools/otlLib/optimize/__main__.py
new file mode 100644
index 0000000..03027ec
--- /dev/null
+++ b/Lib/fontTools/otlLib/optimize/__main__.py
@@ -0,0 +1,6 @@
+import sys
+from fontTools.otlLib.optimize import main
+
+
+if __name__ == '__main__':
+	sys.exit(main())
diff --git a/Lib/fontTools/otlLib/optimize/gpos.py b/Lib/fontTools/otlLib/optimize/gpos.py
new file mode 100644
index 0000000..79873fa
--- /dev/null
+++ b/Lib/fontTools/otlLib/optimize/gpos.py
@@ -0,0 +1,439 @@
+import logging
+from collections import defaultdict, namedtuple
+from functools import reduce
+from itertools import chain
+from math import log2
+from typing import DefaultDict, Dict, Iterable, List, Sequence, Tuple
+
+from fontTools.misc.intTools import bit_count, bit_indices
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.tables import otBase, otTables
+
+# NOTE: activating this optimization via the environment variable is
+# experimental and may not be supported once an alternative mechanism
+# is in place. See: https://github.com/fonttools/fonttools/issues/2349
+GPOS_COMPACT_MODE_ENV_KEY = "FONTTOOLS_GPOS_COMPACT_MODE"
+GPOS_COMPACT_MODE_DEFAULT = "0"
+
+log = logging.getLogger("fontTools.otlLib.optimize.gpos")
+
+
+def compact(font: TTFont, mode: str) -> TTFont:
+    # Ideal plan:
+    #  1. Find lookups of Lookup Type 2: Pair Adjustment Positioning Subtable
+    #     https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-2-pair-adjustment-positioning-subtable
+    #  2. Extract glyph-glyph kerning and class-kerning from all present subtables
+    #  3. Regroup into different subtable arrangements
+    #  4. Put back into the lookup
+    #
+    # Actual implementation:
+    #  2. Only class kerning is optimized currently
+    #  3. If the input kerning is already in several subtables, the subtables
+    #     are not grouped together first; instead each subtable is treated
+    #     independently, so currently this step is:
+    #     Split existing subtables into more smaller subtables
+    gpos = font["GPOS"]
+    for lookup in gpos.table.LookupList.Lookup:
+        if lookup.LookupType == 2:
+            compact_lookup(font, mode, lookup)
+        elif lookup.LookupType == 9 and lookup.SubTable[0].ExtensionLookupType == 2:
+            compact_ext_lookup(font, mode, lookup)
+    return font
+
+
+def compact_lookup(font: TTFont, mode: str, lookup: otTables.Lookup) -> None:
+    new_subtables = compact_pair_pos(font, mode, lookup.SubTable)
+    lookup.SubTable = new_subtables
+    lookup.SubTableCount = len(new_subtables)
+
+
+def compact_ext_lookup(font: TTFont, mode: str, lookup: otTables.Lookup) -> None:
+    new_subtables = compact_pair_pos(
+        font, mode, [ext_subtable.ExtSubTable for ext_subtable in lookup.SubTable]
+    )
+    new_ext_subtables = []
+    for subtable in new_subtables:
+        ext_subtable = otTables.ExtensionPos()
+        ext_subtable.Format = 1
+        ext_subtable.ExtSubTable = subtable
+        new_ext_subtables.append(ext_subtable)
+    lookup.SubTable = new_ext_subtables
+    lookup.SubTableCount = len(new_ext_subtables)
+
+
+def compact_pair_pos(
+    font: TTFont, mode: str, subtables: Sequence[otTables.PairPos]
+) -> Sequence[otTables.PairPos]:
+    new_subtables = []
+    for subtable in subtables:
+        if subtable.Format == 1:
+            # Not doing anything to Format 1 (yet?)
+            new_subtables.append(subtable)
+        elif subtable.Format == 2:
+            new_subtables.extend(compact_class_pairs(font, mode, subtable))
+    return new_subtables
+
+
+def compact_class_pairs(
+    font: TTFont, mode: str, subtable: otTables.PairPos
+) -> List[otTables.PairPos]:
+    from fontTools.otlLib.builder import buildPairPosClassesSubtable
+
+    subtables = []
+    classes1: DefaultDict[int, List[str]] = defaultdict(list)
+    for g in subtable.Coverage.glyphs:
+        classes1[subtable.ClassDef1.classDefs.get(g, 0)].append(g)
+    classes2: DefaultDict[int, List[str]] = defaultdict(list)
+    for g, i in subtable.ClassDef2.classDefs.items():
+        classes2[i].append(g)
+    all_pairs = {}
+    for i, class1 in enumerate(subtable.Class1Record):
+        for j, class2 in enumerate(class1.Class2Record):
+            if is_really_zero(class2):
+                continue
+            all_pairs[(tuple(sorted(classes1[i])), tuple(sorted(classes2[j])))] = (
+                getattr(class2, "Value1", None),
+                getattr(class2, "Value2", None),
+            )
+
+    if len(mode) == 1 and mode in "123456789":
+        grouped_pairs = cluster_pairs_by_class2_coverage_custom_cost(
+            font, all_pairs, int(mode)
+        )
+        for pairs in grouped_pairs:
+            subtables.append(
+                buildPairPosClassesSubtable(pairs, font.getReverseGlyphMap())
+            )
+    else:
+        raise ValueError(f"Bad {GPOS_COMPACT_MODE_ENV_KEY}={mode}")
+    return subtables
+
+
+def is_really_zero(class2: otTables.Class2Record) -> bool:
+    v1 = getattr(class2, "Value1", None)
+    v2 = getattr(class2, "Value2", None)
+    return (v1 is None or v1.getEffectiveFormat() == 0) and (
+        v2 is None or v2.getEffectiveFormat() == 0
+    )
+
+
+Pairs = Dict[
+    Tuple[Tuple[str, ...], Tuple[str, ...]],
+    Tuple[otBase.ValueRecord, otBase.ValueRecord],
+]
+
+# Adapted from https://github.com/fonttools/fonttools/blob/f64f0b42f2d1163b2d85194e0979def539f5dca3/Lib/fontTools/ttLib/tables/otTables.py#L935-L958
+def _getClassRanges(glyphIDs: Iterable[int]):
+    glyphIDs = sorted(glyphIDs)
+    last = glyphIDs[0]
+    ranges = [[last]]
+    for glyphID in glyphIDs[1:]:
+        if glyphID != last + 1:
+            ranges[-1].append(last)
+            ranges.append([glyphID])
+        last = glyphID
+    ranges[-1].append(last)
+    return ranges, glyphIDs[0], glyphIDs[-1]
+
+
+# Adapted from https://github.com/fonttools/fonttools/blob/f64f0b42f2d1163b2d85194e0979def539f5dca3/Lib/fontTools/ttLib/tables/otTables.py#L960-L989
+def _classDef_bytes(
+    class_data: List[Tuple[List[Tuple[int, int]], int, int]],
+    class_ids: List[int],
+    coverage=False,
+):
+    if not class_ids:
+        return 0
+    first_ranges, min_glyph_id, max_glyph_id = class_data[class_ids[0]]
+    range_count = len(first_ranges)
+    for i in class_ids[1:]:
+        data = class_data[i]
+        range_count += len(data[0])
+        min_glyph_id = min(min_glyph_id, data[1])
+        max_glyph_id = max(max_glyph_id, data[2])
+    glyphCount = max_glyph_id - min_glyph_id + 1
+    # https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1
+    format1_bytes = 6 + glyphCount * 2
+    # https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-2
+    format2_bytes = 4 + range_count * 6
+    return min(format1_bytes, format2_bytes)
+
+
+ClusteringContext = namedtuple(
+    "ClusteringContext",
+    [
+        "lines",
+        "all_class1",
+        "all_class1_data",
+        "all_class2_data",
+        "valueFormat1_bytes",
+        "valueFormat2_bytes",
+    ],
+)
+
+
+class Cluster:
+    # TODO(Python 3.7): Turn this into a dataclass
+    # ctx: ClusteringContext
+    # indices: int
+    # Caches
+    # TODO(Python 3.8): use functools.cached_property instead of the
+    # manually cached properties, and remove the cache fields listed below.
+    # _indices: Optional[List[int]] = None
+    # _column_indices: Optional[List[int]] = None
+    # _cost: Optional[int] = None
+
+    __slots__ = "ctx", "indices_bitmask", "_indices", "_column_indices", "_cost"
+
+    def __init__(self, ctx: ClusteringContext, indices_bitmask: int):
+        self.ctx = ctx
+        self.indices_bitmask = indices_bitmask
+        self._indices = None
+        self._column_indices = None
+        self._cost = None
+
+    @property
+    def indices(self):
+        if self._indices is None:
+            self._indices = bit_indices(self.indices_bitmask)
+        return self._indices
+
+    @property
+    def column_indices(self):
+        if self._column_indices is None:
+            # Indices of columns that have a 1 in at least 1 line
+            #   => binary OR all the lines
+            bitmask = reduce(int.__or__, (self.ctx.lines[i] for i in self.indices))
+            self._column_indices = bit_indices(bitmask)
+        return self._column_indices
+
+    @property
+    def width(self):
+        # Add 1 because Class2=0 cannot be used but needs to be encoded.
+        return len(self.column_indices) + 1
+
+    @property
+    def cost(self):
+        if self._cost is None:
+            self._cost = (
+                # 2 bytes to store the offset to this subtable in the Lookup table above
+                2
+                # Contents of the subtable
+                # From: https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#pair-adjustment-positioning-format-2-class-pair-adjustment
+                # uint16	posFormat	Format identifier: format = 2
+                + 2
+                # Offset16	coverageOffset	Offset to Coverage table, from beginning of PairPos subtable.
+                + 2
+                + self.coverage_bytes
+                # uint16	valueFormat1	ValueRecord definition — for the first glyph of the pair (may be zero).
+                + 2
+                # uint16	valueFormat2	ValueRecord definition — for the second glyph of the pair (may be zero).
+                + 2
+                # Offset16	classDef1Offset	Offset to ClassDef table, from beginning of PairPos subtable — for the first glyph of the pair.
+                + 2
+                + self.classDef1_bytes
+                # Offset16	classDef2Offset	Offset to ClassDef table, from beginning of PairPos subtable — for the second glyph of the pair.
+                + 2
+                + self.classDef2_bytes
+                # uint16	class1Count	Number of classes in classDef1 table — includes Class 0.
+                + 2
+                # uint16	class2Count	Number of classes in classDef2 table — includes Class 0.
+                + 2
+                # Class1Record	class1Records[class1Count]	Array of Class1 records, ordered by classes in classDef1.
+                + (self.ctx.valueFormat1_bytes + self.ctx.valueFormat2_bytes)
+                * len(self.indices)
+                * self.width
+            )
+        return self._cost
+
+    @property
+    def coverage_bytes(self):
+        format1_bytes = (
+            # From https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-1
+            # uint16	coverageFormat	Format identifier — format = 1
+            # uint16	glyphCount	Number of glyphs in the glyph array
+            4
+            # uint16	glyphArray[glyphCount]	Array of glyph IDs — in numerical order
+            + sum(len(self.ctx.all_class1[i]) for i in self.indices) * 2
+        )
+        ranges = sorted(
+            chain.from_iterable(self.ctx.all_class1_data[i][0] for i in self.indices)
+        )
+        merged_range_count = 0
+        last = None
+        for (start, end) in ranges:
+            if last is not None and start != last + 1:
+                merged_range_count += 1
+            last = end
+        format2_bytes = (
+            # From https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-2
+            # uint16	coverageFormat	Format identifier — format = 2
+            # uint16	rangeCount	Number of RangeRecords
+            4
+            # RangeRecord	rangeRecords[rangeCount]	Array of glyph ranges — ordered by startGlyphID.
+            # uint16	startGlyphID	First glyph ID in the range
+            # uint16	endGlyphID	Last glyph ID in the range
+            # uint16	startCoverageIndex	Coverage Index of first glyph ID in range
+            + merged_range_count * 6
+        )
+        return min(format1_bytes, format2_bytes)
+
+    @property
+    def classDef1_bytes(self):
+        # We can skip encoding one of the Class1 definitions, and use
+        # Class1=0 to represent it instead, because Class1 is gated by the
+        # Coverage definition. Use Class1=0 for the highest byte savings.
+        # Going through all options takes too long, pick the biggest class
+        # = what happens in otlLib.builder.ClassDefBuilder.classes()
+        biggest_index = max(self.indices, key=lambda i: len(self.ctx.all_class1[i]))
+        return _classDef_bytes(
+            self.ctx.all_class1_data, [i for i in self.indices if i != biggest_index]
+        )
+
+    @property
+    def classDef2_bytes(self):
+        # All Class2 need to be encoded because we can't use Class2=0
+        return _classDef_bytes(self.ctx.all_class2_data, self.column_indices)
+
+
+def cluster_pairs_by_class2_coverage_custom_cost(
+    font: TTFont,
+    pairs: Pairs,
+    compression: int = 5,
+) -> List[Pairs]:
+    if not pairs:
+        # The subtable was actually empty?
+        return [pairs]
+
+    # Sorted for reproducibility/determinism
+    all_class1 = sorted(set(pair[0] for pair in pairs))
+    all_class2 = sorted(set(pair[1] for pair in pairs))
+
+    # Use Python's big ints for binary vectors representing each line
+    lines = [
+        sum(
+            1 << i if (class1, class2) in pairs else 0
+            for i, class2 in enumerate(all_class2)
+        )
+        for class1 in all_class1
+    ]
+
+    # Map glyph names to ids and work with ints throughout for ClassDef formats
+    name_to_id = font.getReverseGlyphMap()
+    # Each entry in the arrays below is (range_count, min_glyph_id, max_glyph_id)
+    all_class1_data = [
+        _getClassRanges(name_to_id[name] for name in cls) for cls in all_class1
+    ]
+    all_class2_data = [
+        _getClassRanges(name_to_id[name] for name in cls) for cls in all_class2
+    ]
+
+    format1 = 0
+    format2 = 0
+    for pair, value in pairs.items():
+        format1 |= value[0].getEffectiveFormat() if value[0] else 0
+        format2 |= value[1].getEffectiveFormat() if value[1] else 0
+    valueFormat1_bytes = bit_count(format1) * 2
+    valueFormat2_bytes = bit_count(format2) * 2
+
+    ctx = ClusteringContext(
+        lines,
+        all_class1,
+        all_class1_data,
+        all_class2_data,
+        valueFormat1_bytes,
+        valueFormat2_bytes,
+    )
+
+    cluster_cache: Dict[int, Cluster] = {}
+
+    def make_cluster(indices: int) -> Cluster:
+        cluster = cluster_cache.get(indices, None)
+        if cluster is not None:
+            return cluster
+        cluster = Cluster(ctx, indices)
+        cluster_cache[indices] = cluster
+        return cluster
+
+    def merge(cluster: Cluster, other: Cluster) -> Cluster:
+        return make_cluster(cluster.indices_bitmask | other.indices_bitmask)
+
+    # Agglomerative clustering by hand, checking the cost gain of the new
+    # cluster against the previously separate clusters
+    # Start with 1 cluster per line
+    # cluster = set of lines = new subtable
+    clusters = [make_cluster(1 << i) for i in range(len(lines))]
+
+    # Cost of 1 cluster with everything
+    # `(1 << len) - 1` gives a bitmask full of 1's of length `len`
+    cost_before_splitting = make_cluster((1 << len(lines)) - 1).cost
+    log.debug(f"        len(clusters) = {len(clusters)}")
+
+    while len(clusters) > 1:
+        lowest_cost_change = None
+        best_cluster_index = None
+        best_other_index = None
+        best_merged = None
+        for i, cluster in enumerate(clusters):
+            for j, other in enumerate(clusters[i + 1 :]):
+                merged = merge(cluster, other)
+                cost_change = merged.cost - cluster.cost - other.cost
+                if lowest_cost_change is None or cost_change < lowest_cost_change:
+                    lowest_cost_change = cost_change
+                    best_cluster_index = i
+                    best_other_index = i + 1 + j
+                    best_merged = merged
+        assert lowest_cost_change is not None
+        assert best_cluster_index is not None
+        assert best_other_index is not None
+        assert best_merged is not None
+
+        # If the best merge we found is still taking down the file size, then
+        # there's no question: we must do it, because it's beneficial in both
+        # ways (lower file size and lower number of subtables).  However, if the
+        # best merge we found is not reducing file size anymore, then we need to
+        # look at the other stop criteria = the compression factor.
+        if lowest_cost_change > 0:
+            # Stop critera: check whether we should keep merging.
+            # Compute size reduction brought by splitting
+            cost_after_splitting = sum(c.cost for c in clusters)
+            # size_reduction so that after = before * (1 - size_reduction)
+            # E.g. before = 1000, after = 800, 1 - 800/1000 = 0.2
+            size_reduction = 1 - cost_after_splitting / cost_before_splitting
+
+            # Force more merging by taking into account the compression number.
+            # Target behaviour: compression number = 1 to 9, default 5 like gzip
+            #   - 1 = accept to add 1 subtable to reduce size by 50%
+            #   - 5 = accept to add 5 subtables to reduce size by 50%
+            # See https://github.com/harfbuzz/packtab/blob/master/Lib/packTab/__init__.py#L690-L691
+            # Given the size reduction we have achieved so far, compute how many
+            # new subtables are acceptable.
+            max_new_subtables = -log2(1 - size_reduction) * compression
+            log.debug(
+                f"            len(clusters) = {len(clusters):3d}    size_reduction={size_reduction:5.2f}    max_new_subtables={max_new_subtables}",
+            )
+            if compression == 9:
+                # Override level 9 to mean: create any number of subtables
+                max_new_subtables = len(clusters)
+
+            # If we have managed to take the number of new subtables below the
+            # threshold, then we can stop.
+            if len(clusters) <= max_new_subtables + 1:
+                break
+
+        # No reason to stop yet, do the merge and move on to the next.
+        del clusters[best_other_index]
+        clusters[best_cluster_index] = best_merged
+
+    # All clusters are final; turn bitmasks back into the "Pairs" format
+    pairs_by_class1: Dict[Tuple[str, ...], Pairs] = defaultdict(dict)
+    for pair, values in pairs.items():
+        pairs_by_class1[pair[0]][pair] = values
+    pairs_groups: List[Pairs] = []
+    for cluster in clusters:
+        pairs_group: Pairs = dict()
+        for i in cluster.indices:
+            class1 = all_class1[i]
+            pairs_group.update(pairs_by_class1[class1])
+        pairs_groups.append(pairs_group)
+    return pairs_groups
diff --git a/Lib/fontTools/pens/__init__.py b/Lib/fontTools/pens/__init__.py
index 3f9abc9..156cb23 100644
--- a/Lib/fontTools/pens/__init__.py
+++ b/Lib/fontTools/pens/__init__.py
@@ -1,4 +1 @@
 """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
index 564caa4..403afe7 100644
--- a/Lib/fontTools/pens/areaPen.py
+++ b/Lib/fontTools/pens/areaPen.py
@@ -1,7 +1,5 @@
 """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
 
 
diff --git a/Lib/fontTools/pens/basePen.py b/Lib/fontTools/pens/basePen.py
index e0d3ae0..72e3918 100644
--- a/Lib/fontTools/pens/basePen.py
+++ b/Lib/fontTools/pens/basePen.py
@@ -36,27 +36,31 @@
 sequence of length 2 will do.
 """
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from typing import Tuple
+
 from fontTools.misc.loggingTools import LogMixin
 
-__all__ =  ["AbstractPen", "NullPen", "BasePen",
+__all__ =  ["AbstractPen", "NullPen", "BasePen", "PenError",
 			"decomposeSuperBezierSegment", "decomposeQuadraticSegment"]
 
 
-class AbstractPen(object):
+class PenError(Exception):
+	"""Represents an error during penning."""
 
-	def moveTo(self, pt):
+
+class AbstractPen:
+
+	def moveTo(self, pt: Tuple[float, float]) -> None:
 		"""Begin a new sub path, set the current point to 'pt'. You must
 		end each sub path with a call to pen.closePath() or pen.endPath().
 		"""
 		raise NotImplementedError
 
-	def lineTo(self, pt):
+	def lineTo(self, pt: Tuple[float, float]) -> None:
 		"""Draw a straight line from the current point to 'pt'."""
 		raise NotImplementedError
 
-	def curveTo(self, *points):
+	def curveTo(self, *points: Tuple[float, float]) -> None:
 		"""Draw a cubic bezier with an arbitrary number of control points.
 
 		The last point specified is on-curve, all others are off-curve
@@ -77,7 +81,7 @@
 		"""
 		raise NotImplementedError
 
-	def qCurveTo(self, *points):
+	def qCurveTo(self, *points: Tuple[float, float]) -> None:
 		"""Draw a whole string of quadratic curve segments.
 
 		The last point specified is on-curve, all others are off-curve
@@ -94,19 +98,23 @@
 		"""
 		raise NotImplementedError
 
-	def closePath(self):
+	def closePath(self) -> None:
 		"""Close the current sub path. You must call either pen.closePath()
 		or pen.endPath() after each sub path.
 		"""
 		pass
 
-	def endPath(self):
+	def endPath(self) -> None:
 		"""End the current sub path, but don't close it. You must call
 		either pen.closePath() or pen.endPath() after each sub path.
 		"""
 		pass
 
-	def addComponent(self, glyphName, transformation):
+	def addComponent(
+		self,
+		glyphName: str,
+		transformation: Tuple[float, float, float, float, float, float]
+	) -> None:
 		"""Add a sub glyph. The 'transformation' argument must be a 6-tuple
 		containing an affine transformation, or a Transform object from the
 		fontTools.misc.transform module. More precisely: it should be a
@@ -115,7 +123,7 @@
 		raise NotImplementedError
 
 
-class NullPen(object):
+class NullPen(AbstractPen):
 
 	"""A pen that does nothing.
 	"""
@@ -148,6 +156,10 @@
 	pass
 
 
+class MissingComponentError(KeyError):
+	"""Indicates a component pointing to a non-existent glyph in the glyphset."""
+
+
 class DecomposingPen(LoggingPen):
 
 	""" Implements a 'addComponent' method that decomposes components
@@ -156,10 +168,12 @@
 
 	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 the class variable ``skipMissingComponents`` to False if you want
+	to raise a :class:`MissingComponentError` exception.
 	"""
 
-	# 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):
@@ -177,7 +191,7 @@
 			glyph = self.glyphSet[glyphName]
 		except KeyError:
 			if not self.skipMissingComponents:
-				raise
+				raise MissingComponentError(glyphName)
 			self.log.warning(
 				"glyph '%s' is missing from glyphSet; skipped" % glyphName)
 		else:
diff --git a/Lib/fontTools/pens/boundsPen.py b/Lib/fontTools/pens/boundsPen.py
index 3a103e3..810715c 100644
--- a/Lib/fontTools/pens/boundsPen.py
+++ b/Lib/fontTools/pens/boundsPen.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect
 from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds
 from fontTools.pens.basePen import BasePen
diff --git a/Lib/fontTools/pens/cocoaPen.py b/Lib/fontTools/pens/cocoaPen.py
index 9920ab0..67482b4 100644
--- a/Lib/fontTools/pens/cocoaPen.py
+++ b/Lib/fontTools/pens/cocoaPen.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.pens.basePen import BasePen
 
 
diff --git a/Lib/fontTools/pens/cu2quPen.py b/Lib/fontTools/pens/cu2quPen.py
new file mode 100644
index 0000000..497585b
--- /dev/null
+++ b/Lib/fontTools/pens/cu2quPen.py
@@ -0,0 +1,257 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+from fontTools.cu2qu import curve_to_quadratic
+from fontTools.pens.basePen import AbstractPen, decomposeSuperBezierSegment
+from fontTools.pens.reverseContourPen import ReverseContourPen
+from fontTools.pens.pointPen import BasePointToSegmentPen
+from fontTools.pens.pointPen import ReverseContourPointPen
+
+
+class Cu2QuPen(AbstractPen):
+    """ A filter pen to convert cubic bezier curves to quadratic b-splines
+    using the FontTools SegmentPen protocol.
+
+    other_pen: another SegmentPen used to draw the transformed outline.
+    max_err: maximum approximation error in font units. For optimal results,
+        if you know the UPEM of the font, we recommend setting this to a
+        value equal, or close to UPEM / 1000.
+    reverse_direction: flip the contours' direction but keep starting point.
+    stats: a dictionary counting the point numbers of quadratic segments.
+    ignore_single_points: don't emit contours containing only a single point
+
+    NOTE: The "ignore_single_points" argument is deprecated since v1.3.0,
+    which dropped Robofab subpport. It's no longer needed to special-case
+    UFO2-style anchors (aka "named points") when using ufoLib >= 2.0,
+    as these are no longer drawn onto pens as single-point contours,
+    but are handled separately as anchors.
+    """
+
+    def __init__(self, other_pen, max_err, reverse_direction=False,
+                 stats=None, ignore_single_points=False):
+        if reverse_direction:
+            self.pen = ReverseContourPen(other_pen)
+        else:
+            self.pen = other_pen
+        self.max_err = max_err
+        self.stats = stats
+        if ignore_single_points:
+            import warnings
+            warnings.warn("ignore_single_points is deprecated and "
+                          "will be removed in future versions",
+                          UserWarning, stacklevel=2)
+        self.ignore_single_points = ignore_single_points
+        self.start_pt = None
+        self.current_pt = None
+
+    def _check_contour_is_open(self):
+        if self.current_pt is None:
+            raise AssertionError("moveTo is required")
+
+    def _check_contour_is_closed(self):
+        if self.current_pt is not None:
+            raise AssertionError("closePath or endPath is required")
+
+    def _add_moveTo(self):
+        if self.start_pt is not None:
+            self.pen.moveTo(self.start_pt)
+            self.start_pt = None
+
+    def moveTo(self, pt):
+        self._check_contour_is_closed()
+        self.start_pt = self.current_pt = pt
+        if not self.ignore_single_points:
+            self._add_moveTo()
+
+    def lineTo(self, pt):
+        self._check_contour_is_open()
+        self._add_moveTo()
+        self.pen.lineTo(pt)
+        self.current_pt = pt
+
+    def qCurveTo(self, *points):
+        self._check_contour_is_open()
+        n = len(points)
+        if n == 1:
+            self.lineTo(points[0])
+        elif n > 1:
+            self._add_moveTo()
+            self.pen.qCurveTo(*points)
+            self.current_pt = points[-1]
+        else:
+            raise AssertionError("illegal qcurve segment point count: %d" % n)
+
+    def _curve_to_quadratic(self, pt1, pt2, pt3):
+        curve = (self.current_pt, pt1, pt2, pt3)
+        quadratic = curve_to_quadratic(curve, self.max_err)
+        if self.stats is not None:
+            n = str(len(quadratic) - 2)
+            self.stats[n] = self.stats.get(n, 0) + 1
+        self.qCurveTo(*quadratic[1:])
+
+    def curveTo(self, *points):
+        self._check_contour_is_open()
+        n = len(points)
+        if n == 3:
+            # this is the most common case, so we special-case it
+            self._curve_to_quadratic(*points)
+        elif n > 3:
+            for segment in decomposeSuperBezierSegment(points):
+                self._curve_to_quadratic(*segment)
+        elif n == 2:
+            self.qCurveTo(*points)
+        elif n == 1:
+            self.lineTo(points[0])
+        else:
+            raise AssertionError("illegal curve segment point count: %d" % n)
+
+    def closePath(self):
+        self._check_contour_is_open()
+        if self.start_pt is None:
+            # if 'start_pt' is _not_ None, we are ignoring single-point paths
+            self.pen.closePath()
+        self.current_pt = self.start_pt = None
+
+    def endPath(self):
+        self._check_contour_is_open()
+        if self.start_pt is None:
+            self.pen.endPath()
+        self.current_pt = self.start_pt = None
+
+    def addComponent(self, glyphName, transformation):
+        self._check_contour_is_closed()
+        self.pen.addComponent(glyphName, transformation)
+
+
+class Cu2QuPointPen(BasePointToSegmentPen):
+    """ A filter pen to convert cubic bezier curves to quadratic b-splines
+    using the RoboFab PointPen protocol.
+
+    other_point_pen: another PointPen used to draw the transformed outline.
+    max_err: maximum approximation error in font units. For optimal results,
+        if you know the UPEM of the font, we recommend setting this to a
+        value equal, or close to UPEM / 1000.
+    reverse_direction: reverse the winding direction of all contours.
+    stats: a dictionary counting the point numbers of quadratic segments.
+    """
+
+    def __init__(self, other_point_pen, max_err, reverse_direction=False,
+                 stats=None):
+        BasePointToSegmentPen.__init__(self)
+        if reverse_direction:
+            self.pen = ReverseContourPointPen(other_point_pen)
+        else:
+            self.pen = other_point_pen
+        self.max_err = max_err
+        self.stats = stats
+
+    def _flushContour(self, segments):
+        assert len(segments) >= 1
+        closed = segments[0][0] != "move"
+        new_segments = []
+        prev_points = segments[-1][1]
+        prev_on_curve = prev_points[-1][0]
+        for segment_type, points in segments:
+            if segment_type == 'curve':
+                for sub_points in self._split_super_bezier_segments(points):
+                    on_curve, smooth, name, kwargs = sub_points[-1]
+                    bcp1, bcp2 = sub_points[0][0], sub_points[1][0]
+                    cubic = [prev_on_curve, bcp1, bcp2, on_curve]
+                    quad = curve_to_quadratic(cubic, self.max_err)
+                    if self.stats is not None:
+                        n = str(len(quad) - 2)
+                        self.stats[n] = self.stats.get(n, 0) + 1
+                    new_points = [(pt, False, None, {}) for pt in quad[1:-1]]
+                    new_points.append((on_curve, smooth, name, kwargs))
+                    new_segments.append(["qcurve", new_points])
+                    prev_on_curve = sub_points[-1][0]
+            else:
+                new_segments.append([segment_type, points])
+                prev_on_curve = points[-1][0]
+        if closed:
+            # the BasePointToSegmentPen.endPath method that calls _flushContour
+            # rotates the point list of closed contours so that they end with
+            # the first on-curve point. We restore the original starting point.
+            new_segments = new_segments[-1:] + new_segments[:-1]
+        self._drawPoints(new_segments)
+
+    def _split_super_bezier_segments(self, points):
+        sub_segments = []
+        # n is the number of control points
+        n = len(points) - 1
+        if n == 2:
+            # a simple bezier curve segment
+            sub_segments.append(points)
+        elif n > 2:
+            # a "super" bezier; decompose it
+            on_curve, smooth, name, kwargs = points[-1]
+            num_sub_segments = n - 1
+            for i, sub_points in enumerate(decomposeSuperBezierSegment([
+                    pt for pt, _, _, _ in points])):
+                new_segment = []
+                for point in sub_points[:-1]:
+                    new_segment.append((point, False, None, {}))
+                if i == (num_sub_segments - 1):
+                    # the last on-curve keeps its original attributes
+                    new_segment.append((on_curve, smooth, name, kwargs))
+                else:
+                    # on-curves of sub-segments are always "smooth"
+                    new_segment.append((sub_points[-1], True, None, {}))
+                sub_segments.append(new_segment)
+        else:
+            raise AssertionError(
+                "expected 2 control points, found: %d" % n)
+        return sub_segments
+
+    def _drawPoints(self, segments):
+        pen = self.pen
+        pen.beginPath()
+        last_offcurves = []
+        for i, (segment_type, points) in enumerate(segments):
+            if segment_type in ("move", "line"):
+                assert len(points) == 1, (
+                    "illegal line segment point count: %d" % len(points))
+                pt, smooth, name, kwargs = points[0]
+                pen.addPoint(pt, segment_type, smooth, name, **kwargs)
+            elif segment_type == "qcurve":
+                assert len(points) >= 2, (
+                    "illegal qcurve segment point count: %d" % len(points))
+                offcurves = points[:-1]
+                if offcurves:
+                    if i == 0:
+                        # any off-curve points preceding the first on-curve
+                        # will be appended at the end of the contour
+                        last_offcurves = offcurves
+                    else:
+                        for (pt, smooth, name, kwargs) in offcurves:
+                            pen.addPoint(pt, None, smooth, name, **kwargs)
+                pt, smooth, name, kwargs = points[-1]
+                if pt is None:
+                    # special quadratic contour with no on-curve points:
+                    # we need to skip the "None" point. See also the Pen
+                    # protocol's qCurveTo() method and fontTools.pens.basePen
+                    pass
+                else:
+                    pen.addPoint(pt, segment_type, smooth, name, **kwargs)
+            else:
+                # 'curve' segments must have been converted to 'qcurve' by now
+                raise AssertionError(
+                    "unexpected segment type: %r" % segment_type)
+        for (pt, smooth, name, kwargs) in last_offcurves:
+            pen.addPoint(pt, None, smooth, name, **kwargs)
+        pen.endPath()
+
+    def addComponent(self, baseGlyphName, transformation):
+        assert self.currentPath is None
+        self.pen.addComponent(baseGlyphName, transformation)
diff --git a/Lib/fontTools/pens/filterPen.py b/Lib/fontTools/pens/filterPen.py
index 2f94550..4355ba4 100644
--- a/Lib/fontTools/pens/filterPen.py
+++ b/Lib/fontTools/pens/filterPen.py
@@ -1,13 +1,12 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.pens.basePen import AbstractPen
+from fontTools.pens.pointPen import AbstractPointPen
 from fontTools.pens.recordingPen import RecordingPen
 
 
 class _PassThruComponentsMixin(object):
 
-    def addComponent(self, glyphName, transformation):
-        self._outPen.addComponent(glyphName, transformation)
+    def addComponent(self, glyphName, transformation, **kwargs):
+        self._outPen.addComponent(glyphName, transformation, **kwargs)
 
 
 class FilterPen(_PassThruComponentsMixin, AbstractPen):
@@ -119,3 +118,41 @@
         Otherwise, the return value is drawn with the output pen.
         """
         return  # or return contour
+
+
+class FilterPointPen(_PassThruComponentsMixin, AbstractPointPen):
+    """ Baseclass for point pens that apply some transformation to the
+    coordinates they receive and pass them to another point 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 RecordingPointPen
+    >>> rec = RecordingPointPen()
+    >>> pen = FilterPointPen(rec)
+    >>> v = iter(rec.value)
+    >>> pen.beginPath(identifier="abc")
+    >>> next(v)
+    ('beginPath', (), {'identifier': 'abc'})
+    >>> pen.addPoint((1, 2), "line", False)
+    >>> next(v)
+    ('addPoint', ((1, 2), 'line', False, None), {})
+    >>> pen.addComponent("a", (2, 0, 0, 2, 10, -10), identifier="0001")
+    >>> next(v)
+    ('addComponent', ('a', (2, 0, 0, 2, 10, -10)), {'identifier': '0001'})
+    >>> pen.endPath()
+    >>> next(v)
+    ('endPath', (), {})
+    """
+
+    def __init__(self, outPointPen):
+        self._outPen = outPointPen
+
+    def beginPath(self, **kwargs):
+        self._outPen.beginPath(**kwargs)
+
+    def endPath(self):
+        self._outPen.endPath()
+
+    def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
+        self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs)
diff --git a/Lib/fontTools/pens/hashPointPen.py b/Lib/fontTools/pens/hashPointPen.py
new file mode 100644
index 0000000..9aef5d8
--- /dev/null
+++ b/Lib/fontTools/pens/hashPointPen.py
@@ -0,0 +1,77 @@
+# Modified from https://github.com/adobe-type-tools/psautohint/blob/08b346865710ed3c172f1eb581d6ef243b203f99/python/psautohint/ufoFont.py#L800-L838
+import hashlib
+
+from fontTools.pens.basePen import MissingComponentError
+from fontTools.pens.pointPen import AbstractPointPen
+
+
+class HashPointPen(AbstractPointPen):
+    """
+    This pen can be used to check if a glyph's contents (outlines plus
+    components) have changed.
+
+    Components are added as the original outline plus each composite's
+    transformation.
+
+    Example: You have some TrueType hinting code for a glyph which you want to
+    compile. The hinting code specifies a hash value computed with HashPointPen
+    that was valid for the glyph's outlines at the time the hinting code was
+    written. Now you can calculate the hash for the glyph's current outlines to
+    check if the outlines have changed, which would probably make the hinting
+    code invalid.
+
+    > glyph = ufo[name]
+    > hash_pen = HashPointPen(glyph.width, ufo)
+    > glyph.drawPoints(hash_pen)
+    > ttdata = glyph.lib.get("public.truetype.instructions", None)
+    > stored_hash = ttdata.get("id", None)  # The hash is stored in the "id" key
+    > if stored_hash is None or stored_hash != hash_pen.hash:
+    >    logger.error(f"Glyph hash mismatch, glyph '{name}' will have no instructions in font.")
+    > else:
+    >    # The hash values are identical, the outline has not changed.
+    >    # Compile the hinting code ...
+    >    pass
+    """
+
+    def __init__(self, glyphWidth=0, glyphSet=None):
+        self.glyphset = glyphSet
+        self.data = ["w%s" % round(glyphWidth, 9)]
+
+    @property
+    def hash(self):
+        data = "".join(self.data)
+        if len(data) >= 128:
+            data = hashlib.sha512(data.encode("ascii")).hexdigest()
+        return data
+
+    def beginPath(self, identifier=None, **kwargs):
+        pass
+
+    def endPath(self):
+        self.data.append("|")
+
+    def addPoint(
+        self,
+        pt,
+        segmentType=None,
+        smooth=False,
+        name=None,
+        identifier=None,
+        **kwargs,
+    ):
+        if segmentType is None:
+            pt_type = "o"  # offcurve
+        else:
+            pt_type = segmentType[0]
+        self.data.append(f"{pt_type}{pt[0]:g}{pt[1]:+g}")
+
+    def addComponent(
+        self, baseGlyphName, transformation, identifier=None, **kwargs
+    ):
+        tr = "".join([f"{t:+}" for t in transformation])
+        self.data.append("[")
+        try:
+            self.glyphset[baseGlyphName].drawPoints(self)
+        except KeyError:
+            raise MissingComponentError(baseGlyphName)
+        self.data.append(f"({tr})]")
diff --git a/Lib/fontTools/pens/momentsPen.py b/Lib/fontTools/pens/momentsPen.py
index b83102c..8c90f70 100644
--- a/Lib/fontTools/pens/momentsPen.py
+++ b/Lib/fontTools/pens/momentsPen.py
@@ -1,13 +1,15 @@
 """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 OpenContourError(NotImplementedError):
+    pass
+
+
 class MomentsPen(BasePen):
 
 	def __init__(self, glyphset=None):
@@ -31,8 +33,9 @@
 	def _endPath(self):
 		p0 = self._getCurrentPoint()
 		if p0 != self.__startPoint:
-			# Green theorem is not defined on open contours.
-			raise NotImplementedError
+			raise OpenContourError(
+							"Green theorem is not defined on open contours."
+			)
 
 	def _lineTo(self, p1):
 		x0,y0 = self._getCurrentPoint()
diff --git a/Lib/fontTools/pens/perimeterPen.py b/Lib/fontTools/pens/perimeterPen.py
index 10a2ff3..9a09cb8 100644
--- a/Lib/fontTools/pens/perimeterPen.py
+++ b/Lib/fontTools/pens/perimeterPen.py
@@ -1,10 +1,8 @@
 # -*- 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 splitQuadraticAtT, splitCubicAtT, approximateQuadraticArcLengthC, calcQuadraticArcLengthC, approximateCubicArcLengthC
+from fontTools.misc.bezierTools import approximateQuadraticArcLengthC, calcQuadraticArcLengthC, approximateCubicArcLengthC, calcCubicArcLengthC
 import math
 
 
@@ -14,18 +12,12 @@
 def _distance(p0, p1):
 	return math.hypot(p0[0] - p1[0], p0[1] - p1[1])
 
-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))
-
 class PerimeterPen(BasePen):
 
 	def __init__(self, glyphset=None, tolerance=0.005):
 		BasePen.__init__(self, glyphset)
 		self.value = 0
-		self._mult = 1.+1.5*tolerance # The 1.5 is a empirical hack; no math
+		self.tolerance = tolerance
 
 		# Choose which algorithm to use for quadratic and for cubic.
 		# Quadrature is faster but has fixed error characteristic with no strong
@@ -55,15 +47,8 @@
 		p0 = self._getCurrentPoint()
 		self._addQuadratic(complex(*p0), complex(*p1), complex(*p2))
 
-	def _addCubicRecursive(self, p0, p1, p2, p3):
-		arch = abs(p0-p3)
-		box = abs(p0-p1) + abs(p1-p2) + abs(p2-p3)
-		if arch * self._mult >= box:
-			self.value += (arch + box) * .5
-		else:
-			one,two = _split_cubic_into_two(p0,p1,p2,p3)
-			self._addCubicRecursive(*one)
-			self._addCubicRecursive(*two)
+	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)
diff --git a/Lib/fontTools/pens/pointInsidePen.py b/Lib/fontTools/pens/pointInsidePen.py
index 3311841..34597f4 100644
--- a/Lib/fontTools/pens/pointInsidePen.py
+++ b/Lib/fontTools/pens/pointInsidePen.py
@@ -2,8 +2,6 @@
 for shapes.
 """
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.pens.basePen import BasePen
 from fontTools.misc.bezierTools import solveQuadratic, solveCubic
 
diff --git a/Lib/fontTools/pens/pointPen.py b/Lib/fontTools/pens/pointPen.py
new file mode 100644
index 0000000..4c3148b
--- /dev/null
+++ b/Lib/fontTools/pens/pointPen.py
@@ -0,0 +1,493 @@
+"""
+=========
+PointPens
+=========
+
+Where **SegmentPens** have an intuitive approach to drawing
+(if you're familiar with postscript anyway), the **PointPen**
+is geared towards accessing all the data in the contours of
+the glyph. A PointPen has a very simple interface, it just
+steps through all the points in a call from glyph.drawPoints().
+This allows the caller to provide more data for each point.
+For instance, whether or not a point is smooth, and its name.
+"""
+
+import math
+from typing import Any, Optional, Tuple
+
+from fontTools.pens.basePen import AbstractPen, PenError
+
+__all__ = [
+	"AbstractPointPen",
+	"BasePointToSegmentPen",
+	"PointToSegmentPen",
+	"SegmentToPointPen",
+	"GuessSmoothPointPen",
+	"ReverseContourPointPen",
+]
+
+
+class AbstractPointPen:
+	"""Baseclass for all PointPens."""
+
+	def beginPath(self, identifier: Optional[str] = None, **kwargs: Any) -> None:
+		"""Start a new sub path."""
+		raise NotImplementedError
+
+	def endPath(self) -> None:
+		"""End the current sub path."""
+		raise NotImplementedError
+
+	def addPoint(
+		self,
+		pt: Tuple[float, float],
+		segmentType: Optional[str] = None,
+		smooth: bool = False,
+		name: Optional[str] = None,
+		identifier: Optional[str] = None,
+		**kwargs: Any
+	) -> None:
+		"""Add a point to the current sub path."""
+		raise NotImplementedError
+
+	def addComponent(
+		self,
+		baseGlyphName: str,
+		transformation: Tuple[float, float, float, float, float, float],
+		identifier: Optional[str] = None,
+		**kwargs: Any
+	) -> None:
+		"""Add a sub glyph."""
+		raise NotImplementedError
+
+
+class BasePointToSegmentPen(AbstractPointPen):
+	"""
+	Base class for retrieving the outline in a segment-oriented
+	way. The PointPen protocol is simple yet also a little tricky,
+	so when you need an outline presented as segments but you have
+	as points, do use this base implementation as it properly takes
+	care of all the edge cases.
+	"""
+
+	def __init__(self):
+		self.currentPath = None
+
+	def beginPath(self, identifier=None, **kwargs):
+		if self.currentPath is not None:
+			raise PenError("Path already begun.")
+		self.currentPath = []
+
+	def _flushContour(self, segments):
+		"""Override this method.
+
+		It will be called for each non-empty sub path with a list
+		of segments: the 'segments' argument.
+
+		The segments list contains tuples of length 2:
+			(segmentType, points)
+
+		segmentType is one of "move", "line", "curve" or "qcurve".
+		"move" may only occur as the first segment, and it signifies
+		an OPEN path. A CLOSED path does NOT start with a "move", in
+		fact it will not contain a "move" at ALL.
+
+		The 'points' field in the 2-tuple is a list of point info
+		tuples. The list has 1 or more items, a point tuple has
+		four items:
+			(point, smooth, name, kwargs)
+		'point' is an (x, y) coordinate pair.
+
+		For a closed path, the initial moveTo point is defined as
+		the last point of the last segment.
+
+		The 'points' list of "move" and "line" segments always contains
+		exactly one point tuple.
+		"""
+		raise NotImplementedError
+
+	def endPath(self):
+		if self.currentPath is None:
+			raise PenError("Path not begun.")
+		points = self.currentPath
+		self.currentPath = None
+		if not points:
+			return
+		if len(points) == 1:
+			# Not much more we can do than output a single move segment.
+			pt, segmentType, smooth, name, kwargs = points[0]
+			segments = [("move", [(pt, smooth, name, kwargs)])]
+			self._flushContour(segments)
+			return
+		segments = []
+		if points[0][1] == "move":
+			# It's an open contour, insert a "move" segment for the first
+			# point and remove that first point from the point list.
+			pt, segmentType, smooth, name, kwargs = points[0]
+			segments.append(("move", [(pt, smooth, name, kwargs)]))
+			points.pop(0)
+		else:
+			# It's a closed contour. Locate the first on-curve point, and
+			# rotate the point list so that it _ends_ with an on-curve
+			# point.
+			firstOnCurve = None
+			for i in range(len(points)):
+				segmentType = points[i][1]
+				if segmentType is not None:
+					firstOnCurve = i
+					break
+			if firstOnCurve is None:
+				# Special case for quadratics: a contour with no on-curve
+				# points. Add a "None" point. (See also the Pen protocol's
+				# qCurveTo() method and fontTools.pens.basePen.py.)
+				points.append((None, "qcurve", None, None, None))
+			else:
+				points = points[firstOnCurve+1:] + points[:firstOnCurve+1]
+
+		currentSegment = []
+		for pt, segmentType, smooth, name, kwargs in points:
+			currentSegment.append((pt, smooth, name, kwargs))
+			if segmentType is None:
+				continue
+			segments.append((segmentType, currentSegment))
+			currentSegment = []
+
+		self._flushContour(segments)
+
+	def addPoint(self, pt, segmentType=None, smooth=False, name=None,
+				 identifier=None, **kwargs):
+		if self.currentPath is None:
+			raise PenError("Path not begun")
+		self.currentPath.append((pt, segmentType, smooth, name, kwargs))
+
+
+class PointToSegmentPen(BasePointToSegmentPen):
+	"""
+	Adapter class that converts the PointPen protocol to the
+	(Segment)Pen protocol.
+
+	NOTE: The segment pen does not support and will drop point names, identifiers
+	and kwargs.
+	"""
+
+	def __init__(self, segmentPen, outputImpliedClosingLine=False):
+		BasePointToSegmentPen.__init__(self)
+		self.pen = segmentPen
+		self.outputImpliedClosingLine = outputImpliedClosingLine
+
+	def _flushContour(self, segments):
+		if not segments:
+			raise PenError("Must have at least one segment.")
+		pen = self.pen
+		if segments[0][0] == "move":
+			# It's an open path.
+			closed = False
+			points = segments[0][1]
+			if len(points) != 1:
+				raise PenError(f"Illegal move segment point count: {len(points)}")
+			movePt, _, _ , _ = points[0]
+			del segments[0]
+		else:
+			# It's a closed path, do a moveTo to the last
+			# point of the last segment.
+			closed = True
+			segmentType, points = segments[-1]
+			movePt, _, _ , _ = points[-1]
+		if movePt is None:
+			# quad special case: a contour with no on-curve points contains
+			# one "qcurve" segment that ends with a point that's None. We
+			# must not output a moveTo() in that case.
+			pass
+		else:
+			pen.moveTo(movePt)
+		outputImpliedClosingLine = self.outputImpliedClosingLine
+		nSegments = len(segments)
+		lastPt = movePt
+		for i in range(nSegments):
+			segmentType, points = segments[i]
+			points = [pt for pt, _, _ , _ in points]
+			if segmentType == "line":
+				if len(points) != 1:
+					raise PenError(f"Illegal line segment point count: {len(points)}")
+				pt = points[0]
+				# For closed contours, a 'lineTo' is always implied from the last oncurve
+				# point to the starting point, thus we can omit it when the last and
+				# starting point don't overlap.
+				# However, when the last oncurve point is a "line" segment and has same
+				# coordinates as the starting point of a closed contour, we need to output
+				# the closing 'lineTo' explicitly (regardless of the value of the
+				# 'outputImpliedClosingLine' option) in order to disambiguate this case from
+				# the implied closing 'lineTo', otherwise the duplicate point would be lost.
+				# See https://github.com/googlefonts/fontmake/issues/572.
+				if (
+					i + 1 != nSegments
+					or outputImpliedClosingLine
+					or not closed
+					or pt == lastPt
+				):
+					pen.lineTo(pt)
+					lastPt = pt
+			elif segmentType == "curve":
+				pen.curveTo(*points)
+				lastPt = points[-1]
+			elif segmentType == "qcurve":
+				pen.qCurveTo(*points)
+				lastPt = points[-1]
+			else:
+				raise PenError(f"Illegal segmentType: {segmentType}")
+		if closed:
+			pen.closePath()
+		else:
+			pen.endPath()
+
+	def addComponent(self, glyphName, transform, identifier=None, **kwargs):
+		del identifier  # unused
+		del kwargs  # unused
+		self.pen.addComponent(glyphName, transform)
+
+
+class SegmentToPointPen(AbstractPen):
+	"""
+	Adapter class that converts the (Segment)Pen protocol to the
+	PointPen protocol.
+	"""
+
+	def __init__(self, pointPen, guessSmooth=True):
+		if guessSmooth:
+			self.pen = GuessSmoothPointPen(pointPen)
+		else:
+			self.pen = pointPen
+		self.contour = None
+
+	def _flushContour(self):
+		pen = self.pen
+		pen.beginPath()
+		for pt, segmentType in self.contour:
+			pen.addPoint(pt, segmentType=segmentType)
+		pen.endPath()
+
+	def moveTo(self, pt):
+		self.contour = []
+		self.contour.append((pt, "move"))
+
+	def lineTo(self, pt):
+		if self.contour is None:
+			raise PenError("Contour missing required initial moveTo")
+		self.contour.append((pt, "line"))
+
+	def curveTo(self, *pts):
+		if not pts:
+			raise TypeError("Must pass in at least one point")
+		if self.contour is None:
+			raise PenError("Contour missing required initial moveTo")
+		for pt in pts[:-1]:
+			self.contour.append((pt, None))
+		self.contour.append((pts[-1], "curve"))
+
+	def qCurveTo(self, *pts):
+		if not pts:
+			raise TypeError("Must pass in at least one point")
+		if pts[-1] is None:
+			self.contour = []
+		else:
+			if self.contour is None:
+				raise PenError("Contour missing required initial moveTo")
+		for pt in pts[:-1]:
+			self.contour.append((pt, None))
+		if pts[-1] is not None:
+			self.contour.append((pts[-1], "qcurve"))
+
+	def closePath(self):
+		if self.contour is None:
+			raise PenError("Contour missing required initial moveTo")
+		if len(self.contour) > 1 and self.contour[0][0] == self.contour[-1][0]:
+			self.contour[0] = self.contour[-1]
+			del self.contour[-1]
+		else:
+			# There's an implied line at the end, replace "move" with "line"
+			# for the first point
+			pt, tp = self.contour[0]
+			if tp == "move":
+				self.contour[0] = pt, "line"
+		self._flushContour()
+		self.contour = None
+
+	def endPath(self):
+		if self.contour is None:
+			raise PenError("Contour missing required initial moveTo")
+		self._flushContour()
+		self.contour = None
+
+	def addComponent(self, glyphName, transform):
+		if self.contour is not None:
+			raise PenError("Components must be added before or after contours")
+		self.pen.addComponent(glyphName, transform)
+
+
+class GuessSmoothPointPen(AbstractPointPen):
+	"""
+	Filtering PointPen that tries to determine whether an on-curve point
+	should be "smooth", ie. that it's a "tangent" point or a "curve" point.
+	"""
+
+	def __init__(self, outPen, error=0.05):
+		self._outPen = outPen
+		self._error = error
+		self._points = None
+
+	def _flushContour(self):
+		if self._points is None:
+			raise PenError("Path not begun")
+		points = self._points
+		nPoints = len(points)
+		if not nPoints:
+			return
+		if points[0][1] == "move":
+			# Open path.
+			indices = range(1, nPoints - 1)
+		elif nPoints > 1:
+			# Closed path. To avoid having to mod the contour index, we
+			# simply abuse Python's negative index feature, and start at -1
+			indices = range(-1, nPoints - 1)
+		else:
+			# closed path containing 1 point (!), ignore.
+			indices = []
+		for i in indices:
+			pt, segmentType, _, name, kwargs = points[i]
+			if segmentType is None:
+				continue
+			prev = i - 1
+			next = i + 1
+			if points[prev][1] is not None and points[next][1] is not None:
+				continue
+			# At least one of our neighbors is an off-curve point
+			pt = points[i][0]
+			prevPt = points[prev][0]
+			nextPt = points[next][0]
+			if pt != prevPt and pt != nextPt:
+				dx1, dy1 = pt[0] - prevPt[0], pt[1] - prevPt[1]
+				dx2, dy2 = nextPt[0] - pt[0], nextPt[1] - pt[1]
+				a1 = math.atan2(dy1, dx1)
+				a2 = math.atan2(dy2, dx2)
+				if abs(a1 - a2) < self._error:
+					points[i] = pt, segmentType, True, name, kwargs
+
+		for pt, segmentType, smooth, name, kwargs in points:
+			self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs)
+
+	def beginPath(self, identifier=None, **kwargs):
+		if self._points is not None:
+			raise PenError("Path already begun")
+		self._points = []
+		if identifier is not None:
+			kwargs["identifier"] = identifier
+		self._outPen.beginPath(**kwargs)
+
+	def endPath(self):
+		self._flushContour()
+		self._outPen.endPath()
+		self._points = None
+
+	def addPoint(self, pt, segmentType=None, smooth=False, name=None,
+				 identifier=None, **kwargs):
+		if self._points is None:
+			raise PenError("Path not begun")
+		if identifier is not None:
+			kwargs["identifier"] = identifier
+		self._points.append((pt, segmentType, False, name, kwargs))
+
+	def addComponent(self, glyphName, transformation, identifier=None, **kwargs):
+		if self._points is not None:
+			raise PenError("Components must be added before or after contours")
+		if identifier is not None:
+			kwargs["identifier"] = identifier
+		self._outPen.addComponent(glyphName, transformation, **kwargs)
+
+
+class ReverseContourPointPen(AbstractPointPen):
+	"""
+	This is a PointPen that passes outline data to another PointPen, 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 __init__(self, outputPointPen):
+		self.pen = outputPointPen
+		# a place to store the points for the current sub path
+		self.currentContour = None
+
+	def _flushContour(self):
+		pen = self.pen
+		contour = self.currentContour
+		if not contour:
+			pen.beginPath(identifier=self.currentContourIdentifier)
+			pen.endPath()
+			return
+
+		closed = contour[0][1] != "move"
+		if not closed:
+			lastSegmentType = "move"
+		else:
+			# Remove the first point and insert it at the end. When
+			# the list of points gets reversed, this point will then
+			# again be at the start. In other words, the following
+			# will hold:
+			#   for N in range(len(originalContour)):
+			#       originalContour[N] == reversedContour[-N]
+			contour.append(contour.pop(0))
+			# Find the first on-curve point.
+			firstOnCurve = None
+			for i in range(len(contour)):
+				if contour[i][1] is not None:
+					firstOnCurve = i
+					break
+			if firstOnCurve is None:
+				# There are no on-curve points, be basically have to
+				# do nothing but contour.reverse().
+				lastSegmentType = None
+			else:
+				lastSegmentType = contour[firstOnCurve][1]
+
+		contour.reverse()
+		if not closed:
+			# Open paths must start with a move, so we simply dump
+			# all off-curve points leading up to the first on-curve.
+			while contour[0][1] is None:
+				contour.pop(0)
+		pen.beginPath(identifier=self.currentContourIdentifier)
+		for pt, nextSegmentType, smooth, name, kwargs in contour:
+			if nextSegmentType is not None:
+				segmentType = lastSegmentType
+				lastSegmentType = nextSegmentType
+			else:
+				segmentType = None
+			pen.addPoint(pt, segmentType=segmentType, smooth=smooth, name=name, **kwargs)
+		pen.endPath()
+
+	def beginPath(self, identifier=None, **kwargs):
+		if self.currentContour is not None:
+			raise PenError("Path already begun")
+		self.currentContour = []
+		self.currentContourIdentifier = identifier
+		self.onCurve = []
+
+	def endPath(self):
+		if self.currentContour is None:
+			raise PenError("Path not begun")
+		self._flushContour()
+		self.currentContour = None
+
+	def addPoint(self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs):
+		if self.currentContour is None:
+			raise PenError("Path not begun")
+		if identifier is not None:
+			kwargs["identifier"] = identifier
+		self.currentContour.append((pt, segmentType, smooth, name, kwargs))
+
+	def addComponent(self, glyphName, transform, identifier=None, **kwargs):
+		if self.currentContour is not None:
+			raise PenError("Components must be added before or after contours")
+		self.pen.addComponent(glyphName, transform, identifier=identifier, **kwargs)
diff --git a/Lib/fontTools/pens/qtPen.py b/Lib/fontTools/pens/qtPen.py
index 01cb37a..3473645 100644
--- a/Lib/fontTools/pens/qtPen.py
+++ b/Lib/fontTools/pens/qtPen.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.pens.basePen import BasePen
 
 
diff --git a/Lib/fontTools/pens/quartzPen.py b/Lib/fontTools/pens/quartzPen.py
new file mode 100644
index 0000000..16b9c2d
--- /dev/null
+++ b/Lib/fontTools/pens/quartzPen.py
@@ -0,0 +1,45 @@
+from fontTools.pens.basePen import BasePen
+
+from Quartz.CoreGraphics import CGPathCreateMutable, CGPathMoveToPoint
+from Quartz.CoreGraphics import CGPathAddLineToPoint, CGPathAddCurveToPoint
+from Quartz.CoreGraphics import CGPathAddQuadCurveToPoint, CGPathCloseSubpath
+	
+
+__all__ = ["QuartzPen"]
+
+
+class QuartzPen(BasePen):
+	
+	"""A pen that creates a CGPath
+	
+	Parameters
+	- path: an optional CGPath to add to
+	- xform: an optional CGAffineTransform to apply to the path
+	"""
+
+	def __init__(self, glyphSet, path=None, xform=None):
+		BasePen.__init__(self, glyphSet)
+		if path is None:
+			path = CGPathCreateMutable()
+		self.path = path
+		self.xform = xform
+
+	def _moveTo(self, pt):
+		x, y = pt
+		CGPathMoveToPoint(self.path, self.xform, x, y)
+
+	def _lineTo(self, pt):
+		x, y = pt
+		CGPathAddLineToPoint(self.path, self.xform, x, y)
+
+	def _curveToOne(self, p1, p2, p3):
+		(x1, y1), (x2, y2), (x3, y3) = p1, p2, p3
+		CGPathAddCurveToPoint(self.path, self.xform, x1, y1, x2, y2, x3, y3)
+		
+	def _qCurveToOne(self, p1, p2):
+		(x1, y1), (x2, y2) = p1, p2
+		CGPathAddQuadCurveToPoint(self.path, self.xform, x1, y1, x2, y2)
+	
+	def _closePath(self):
+		CGPathCloseSubpath(self.path)
+
diff --git a/Lib/fontTools/pens/recordingPen.py b/Lib/fontTools/pens/recordingPen.py
index e583c8f..203082a 100644
--- a/Lib/fontTools/pens/recordingPen.py
+++ b/Lib/fontTools/pens/recordingPen.py
@@ -1,10 +1,14 @@
 """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
+from fontTools.pens.pointPen import AbstractPointPen
 
 
-__all__ = ["replayRecording", "RecordingPen", "DecomposingRecordingPen"]
+__all__ = [
+	"replayRecording",
+	"RecordingPen",
+	"DecomposingRecordingPen",
+	"RecordingPointPen",
+]
 
 
 def replayRecording(recording, pen):
@@ -90,8 +94,58 @@
 	skipMissingComponents = False
 
 
+class RecordingPointPen(AbstractPointPen):
+	"""PointPen recording operations that can be accessed or replayed.
+
+	The recording can be accessed as pen.value; or replayed using
+	pointPen.replay(otherPointPen).
+
+	Usage example:
+	==============
+	from defcon import Font
+	from fontTools.pens.recordingPen import RecordingPointPen
+
+	glyph_name = 'a'
+	font_path = 'MyFont.ufo'
+
+	font = Font(font_path)
+	glyph = font[glyph_name]
+
+	pen = RecordingPointPen()
+	glyph.drawPoints(pen)
+	print(pen.value)
+
+	new_glyph = font.newGlyph('b')
+	pen.replay(new_glyph.getPointPen())
+	"""
+
+	def __init__(self):
+		self.value = []
+
+	def beginPath(self, identifier=None, **kwargs):
+		if identifier is not None:
+			kwargs["identifier"] = identifier
+		self.value.append(("beginPath", (), kwargs))
+
+	def endPath(self):
+		self.value.append(("endPath", (), {}))
+
+	def addPoint(self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs):
+		if identifier is not None:
+			kwargs["identifier"] = identifier
+		self.value.append(("addPoint", (pt, segmentType, smooth, name), kwargs))
+
+	def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
+		if identifier is not None:
+			kwargs["identifier"] = identifier
+		self.value.append(("addComponent", (baseGlyphName, transformation), kwargs))
+
+	def replay(self, pointPen):
+		for operator, args, kwargs in self.value:
+			getattr(pointPen, operator)(*args, **kwargs)
+
+
 if __name__ == "__main__":
-	from fontTools.pens.basePen import _TestPen
 	pen = RecordingPen()
 	pen.moveTo((0, 0))
 	pen.lineTo((0, 100))
diff --git a/Lib/fontTools/pens/reportLabPen.py b/Lib/fontTools/pens/reportLabPen.py
index 2068b25..c0a4610 100644
--- a/Lib/fontTools/pens/reportLabPen.py
+++ b/Lib/fontTools/pens/reportLabPen.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.pens.basePen import BasePen
 from reportlab.graphics.shapes import Path
 
diff --git a/Lib/fontTools/pens/reverseContourPen.py b/Lib/fontTools/pens/reverseContourPen.py
index 27e54d7..9b3241b 100644
--- a/Lib/fontTools/pens/reverseContourPen.py
+++ b/Lib/fontTools/pens/reverseContourPen.py
@@ -1,5 +1,3 @@
-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
 
diff --git a/Lib/fontTools/pens/roundingPen.py b/Lib/fontTools/pens/roundingPen.py
new file mode 100644
index 0000000..2a7c476
--- /dev/null
+++ b/Lib/fontTools/pens/roundingPen.py
@@ -0,0 +1,112 @@
+from fontTools.misc.roundTools import otRound
+from fontTools.misc.transform import Transform
+from fontTools.pens.filterPen import FilterPen, FilterPointPen
+
+
+__all__ = ["RoundingPen", "RoundingPointPen"]
+
+
+class RoundingPen(FilterPen):
+    """
+    Filter pen that rounds point coordinates and component XY offsets to integer.
+
+    >>> from fontTools.pens.recordingPen import RecordingPen
+    >>> recpen = RecordingPen()
+    >>> roundpen = RoundingPen(recpen)
+    >>> roundpen.moveTo((0.4, 0.6))
+    >>> roundpen.lineTo((1.6, 2.5))
+    >>> roundpen.qCurveTo((2.4, 4.6), (3.3, 5.7), (4.9, 6.1))
+    >>> roundpen.curveTo((6.4, 8.6), (7.3, 9.7), (8.9, 10.1))
+    >>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5))
+    >>> recpen.value == [
+    ...     ('moveTo', ((0, 1),)),
+    ...     ('lineTo', ((2, 3),)),
+    ...     ('qCurveTo', ((2, 5), (3, 6), (5, 6))),
+    ...     ('curveTo', ((6, 9), (7, 10), (9, 10))),
+    ...     ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10))),
+    ... ]
+    True
+    """
+
+    def __init__(self, outPen, roundFunc=otRound):
+        super().__init__(outPen)
+        self.roundFunc = roundFunc
+
+    def moveTo(self, pt):
+        self._outPen.moveTo((self.roundFunc(pt[0]), self.roundFunc(pt[1])))
+
+    def lineTo(self, pt):
+        self._outPen.lineTo((self.roundFunc(pt[0]), self.roundFunc(pt[1])))
+
+    def curveTo(self, *points):
+        self._outPen.curveTo(
+            *((self.roundFunc(x), self.roundFunc(y)) for x, y in points)
+        )
+
+    def qCurveTo(self, *points):
+        self._outPen.qCurveTo(
+            *((self.roundFunc(x), self.roundFunc(y)) for x, y in points)
+        )
+
+    def addComponent(self, glyphName, transformation):
+        self._outPen.addComponent(
+            glyphName,
+            Transform(
+                *transformation[:4],
+                self.roundFunc(transformation[4]),
+                self.roundFunc(transformation[5]),
+            ),
+        )
+
+
+class RoundingPointPen(FilterPointPen):
+    """
+    Filter point pen that rounds point coordinates and component XY offsets to integer.
+
+    >>> from fontTools.pens.recordingPen import RecordingPointPen
+    >>> recpen = RecordingPointPen()
+    >>> roundpen = RoundingPointPen(recpen)
+    >>> roundpen.beginPath()
+    >>> roundpen.addPoint((0.4, 0.6), 'line')
+    >>> roundpen.addPoint((1.6, 2.5), 'line')
+    >>> roundpen.addPoint((2.4, 4.6))
+    >>> roundpen.addPoint((3.3, 5.7))
+    >>> roundpen.addPoint((4.9, 6.1), 'qcurve')
+    >>> roundpen.endPath()
+    >>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5))
+    >>> recpen.value == [
+    ...     ('beginPath', (), {}),
+    ...     ('addPoint', ((0, 1), 'line', False, None), {}),
+    ...     ('addPoint', ((2, 3), 'line', False, None), {}),
+    ...     ('addPoint', ((2, 5), None, False, None), {}),
+    ...     ('addPoint', ((3, 6), None, False, None), {}),
+    ...     ('addPoint', ((5, 6), 'qcurve', False, None), {}),
+    ...     ('endPath', (), {}),
+    ...     ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10)), {}),
+    ... ]
+    True
+    """
+
+    def __init__(self, outPen, roundFunc=otRound):
+        super().__init__(outPen)
+        self.roundFunc = roundFunc
+
+    def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
+        self._outPen.addPoint(
+            (self.roundFunc(pt[0]), self.roundFunc(pt[1])),
+            segmentType=segmentType,
+            smooth=smooth,
+            name=name,
+            **kwargs,
+        )
+
+    def addComponent(self, baseGlyphName, transformation, **kwargs):
+        self._outPen.addComponent(
+            baseGlyphName,
+            Transform(
+                *transformation[:4],
+                self.roundFunc(transformation[4]),
+                self.roundFunc(transformation[5]),
+            ),
+            **kwargs,
+        )
diff --git a/Lib/fontTools/pens/statisticsPen.py b/Lib/fontTools/pens/statisticsPen.py
index 7c2e85c..abd6ff5 100644
--- a/Lib/fontTools/pens/statisticsPen.py
+++ b/Lib/fontTools/pens/statisticsPen.py
@@ -1,7 +1,5 @@
 """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
 
diff --git a/Lib/fontTools/pens/svgPathPen.py b/Lib/fontTools/pens/svgPathPen.py
index 17e4ccc..4352ba4 100644
--- a/Lib/fontTools/pens/svgPathPen.py
+++ b/Lib/fontTools/pens/svgPathPen.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.pens.basePen import BasePen
 
 
diff --git a/Lib/fontTools/pens/t2CharStringPen.py b/Lib/fontTools/pens/t2CharStringPen.py
index d23fae1..0fddec1 100644
--- a/Lib/fontTools/pens/t2CharStringPen.py
+++ b/Lib/fontTools/pens/t2CharStringPen.py
@@ -1,37 +1,12 @@
 # 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.roundTools import otRound, roundFunc
 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 = round(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.
 
@@ -45,7 +20,7 @@
 
     def __init__(self, width, glyphSet, roundTolerance=0.5, CFF2=False):
         super(T2CharStringPen, self).__init__(glyphSet)
-        self.roundPoint = makeRoundFunc(roundTolerance)
+        self.round = roundFunc(roundTolerance)
         self._CFF2 = CFF2
         self._width = width
         self._commands = []
@@ -53,7 +28,7 @@
 
     def _p(self, pt):
         p0 = self._p0
-        pt = self._p0 = self.roundPoint(pt)
+        pt = self._p0 = (self.round(pt[0]), self.round(pt[1]))
         return [pt[0]-p0[0], pt[1]-p0[1]]
 
     def _moveTo(self, pt):
@@ -82,7 +57,7 @@
         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, round(self._width))
+            program.insert(0, otRound(self._width))
         if not self._CFF2:
             program.append('endchar')
         charString = T2CharString(
diff --git a/Lib/fontTools/pens/teePen.py b/Lib/fontTools/pens/teePen.py
index ce22c85..2f30e92 100644
--- a/Lib/fontTools/pens/teePen.py
+++ b/Lib/fontTools/pens/teePen.py
@@ -1,6 +1,4 @@
 """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
 
 
diff --git a/Lib/fontTools/pens/transformPen.py b/Lib/fontTools/pens/transformPen.py
index 8f4fcd0..93d1919 100644
--- a/Lib/fontTools/pens/transformPen.py
+++ b/Lib/fontTools/pens/transformPen.py
@@ -1,9 +1,7 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.pens.filterPen import FilterPen
+from fontTools.pens.filterPen import FilterPen, FilterPointPen
 
 
-__all__ = ["TransformPen"]
+__all__ = ["TransformPen", "TransformPointPen"]
 
 
 class TransformPen(FilterPen):
@@ -56,6 +54,51 @@
 		self._outPen.addComponent(glyphName, transformation)
 
 
+class TransformPointPen(FilterPointPen):
+	"""PointPen that transforms all coordinates using a Affine transformation,
+	and passes them to another PointPen.
+
+	>>> from fontTools.pens.recordingPen import RecordingPointPen
+	>>> rec = RecordingPointPen()
+	>>> pen = TransformPointPen(rec, (2, 0, 0, 2, -10, 5))
+	>>> v = iter(rec.value)
+	>>> pen.beginPath(identifier="contour-0")
+	>>> next(v)
+	('beginPath', (), {'identifier': 'contour-0'})
+	>>> pen.addPoint((100, 100), "line")
+	>>> next(v)
+	('addPoint', ((190, 205), 'line', False, None), {})
+	>>> pen.endPath()
+	>>> next(v)
+	('endPath', (), {})
+	>>> pen.addComponent("a", (1, 0, 0, 1, -10, 5), identifier="component-0")
+	>>> next(v)
+	('addComponent', ('a', <Transform [2 0 0 2 -30 15]>), {'identifier': 'component-0'})
+	"""
+
+	def __init__(self, outPointPen, transformation):
+		"""The 'outPointPen' argument is another point pen object.
+		It will receive the transformed coordinates.
+		The 'transformation' argument can either be a six-tuple, or a
+		fontTools.misc.transform.Transform object.
+		"""
+		super().__init__(outPointPen)
+		if not hasattr(transformation, "transformPoint"):
+			from fontTools.misc.transform import Transform
+			transformation = Transform(*transformation)
+		self._transformation = transformation
+		self._transformPoint = transformation.transformPoint
+
+	def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
+		self._outPen.addPoint(
+			self._transformPoint(pt), segmentType, smooth, name, **kwargs
+		)
+
+	def addComponent(self, baseGlyphName, transformation, **kwargs):
+		transformation = self._transformation.transform(transformation)
+		self._outPen.addComponent(baseGlyphName, transformation, **kwargs)
+
+
 if __name__ == "__main__":
 	from fontTools.pens.basePen import _TestPen
 	pen = TransformPen(_TestPen(None), (2, 0, 0.5, 2, -10, 0))
diff --git a/Lib/fontTools/pens/ttGlyphPen.py b/Lib/fontTools/pens/ttGlyphPen.py
index 09fa188..5087e15 100644
--- a/Lib/fontTools/pens/ttGlyphPen.py
+++ b/Lib/fontTools/pens/ttGlyphPen.py
@@ -1,51 +1,194 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from array import array
-from fontTools.pens.basePen import AbstractPen
-from fontTools.pens.transformPen import TransformPen
+from typing import Any, Dict, Optional, Tuple
+from fontTools.misc.fixedTools import MAX_F2DOT14, floatToFixedToFloat
+from fontTools.misc.loggingTools import LogMixin
+from fontTools.pens.pointPen import AbstractPointPen
+from fontTools.misc.roundTools import otRound
+from fontTools.pens.basePen import LoggingPen, PenError
+from fontTools.pens.transformPen import TransformPen, TransformPointPen
 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"]
+__all__ = ["TTGlyphPen", "TTGlyphPointPen"]
 
 
-class TTGlyphPen(AbstractPen):
-    """Pen used for drawing to a TrueType glyph."""
+class _TTGlyphBasePen:
+    def __init__(
+        self,
+        glyphSet: Optional[Dict[str, Any]],
+        handleOverflowingTransforms: bool = True,
+    ) -> None:
+        """
+        Construct a new pen.
 
-    def __init__(self, glyphSet):
+        Args:
+            glyphSet (Dict[str, Any]): A glyphset object, used to resolve components.
+            handleOverflowingTransforms (bool): See below.
+
+        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.
+
+        If both contours and components are present in a glyph, the components
+        are decomposed.
+        """
         self.glyphSet = glyphSet
+        self.handleOverflowingTransforms = handleOverflowingTransforms
         self.init()
 
-    def init(self):
+    def _decompose(
+        self,
+        glyphName: str,
+        transformation: Tuple[float, float, float, float, float, float],
+    ):
+        tpen = self.transformPen(self, transformation)
+        getattr(self.glyphSet[glyphName], self.drawMethod)(tpen)
+
+    def _isClosed(self):
+        """
+        Check if the current path is closed.
+        """
+        raise NotImplementedError
+
+    def init(self) -> None:
         self.points = []
         self.endPts = []
         self.types = []
         self.components = []
 
-    def _addPoint(self, pt, onCurve):
+    def addComponent(
+        self,
+        baseGlyphName: str,
+        transformation: Tuple[float, float, float, float, float, float],
+        identifier: Optional[str] = None,
+        **kwargs: Any,
+    ) -> None:
+        """
+        Add a sub glyph.
+        """
+        self.components.append((baseGlyphName, 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(f"skipped non-existing component '{glyphName}'")
+                continue
+            if self.points or (self.handleOverflowingTransforms and overflowing):
+                # can't have both coordinates and components, so decompose
+                self._decompose(glyphName, transformation)
+                continue
+
+            component = GlyphComponent()
+            component.glyphName = glyphName
+            component.x, component.y = (otRound(v) for v in transformation[4:])
+            # quantize floats to F2Dot14 so we get same values as when decompiled
+            # from a binary glyf table
+            transformation = tuple(
+                floatToFixedToFloat(v, 14) for v in 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: int = 0x4) -> Glyph:
+        """
+        Returns a :py:class:`~._g_l_y_f.Glyph` object representing the glyph.
+        """
+        if not self._isClosed():
+            raise PenError("Didn't close last contour.")
+        components = self._buildComponents(componentFlags)
+
+        glyph = Glyph()
+        glyph.coordinates = GlyphCoordinates(self.points)
+        glyph.coordinates.toInt()
+        glyph.endPtsOfContours = self.endPts
+        glyph.flags = array("B", self.types)
+        self.init()
+
+        if components:
+            # If both components and contours were present, they have by now
+            # been decomposed by _buildComponents.
+            glyph.components = components
+            glyph.numberOfContours = -1
+        else:
+            glyph.numberOfContours = len(glyph.endPtsOfContours)
+            glyph.program = ttProgram.Program()
+            glyph.program.fromBytecode(b"")
+
+        return glyph
+
+
+class TTGlyphPen(_TTGlyphBasePen, LoggingPen):
+    """
+    Pen used for drawing to a TrueType glyph.
+
+    This pen can be used to construct or modify glyphs in a TrueType format
+    font. After using the pen to draw, use the ``.glyph()`` method to retrieve
+    a :py:class:`~._g_l_y_f.Glyph` object representing the glyph.
+    """
+
+    drawMethod = "draw"
+    transformPen = TransformPen
+
+    def _addPoint(self, pt: Tuple[float, float], onCurve: int) -> None:
         self.points.append(pt)
         self.types.append(onCurve)
 
-    def _popPoint(self):
+    def _popPoint(self) -> None:
         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 _isClosed(self) -> bool:
+        return (not self.points) or (
+            self.endPts and self.endPts[-1] == len(self.points) - 1
+        )
 
-    def lineTo(self, pt):
+    def lineTo(self, pt: Tuple[float, float]) -> None:
         self._addPoint(pt, 1)
 
-    def moveTo(self, pt):
-        assert self._isClosed(), '"move"-type point must begin a new contour.'
+    def moveTo(self, pt: Tuple[float, float]) -> None:
+        if not self._isClosed():
+            raise PenError('"move"-type point must begin a new contour.')
         self._addPoint(pt, 1)
 
-    def qCurveTo(self, *points):
+    def curveTo(self, *points) -> None:
+        raise NotImplementedError
+
+    def qCurveTo(self, *points) -> None:
         assert len(points) >= 1
         for pt in points[:-1]:
             self._addPoint(pt, 0)
@@ -54,7 +197,7 @@
         if points[-1] is not None:
             self._addPoint(points[-1], 1)
 
-    def closePath(self):
+    def closePath(self) -> None:
         endPt = len(self.points) - 1
 
         # ignore anchors (one-point paths)
@@ -72,44 +215,71 @@
 
         self.endPts.append(endPt)
 
-    def endPath(self):
+    def endPath(self) -> None:
         # TrueType contours are always "closed"
         self.closePath()
 
-    def addComponent(self, glyphName, transformation):
-        self.components.append((glyphName, transformation))
 
-    def glyph(self, componentFlags=0x4):
-        assert self._isClosed(), "Didn't close last contour."
+class TTGlyphPointPen(_TTGlyphBasePen, LogMixin, AbstractPointPen):
+    """
+    Point pen used for drawing to a TrueType glyph.
 
-        components = []
-        for glyphName, transformation in self.components:
-            if self.points:
-                # can't have both, so decompose the glyph
-                tpen = TransformPen(self, transformation)
-                self.glyphSet[glyphName].draw(tpen)
-                continue
+    This pen can be used to construct or modify glyphs in a TrueType format
+    font. After using the pen to draw, use the ``.glyph()`` method to retrieve
+    a :py:class:`~._g_l_y_f.Glyph` object representing the glyph.
+    """
 
-            component = GlyphComponent()
-            component.glyphName = glyphName
-            if transformation[:4] != (1, 0, 0, 1):
-                component.transform = (transformation[:2], transformation[2:4])
-            component.x, component.y = transformation[4:]
-            component.flags = componentFlags
-            components.append(component)
+    drawMethod = "drawPoints"
+    transformPen = TransformPointPen
 
-        glyph = Glyph()
-        glyph.coordinates = GlyphCoordinates(self.points)
-        glyph.endPtsOfContours = self.endPts
-        glyph.flags = array("B", self.types)
-        self.init()
+    def init(self) -> None:
+        super().init()
+        self._currentContourStartIndex = None
 
-        if components:
-            glyph.components = components
-            glyph.numberOfContours = -1
+    def _isClosed(self) -> bool:
+        return self._currentContourStartIndex is None
+
+    def beginPath(self, identifier: Optional[str] = None, **kwargs: Any) -> None:
+        """
+        Start a new sub path.
+        """
+        if not self._isClosed():
+            raise PenError("Didn't close previous contour.")
+        self._currentContourStartIndex = len(self.points)
+
+    def endPath(self) -> None:
+        """
+        End the current sub path.
+        """
+        # TrueType contours are always "closed"
+        if self._isClosed():
+            raise PenError("Contour is already closed.")
+        if self._currentContourStartIndex == len(self.points):
+            raise PenError("Tried to end an empty contour.")
+        self.endPts.append(len(self.points) - 1)
+        self._currentContourStartIndex = None
+
+    def addPoint(
+        self,
+        pt: Tuple[float, float],
+        segmentType: Optional[str] = None,
+        smooth: bool = False,
+        name: Optional[str] = None,
+        identifier: Optional[str] = None,
+        **kwargs: Any,
+    ) -> None:
+        """
+        Add a point to the current sub path.
+        """
+        if self._isClosed():
+            raise PenError("Can't add a point to a closed contour.")
+        if segmentType is None:
+            self.types.append(0)  # offcurve
+        elif segmentType in ("qcurve", "line", "move"):
+            self.types.append(1)  # oncurve
+        elif segmentType == "curve":
+            raise NotImplementedError("cubic curves are not supported")
         else:
-            glyph.numberOfContours = len(glyph.endPtsOfContours)
-            glyph.program = ttProgram.Program()
-            glyph.program.fromBytecode(b"")
+            raise AssertionError(segmentType)
 
-        return glyph
+        self.points.append(pt)
diff --git a/Lib/fontTools/pens/wxPen.py b/Lib/fontTools/pens/wxPen.py
index 8e0e463..1504f08 100644
--- a/Lib/fontTools/pens/wxPen.py
+++ b/Lib/fontTools/pens/wxPen.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.pens.basePen import BasePen
 
 
diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py
index c2fc098..8616053 100644
--- a/Lib/fontTools/subset/__init__.py
+++ b/Lib/fontTools/subset/__init__.py
@@ -2,18 +2,19 @@
 #
 # Google Author(s): Behdad Esfahbod
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.roundTools import otRound
 from fontTools import ttLib
 from fontTools.ttLib.tables import otTables
-from fontTools.misc import psCharStrings
+from fontTools.otlLib.maxContextCalc import maxCtxFont
 from fontTools.pens.basePen import NullPen
 from fontTools.misc.loggingTools import Timer
+from fontTools.subset.cff import *
 import sys
 import struct
 import array
 import logging
-from collections import Counter
+from collections import Counter, defaultdict
+from functools import reduce
 from types import MethodType
 
 __usage__ = "pyftsubset font-file [glyph...] [--option=value]..."
@@ -100,7 +101,7 @@
     $ pyftsubset --glyph-names?
     Current setting for 'glyph-names' is: False
     $ ./pyftsubset --name-IDs=?
-    Current setting for 'name-IDs' is: [1, 2]
+    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
@@ -121,6 +122,8 @@
 
 Glyph set expansion:
   These options control how additional glyphs are added to the subset.
+  --retain-gids
+      Retain glyph indices; just empty glyphs not needed in-place.
   --notdef-glyph
       Add the '.notdef' glyph to the subset (ie, keep it). [default]
   --no-notdef-glyph
@@ -143,6 +146,10 @@
   --no-recommended-glyphs
       Do not add glyphs 0, 1, 2, and 3 to the subset, unless specified in
       glyph set. [default]
+  --no-layout-closure
+      Do not expand glyph set to add glyphs produced by OpenType layout
+      features.  Instead, OpenType layout features will be subset to only
+      rules that are relevant to the otherwise-specified glyph set.
   --layout-features[+|-]=<feature>[,<feature>...]
       Specify (=), add to (+=) or exclude from (-=) the comma-separated
       set of OpenType layout feature tags that will be preserved.
@@ -166,6 +173,11 @@
             * Keep all features.
         --layout-features+=aalt --layout-features-=vrt2
             * Keep default set of features plus 'aalt', but drop 'vrt2'.
+  --layout-scripts[+|-]=<script>[,<script>...]
+      Specify (=), add to (+=) or exclude from (-=) the comma-separated
+      set of OpenType layout script tags that will be preserved. LangSys tags
+      can be appended to script tag, separated by '.', for example:
+      'arab.dflt,arab.URD,latn.TRK'. By default all scripts are retained ('*').
 
 Hinting options:
   --hinting
@@ -195,8 +207,7 @@
       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'.
+      and Graphite tables: 'Feat', 'Glat', 'Gloc', 'Silf', 'Sill'.
       The tool will attempt to subset the remaining tables.
       Examples:
         --drop-tables-='SVG '
@@ -211,8 +222,8 @@
       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', 'STAT'.
+      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.
@@ -242,11 +253,11 @@
   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 nameID 1 (Family)
-      and nameID 2 (Style) are preserved. Use '*' to keep all entries.
+      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+=0,4,6
-            * Also keep Copyright, Full name and PostScript name entry.
+        --name-IDs+=7,8,9
+            * Also keep Trademark, Manufacturer and Designer name entries.
         --name-IDs=''
             * Drop all 'name' table entries.
         --name-IDs='*'
@@ -254,7 +265,7 @@
   --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
+      entires for English. See: https://github.com/fonttools/fonttools/issues/146
   --no-name-legacy
       Drop legacy (non-Unicode) 'name' table entries [default]
   --name-languages[+|-]=<langID>[,<langID>]
@@ -310,6 +321,12 @@
       Update the 'OS/2 xAvgCharWidth' field after subsetting.
   --no-recalc-average-width
       Don't change the 'OS/2 xAvgCharWidth' field. [default]
+  --recalc-max-context
+      Update the 'OS/2 usMaxContext' field after subsetting.
+  --no-recalc-max-context
+      Don't change the 'OS/2 usMaxContext' field. [default]
+  --font-number=<number>
+      Select font number for TrueType Collection (.ttc/.otc), starting from 0.
 
 Application options:
   --verbose
@@ -333,10 +350,10 @@
 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))
+	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)
@@ -347,2203 +364,2121 @@
 
 
 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
+	"""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)
+	return sorted(set(l))
 
 def _dict_subset(d, glyphs):
-    return {g:d[g] for g in glyphs}
+	return {g:d[g] for g in glyphs}
 
+def _list_subset(l, indices):
+	count = len(l)
+	return [l[i] for i in indices if i < count]
 
 @_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]
+	"""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)
+	"""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
+	"""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]
+	"""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])
+	"""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)
+	"""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
+def subset(self, glyphs, remap=False, useClass0=True):
+	"""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.
+	# Only do this if allowed.
+	indices = _uniq_sort(
+		 ([0] if ((not useClass0) or 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()}
+	"""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)
+	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)
+	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)
+	for glyph, subst in self.mapping.items():
+		if glyph in cur_glyphs:
+			s.glyphs.update(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)
+	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))
+	s.glyphs.update(*(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)
+	self.alternates = {g:[v for v in vlist if v in s.glyphs]
+					   for g,vlist in self.alternates.items()
+					   if g in s.glyphs and
+					   any(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))
+	s.glyphs.update(*([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)
+	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
+	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
+	if self.Format == 1:
+		indices = self.Coverage.subset(s.glyphs)
+		self.Substitute = _list_subset(self.Substitute, 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 = _list_subset(self.Substitute, 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.Device)
+def is_hinting(self):
+	return self.DeltaFormat in (1,2,3)
+
+@_add_method(otTables.ValueRecord)
+def prune_hints(self):
+	for name in ['XPlaDevice', 'YPlaDevice', 'XAdvDevice', 'YAdvDevice']:
+		v = getattr(self, name, None)
+		if v is not None and v.is_hinting():
+			delattr(self, name)
 
 @_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
+	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, options):
-    if not options.hinting:
-        # Drop device tables
-        self.ValueFormat &= ~0x00F0
-    return True
+def prune_post_subset(self, font, options):
+	if self.Value is None:
+		assert self.ValueFormat == 0
+		return True
+
+	# Shrink ValueFormat
+	if self.Format == 1:
+		if not options.hinting:
+			self.Value.prune_hints()
+		self.ValueFormat = self.Value.getEffectiveFormat()
+	elif self.Format == 2:
+		if not options.hinting:
+			for v in self.Value:
+				v.prune_hints()
+		self.ValueFormat = reduce(int.__or__, [v.getEffectiveFormat() for v in self.Value], 0)
+
+	# Downgrade to Format 1 if all ValueRecords are the same
+	if self.Format == 2 and all(v == self.Value[0] for v in self.Value):
+		self.Format = 1
+		self.Value = self.Value[0] if self.ValueFormat != 0 else None
+		del self.ValueCount
+
+	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
+	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 = _list_subset(self.PairSet, 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.intersection(self.Coverage.glyphs), remap=True) if c < self.Class1Count]
+		class2_map = [c for c in self.ClassDef2.subset(s.glyphs, remap=True, useClass0=False) 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)
+		# If only Class2 0 left, no need to keep anything.
+		return bool(self.Class1Count and
+					(self.Class2Count > 1) 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
+def prune_post_subset(self, font, options):
+	if not options.hinting:
+		attr1, attr2 = {
+			1: ('PairSet', 'PairValueRecord'),
+			2: ('Class1Record', 'Class2Record'),
+		}[self.Format]
+
+		self.ValueFormat1 = self.ValueFormat2 = 0
+		for row in getattr(self, attr1):
+			for r in getattr(row, attr2):
+				if r.Value1:
+					r.Value1.prune_hints()
+					self.ValueFormat1 |= r.Value1.getEffectiveFormat()
+				if r.Value2:
+					r.Value2.prune_hints()
+					self.ValueFormat2 |= r.Value2.getEffectiveFormat()
+
+	return bool(self.ValueFormat1 | self.ValueFormat2)
 
 @_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
+	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
+	if self.Format == 2:
+		self.Format = 1
+	elif self.Format == 3:
+		for name in ('XDeviceTable', 'YDeviceTable'):
+			v = getattr(self, name, None)
+			if v is not None and v.is_hinting():
+				setattr(self, name, None)
+		if self.XDeviceTable is None and self.YDeviceTable is None:
+			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
+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
+	if self.Format == 1:
+		mark_indices = self.MarkCoverage.subset(s.glyphs)
+		self.MarkArray.MarkRecord = _list_subset(self.MarkArray.MarkRecord, mark_indices)
+		self.MarkArray.MarkCount = len(self.MarkArray.MarkRecord)
+		base_indices = self.BaseCoverage.subset(s.glyphs)
+		self.BaseArray.BaseRecord = _list_subset(self.BaseArray.BaseRecord, 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 = _list_subset(b.BaseAnchor, 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
+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
+	if self.Format == 1:
+		mark_indices = self.MarkCoverage.subset(s.glyphs)
+		self.MarkArray.MarkRecord = _list_subset(self.MarkArray.MarkRecord, mark_indices)
+		self.MarkArray.MarkCount = len(self.MarkArray.MarkRecord)
+		ligature_indices = self.LigatureCoverage.subset(s.glyphs)
+		self.LigatureArray.LigatureAttach = _list_subset(self.LigatureArray.LigatureAttach, 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 = _list_subset(c.LigatureAnchor, 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
+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
+	if self.Format == 1:
+		mark1_indices = self.Mark1Coverage.subset(s.glyphs)
+		self.Mark1Array.MarkRecord = _list_subset(self.Mark1Array.MarkRecord, mark1_indices)
+		self.Mark1Array.MarkCount = len(self.Mark1Array.MarkRecord)
+		mark2_indices = self.Mark2Coverage.subset(s.glyphs)
+		self.Mark2Array.Mark2Record = _list_subset(self.Mark2Array.Mark2Record, 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 = _list_subset(b.Mark2Anchor, 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
+def prune_post_subset(self, font, options):
+		if not options.hinting:
+			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)
+			 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
+	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)
+			 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 []
+	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, options):
-    return True
+			 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)
+			 otTables.AlternateSubst,
+			 otTables.ReverseChainSingleSubst)
 def may_have_non_1to1(self):
-    return False
+	return False
 
 @_add_method(otTables.MultipleSubst,
-             otTables.LigatureSubst,
-             otTables.ContextSubst,
-             otTables.ChainContextSubst)
+			 otTables.LigatureSubst,
+			 otTables.ContextSubst,
+			 otTables.ChainContextSubst)
 def may_have_non_1to1(self):
-    return True
+	return True
 
 @_add_method(otTables.ContextSubst,
-             otTables.ChainContextSubst,
-             otTables.ContextPos,
-             otTables.ChainContextPos)
+			 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
+	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.Typ = Typ
+			self.Type = Type
+			self.Chain = Chain
+			self.ChainTyp = ChainTyp
+			self.InputIdx = InputIdx
+			self.DataLen = DataLen
 
-            self.LookupRecord = Type+'LookupRecord'
+			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 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 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()))
+			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'
+				self.ClassDef = 'InputClassDef' if Chain else 'ClassDef'
+				self.ClassDefIndex = 1 if Chain else 0
+				self.Input = 'Input' if Chain else 'Class'
+			elif Format == 3:
+				self.Input = 'InputCoverage' if Chain else 'Coverage'
 
-    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__, "_subset__ContextHelpers"):
+		self.__class__._subset__ContextHelpers = {}
+	if self.Format not in self.__class__._subset__ContextHelpers:
+		helper = ContextHelper(self.__class__, self.Format)
+		self.__class__._subset__ContextHelpers[self.Format] = helper
+	return self.__class__._subset__ContextHelpers[self.Format]
 
 @_add_method(otTables.ContextSubst,
-             otTables.ChainContextSubst)
+			 otTables.ChainContextSubst)
 def closure_glyphs(self, s, cur_glyphs):
-    c = self.__subset_classify_context()
+	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)
+	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
+	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
+		input_coverages = getattr(r, c.Input)
+		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(input_coverages[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(input_coverages)+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)
+			 otTables.ContextPos,
+			 otTables.ChainContextSubst,
+			 otTables.ChainContextPos)
 def subset_glyphs(self, s):
-    c = self.__subset_classify_context()
+	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]
+	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 = _list_subset(rss, 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)]
+		# 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))
+		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))])
+			# 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))
+		# 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.
+		# 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
+		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)
+			 otTables.ChainContextSubst,
+			 otTables.ContextPos,
+			 otTables.ChainContextPos)
 def subset_lookups(self, lookup_indices):
-    c = self.__subset_classify_context()
+	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
+	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)
+			 otTables.ChainContextSubst,
+			 otTables.ContextPos,
+			 otTables.ChainContextPos)
 def collect_lookups(self):
-    c = self.__subset_classify_context()
+	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
+	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
+	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
+	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)
+			 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
+	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
+			 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)
+			 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
+	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)
+			 otTables.ExtensionPos)
 def collect_lookups(self):
-    if self.Format == 1:
-        return self.ExtSubTable.collect_lookups()
-    else:
-        assert 0, "unknown format: %s" % self.Format
+	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)
+	if cur_glyphs is None:
+		cur_glyphs = frozenset(s.glyphs)
 
-    # Memoize
-    if (id(self), cur_glyphs) in s._doneLookups:
-        return
-    s._doneLookups.add((id(self), cur_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)
 
-    if self in s._activeLookups:
-        raise Exception("Circular loop in lookup recursion")
-    s._activeLookups.append(self)
-    for st in self.SubTable:
-        if not st: continue
-        st.closure_glyphs(s, cur_glyphs)
-    assert(s._activeLookups[-1] == self)
-    del s._activeLookups[-1]
+	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)
+	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
+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)
+	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), [])
+	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)
+	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)]
+	"""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
+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)
+	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)]
+	"""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
+	"""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
+	""""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)
+	# keep 'size' feature even if it contains no lookups; but drop any other
+	# empty feature (e.g. FeatureParams for stylistic set names)
+	# https://github.com/fonttools/fonttools/issues/2324
+	return (
+	  self.LookupCount or
+	  isinstance(self.FeatureParams, otTables.FeatureParamsSize)
+	)
 
 @_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')]
+	"""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), [])
+	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)
+	self.ensureDecompiled()
+	self.FeatureRecord = _list_subset(self.FeatureRecord, 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)]
+	"""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), [])
+	"""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), [])
+	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)
+	self.ensureDecompiled()
+	self.SubstitutionRecord = [r for r in self.SubstitutionRecord
+				     if r.FeatureIndex in feature_indices]
+	# remap feature indices
+	for r in self.SubstitutionRecord:
+		r.FeatureIndex = feature_indices.index(r.FeatureIndex)
+	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)
+	self.ensureDecompiled()
+	for r in self.FeatureVariationRecord:
+		r.FeatureTableSubstitution.subset_features(feature_indices)
+	# Prune empty records at the end only
+	# https://github.com/fonttools/fonttools/issues/1881
+	while (self.FeatureVariationRecord and
+		not self.FeatureVariationRecord[-1]
+			.FeatureTableSubstitution.SubstitutionCount):
+		self.FeatureVariationRecord.pop()
+	self.FeatureVariationCount = len(self.FeatureVariationRecord)
+	return bool(self.FeatureVariationCount)
 
 @_add_method(otTables.DefaultLangSys,
-             otTables.LangSys)
+			 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)
+	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)
+			 otTables.LangSys)
 def collect_features(self):
-    feature_indices = self.FeatureIndex[:]
-    if self.ReqFeatureIndex != 65535:
-        feature_indices.append(self.ReqFeatureIndex)
-    return _uniq_sort(feature_indices)
+	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)
+	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, []))
+	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)
+	# 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), []))
+	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]
+	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
+	return True
 
-# CBDC will inherit it
+# CBDT 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
+	strikeData = [
+		{g: strike[g] for g in s.glyphs if g in strike}
+		for strike in self.strikeData
+	]
+	# Prune empty strikes
+	# https://github.com/fonttools/fonttools/issues/1633
+	self.strikeData = [strike for strike in strikeData if strike]
+	return True
+
+@_add_method(ttLib.getTableClass('sbix'))
+def subset_glyphs(self, s):
+	for strike in self.strikes.values():
+		strike.glyphs = {g: strike.glyphs[g] for g in s.glyphs if g in strike.glyphs}
+
+	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:
-        while True:
-            orig_glyphs = frozenset(s.glyphs)
-            s._activeLookups = []
-            s._doneLookups = set()
-            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)
-            del s._activeLookups, s._doneLookups
-            if orig_glyphs == s.glyphs:
-                break
-    del s.table
+	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'))
+	     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
+	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'))
+	     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')
+	# https://github.com/fonttools/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'))
+	     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())
+	"""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'))
+	     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)
+	"""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'))
+	     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)
+	"""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'))
+	     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())
+	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'))
+	     ttLib.getTableClass('GPOS'))
+def subset_script_tags(self, tags):
+	langsys = {}
+	script_tags = set()
+	for tag in tags:
+		script_tag, lang_tag = tag.split(".") if "." in tag else (tag, '*')
+		script_tags.add(script_tag.ljust(4))
+		langsys.setdefault(script_tag, set()).add(lang_tag.ljust(4))
+
+	if self.table.ScriptList:
+		self.table.ScriptList.ScriptRecord = \
+			[s for s in self.table.ScriptList.ScriptRecord
+			 if s.ScriptTag in script_tags]
+		self.table.ScriptList.ScriptCount = len(self.table.ScriptList.ScriptRecord)
+
+		for record in self.table.ScriptList.ScriptRecord:
+			if record.ScriptTag in langsys and '*   ' not in langsys[record.ScriptTag]:
+				record.Script.LangSysRecord = \
+					[l for l in record.Script.LangSysRecord
+					 if l.LangSysTag in langsys[record.ScriptTag]]
+				record.Script.LangSysCount = len(record.Script.LangSysRecord)
+				if "dflt" not in langsys[record.ScriptTag]:
+					record.Script.DefaultLangSys = None
+
+@_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())
+	"""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'))
+	     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
+	# Drop undesired features
+	if '*' not in options.layout_scripts:
+		self.subset_script_tags(options.layout_scripts)
+	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'))
+	     ttLib.getTableClass('GPOS'))
 def remove_redundant_langsys(self):
-    table = self.table
-    if not table.ScriptList or not table.FeatureList:
-        return
+	table = self.table
+	if not table.ScriptList or not table.FeatureList:
+		return
 
-    features = table.FeatureList.FeatureRecord
+	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
+	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
+			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)
+			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
+	     ttLib.getTableClass('GPOS'))
+def prune_post_subset(self, font, options):
+	table = self.table
 
-    self.prune_lookups() # XXX Is this actually needed?!
+	self.prune_lookups() # XXX Is this actually needed?!
 
-    if table.LookupList:
-        table.LookupList.prune_post_subset(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 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 not table.LookupList:
+		table.FeatureList = None
 
 
-    if table.FeatureList:
-        self.remove_redundant_langsys()
-        # Remove unreferenced features
-        self.prune_features()
+	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
+	# 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
+	# 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'):
+		# drop FeatureVariations if there are no features to substitute
+		if table.FeatureVariations and not (
+			table.FeatureList and table.FeatureVariations.FeatureVariationRecord
+		):
+			table.FeatureVariations = None
 
-    if hasattr(table, 'FeatureVariations') and not table.FeatureVariations:
-        if table.Version == 0x00010001:
-            table.Version = 0x00010000
+		# downgrade table version if there are no FeatureVariations
+		if not table.FeatureVariations and table.Version == 0x00010001:
+			table.Version = 0x00010000
 
-    return True
+	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:
-            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
+	glyphs = s.glyphs_gsubed
+	table = self.table
+	if table.LigCaretList:
+		indices = table.LigCaretList.Coverage.subset(glyphs)
+		table.LigCaretList.LigGlyph = _list_subset(table.LigCaretList.LigGlyph, 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, 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:
-            table.Version = 0x00010000
-    return bool(table.LigCaretList or
-                table.MarkAttachClassDef or
-                table.GlyphClassDef or
-                table.AttachList or
-                (table.Version >= 0x00010002 and table.MarkGlyphSetsDef))
+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)
+	# 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)
+	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)
+	self.metrics = _dict_subset(self.metrics, s.glyphs)
+	for g in s.glyphs_emptied:
+		self.metrics[g] = (0,0)
+	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
+	self.metrics = _dict_subset(self.metrics, s.glyphs)
+	for g in s.glyphs_emptied:
+		self.metrics[g] = (0,0)
+	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)
+	self.hdmx = {sz:_dict_subset(l, s.glyphs) for sz,l in self.hdmx.items()}
+	for sz in self.hdmx:
+		for g in s.glyphs_emptied:
+			self.hdmx[sz][g] = 0
+	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
+	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)
+	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
+	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
+	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
+	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)
+	self.variations = _dict_subset(self.variations, s.glyphs)
+	self.glyphCount = len(self.variations)
+	return bool(self.variations)
+
+def _remap_index_map(s, varidx_map, table_map):
+	map_ = {k:varidx_map[v] for k,v in table_map.mapping.items()}
+	# Emptied glyphs are remapped to:
+	# if GID <= last retained GID, 0/0: delta set for 0/0 is expected to exist & zeros compress well
+	# if GID > last retained GID, major/minor of the last retained glyph: will be optimized out by table compiler
+	last_idx = varidx_map[table_map.mapping[s.last_retained_glyph]]
+	for g,i in s.reverseEmptiedGlyphMap.items():
+		map_[g] = last_idx if i > s.last_retained_order else 0
+	return map_
+
+@_add_method(ttLib.getTableClass('HVAR'))
+def subset_glyphs(self, s):
+	table = self.table
+
+	used = set()
+	advIdxes_ = set()
+	retainAdvMap = False
+
+	if table.AdvWidthMap:
+		table.AdvWidthMap.mapping = _dict_subset(table.AdvWidthMap.mapping, s.glyphs)
+		used.update(table.AdvWidthMap.mapping.values())
+	else:
+		used.update(s.reverseOrigGlyphMap.values())
+		advIdxes_ = used.copy()
+		retainAdvMap = s.options.retain_gids
+
+	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 = table.VarStore.subset_varidxes(used, retainFirstMap=retainAdvMap, advIdxes=advIdxes_)
+
+	if table.AdvWidthMap:
+		table.AdvWidthMap.mapping = _remap_index_map(s, varidx_map, table.AdvWidthMap)
+	if table.LsbMap:
+		table.LsbMap.mapping = _remap_index_map(s, varidx_map, table.LsbMap)
+	if table.RsbMap:
+		table.RsbMap.mapping = _remap_index_map(s, varidx_map, table.RsbMap)
+
+	# TODO Return emptiness...
+	return True
+
+@_add_method(ttLib.getTableClass('VVAR'))
+def subset_glyphs(self, s):
+	table = self.table
+
+	used = set()
+	advIdxes_ = set()
+	retainAdvMap = False
+
+	if table.AdvHeightMap:
+		table.AdvHeightMap.mapping = _dict_subset(table.AdvHeightMap.mapping, s.glyphs)
+		used.update(table.AdvHeightMap.mapping.values())
+	else:
+		used.update(s.reverseOrigGlyphMap.values())
+		advIdxes_ = used.copy()
+		retainAdvMap = s.options.retain_gids
+
+	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 = table.VarStore.subset_varidxes(used, retainFirstMap=retainAdvMap, advIdxes=advIdxes_)
+
+	if table.AdvHeightMap:
+		table.AdvHeightMap.mapping = _remap_index_map(s, varidx_map, table.AdvHeightMap)
+	if table.TsbMap:
+		table.TsbMap.mapping = _remap_index_map(s, varidx_map, table.TsbMap)
+	if table.BsbMap:
+		table.BsbMap.mapping = _remap_index_map(s, varidx_map, table.BsbMap)
+	if table.VOrgMap:
+		table.VOrgMap.mapping = _remap_index_map(s, varidx_map, table.VOrgMap)
+
+	# 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
+	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
+	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
+	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
+	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
+	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
+
+def _paint_glyph_names(paint, colr):
+	result = set()
+
+	def callback(paint):
+		if paint.Format in {
+			otTables.PaintFormat.PaintGlyph,
+			otTables.PaintFormat.PaintColrGlyph,
+		}:
+			result.add(paint.Glyph)
+
+	paint.traverse(colr, callback)
+	return result
 
 @_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
+	if self.version > 0:
+		# on decompiling COLRv1, we only keep around the raw otTables
+		# but for subsetting we need dicts with fully decompiled layers;
+		# we store them temporarily in the C_O_L_R_ instance and delete
+		# them after we have finished subsetting.
+		self.ColorLayers = self._decompileColorLayersV0(self.table)
+		self.ColorLayersV1 = {
+			rec.BaseGlyph: rec.Paint
+			for rec in self.table.BaseGlyphList.BaseGlyphPaintRecord
+		}
+
+	decompose = s.glyphs
+	while decompose:
+		layers = set()
+		for g in decompose:
+			for layer in self.ColorLayers.get(g, []):
+				layers.add(layer.name)
+
+			if self.version > 0:
+				paint = self.ColorLayersV1.get(g)
+				if paint is not None:
+					layers.update(_paint_glyph_names(paint, self.table))
+
+		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)
+	from fontTools.colorLib.unbuilder import unbuildColrV1
+	from fontTools.colorLib.builder import buildColrV1, populateCOLRv0
 
-# TODO: prune unused palettes
+	self.ColorLayers = {g: self.ColorLayers[g] for g in s.glyphs if g in self.ColorLayers}
+	if self.version == 0:
+		return bool(self.ColorLayers)
+
+	colorGlyphsV1 = unbuildColrV1(self.table.LayerList, self.table.BaseGlyphList)
+	self.table.LayerList, self.table.BaseGlyphList = buildColrV1(
+		{g: colorGlyphsV1[g] for g in colorGlyphsV1 if g in s.glyphs}
+	)
+	del self.ColorLayersV1
+
+	layersV0 = self.ColorLayers
+	if not self.table.BaseGlyphList.BaseGlyphPaintRecord:
+		# no more COLRv1 glyphs: downgrade to version 0
+		self.version = 0
+		del self.table
+		return bool(layersV0)
+
+	populateCOLRv0(
+		self.table,
+		{
+			g: [(layer.name, layer.colorID) for layer in layersV0[g]]
+			for g in layersV0
+		},
+	)
+	del self.ColorLayers
+
+	# TODO: also prune ununsed varIndices in COLR.VarStore
+	return True
+
 @_add_method(ttLib.getTableClass('CPAL'))
-def prune_post_subset(self, options):
-    return True
+def prune_post_subset(self, font, options):
+	colr = font.get("COLR")
+	if not colr:  # drop CPAL if COLR was subsetted to empty
+		return False
+
+	colors_by_index = defaultdict(list)
+
+	def collect_colors_by_index(paint):
+		if hasattr(paint, "PaletteIndex"):  # either solid colors...
+			colors_by_index[paint.PaletteIndex].append(paint)
+		elif hasattr(paint, "ColorLine"):  # ... or gradient color stops
+			for stop in paint.ColorLine.ColorStop:
+				colors_by_index[stop.PaletteIndex].append(stop)
+
+	if colr.version == 0:
+		for layers in colr.ColorLayers.values():
+			for layer in layers:
+				colors_by_index[layer.colorID].append(layer)
+	else:
+		if colr.table.LayerRecordArray:
+			for layer in colr.table.LayerRecordArray.LayerRecord:
+				colors_by_index[layer.PaletteIndex].append(layer)
+		for record in colr.table.BaseGlyphList.BaseGlyphPaintRecord:
+			record.Paint.traverse(colr.table, collect_colors_by_index)
+
+	# don't remap palette entry index 0xFFFF, this is always the foreground color
+	# https://github.com/fonttools/fonttools/issues/2257
+	retained_palette_indices = set(colors_by_index.keys()) - {0xFFFF}
+	for palette in self.palettes:
+		palette[:] = [c for i, c in enumerate(palette) if i in retained_palette_indices]
+		assert len(palette) == len(retained_palette_indices)
+
+	for new_index, old_index in enumerate(sorted(retained_palette_indices)):
+		for record in colors_by_index[old_index]:
+			if hasattr(record, "colorID"):  # v0
+				record.colorID = new_index
+			elif hasattr(record, "PaletteIndex"):  # v1
+				record.PaletteIndex = new_index
+			else:
+				raise AssertionError(record)
+
+	self.numPaletteEntries = len(self.palettes[0])
+
+	if self.version == 1:
+		self.paletteEntryLabels = [
+			label for i, label in self.paletteEntryLabels if i in retained_palette_indices
+		]
+	return bool(self.numPaletteEntries)
 
 @_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
+	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()
+	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.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))
+	if self.HorizGlyphCoverage:
+		indices = self.HorizGlyphCoverage.intersect(glyphs)
+		for i in indices:
+			variants.update(self.HorizGlyphConstruction[i].closure_glyphs(glyphs))
 
-    s.glyphs.update(variants)
+	s.glyphs.update(variants)
 
 @_add_method(ttLib.getTableClass('MATH'))
 def closure_glyphs(self, s):
-    self.table.MathVariants.closure_glyphs(s)
+	if self.table.MathVariants:
+		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)
+	indices = self.Coverage.subset(s.glyphs)
+	self.ItalicsCorrection = _list_subset(self.ItalicsCorrection, 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)
+	indices = self.TopAccentCoverage.subset(s.glyphs)
+	self.TopAccentAttachment = _list_subset(self.TopAccentAttachment, 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)
+	indices = self.MathKernCoverage.subset(s.glyphs)
+	self.MathKernInfoRecords = _list_subset(self.MathKernInfoRecords, 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
+	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.VertGlyphCoverage:
+		indices = self.VertGlyphCoverage.subset(s.glyphs)
+		self.VertGlyphConstruction = _list_subset(self.VertGlyphConstruction, 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)
+	if self.HorizGlyphCoverage:
+		indices = self.HorizGlyphCoverage.subset(s.glyphs)
+		self.HorizGlyphConstruction = _list_subset(self.HorizGlyphConstruction, indices)
+		self.HorizGlyphCount = len(self.HorizGlyphConstruction)
 
-    return True
+	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
+	s.glyphs = s.glyphs_mathed
+	if self.table.MathGlyphInfo:
+		self.table.MathGlyphInfo.subset_glyphs(s)
+	if self.table.MathVariants:
+		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)
+def remapComponentsFast(self, glyphidmap):
+	if not self.data or struct.unpack(">h", self.data[:2])[0] >= 0:
+		return	# Not composite
+	data = self.data = bytearray(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 = glyphidmap[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()
+		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
 
 @_add_method(ttLib.getTableClass('glyf'))
 def closure_glyphs(self, s):
-    decompose = s.glyphs
-    while decompose:
-        components = set()
-        for g in decompose:
-            if g not in self.glyphs:
-                continue
-            gl = self.glyphs[g]
-            for c in gl.getComponentNames(self):
-                components.add(c)
-        components -= s.glyphs
-        s.glyphs.update(components)
-        decompose = components
+	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
+	if options.notdef_glyph and not options.notdef_outline:
+		g = self[self.glyphOrder[0]]
+		# Yay, easy!
+		g.__dict__.clear()
+		g.data = b''
+	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
+	self.glyphs = _dict_subset(self.glyphs, s.glyphs)
+	if not s.options.retain_gids:
+		indices = [i for i,g in enumerate(self.glyphOrder) if g in s.glyphs]
+		glyphmap = {o:n for n,o in enumerate(indices)}
+		for v in self.glyphs.values():
+			if hasattr(v, "data"):
+				v.remapComponentsFast(glyphmap)
+	Glyph = ttLib.getTableModule('glyf').Glyph
+	for g in s.glyphs_emptied:
+		self.glyphs[g] = Glyph()
+		self.glyphs[g].data = b''
+	self.glyphOrder = [g for g in self.glyphOrder if g in s.glyphs or g in s.glyphs_emptied]
+	# 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):
-    remove_hinting = not options.hinting
-    for v in self.glyphs.values():
-        v.trim(remove_hinting=remove_hinting)
-    return True
-
-@_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, 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
+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
 
 
 @_add_method(ttLib.getTableClass('cmap'))
 def closure_glyphs(self, s):
-    tables = [t for t in self.tables if t.isUnicode()]
+	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)
+	# 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)
+	# 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
+	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
+	s.glyphs = None # We use s.glyphs_requested and s.unicodes_requested only
+
+	tables_format12_bmp = []
+	table_plat0_enc3 = {}  # Unicode platform, Unicode BMP only, keyed by language
+	table_plat3_enc1 = {}  # Windows platform, Unicode BMP, keyed by language
+
+	for t in self.tables:
+		if t.platformID == 0 and t.platEncID == 3:
+			table_plat0_enc3[t.language] = t
+		if t.platformID == 3 and t.platEncID == 1:
+			table_plat3_enc1[t.language] = t
+
+		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}
+			# Collect format 12 tables that hold only basic multilingual plane
+			# codepoints.
+			if t.format == 12 and t.cmap and max(t.cmap.keys()) < 0x10000:
+				tables_format12_bmp.append(t)
+		else:
+			t.cmap = {u:g for u,g in t.cmap.items()
+				      if g in s.glyphs_requested}
+
+	# Fomat 12 tables are redundant if they contain just the same BMP codepoints
+	# their little BMP-only encoding siblings contain.
+	for t in tables_format12_bmp:
+		if (
+			t.platformID == 0  # Unicode platform
+			and t.platEncID == 4  # Unicode full repertoire
+			and t.language in table_plat0_enc3  # Have a BMP-only sibling?
+			and table_plat0_enc3[t.language].cmap == t.cmap
+		):
+			t.cmap.clear()
+		elif (
+			t.platformID == 3  # Windows platform
+			and t.platEncID == 10  # Unicode full repertoire
+			and t.language in table_plat3_enc1  # Have a BMP-only sibling?
+			and table_plat3_enc1[t.language].cmap == t.cmap
+		):
+			t.cmap.clear()
+
+	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, 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
+	# 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
+	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
+	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])
+	stat = font.get('STAT')
+	if stat:
+		if stat.table.AxisValueArray:
+			nameIDs.update([val_rec.ValueNameID for val_rec in stat.table.AxisValueArray.AxisValue])
+		nameIDs.update([axis_rec.AxisNameID for axis_rec in stat.table.DesignAxisRecord.Axis])
+	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
+
+
+@_add_method(ttLib.getTableClass('head'))
+def prune_post_subset(self, font, options):
+	# Force re-compiling head table, to update any recalculated values.
+	return True
 
 
 # TODO(behdad) OS/2 ulCodePageRange?
@@ -2561,586 +2496,636 @@
 
 class Options(object):
 
-    class OptionError(Exception): pass
-    class UnknownOptionError(OptionError): pass
+	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', 'STAT']
-    _hinting_tables_default = ['cvar', 'cvt', 'fpgm', 'prep', 'hdmx', 'VDMX']
+	# 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
+	_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()), []))
+	# 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):
+	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 = [1, 2]    # Family and Style
-        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.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_closure = True
+		self.layout_features = self._layout_features_default[:]
+		self.layout_scripts = ['*']
+		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.retain_gids = False
+		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.recalc_max_context = False # update 'usMaxContext'
+		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)
+		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 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)
+	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
+			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)
+			setattr(self, k, v)
 
-        return posargs + passthru_options
+		return posargs + passthru_options
 
 
 class Subsetter(object):
 
-    class SubsettingError(Exception): pass
-    class MissingGlyphsSubsettingError(SubsettingError): pass
-    class MissingUnicodesSubsettingError(SubsettingError): pass
+	class SubsettingError(Exception): pass
+	class MissingGlyphsSubsettingError(SubsettingError): pass
+	class MissingUnicodesSubsettingError(SubsettingError): pass
 
-    def __init__(self, options=None):
+	def __init__(self, options=None):
 
-        if not options:
-            options = Options()
+		if not options:
+			options = Options()
 
-        self.options = options
-        self.unicodes_requested = set()
-        self.glyph_names_requested = set()
-        self.glyph_ids_requested = set()
+		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 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
+	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)
+			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)
+			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):
+	def _closure_glyphs(self, font):
 
-        realGlyphs = set(font.getGlyphOrder())
-        glyph_order = font.getGlyphOrder()
+		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_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_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.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
+		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 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 self.options.layout_closure and '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)
+		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)
+		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 '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)
 
-        self.glyphs_all = 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)
 
-        log.info("Retaining %d glyphs", len(self.glyphs_all))
+		self.glyphs_retained = frozenset(self.glyphs)
 
-        del self.glyphs
+		order = font.getReverseGlyphMap()
+		self.reverseOrigGlyphMap = {g:order[g] for g in self.glyphs_retained}
 
-    def _subset_glyphs(self, font):
-        for tag in self._sort_tables(font):
-            clazz = ttLib.getTableClass(tag)
+		self.last_retained_order = max(self.reverseOrigGlyphMap.values())
+		self.last_retained_glyph = font.getGlyphOrder()[self.last_retained_order]
 
-            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.info("%s NOT subset; don't know how to subset; dropped", tag)
-                del font[tag]
+		self.glyphs_emptied = frozenset()
+		if self.options.retain_gids:
+			self.glyphs_emptied = {g for g in realGlyphs - self.glyphs_retained if order[g] <= self.last_retained_order}
 
-        with timer("subset GlyphOrder"):
-            glyphOrder = font.getGlyphOrder()
-            glyphOrder = [g for g in glyphOrder if g in self.glyphs_all]
-            font.setGlyphOrder(glyphOrder)
-            font._buildReverseGlyphOrderDict()
+		self.reverseEmptiedGlyphMap = {g:order[g] for g in self.glyphs_emptied}
 
-    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 = round(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(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']
+		log.info("Retaining %d glyphs", len(self.glyphs_retained))
 
-    def subset(self, font):
-        self._prune_pre_subset(font)
-        self._closure_glyphs(font)
-        self._subset_glyphs(font)
-        self._prune_post_subset(font)
+		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_retained
+					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()
+			if not self.options.retain_gids:
+				glyphOrder = [g for g in glyphOrder if g in self.glyphs_retained]
+			else:
+				glyphOrder = [g for g in glyphOrder if font.getGlyphID(g) <= self.last_retained_order]
+
+			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)
+				if self.options.recalc_max_context:
+					max_context = maxCtxFont(font)
+					if max_context != font[tag].usMaxContext:
+						font[tag].usMaxContext = max_context
+						log.info("%s usMaxContext updated: %d", tag, max_context)
+			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):
+	      options,
+	      allowVID=False,
+	      checkChecksums=0,
+	      dontLoadGlyphNames=False,
+	      lazy=True):
 
-    font = ttLib.TTFont(fontFile,
-                        allowVID=allowVID,
-                        checkChecksums=checkChecksums,
-                        recalcBBoxes=options.recalc_bounds,
-                        recalcTimestamp=options.recalc_timestamp,
-                        lazy=lazy)
+	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
+	# 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
+	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)
+	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
+	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
+	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()
+	return s.replace(',', ' ').split()
 
 def usage():
-    print("usage:", __usage__, file=sys.stderr)
-    print("Try pyftsubset --help for more information.\n", file=sys.stderr)
+	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
+	"""OpenType font subsetter and optimizer"""
+	from os.path import splitext
+	from fontTools import configLogger
 
-    if args is None:
-        args = sys.argv[1:]
+	if args is None:
+		args = sys.argv[1:]
 
-    if '--help' in args:
-        print(__doc__)
-        return 0
+	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
+	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
+	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
+	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:]
+	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)
+	subsetter = Subsetter(options=options)
+	outfile = None
+	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='):
+			with open(g[12:], encoding='utf-8') as f:
+				text += f.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='):
+			with open(g[16:]) as f:
+				for line in f.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='):
+			with open(g[12:]) as f:
+				for line in f.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='):
+			with open(g[14:]) as f:
+				for line in f.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)
+	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
+	if outfile is None:
+		basename, _ = splitext(fontfile)
+		if options.flavor is not None:
+			ext = "." + options.flavor.lower()
+		else:
+			ext = ".ttf" if font.sfntVersion == "\0\1\0\0" else ".otf"
+		outfile = basename + ".subset" + ext
 
-    log.info("Text: '%s'" % text)
-    log.info("Unicodes: %s", unicodes)
-    log.info("Glyphs: %s", glyphs)
-    log.info("Gids: %s", gids)
+	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
 
-    subsetter.populate(glyphs=glyphs, gids=gids, unicodes=unicodes, text=text)
-    subsetter.subset(font)
+	log.info("Text: '%s'" % text)
+	log.info("Unicodes: %s", unicodes)
+	log.info("Glyphs: %s", glyphs)
+	log.info("Gids: %s", gids)
 
-    save_font(font, outfile, options)
+	subsetter.populate(glyphs=glyphs, gids=gids, unicodes=unicodes, text=text)
+	subsetter.subset(font)
 
-    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))
+	save_font(font, outfile, options)
 
-    if options.xml:
-        font.saveXML(sys.stdout)
+	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))
 
-    font.close()
+	if options.xml:
+		font.saveXML(sys.stdout)
+
+	font.close()
 
 
 __all__ = [
-    'Options',
-    'Subsetter',
-    'load_font',
-    'save_font',
-    'parse_gids',
-    'parse_glyphs',
-    'parse_unicodes',
-    'main'
+	'Options',
+	'Subsetter',
+	'load_font',
+	'save_font',
+	'parse_gids',
+	'parse_glyphs',
+	'parse_unicodes',
+	'main'
 ]
 
 if __name__ == '__main__':
-    sys.exit(main())
+	sys.exit(main())
diff --git a/Lib/fontTools/subset/__main__.py b/Lib/fontTools/subset/__main__.py
index 10e470f..2203847 100644
--- a/Lib/fontTools/subset/__main__.py
+++ b/Lib/fontTools/subset/__main__.py
@@ -1,7 +1,6 @@
-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/subset/cff.py b/Lib/fontTools/subset/cff.py
new file mode 100644
index 0000000..b59c6b9
--- /dev/null
+++ b/Lib/fontTools/subset/cff.py
@@ -0,0 +1,631 @@
+from fontTools.misc import psCharStrings
+from fontTools import ttLib
+from fontTools.pens.basePen import NullPen
+from fontTools.misc.roundTools import otRound
+from fontTools.varLib.varStore import VarStoreInstancer
+
+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))
+
+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
+
+def _empty_charstring(font, glyphName, isCFF2, ignoreWidth=False):
+	c, fdSelectIndex = font.CharStrings.getItemAndSelector(glyphName)
+	if isCFF2 or ignoreWidth:
+		# CFF2 charstrings have no widths nor 'endchar' operators
+		c.decompile()
+		c.program = [] if isCFF2 else ['endchar']
+	else:
+		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']
+
+@_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:
+		isCFF2 = cff.major > 1
+		for fontname in cff.keys():
+			font = cff[fontname]
+			_empty_charstring(font, ".notdef", isCFF2=isCFF2)
+
+	# Clear useless Encoding
+	for fontname in cff.keys():
+		font = cff[fontname]
+		# https://github.com/fonttools/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
+
+		glyphs = s.glyphs.union(s.glyphs_emptied)
+
+		# Load all glyphs
+		for g in font.charset:
+			if g not in glyphs: continue
+			c, _ = cs.getItemAndSelector(g)
+
+		if cs.charStringsAreIndexed:
+			indices = [i for i,g in enumerate(font.charset) if g in 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 glyphs}
+		else:
+			cs.charStrings = {g:v
+					  for g,v in cs.charStrings.items()
+					  if g in glyphs}
+		font.charset = [g for g in font.charset if g in glyphs]
+		font.numGlyphs = len(font.charset)
+
+
+		if s.options.retain_gids:
+			isCFF2 = cff.major > 1
+			for g in s.glyphs_emptied:
+				_empty_charstring(font, g, isCFF2=isCFF2, ignoreWidth=True)
+
+
+	return True # any(cff[fontname].numGlyphs for fontname in cff.keys())
+
+@_add_method(psCharStrings.T2CharString)
+def subset_subroutines(self, subrs, gsubrs):
+	p = self.program
+	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 not self.program:
+			# TODO CFF2 no need for endchar.
+			self.program.append('endchar')
+		if hasattr(self, 'width'):
+			# Insert width back if needed
+			if self.width != self.private.defaultWidthX:
+				# For CFF2 charstrings, this should never happen
+				assert self.private.defaultWidthX is not None, "CFF2 CharStrings must not have an initial width value"
+				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, private):
+		psCharStrings.SimpleT2Decompiler.__init__(self,
+							  localSubrs,
+							  globalSubrs,
+							  private)
+		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, or there might be
+			#	other things.  Not clear yet.
+			# 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, private=None):
+		self._css = css
+		psCharStrings.T2WidthExtractor.__init__(
+			self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX)
+		self.private = private
+
+	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 StopHintCountEvent(Exception):
+	pass
+
+
+
+
+class _DesubroutinizingT2Decompiler(psCharStrings.SimpleT2Decompiler):
+	stop_hintcount_ops = ("op_hintmask", "op_cntrmask", "op_rmoveto", "op_hmoveto",
+							"op_vmoveto")
+
+	def __init__(self, localSubrs, globalSubrs, private=None):
+		psCharStrings.SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs,
+												private)
+
+	def execute(self, charString):
+		self.need_hintcount = True  # until proven otherwise
+		for op_name in self.stop_hintcount_ops:
+			setattr(self, op_name, self.stop_hint_count)
+
+		if hasattr(charString, '_desubroutinized'):
+			# If a charstring has already been desubroutinized, we will still
+			# need to execute it if we need to count hints in order to
+			# compute the byte length for mask arguments, and haven't finished
+			# counting hints pairs.
+			if self.need_hintcount and self.callingStack:
+				try:
+					psCharStrings.SimpleT2Decompiler.execute(self, charString)
+				except StopHintCountEvent:
+					del self.callingStack[-1]
+			return
+
+		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 not self.private.in_cff2:
+			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
+
+	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 stop_hint_count(self, *args):
+		self.need_hintcount = False
+		for op_name in self.stop_hintcount_ops:
+			setattr(self, op_name, None)
+		cs = self.callingStack[-1]
+		if hasattr(cs, '_desubroutinized'):
+			raise StopHintCountEvent()
+
+	def op_hintmask(self, index):
+		psCharStrings.SimpleT2Decompiler.op_hintmask(self, index)
+		if self.need_hintcount:
+			self.stop_hint_count()
+
+	def processSubr(self, index, subr):
+		cs = self.callingStack[-1]
+		if not hasattr(cs, '_desubroutinized'):
+			cs._patches.append((index, subr._desubroutinized))
+
+
+@_add_method(ttLib.getTableClass('CFF '))
+def prune_post_subset(self, ttfFont, 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:
+		self.desubroutinize()
+
+	# Drop hints if not needed
+	if not options.hinting:
+		self.remove_hints()
+	elif not options.desubroutinize:
+		self.remove_unused_subroutines()
+	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('CFF '))
+def desubroutinize(self):
+	cff = self.cff
+	for fontname in cff.keys():
+		font = cff[fontname]
+		cs = font.CharStrings
+		for g in font.charset:
+			c, _ = cs.getItemAndSelector(g)
+			c.decompile()
+			subrs = getattr(c.private, "Subrs", [])
+			decompiler = _DesubroutinizingT2Decompiler(subrs, c.globalSubrs, c.private)
+			decompiler.execute(c)
+			c.program = c._desubroutinized
+			del c._desubroutinized
+		# Delete all the local subrs
+		if hasattr(font, 'FDArray'):
+			for fd in font.FDArray:
+				pd = fd.Private
+				if hasattr(pd, 'Subrs'):
+					del pd.Subrs
+				if 'Subrs' in pd.rawDict:
+					del pd.rawDict['Subrs']
+		else:
+			pd = font.Private
+			if hasattr(pd, 'Subrs'):
+				del pd.Subrs
+			if 'Subrs' in pd.rawDict:
+				del pd.rawDict['Subrs']
+	# as well as the global subrs
+	cff.GlobalSubrs.clear()
+
+
+@_add_method(ttLib.getTableClass('CFF '))
+def remove_hints(self):
+	cff = self.cff
+	for fontname in cff.keys():
+		font = cff[fontname]
+		cs = font.CharStrings
+		# 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,
+								c.private)
+			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, 'FDArray'):
+			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',
+				  'ForceBold', 'LanguageGroup', 'ExpansionFactor']:
+				if hasattr(priv, k):
+					setattr(priv, k, None)
+	self.remove_unused_subroutines()
+
+
+@_add_method(ttLib.getTableClass('CFF '))
+def remove_unused_subroutines(self):
+	cff = self.cff
+	for fontname in cff.keys():
+		font = cff[fontname]
+		cs = font.CharStrings
+		# 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, c.private)
+			decompiler.execute(c)
+
+		all_subrs = [font.GlobalSubrs]
+		if hasattr(font, 'FDArray'):
+			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, 'FDArray') 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, 'FDArray'):
+			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
diff --git a/Lib/fontTools/svgLib/__init__.py b/Lib/fontTools/svgLib/__init__.py
index b301a3b..c049006 100644
--- a/Lib/fontTools/svgLib/__init__.py
+++ b/Lib/fontTools/svgLib/__init__.py
@@ -1,6 +1,3 @@
-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
index 4f17e76..9440429 100644
--- a/Lib/fontTools/svgLib/path/__init__.py
+++ b/Lib/fontTools/svgLib/path/__init__.py
@@ -1,14 +1,9 @@
-from __future__ import (
-    print_function, division, absolute_import, unicode_literals)
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import tostr
 
 from fontTools.pens.transformPen import TransformPen
+from fontTools.misc import etree
 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
+from .shapes import PathBuilder
 
 
 __all__ = [tostr(s) for s in ("SVGPath", "parse_path")]
@@ -39,20 +34,29 @@
 
     def __init__(self, filename=None, transform=None):
         if filename is None:
-            self.root = ElementTree.ElementTree()
+            self.root = etree.ElementTree()
         else:
-            tree = ElementTree.parse(filename)
+            tree = etree.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)
+        self.root = etree.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)
+        pb = PathBuilder()
+        # xpath | doesn't seem to reliable work so just walk it
+        for el in self.root.iter():
+            pb.add_path_from_element(el)
+        original_pen = pen
+        for path, transform in zip(pb.paths, pb.transforms):
+            if transform:
+                pen = TransformPen(original_pen, transform)
+            else:
+                pen = original_pen
+            parse_path(path, pen)
diff --git a/Lib/fontTools/svgLib/path/arc.py b/Lib/fontTools/svgLib/path/arc.py
new file mode 100644
index 0000000..3181071
--- /dev/null
+++ b/Lib/fontTools/svgLib/path/arc.py
@@ -0,0 +1,154 @@
+"""Convert SVG Path's elliptical arcs to Bezier curves.
+
+The code is mostly adapted from Blink's SVGPathNormalizer::DecomposeArcToCubic
+https://github.com/chromium/chromium/blob/93831f2/third_party/
+blink/renderer/core/svg/svg_path_parser.cc#L169-L278
+"""
+from fontTools.misc.transform import Identity, Scale
+from math import atan2, ceil, cos, fabs, isfinite, pi, radians, sin, sqrt, tan
+
+
+TWO_PI = 2 * pi
+PI_OVER_TWO = 0.5 * pi
+
+
+def _map_point(matrix, pt):
+    # apply Transform matrix to a point represented as a complex number
+    r = matrix.transformPoint((pt.real, pt.imag))
+    return r[0] + r[1] * 1j
+
+
+class EllipticalArc(object):
+
+    def __init__(self, current_point, rx, ry, rotation, large, sweep, target_point):
+        self.current_point = current_point
+        self.rx = rx
+        self.ry = ry
+        self.rotation = rotation
+        self.large = large
+        self.sweep = sweep
+        self.target_point = target_point
+
+        # SVG arc's rotation angle is expressed in degrees, whereas Transform.rotate
+        # uses radians
+        self.angle = radians(rotation)
+
+        # these derived attributes are computed by the _parametrize method
+        self.center_point = self.theta1 = self.theta2 = self.theta_arc = None
+
+    def _parametrize(self):
+        # convert from endopoint to center parametrization:
+        # https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
+
+        # If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a
+        # "lineto") joining the endpoints.
+        # http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
+        rx = fabs(self.rx)
+        ry = fabs(self.ry)
+        if not (rx and ry):
+            return False
+
+        # If the current point and target point for the arc are identical, it should
+        # be treated as a zero length path. This ensures continuity in animations.
+        if self.target_point == self.current_point:
+            return False
+
+        mid_point_distance = (self.current_point - self.target_point) * 0.5
+
+        point_transform = Identity.rotate(-self.angle)
+
+        transformed_mid_point = _map_point(point_transform, mid_point_distance)
+        square_rx = rx * rx
+        square_ry = ry * ry
+        square_x = transformed_mid_point.real * transformed_mid_point.real
+        square_y = transformed_mid_point.imag * transformed_mid_point.imag
+
+        # Check if the radii are big enough to draw the arc, scale radii if not.
+        # http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
+        radii_scale = square_x / square_rx + square_y / square_ry
+        if radii_scale > 1:
+            rx *= sqrt(radii_scale)
+            ry *= sqrt(radii_scale)
+            self.rx, self.ry = rx, ry
+
+        point_transform = Scale(1 / rx, 1 / ry).rotate(-self.angle)
+
+        point1 = _map_point(point_transform, self.current_point)
+        point2 = _map_point(point_transform, self.target_point)
+        delta = point2 - point1
+
+        d = delta.real * delta.real + delta.imag * delta.imag
+        scale_factor_squared = max(1 / d - 0.25, 0.0)
+
+        scale_factor = sqrt(scale_factor_squared)
+        if self.sweep == self.large:
+            scale_factor = -scale_factor
+
+        delta *= scale_factor
+        center_point = (point1 + point2) * 0.5
+        center_point += complex(-delta.imag, delta.real)
+        point1 -= center_point
+        point2 -= center_point
+
+        theta1 = atan2(point1.imag, point1.real)
+        theta2 = atan2(point2.imag, point2.real)
+
+        theta_arc = theta2 - theta1
+        if theta_arc < 0 and self.sweep:
+            theta_arc += TWO_PI
+        elif theta_arc > 0 and not self.sweep:
+            theta_arc -= TWO_PI
+
+        self.theta1 = theta1
+        self.theta2 = theta1 + theta_arc
+        self.theta_arc = theta_arc
+        self.center_point = center_point
+
+        return True
+
+    def _decompose_to_cubic_curves(self):
+        if self.center_point is None and not self._parametrize():
+            return
+
+        point_transform = Identity.rotate(self.angle).scale(self.rx, self.ry)
+
+        # Some results of atan2 on some platform implementations are not exact
+        # enough. So that we get more cubic curves than expected here. Adding 0.001f
+        # reduces the count of sgements to the correct count.
+        num_segments = int(ceil(fabs(self.theta_arc / (PI_OVER_TWO + 0.001))))
+        for i in range(num_segments):
+            start_theta = self.theta1 + i * self.theta_arc / num_segments
+            end_theta = self.theta1 + (i + 1) * self.theta_arc / num_segments
+
+            t = (4 / 3) * tan(0.25 * (end_theta - start_theta))
+            if not isfinite(t):
+                return
+
+            sin_start_theta = sin(start_theta)
+            cos_start_theta = cos(start_theta)
+            sin_end_theta = sin(end_theta)
+            cos_end_theta = cos(end_theta)
+
+            point1 = complex(
+                cos_start_theta - t * sin_start_theta,
+                sin_start_theta + t * cos_start_theta,
+            )
+            point1 += self.center_point
+            target_point = complex(cos_end_theta, sin_end_theta)
+            target_point += self.center_point
+            point2 = target_point
+            point2 += complex(t * sin_end_theta, -t * cos_end_theta)
+
+            point1 = _map_point(point_transform, point1)
+            point2 = _map_point(point_transform, point2)
+            target_point = _map_point(point_transform, target_point)
+
+            yield point1, point2, target_point
+
+    def draw(self, pen):
+        for point1, point2, target_point in self._decompose_to_cubic_curves():
+            pen.curveTo(
+                (point1.real, point1.imag),
+                (point2.real, point2.imag),
+                (target_point.real, target_point.imag),
+            )
diff --git a/Lib/fontTools/svgLib/path/parser.py b/Lib/fontTools/svgLib/path/parser.py
index 4daefca..1fcf899 100644
--- a/Lib/fontTools/svgLib/path/parser.py
+++ b/Lib/fontTools/svgLib/path/parser.py
@@ -7,27 +7,89 @@
 # Copyright (c) 2013-2014 Lennart Regebro
 # License: MIT
 
-from __future__ import (
-    print_function, division, absolute_import, unicode_literals)
-from fontTools.misc.py23 import *
+from .arc import EllipticalArc
 import re
 
+
 COMMANDS = set('MmZzLlHhVvCcSsQqTtAa')
+ARC_COMMANDS = set("Aa")
 UPPERCASE = set('MZLHVCSQTA')
 
 COMMAND_RE = re.compile("([MmZzLlHhVvCcSsQqTtAa])")
-FLOAT_RE = re.compile("[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?")
+FLOAT_RE = re.compile(
+    r"[-+]?"  # optional sign
+    r"(?:"
+    r"(?:0|[1-9][0-9]*)(?:\.[0-9]+(?:[eE][-+]?[0-9]+)?)?"  # int/float
+    r"|"
+    r"(?:\.[0-9]+(?:[eE][-+]?[0-9]+)?)"  # float with leading dot (e.g. '.42')
+    r")"
+)
+BOOL_RE = re.compile("^[01]")
+SEPARATOR_RE = re.compile(f"[, \t]")
 
 
 def _tokenize_path(pathdef):
+    arc_cmd = None
     for x in COMMAND_RE.split(pathdef):
         if x in COMMANDS:
+            arc_cmd = x if x in ARC_COMMANDS else None
             yield x
-        for token in FLOAT_RE.findall(x):
-            yield token
+            continue
+
+        if arc_cmd:
+            try:
+                yield from _tokenize_arc_arguments(x)
+            except ValueError as e:
+                raise ValueError(f"Invalid arc command: '{arc_cmd}{x}'") from e
+        else:
+            for token in FLOAT_RE.findall(x):
+                yield token
 
 
-def parse_path(pathdef, pen, current_pos=(0, 0)):
+ARC_ARGUMENT_TYPES = (
+    ("rx", FLOAT_RE),
+    ("ry", FLOAT_RE),
+    ("x-axis-rotation", FLOAT_RE),
+    ("large-arc-flag", BOOL_RE),
+    ("sweep-flag", BOOL_RE),
+    ("x", FLOAT_RE),
+    ("y", FLOAT_RE),
+)
+
+
+def _tokenize_arc_arguments(arcdef):
+    raw_args = [s for s in SEPARATOR_RE.split(arcdef) if s]
+    if not raw_args:
+        raise ValueError(f"Not enough arguments: '{arcdef}'")
+    raw_args.reverse()
+
+    i = 0
+    while raw_args:
+        arg = raw_args.pop()
+
+        name, pattern = ARC_ARGUMENT_TYPES[i]
+        match = pattern.search(arg)
+        if not match:
+            raise ValueError(f"Invalid argument for '{name}' parameter: {arg!r}")
+
+        j, k = match.span()
+        yield arg[j:k]
+        arg = arg[k:]
+
+        if arg:
+            raw_args.append(arg)
+
+        # wrap around every 7 consecutive arguments
+        if i == 6:
+            i = 0
+        else:
+            i += 1
+
+    if i != 0:
+        raise ValueError(f"Not enough arguments: '{arcdef}'")
+
+
+def parse_path(pathdef, pen, current_pos=(0, 0), arc_class=EllipticalArc):
     """ Parse SVG path definition (i.e. "d" attribute of <path> elements)
     and call a 'pen' object's moveTo, lineTo, curveTo, qCurveTo and closePath
     methods.
@@ -35,8 +97,13 @@
     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.
+    If the pen has an "arcTo" method, it is called with the original values
+    of the elliptical arc curve commands:
+
+        pen.arcTo(rx, ry, rotation, arc_large, arc_sweep, (x, y))
+
+    Otherwise, the arcs are approximated by series of cubic Bezier segments
+    ("curveTo"), one every 90 degrees.
     """
     # In the SVG specs, initial movetos are absolute, even if
     # specified as 'm'. This is the default behavior here as well.
@@ -52,6 +119,8 @@
     command = None
     last_control = None
 
+    have_arcTo = hasattr(pen, "arcTo")
+
     while elements:
 
         if elements[-1] in COMMANDS:
@@ -209,7 +278,34 @@
             last_control = control
 
         elif command == 'A':
-            raise NotImplementedError('arcs are not supported')
+            rx = float(elements.pop())
+            ry = float(elements.pop())
+            rotation = float(elements.pop())
+            arc_large = bool(int(elements.pop()))
+            arc_sweep = bool(int(elements.pop()))
+            end = float(elements.pop()) + float(elements.pop()) * 1j
+
+            if not absolute:
+                end += current_pos
+
+            # if the pen supports arcs, pass the values unchanged, otherwise
+            # approximate the arc with a series of cubic bezier curves
+            if have_arcTo:
+                pen.arcTo(
+                    rx,
+                    ry,
+                    rotation,
+                    arc_large,
+                    arc_sweep,
+                    (end.real, end.imag),
+                )
+            else:
+                arc = arc_class(
+                    current_pos, rx, ry, rotation, arc_large, arc_sweep, end
+                )
+                arc.draw(pen)
+
+            current_pos = end
 
     # no final Z command, it's an open path
     if start_pos is not None:
diff --git a/Lib/fontTools/svgLib/path/shapes.py b/Lib/fontTools/svgLib/path/shapes.py
new file mode 100644
index 0000000..4cc633a
--- /dev/null
+++ b/Lib/fontTools/svgLib/path/shapes.py
@@ -0,0 +1,181 @@
+import re
+
+
+def _prefer_non_zero(*args):
+    for arg in args:
+        if arg != 0:
+            return arg
+    return 0.
+
+
+def _ntos(n):
+    # %f likes to add unnecessary 0's, %g isn't consistent about # decimals
+    return ('%.3f' % n).rstrip('0').rstrip('.')
+
+
+def _strip_xml_ns(tag):
+    # ElementTree API doesn't provide a way to ignore XML namespaces in tags
+    # so we here strip them ourselves: cf. https://bugs.python.org/issue18304
+    return tag.split('}', 1)[1] if '}' in tag else tag
+
+
+def _transform(raw_value):
+    # TODO assumes a 'matrix' transform.
+    # No other transform functions are supported at the moment.
+    # https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform
+    # start simple: if you aren't exactly matrix(...) then no love
+    match = re.match(r'matrix\((.*)\)', raw_value)
+    if not match:
+        raise NotImplementedError
+    matrix = tuple(float(p) for p in re.split(r'\s+|,', match.group(1)))
+    if len(matrix) != 6:
+        raise ValueError('wrong # of terms in %s' % raw_value)
+    return matrix
+
+
+class PathBuilder(object):
+    def __init__(self):
+        self.paths = []
+        self.transforms = []
+
+    def _start_path(self, initial_path=''):
+        self.paths.append(initial_path)
+        self.transforms.append(None)
+
+    def _end_path(self):
+        self._add('z')
+
+    def _add(self, path_snippet):
+        path = self.paths[-1]
+        if path:
+            path += ' ' + path_snippet
+        else:
+            path = path_snippet
+        self.paths[-1] = path
+
+    def _move(self, c, x, y):
+        self._add('%s%s,%s' % (c, _ntos(x), _ntos(y)))
+
+    def M(self, x, y):
+        self._move('M', x, y)
+
+    def m(self, x, y):
+        self._move('m', x, y)
+
+    def _arc(self, c, rx, ry, x, y, large_arc):
+        self._add('%s%s,%s 0 %d 1 %s,%s' % (c, _ntos(rx), _ntos(ry), large_arc,
+                                            _ntos(x), _ntos(y)))
+
+    def A(self, rx, ry, x, y, large_arc=0):
+        self._arc('A', rx, ry, x, y, large_arc)
+
+    def a(self, rx, ry, x, y, large_arc=0):
+        self._arc('a', rx, ry, x, y, large_arc)
+
+    def _vhline(self, c, x):
+        self._add('%s%s' % (c, _ntos(x)))
+
+    def H(self, x):
+        self._vhline('H', x)
+
+    def h(self, x):
+        self._vhline('h', x)
+
+    def V(self, y):
+        self._vhline('V', y)
+
+    def v(self, y):
+        self._vhline('v', y)
+
+    def _line(self, c, x, y):
+        self._add('%s%s,%s' % (c, _ntos(x), _ntos(y)))
+
+    def L(self, x, y):
+        self._line('L', x, y)
+
+    def l(self, x, y):
+        self._line('l', x, y)
+
+    def _parse_line(self, line):
+        x1 = float(line.attrib.get('x1', 0))
+        y1 = float(line.attrib.get('y1', 0))
+        x2 = float(line.attrib.get('x2', 0))
+        y2 = float(line.attrib.get('y2', 0))
+
+        self._start_path()
+        self.M(x1, y1)
+        self.L(x2, y2)
+
+    def _parse_rect(self, rect):
+        x = float(rect.attrib.get('x', 0))
+        y = float(rect.attrib.get('y', 0))
+        w = float(rect.attrib.get('width'))
+        h = float(rect.attrib.get('height'))
+        rx = float(rect.attrib.get('rx', 0))
+        ry = float(rect.attrib.get('ry', 0))
+
+        rx = _prefer_non_zero(rx, ry)
+        ry = _prefer_non_zero(ry, rx)
+        # TODO there are more rules for adjusting rx, ry
+
+        self._start_path()
+        self.M(x + rx, y)
+        self.H(x + w - rx)
+        if rx > 0:
+            self.A(rx, ry, x + w, y + ry)
+        self.V(y + h - ry)
+        if rx > 0:
+            self.A(rx, ry, x + w - rx, y + h)
+        self.H(x + rx)
+        if rx > 0:
+            self.A(rx, ry, x, y + h - ry)
+        self.V(y + ry)
+        if rx > 0:
+            self.A(rx, ry, x + rx, y)
+        self._end_path()
+
+    def _parse_path(self, path):
+        if 'd' in path.attrib:
+            self._start_path(initial_path=path.attrib['d'])
+
+    def _parse_polygon(self, poly):
+        if 'points' in poly.attrib:
+            self._start_path('M' + poly.attrib['points'])
+            self._end_path()
+
+    def _parse_polyline(self, poly):
+        if 'points' in poly.attrib:
+            self._start_path('M' + poly.attrib['points'])
+
+    def _parse_circle(self, circle):
+        cx = float(circle.attrib.get('cx', 0))
+        cy = float(circle.attrib.get('cy', 0))
+        r = float(circle.attrib.get('r'))
+
+        # arc doesn't seem to like being a complete shape, draw two halves
+        self._start_path()
+        self.M(cx - r, cy)
+        self.A(r, r, cx + r, cy, large_arc=1)
+        self.A(r, r, cx - r, cy, large_arc=1)
+
+    def _parse_ellipse(self, ellipse):
+        cx = float(ellipse.attrib.get('cx', 0))
+        cy = float(ellipse.attrib.get('cy', 0))
+        rx = float(ellipse.attrib.get('rx'))
+        ry = float(ellipse.attrib.get('ry'))
+
+        # arc doesn't seem to like being a complete shape, draw two halves
+        self._start_path()
+        self.M(cx - rx, cy)
+        self.A(rx, ry, cx + rx, cy, large_arc=1)
+        self.A(rx, ry, cx - rx, cy, large_arc=1)
+
+    def add_path_from_element(self, el):
+        tag = _strip_xml_ns(el.tag)
+        parse_fn = getattr(self, '_parse_%s' % tag.lower(), None)
+        if not callable(parse_fn):
+            return False
+        parse_fn(el)
+        if 'transform' in el.attrib:
+            self.transforms[-1] = _transform(el.attrib['transform'])
+        return True
diff --git a/Lib/fontTools/t1Lib/__init__.py b/Lib/fontTools/t1Lib/__init__.py
index 6632b05..e1d94d3 100644
--- a/Lib/fontTools/t1Lib/__init__.py
+++ b/Lib/fontTools/t1Lib/__init__.py
@@ -15,8 +15,7 @@
 	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.py23 import bytechr, byteord, bytesjoin
 from fontTools.misc import eexec
 from fontTools.misc.macCreatorType import getMacCreatorAndType
 import os
@@ -49,11 +48,18 @@
 	Type 1 fonts.
 	"""
 
-	def __init__(self, path=None):
-		if path is not None:
-			self.data, type = read(path)
+	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:
-			pass # XXX
+			raise ValueError(kind)
+		self.encoding = encoding
 
 	def saveAs(self, path, type, dohex=False):
 		write(path, self.getData(), type, dohex)
@@ -82,7 +88,7 @@
 	def parse(self):
 		from fontTools.misc import psLib
 		from fontTools.misc import psCharStrings
-		self.font = psLib.suckfont(self.data)
+		self.font = psLib.suckfont(self.data, self.encoding)
 		charStrings = self.font["CharStrings"]
 		lenIV = self.font["Private"].get("lenIV", 4)
 		assert lenIV >= 0
@@ -101,11 +107,12 @@
 
 def read(path, onlyHeader=False):
 	"""reads any Type 1 font file, returns raw data"""
-	normpath = path.lower()
+	_, ext = os.path.splitext(path)
+	ext = ext.lower()
 	creator, typ = getMacCreatorAndType(path)
 	if typ == 'LWFN':
 		return readLWFN(path, onlyHeader), 'LWFN'
-	if normpath[-4:] == '.pfb':
+	if ext == '.pfb':
 		return readPFB(path, onlyHeader), 'PFB'
 	else:
 		return readOther(path), 'OTHER'
@@ -157,9 +164,8 @@
 			elif code in [3, 5]:
 				break
 			elif code == 4:
-				f = open(path, "rb")
-				data.append(f.read())
-				f.close()
+				with open(path, "rb") as f:
+					data.append(f.read())
 			elif code == 0:
 				pass # comment, ignore
 			else:
@@ -172,35 +178,32 @@
 
 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()
+	with open(path, "rb") as f:
+		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
 	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()
+	with open(path, "rb") as f:
+		data = f.read()
 	assertType1(data)
-
 	chunks = findEncryptedChunks(data)
 	data = []
 	for isEncrypted, chunk in chunks:
@@ -237,8 +240,7 @@
 
 def writePFB(path, data):
 	chunks = findEncryptedChunks(data)
-	f = open(path, "wb")
-	try:
+	with open(path, "wb") as f:
 		for isEncrypted, chunk in chunks:
 			if isEncrypted:
 				code = 2
@@ -248,13 +250,10 @@
 			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:
+	with open(path, "wb") as f:
 		hexlinelen = HEXLINELENGTH // 2
 		for isEncrypted, chunk in chunks:
 			if isEncrypted:
@@ -268,14 +267,14 @@
 					chunk = chunk[hexlinelen:]
 			else:
 				f.write(chunk)
-	finally:
-		f.close()
 
 
 # decryption tools
 
 EEXECBEGIN = b"currentfile eexec"
-EEXECEND = b'0' * 64
+# The spec allows for 512 ASCII zeros interrupted by arbitrary whitespace to
+# follow eexec
+EEXECEND = re.compile(b'(0[ \t\r\n]*){512}', flags=re.M)
 EEXECINTERNALEND = b"currentfile closefile"
 EEXECBEGINMARKER = b"%-- eexec start\r"
 EEXECENDMARKER = b"%-- eexec end\r"
@@ -314,9 +313,10 @@
 		if eBegin < 0:
 			break
 		eBegin = eBegin + len(EEXECBEGIN) + 1
-		eEnd = data.find(EEXECEND, eBegin)
-		if eEnd < 0:
+		endMatch = EEXECEND.search(data, eBegin)
+		if endMatch is None:
 			raise T1Error("can't find end of eexec part")
+		eEnd = endMatch.start()
 		cypherText = data[eBegin:eEnd + 2]
 		if isHex(cypherText[:4]):
 			cypherText = deHexString(cypherText)
diff --git a/Lib/fontTools/tfmLib.py b/Lib/fontTools/tfmLib.py
new file mode 100644
index 0000000..673373f
--- /dev/null
+++ b/Lib/fontTools/tfmLib.py
@@ -0,0 +1,460 @@
+"""Module for reading TFM (TeX Font Metrics) files.
+
+The TFM format is described in the TFtoPL WEB source code, whose typeset form
+can be found on `CTAN <http://mirrors.ctan.org/info/knuth-pdf/texware/tftopl.pdf>`_.
+
+	>>> from fontTools.tfmLib import TFM
+	>>> tfm = TFM("Tests/tfmLib/data/cmr10.tfm")
+	>>>
+	>>> # Accessing an attribute gets you metadata.
+	>>> tfm.checksum
+	1274110073
+	>>> tfm.designsize
+	10.0
+	>>> tfm.codingscheme
+	'TeX text'
+	>>> tfm.family
+	'CMR'
+	>>> tfm.seven_bit_safe_flag
+	False
+	>>> tfm.face
+	234
+	>>> tfm.extraheader
+	{}
+	>>> tfm.fontdimens
+	{'SLANT': 0.0, 'SPACE': 0.33333396911621094, 'STRETCH': 0.16666698455810547, 'SHRINK': 0.11111164093017578, 'XHEIGHT': 0.4305553436279297, 'QUAD': 1.0000028610229492, 'EXTRASPACE': 0.11111164093017578}
+	>>> # Accessing a character gets you its metrics.
+	>>> # “width” is always available, other metrics are available only when
+	>>> # applicable. All values are relative to “designsize”.
+	>>> tfm.chars[ord("g")]
+	{'width': 0.5000019073486328, 'height': 0.4305553436279297, 'depth': 0.1944446563720703, 'italic': 0.013888359069824219}
+	>>> # Kerning and ligature can be accessed as well.
+	>>> tfm.kerning[ord("c")]
+	{104: -0.02777862548828125, 107: -0.02777862548828125}
+	>>> tfm.ligatures[ord("f")]
+	{105: ('LIG', 12), 102: ('LIG', 11), 108: ('LIG', 13)}
+"""
+
+from types import SimpleNamespace
+
+from fontTools.misc.sstruct import calcsize, unpack, unpack2
+
+SIZES_FORMAT = """
+    >
+    lf: h    # length of the entire file, in words
+    lh: h    # length of the header data, in words
+    bc: h    # smallest character code in the font
+    ec: h    # largest character code in the font
+    nw: h    # number of words in the width table
+    nh: h    # number of words in the height table
+    nd: h    # number of words in the depth table
+    ni: h    # number of words in the italic correction table
+    nl: h    # number of words in the ligature/kern table
+    nk: h    # number of words in the kern table
+    ne: h    # number of words in the extensible character table
+    np: h    # number of font parameter words
+"""
+
+SIZES_SIZE = calcsize(SIZES_FORMAT)
+
+FIXED_FORMAT = "12.20F"
+
+HEADER_FORMAT1 = f"""
+    >
+    checksum:            L
+    designsize:          {FIXED_FORMAT}
+"""
+
+HEADER_FORMAT2 = f"""
+    {HEADER_FORMAT1}
+    codingscheme:        40p
+"""
+
+HEADER_FORMAT3 = f"""
+    {HEADER_FORMAT2}
+    family:              20p
+"""
+
+HEADER_FORMAT4 = f"""
+    {HEADER_FORMAT3}
+    seven_bit_safe_flag: ?
+    ignored:             x
+    ignored:             x
+    face:                B
+"""
+
+HEADER_SIZE1 = calcsize(HEADER_FORMAT1)
+HEADER_SIZE2 = calcsize(HEADER_FORMAT2)
+HEADER_SIZE3 = calcsize(HEADER_FORMAT3)
+HEADER_SIZE4 = calcsize(HEADER_FORMAT4)
+
+LIG_KERN_COMMAND = """
+    >
+    skip_byte: B
+    next_char: B
+    op_byte: B
+    remainder: B
+"""
+
+BASE_PARAMS = [
+    "SLANT",
+    "SPACE",
+    "STRETCH",
+    "SHRINK",
+    "XHEIGHT",
+    "QUAD",
+    "EXTRASPACE",
+]
+
+MATHSY_PARAMS = [
+    "NUM1",
+    "NUM2",
+    "NUM3",
+    "DENOM1",
+    "DENOM2",
+    "SUP1",
+    "SUP2",
+    "SUP3",
+    "SUB1",
+    "SUB2",
+    "SUPDROP",
+    "SUBDROP",
+    "DELIM1",
+    "DELIM2",
+    "AXISHEIGHT",
+]
+
+MATHEX_PARAMS = [
+    "DEFAULTRULETHICKNESS",
+    "BIGOPSPACING1",
+    "BIGOPSPACING2",
+    "BIGOPSPACING3",
+    "BIGOPSPACING4",
+    "BIGOPSPACING5",
+]
+
+VANILLA = 0
+MATHSY = 1
+MATHEX = 2
+
+UNREACHABLE = 0
+PASSTHROUGH = 1
+ACCESSABLE = 2
+
+NO_TAG = 0
+LIG_TAG = 1
+LIST_TAG = 2
+EXT_TAG = 3
+
+STOP_FLAG = 128
+KERN_FLAG = 128
+
+
+class TFMException(Exception):
+    def __init__(self, message):
+        super().__init__(message)
+
+
+class TFM:
+    def __init__(self, file):
+        self._read(file)
+
+    def __repr__(self):
+        return (
+            f"<TFM"
+            f" for {self.family}"
+            f" in {self.codingscheme}"
+            f" at {self.designsize:g}pt>"
+        )
+
+    def _read(self, file):
+        if hasattr(file, "read"):
+            data = file.read()
+        else:
+            with open(file, "rb") as fp:
+                data = fp.read()
+
+        self._data = data
+
+        if len(data) < SIZES_SIZE:
+            raise TFMException("Too short input file")
+
+        sizes = SimpleNamespace()
+        unpack2(SIZES_FORMAT, data, sizes)
+
+        # Do some file structure sanity checks.
+        # TeX and TFtoPL do additional functional checks and might even correct
+        # “errors” in the input file, but we instead try to output the file as
+        # it is as long as it is parsable, even if the data make no sense.
+
+        if sizes.lf < 0:
+            raise TFMException("The file claims to have negative or zero length!")
+
+        if len(data) < sizes.lf * 4:
+            raise TFMException("The file has fewer bytes than it claims!")
+
+        for name, length in vars(sizes).items():
+            if length < 0:
+                raise TFMException("The subfile size: '{name}' is negative!")
+
+        if sizes.lh < 2:
+            raise TFMException(f"The header length is only {sizes.lh}!")
+
+        if sizes.bc > sizes.ec + 1 or sizes.ec > 255:
+            raise TFMException(
+                f"The character code range {sizes.bc}..{sizes.ec} is illegal!"
+            )
+
+        if sizes.nw == 0 or sizes.nh == 0 or sizes.nd == 0 or sizes.ni == 0:
+            raise TFMException("Incomplete subfiles for character dimensions!")
+
+        if sizes.ne > 256:
+            raise TFMException(f"There are {ne} extensible recipes!")
+
+        if sizes.lf != (
+            6
+            + sizes.lh
+            + (sizes.ec - sizes.bc + 1)
+            + sizes.nw
+            + sizes.nh
+            + sizes.nd
+            + sizes.ni
+            + sizes.nl
+            + sizes.nk
+            + sizes.ne
+            + sizes.np
+        ):
+            raise TFMException("Subfile sizes don’t add up to the stated total")
+
+        # Subfile offsets, used in the helper function below. These all are
+        # 32-bit word offsets not 8-bit byte offsets.
+        char_base = 6 + sizes.lh - sizes.bc
+        width_base = char_base + sizes.ec + 1
+        height_base = width_base + sizes.nw
+        depth_base = height_base + sizes.nh
+        italic_base = depth_base + sizes.nd
+        lig_kern_base = italic_base + sizes.ni
+        kern_base = lig_kern_base + sizes.nl
+        exten_base = kern_base + sizes.nk
+        param_base = exten_base + sizes.ne
+
+        # Helper functions for accessing individual data. If this looks
+        # nonidiomatic Python, I blame the effect of reading the literate WEB
+        # documentation of TFtoPL.
+        def char_info(c):
+            return 4 * (char_base + c)
+
+        def width_index(c):
+            return data[char_info(c)]
+
+        def noneexistent(c):
+            return c < sizes.bc or c > sizes.ec or width_index(c) == 0
+
+        def height_index(c):
+            return data[char_info(c) + 1] // 16
+
+        def depth_index(c):
+            return data[char_info(c) + 1] % 16
+
+        def italic_index(c):
+            return data[char_info(c) + 2] // 4
+
+        def tag(c):
+            return data[char_info(c) + 2] % 4
+
+        def remainder(c):
+            return data[char_info(c) + 3]
+
+        def width(c):
+            r = 4 * (width_base + width_index(c))
+            return read_fixed(r, "v")["v"]
+
+        def height(c):
+            r = 4 * (height_base + height_index(c))
+            return read_fixed(r, "v")["v"]
+
+        def depth(c):
+            r = 4 * (depth_base + depth_index(c))
+            return read_fixed(r, "v")["v"]
+
+        def italic(c):
+            r = 4 * (italic_base + italic_index(c))
+            return read_fixed(r, "v")["v"]
+
+        def exten(c):
+            return 4 * (exten_base + remainder(c))
+
+        def lig_step(i):
+            return 4 * (lig_kern_base + i)
+
+        def lig_kern_command(i):
+            command = SimpleNamespace()
+            unpack2(LIG_KERN_COMMAND, data[i:], command)
+            return command
+
+        def kern(i):
+            r = 4 * (kern_base + i)
+            return read_fixed(r, "v")["v"]
+
+        def param(i):
+            return 4 * (param_base + i)
+
+        def read_fixed(index, key, obj=None):
+            ret = unpack2(f">;{key}:{FIXED_FORMAT}", data[index:], obj)
+            return ret[0]
+
+        # Set all attributes to empty values regardless of the header size.
+        unpack(HEADER_FORMAT4, [0] * HEADER_SIZE4, self)
+
+        offset = 24
+        length = sizes.lh * 4
+        self.extraheader = {}
+        if length >= HEADER_SIZE4:
+            rest = unpack2(HEADER_FORMAT4, data[offset:], self)[1]
+            if self.face < 18:
+                s = self.face % 2
+                b = self.face // 2
+                self.face = "MBL"[b % 3] + "RI"[s] + "RCE"[b // 3]
+            for i in range(sizes.lh - HEADER_SIZE4 // 4):
+                rest = unpack2(f">;HEADER{i + 18}:l", rest, self.extraheader)[1]
+        elif length >= HEADER_SIZE3:
+            unpack2(HEADER_FORMAT3, data[offset:], self)
+        elif length >= HEADER_SIZE2:
+            unpack2(HEADER_FORMAT2, data[offset:], self)
+        elif length >= HEADER_SIZE1:
+            unpack2(HEADER_FORMAT1, data[offset:], self)
+
+        self.fonttype = VANILLA
+        scheme = self.codingscheme.upper()
+        if scheme.startswith("TEX MATH SY"):
+            self.fonttype = MATHSY
+        elif scheme.startswith("TEX MATH EX"):
+            self.fonttype = MATHEX
+
+        self.fontdimens = {}
+        for i in range(sizes.np):
+            name = f"PARAMETER{i+1}"
+            if i <= 6:
+                name = BASE_PARAMS[i]
+            elif self.fonttype == MATHSY and i <= 21:
+                name = MATHSY_PARAMS[i - 7]
+            elif self.fonttype == MATHEX and i <= 12:
+                name = MATHEX_PARAMS[i - 7]
+            read_fixed(param(i), name, self.fontdimens)
+
+        lig_kern_map = {}
+        self.right_boundary_char = None
+        self.left_boundary_char = None
+        if sizes.nl > 0:
+            cmd = lig_kern_command(lig_step(0))
+            if cmd.skip_byte == 255:
+                self.right_boundary_char = cmd.next_char
+
+            cmd = lig_kern_command(lig_step((sizes.nl - 1)))
+            if cmd.skip_byte == 255:
+                self.left_boundary_char = 256
+                r = 256 * cmd.op_byte + cmd.remainder
+                lig_kern_map[self.left_boundary_char] = r
+
+        self.chars = {}
+        for c in range(sizes.bc, sizes.ec + 1):
+            if width_index(c) > 0:
+                self.chars[c] = info = {}
+                info["width"] = width(c)
+                if height_index(c) > 0:
+                    info["height"] = height(c)
+                if depth_index(c) > 0:
+                    info["depth"] = depth(c)
+                if italic_index(c) > 0:
+                    info["italic"] = italic(c)
+                char_tag = tag(c)
+                if char_tag == NO_TAG:
+                    pass
+                elif char_tag == LIG_TAG:
+                    lig_kern_map[c] = remainder(c)
+                elif char_tag == LIST_TAG:
+                    info["nextlarger"] = remainder(c)
+                elif char_tag == EXT_TAG:
+                    info["varchar"] = varchar = {}
+                    for i in range(4):
+                        part = data[exten(c) + i]
+                        if i == 3 or part > 0:
+                            name = "rep"
+                            if i == 0:
+                                name = "top"
+                            elif i == 1:
+                                name = "mid"
+                            elif i == 2:
+                                name = "bot"
+                            if noneexistent(part):
+                                varchar[name] = c
+                            else:
+                                varchar[name] = part
+
+        self.ligatures = {}
+        self.kerning = {}
+        for c, i in sorted(lig_kern_map.items()):
+            cmd = lig_kern_command(lig_step(i))
+            if cmd.skip_byte > STOP_FLAG:
+                i = 256 * cmd.op_byte + cmd.remainder
+
+            while i < sizes.nl:
+                cmd = lig_kern_command(lig_step(i))
+                if cmd.skip_byte > STOP_FLAG:
+                    pass
+                else:
+                    if cmd.op_byte >= KERN_FLAG:
+                        r = 256 * (cmd.op_byte - KERN_FLAG) + cmd.remainder
+                        self.kerning.setdefault(c, {})[cmd.next_char] = kern(r)
+                    else:
+                        r = cmd.op_byte
+                        if r == 4 or (r > 7 and r != 11):
+                            # Ligature step with nonstandard code, we output
+                            # the code verbatim.
+                            lig = r
+                        else:
+                            lig = ""
+                            if r % 4 > 1:
+                                lig += "/"
+                            lig += "LIG"
+                            if r % 2 != 0:
+                                lig += "/"
+                            while r > 3:
+                                lig += ">"
+                                r -= 4
+                        self.ligatures.setdefault(c, {})[cmd.next_char] = (
+                            lig,
+                            cmd.remainder,
+                        )
+
+                if cmd.skip_byte >= STOP_FLAG:
+                    break
+                i += cmd.skip_byte + 1
+
+
+if __name__ == "__main__":
+    import sys
+
+    tfm = TFM(sys.argv[1])
+    print(
+        "\n".join(
+            x
+            for x in [
+                f"tfm.checksum={tfm.checksum}",
+                f"tfm.designsize={tfm.designsize}",
+                f"tfm.codingscheme={tfm.codingscheme}",
+                f"tfm.fonttype={tfm.fonttype}",
+                f"tfm.family={tfm.family}",
+                f"tfm.seven_bit_safe_flag={tfm.seven_bit_safe_flag}",
+                f"tfm.face={tfm.face}",
+                f"tfm.extraheader={tfm.extraheader}",
+                f"tfm.fontdimens={tfm.fontdimens}",
+                f"tfm.right_boundary_char={tfm.right_boundary_char}",
+                f"tfm.left_boundary_char={tfm.left_boundary_char}",
+                f"tfm.kerning={tfm.kerning}",
+                f"tfm.ligatures={tfm.ligatures}",
+                f"tfm.chars={tfm.chars}",
+            ]
+        )
+    )
+    print(tfm)
diff --git a/Lib/fontTools/ttLib/__init__.py b/Lib/fontTools/ttLib/__init__.py
index 100903f..16417e7 100644
--- a/Lib/fontTools/ttLib/__init__.py
+++ b/Lib/fontTools/ttLib/__init__.py
@@ -41,11 +41,7 @@
 
 """
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.misc.loggingTools import deprecateArgument, deprecateFunction
-import os
-import sys
+from fontTools.misc.loggingTools import deprecateFunction
 import logging
 
 
@@ -53,983 +49,10 @@
 
 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=None, recalcBBoxes=True, allowVID=False, ignoreDecompileErrors=False,
-			recalcTimestamp=True, fontNumber=-1, lazy=None, quiet=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.
-		"""
-
-		from fontTools.ttLib import sfnt
-
-		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
-		if not self.lazy:
-			# read input file in memory and wrap a stream around it to allow overwriting
-			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.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, reorderTables=True):
-		"""Save the font to disk. Similarly to the constructor,
-		the 'file' argument can be either a pathname or a writable
-		file object.
-		"""
-		from fontTools.ttLib import sfnt
-		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
-
-		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
-		tmp = BytesIO()
-		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 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()
-			tmp.seek(0)
-			tmp2 = BytesIO()
-			reorderFontTables(tmp, tmp2, tableOrder)
-			file.write(tmp2.getvalue())
-			tmp.close()
-			tmp2.close()
-
-		if closeStream:
-			file.close()
-
-	def saveXML(self, fileOrPath, progress=None, quiet=None,
-			tables=None, skipTables=None, splitTables=False, disassembleInstructions=True,
-			splitGlyphs=False, bitmapGlyphDataFormat='raw', newlinestr=None):
-
-		"""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
-
-		# only write the MAJOR.MINOR version in the 'ttLibVersion' attribute of
-		# TTX files' root element (without PATCH or .dev suffixes)
-		version = ".".join(version.split('.')[:2])
-
-		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 progress:
-			progress.set(0, numTables)
-			idlefunc = getattr(progress, "idle", None)
-		else:
-			idlefunc = None
-
-		writer = xmlWriter.XMLWriter(fileOrPath, idlefunc=idlefunc,
-				newlinestr=newlinestr)
-		writer.begintag("ttFont", sfntVersion=repr(tostr(self.sfntVersion))[1:-1],
-				ttLibVersion=version)
-		writer.newline()
-
-		if not splitTables:
-			writer.newline()
-		if splitTables or splitGlyphs:
-			# '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 or (splitGlyphs and tag == 'glyf'):
-				tablePath = fileNameTemplate % tagToIdentifier(tag)
-			else:
-				tablePath = None
-			if splitTables:
-				tableWriter = xmlWriter.XMLWriter(tablePath, idlefunc=idlefunc,
-						newlinestr=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, progress, splitGlyphs=splitGlyphs)
-			if splitTables:
-				tableWriter.endtag("ttFont")
-				tableWriter.newline()
-				tableWriter.close()
-		if progress:
-			progress.set((i + 1))
-		writer.endtag("ttFont")
-		writer.newline()
-		# close if 'fileOrPath' is a path; leave it open if it's a file.
-		# The special string "-" means standard output so leave that open too
-		if not hasattr(fileOrPath, "write") and fileOrPath != "-":
-			writer.close()
-
-	def _tableToXML(self, writer, tag, progress, splitGlyphs=False, quiet=None):
-		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
-		if progress:
-			progress.setLabel(report)
-		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, progress, splitGlyphs)
-		elif tag == "CFF ":
-			table.toXML(writer, self, progress)
-		else:
-			table.toXML(writer, self)
-		writer.endtag(xmlTag)
-		writer.newline()
-		writer.newline()
-
-	def importXML(self, fileOrPath, progress=None, 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, progress)
-		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]
-				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)
-				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.
-		reversecmap = self['cmap'].buildReversed()
-		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
-
-		# 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):
-		"""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)
-		log.debug("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):
-			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 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 nameToIdentifier(name):
-	"""Convert a name 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
-	while len(name) > 1 and name[-1] == ' ':
-		name = name[:-1]
-	ident = ""
-	for c in name:
-		ident = ident + _escapechar(c)
-	if re.match("[0-9]", ident):
-		ident = "_" + ident
-	return ident
-
-def tagToIdentifier(tag):
-	"""This performs the same conversion which nameToIdentifiier does
-	with the additional assertion that the source tag is 4 characters
-	long which is criteria for a valid tag name.
-	"""
-	if tag == "GlyphOrder":
-		return tag
-	ret = nameToIdentifier(tag)
-	assert len(tag) == 4, "tag should be 4 characters long"
-	return ret
-
-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=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
+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 17dd5ef..496fb67 100644
--- a/Lib/fontTools/ttLib/macUtils.py
+++ b/Lib/fontTools/ttLib/macUtils.py
@@ -1,6 +1,5 @@
 """ttLib.macUtils.py -- Various Mac-specific stuff."""
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from io import BytesIO
 from fontTools.misc.macRes import ResourceReader, ResourceError
 
 
@@ -41,7 +40,7 @@
 	def __init__(self, path, res_name_or_index):
 		from fontTools import ttLib
 		reader = ResourceReader(path)
-		if isinstance(res_name_or_index, basestring):
+		if isinstance(res_name_or_index, str):
 			rsrc = reader.getNamedResource('sfnt', res_name_or_index)
 		else:
 			rsrc = reader.getIndResource('sfnt', res_name_or_index)
diff --git a/Lib/fontTools/ttLib/removeOverlaps.py b/Lib/fontTools/ttLib/removeOverlaps.py
new file mode 100644
index 0000000..624cd47
--- /dev/null
+++ b/Lib/fontTools/ttLib/removeOverlaps.py
@@ -0,0 +1,248 @@
+""" Simplify TrueType glyphs by merging overlapping contours/components.
+
+Requires https://github.com/fonttools/skia-pathops
+"""
+
+import itertools
+import logging
+from typing import Callable, Iterable, Optional, Mapping
+
+from fontTools.misc.roundTools import otRound
+from fontTools.ttLib import ttFont
+from fontTools.ttLib.tables import _g_l_y_f
+from fontTools.ttLib.tables import _h_m_t_x
+from fontTools.pens.ttGlyphPen import TTGlyphPen
+
+import pathops
+
+
+__all__ = ["removeOverlaps"]
+
+
+class RemoveOverlapsError(Exception):
+    pass
+
+
+log = logging.getLogger("fontTools.ttLib.removeOverlaps")
+
+_TTGlyphMapping = Mapping[str, ttFont._TTGlyph]
+
+
+def skPathFromGlyph(glyphName: str, glyphSet: _TTGlyphMapping) -> pathops.Path:
+    path = pathops.Path()
+    pathPen = path.getPen(glyphSet=glyphSet)
+    glyphSet[glyphName].draw(pathPen)
+    return path
+
+
+def skPathFromGlyphComponent(
+    component: _g_l_y_f.GlyphComponent, glyphSet: _TTGlyphMapping
+):
+    baseGlyphName, transformation = component.getComponentInfo()
+    path = skPathFromGlyph(baseGlyphName, glyphSet)
+    return path.transform(*transformation)
+
+
+def componentsOverlap(glyph: _g_l_y_f.Glyph, glyphSet: _TTGlyphMapping) -> bool:
+    if not glyph.isComposite():
+        raise ValueError("This method only works with TrueType composite glyphs")
+    if len(glyph.components) < 2:
+        return False  # single component, no overlaps
+
+    component_paths = {}
+
+    def _get_nth_component_path(index: int) -> pathops.Path:
+        if index not in component_paths:
+            component_paths[index] = skPathFromGlyphComponent(
+                glyph.components[index], glyphSet
+            )
+        return component_paths[index]
+
+    return any(
+        pathops.op(
+            _get_nth_component_path(i),
+            _get_nth_component_path(j),
+            pathops.PathOp.INTERSECTION,
+            fix_winding=False,
+            keep_starting_points=False,
+        )
+        for i, j in itertools.combinations(range(len(glyph.components)), 2)
+    )
+
+
+def ttfGlyphFromSkPath(path: pathops.Path) -> _g_l_y_f.Glyph:
+    # Skia paths have no 'components', no need for glyphSet
+    ttPen = TTGlyphPen(glyphSet=None)
+    path.draw(ttPen)
+    glyph = ttPen.glyph()
+    assert not glyph.isComposite()
+    # compute glyph.xMin (glyfTable parameter unused for non composites)
+    glyph.recalcBounds(glyfTable=None)
+    return glyph
+
+
+def _round_path(
+    path: pathops.Path, round: Callable[[float], float] = otRound
+) -> pathops.Path:
+    rounded_path = pathops.Path()
+    for verb, points in path:
+        rounded_path.add(verb, *((round(p[0]), round(p[1])) for p in points))
+    return rounded_path
+
+
+def _simplify(path: pathops.Path, debugGlyphName: str) -> pathops.Path:
+    # skia-pathops has a bug where it sometimes fails to simplify paths when there
+    # are float coordinates and control points are very close to one another.
+    # Rounding coordinates to integers works around the bug.
+    # Since we are going to round glyf coordinates later on anyway, here it is
+    # ok(-ish) to also round before simplify. Better than failing the whole process
+    # for the entire font.
+    # https://bugs.chromium.org/p/skia/issues/detail?id=11958
+    # https://github.com/google/fonts/issues/3365
+    # TODO(anthrotype): remove once this Skia bug is fixed
+    try:
+        return pathops.simplify(path, clockwise=path.clockwise)
+    except pathops.PathOpsError:
+        pass
+
+    path = _round_path(path)
+    try:
+        path = pathops.simplify(path, clockwise=path.clockwise)
+        log.debug(
+            "skia-pathops failed to simplify '%s' with float coordinates, "
+            "but succeded using rounded integer coordinates",
+            debugGlyphName,
+        )
+        return path
+    except pathops.PathOpsError as e:
+        if log.isEnabledFor(logging.DEBUG):
+            path.dump()
+        raise RemoveOverlapsError(
+            f"Failed to remove overlaps from glyph {debugGlyphName!r}"
+        ) from e
+
+    raise AssertionError("Unreachable")
+
+
+def removeTTGlyphOverlaps(
+    glyphName: str,
+    glyphSet: _TTGlyphMapping,
+    glyfTable: _g_l_y_f.table__g_l_y_f,
+    hmtxTable: _h_m_t_x.table__h_m_t_x,
+    removeHinting: bool = True,
+) -> bool:
+    glyph = glyfTable[glyphName]
+    # decompose composite glyphs only if components overlap each other
+    if (
+        glyph.numberOfContours > 0
+        or glyph.isComposite()
+        and componentsOverlap(glyph, glyphSet)
+    ):
+        path = skPathFromGlyph(glyphName, glyphSet)
+
+        # remove overlaps
+        path2 = _simplify(path, glyphName)
+
+        # replace TTGlyph if simplified path is different (ignoring contour order)
+        if {tuple(c) for c in path.contours} != {tuple(c) for c in path2.contours}:
+            glyfTable[glyphName] = glyph = ttfGlyphFromSkPath(path2)
+            # simplified glyph is always unhinted
+            assert not glyph.program
+            # also ensure hmtx LSB == glyph.xMin so glyph origin is at x=0
+            width, lsb = hmtxTable[glyphName]
+            if lsb != glyph.xMin:
+                hmtxTable[glyphName] = (width, glyph.xMin)
+            return True
+
+    if removeHinting:
+        glyph.removeHinting()
+    return False
+
+
+def removeOverlaps(
+    font: ttFont.TTFont,
+    glyphNames: Optional[Iterable[str]] = None,
+    removeHinting: bool = True,
+    ignoreErrors=False,
+) -> None:
+    """Simplify glyphs in TTFont by merging overlapping contours.
+
+    Overlapping components are first decomposed to simple contours, then merged.
+
+    Currently this only works with TrueType fonts with 'glyf' table.
+    Raises NotImplementedError if 'glyf' table is absent.
+
+    Note that removing overlaps invalidates the hinting. By default we drop hinting
+    from all glyphs whether or not overlaps are removed from a given one, as it would
+    look weird if only some glyphs are left (un)hinted.
+
+    Args:
+        font: input TTFont object, modified in place.
+        glyphNames: optional iterable of glyph names (str) to remove overlaps from.
+            By default, all glyphs in the font are processed.
+        removeHinting (bool): set to False to keep hinting for unmodified glyphs.
+        ignoreErrors (bool): set to True to ignore errors while removing overlaps,
+            thus keeping the tricky glyphs unchanged (fonttools/fonttools#2363).
+    """
+    try:
+        glyfTable = font["glyf"]
+    except KeyError:
+        raise NotImplementedError("removeOverlaps currently only works with TTFs")
+
+    hmtxTable = font["hmtx"]
+    # wraps the underlying glyf Glyphs, takes care of interfacing with drawing pens
+    glyphSet = font.getGlyphSet()
+
+    if glyphNames is None:
+        glyphNames = font.getGlyphOrder()
+
+    # process all simple glyphs first, then composites with increasing component depth,
+    # so that by the time we test for component intersections the respective base glyphs
+    # have already been simplified
+    glyphNames = sorted(
+        glyphNames,
+        key=lambda name: (
+            glyfTable[name].getCompositeMaxpValues(glyfTable).maxComponentDepth
+            if glyfTable[name].isComposite()
+            else 0,
+            name,
+        ),
+    )
+    modified = set()
+    for glyphName in glyphNames:
+        try:
+            if removeTTGlyphOverlaps(
+                glyphName, glyphSet, glyfTable, hmtxTable, removeHinting
+            ):
+                modified.add(glyphName)
+        except RemoveOverlapsError:
+            if not ignoreErrors:
+                raise
+            log.error("Failed to remove overlaps for '%s'", glyphName)
+
+    log.debug("Removed overlaps for %s glyphs:\n%s", len(modified), " ".join(modified))
+
+
+def main(args=None):
+    import sys
+
+    if args is None:
+        args = sys.argv[1:]
+
+    if len(args) < 2:
+        print(
+            f"usage: fonttools ttLib.removeOverlaps INPUT.ttf OUTPUT.ttf [GLYPHS ...]"
+        )
+        sys.exit(1)
+
+    src = args[0]
+    dst = args[1]
+    glyphNames = args[2:] or None
+
+    with ttFont.TTFont(src) as f:
+        removeOverlaps(f, glyphNames)
+        f.save(dst)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/Lib/fontTools/ttLib/sfnt.py b/Lib/fontTools/ttLib/sfnt.py
index 60df7e3..d609dc5 100644
--- a/Lib/fontTools/ttLib/sfnt.py
+++ b/Lib/fontTools/ttLib/sfnt.py
@@ -12,10 +12,11 @@
 a table's length chages you need to rewrite the whole file anyway.
 """
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from io import BytesIO
+from types import SimpleNamespace
+from fontTools.misc.py23 import Tag
 from fontTools.misc import sstruct
-from fontTools.ttLib import getSearchRange
+from fontTools.ttLib import TTLibError
 import struct
 from collections import OrderedDict
 import logging
@@ -32,6 +33,7 @@
 		"""
 		if args and cls is SFNTReader:
 			infile = args[0]
+			infile.seek(0)
 			sfntVersion = Tag(infile.read(4))
 			infile.seek(0)
 			if sfntVersion == "wOF2":
@@ -41,53 +43,43 @@
 		# return default object
 		return object.__new__(cls)
 
-	def __init__(self, file, checkChecksums=1, fontNumber=-1):
+	def __init__(self, file, checkChecksums=0, fontNumber=-1):
 		self.file = file
 		self.checkChecksums = checkChecksums
 
 		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":
-			data = self.file.read(ttcHeaderSize)
-			if len(data) != ttcHeaderSize:
-				from fontTools import ttLib
-				raise ttLib.TTLibError("Not a Font Collection (not enough data)")
-			sstruct.unpack(ttcHeaderFormat, data, 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])
+			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:
-				from fontTools import ttLib
-				raise ttLib.TTLibError("Not a Font Collection (not enough data)")
+				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
 			data = self.file.read(woffDirectorySize)
 			if len(data) != woffDirectorySize:
-				from fontTools import ttLib
-				raise ttLib.TTLibError("Not a WOFF font (not enough data)")
+				raise TTLibError("Not a WOFF font (not enough data)")
 			sstruct.unpack(woffDirectoryFormat, data, self)
 		else:
 			data = self.file.read(sfntDirectorySize)
 			if len(data) != sfntDirectorySize:
-				from fontTools import ttLib
-				raise ttLib.TTLibError("Not a TrueType or OpenType font (not enough data)")
+				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)")
+			raise TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")
 		tables = {}
 		for i in range(self.numTables):
 			entry = self.DirectoryEntry()
@@ -132,6 +124,30 @@
 	def close(self):
 		self.file.close()
 
+	# We define custom __getstate__ and __setstate__ to make SFNTReader pickle-able
+	# and deepcopy-able. When a TTFont is loaded as lazy=True, SFNTReader holds a
+	# reference to an external file object which is not pickleable. So in __getstate__
+	# we store the file name and current position, and in __setstate__ we reopen the
+	# same named file after unpickling.
+
+	def __getstate__(self):
+		if isinstance(self.file, BytesIO):
+			# BytesIO is already pickleable, return the state unmodified
+			return self.__dict__
+
+		# remove unpickleable file attribute, and only store its name and pos
+		state = self.__dict__.copy()
+		del state["file"]
+		state["_filename"] = self.file.name
+		state["_filepos"] = self.file.tell()
+		return state
+
+	def __setstate__(self, state):
+		if "file" not in state:
+			self.file = open(state.pop("_filename"), "rb")
+			self.file.seek(state.pop("_filepos"))
+		self.__dict__.update(state)
+
 
 # default compression level for WOFF 1.0 tables and metadata
 ZLIB_COMPRESSION_LEVEL = 6
@@ -215,20 +231,27 @@
 			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 = 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."""
 		if tag in self.tables:
-			from fontTools import ttLib
-			raise ttLib.TTLibError("cannot rewrite '%s' table" % tag)
+			raise TTLibError("cannot rewrite '%s' table" % tag)
 
 		entry = self.DirectoryEntry()
 		entry.tag = tag
@@ -253,7 +276,10 @@
 		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
@@ -261,8 +287,7 @@
 		"""
 		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"
@@ -311,7 +336,7 @@
 
 		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":
@@ -319,7 +344,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):
@@ -331,6 +356,7 @@
 
 		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())
@@ -529,8 +555,7 @@
 				reader.file.seek(reader.metaOffset)
 				rawData = reader.file.read(reader.metaLength)
 				assert len(rawData) == reader.metaLength
-				import zlib
-				data = zlib.decompress(rawData)
+				data = self._decompress(rawData)
 				assert len(data) == reader.metaOrigLength
 				self.metaData = data
 			if reader.privLength:
@@ -539,6 +564,10 @@
 				assert len(data) == reader.privLength
 				self.privData = data
 
+	def _decompress(self, rawData):
+		import zlib
+		return zlib.decompress(rawData)
+
 
 def calcChecksum(data):
 	"""Calculate the checksum for an arbitrary block of data.
@@ -566,6 +595,31 @@
 		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 sys
diff --git a/Lib/fontTools/ttLib/standardGlyphOrder.py b/Lib/fontTools/ttLib/standardGlyphOrder.py
index 510773a..1f980e4 100644
--- a/Lib/fontTools/ttLib/standardGlyphOrder.py
+++ b/Lib/fontTools/ttLib/standardGlyphOrder.py
@@ -1,6 +1,3 @@
-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.
diff --git a/Lib/fontTools/ttLib/tables/B_A_S_E_.py b/Lib/fontTools/ttLib/tables/B_A_S_E_.py
index 14906b4..9551e2c 100644
--- a/Lib/fontTools/ttLib/tables/B_A_S_E_.py
+++ b/Lib/fontTools/ttLib/tables/B_A_S_E_.py
@@ -1,5 +1,3 @@
-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 685979a..9197923 100644
--- a/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py
+++ b/Lib/fontTools/ttLib/tables/BitmapGlyphMetrics.py
@@ -1,7 +1,5 @@
 # Since bitmap glyph metrics are shared between EBLC and EBDT
 # this class gets its own python file.
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
 import logging
diff --git a/Lib/fontTools/ttLib/tables/C_B_D_T_.py b/Lib/fontTools/ttLib/tables/C_B_D_T_.py
index ba02910..11bb60b 100644
--- a/Lib/fontTools/ttLib/tables/C_B_D_T_.py
+++ b/Lib/fontTools/ttLib/tables/C_B_D_T_.py
@@ -3,8 +3,7 @@
 # Google Author(s): Matt Fontaine
 
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin
 from fontTools.misc import sstruct
 from . import E_B_D_T_
 from .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
diff --git a/Lib/fontTools/ttLib/tables/C_B_L_C_.py b/Lib/fontTools/ttLib/tables/C_B_L_C_.py
index 3d67dd0..2f78571 100644
--- a/Lib/fontTools/ttLib/tables/C_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/C_B_L_C_.py
@@ -2,8 +2,6 @@
 #
 # 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 126fd89..d12b89d 100644
--- a/Lib/fontTools/ttLib/tables/C_F_F_.py
+++ b/Lib/fontTools/ttLib/tables/C_F_F_.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from io import BytesIO
 from fontTools import cffLib
 from . import DefaultTable
 
@@ -38,8 +37,8 @@
 		# 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"):
diff --git a/Lib/fontTools/ttLib/tables/C_F_F__2.py b/Lib/fontTools/ttLib/tables/C_F_F__2.py
index 7e30c8f..6217ebb 100644
--- a/Lib/fontTools/ttLib/tables/C_F_F__2.py
+++ b/Lib/fontTools/ttLib/tables/C_F_F__2.py
@@ -1,6 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools import cffLib
+from io import BytesIO
 from fontTools.ttLib.tables.C_F_F_ import table_C_F_F_
 
 
diff --git a/Lib/fontTools/ttLib/tables/C_O_L_R_.py b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
index 743fa91..4004d41 100644
--- a/Lib/fontTools/ttLib/tables/C_O_L_R_.py
+++ b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
@@ -2,12 +2,8 @@
 #
 # Google Author(s): Behdad Esfahbod
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
-import operator
-import struct
 
 
 class table_C_O_L_R_(DefaultTable.DefaultTable):
@@ -17,127 +13,132 @@
 	ttFont['COLR'][<glyphName>] = <value> will set the color layers for any glyph.
 	"""
 
-	def decompile(self, data, ttFont):
-		self.getGlyphName = ttFont.getGlyphName # for use in get/set item functions, for access by GID
-		self.version, numBaseGlyphRecords, offsetBaseGlyphRecord, offsetLayerRecord, numLayerRecords = struct.unpack(">HHLLH", data[:14])
-		assert (self.version == 0), "Version of COLR table is higher than I know how to handle"
-		glyphOrder = ttFont.getGlyphOrder()
-		gids = []
-		layerLists = []
-		glyphPos = offsetBaseGlyphRecord
-		for i in range(numBaseGlyphRecords):
-			gid, firstLayerIndex, numLayers = struct.unpack(">HHH", data[glyphPos:glyphPos+6])
-			glyphPos += 6
-			gids.append(gid)
+	@staticmethod
+	def _decompileColorLayersV0(table):
+		if not table.LayerRecordArray:
+			return {}
+		colorLayerLists = {}
+		layerRecords = table.LayerRecordArray.LayerRecord
+		numLayerRecords = len(layerRecords)
+		for baseRec in table.BaseGlyphRecordArray.BaseGlyphRecord:
+			baseGlyph = baseRec.BaseGlyph
+			firstLayerIndex = baseRec.FirstLayerIndex
+			numLayers = baseRec.NumLayers
 			assert (firstLayerIndex + numLayers <= numLayerRecords)
-			layerPos = offsetLayerRecord + firstLayerIndex * 4
 			layers = []
-			for j in range(numLayers):
-				layerGid, colorID = struct.unpack(">HH", data[layerPos:layerPos+4])
-				try:
-					layerName = glyphOrder[layerGid]
-				except IndexError:
-					layerName = self.getGlyphName(layerGid)
-				layerPos += 4
-				layers.append(LayerRecord(layerName, colorID))
-			layerLists.append(layers)
+			for i in range(firstLayerIndex, firstLayerIndex+numLayers):
+				layerRec = layerRecords[i]
+				layers.append(
+					LayerRecord(layerRec.LayerGlyph, layerRec.PaletteIndex)
+				)
+			colorLayerLists[baseGlyph] = layers
+		return colorLayerLists
 
-		self.ColorLayers = colorLayerLists = {}
-		try:
-			names = list(map(operator.getitem, [glyphOrder]*numBaseGlyphRecords, gids))
-		except IndexError:
-			getGlyphName = self.getGlyphName
-			names = list(map(getGlyphName, gids ))
+	def _toOTTable(self, ttFont):
+		from . import otTables
+		from fontTools.colorLib.builder import populateCOLRv0
 
-		list(map(operator.setitem, [colorLayerLists]*numBaseGlyphRecords, names, layerLists))
+		tableClass = getattr(otTables, self.tableTag)
+		table = tableClass()
+		table.Version = self.version
+
+		populateCOLRv0(
+			table,
+			{
+				baseGlyph: [(layer.name, layer.colorID) for layer in layers]
+				for baseGlyph, layers in self.ColorLayers.items()
+			},
+			glyphMap=ttFont.getReverseGlyphMap(rebuild=True),
+		)
+		return table
+
+	def decompile(self, data, ttFont):
+		from .otBase import OTTableReader
+		from . import otTables
+
+		# We use otData to decompile, but we adapt the decompiled otTables to the
+		# existing COLR v0 API for backward compatibility.
+		reader = OTTableReader(data, tableTag=self.tableTag)
+		tableClass = getattr(otTables, self.tableTag)
+		table = tableClass()
+		table.decompile(reader, ttFont)
+
+		self.version = table.Version
+		if self.version == 0:
+			self.ColorLayers = self._decompileColorLayersV0(table)
+		else:
+			# for new versions, keep the raw otTables around
+			self.table = table
 
 	def compile(self, ttFont):
-		ordered = []
-		ttFont.getReverseGlyphMap(rebuild=True)
-		glyphNames = self.ColorLayers.keys()
-		for glyphName in glyphNames:
-			try:
-				gid = ttFont.getGlyphID(glyphName)
-			except:
-				assert 0, "COLR table contains a glyph name not in ttFont.getGlyphNames(): " + str(glyphName)
-			ordered.append([gid, glyphName, self.ColorLayers[glyphName]])
-		ordered.sort()
+		from .otBase import OTTableWriter
 
-		glyphMap = []
-		layerMap = []
-		for (gid, glyphName, layers) in ordered:
-			glyphMap.append(struct.pack(">HHH", gid, len(layerMap), len(layers)))
-			for layer in layers:
-				layerMap.append(struct.pack(">HH", ttFont.getGlyphID(layer.name), layer.colorID))
+		if hasattr(self, "table"):
+			table = self.table
+		else:
+			table = self._toOTTable(ttFont)
 
-		dataList = [struct.pack(">HHLLH", self.version, len(glyphMap), 14, 14+6*len(glyphMap), len(layerMap))]
-		dataList.extend(glyphMap)
-		dataList.extend(layerMap)
-		data = bytesjoin(dataList)
-		return data
+		writer = OTTableWriter(tableTag=self.tableTag)
+		table.compile(writer, ttFont)
+		return writer.getAllData()
 
 	def toXML(self, writer, ttFont):
-		writer.simpletag("version", value=self.version)
-		writer.newline()
-		ordered = []
-		glyphNames = self.ColorLayers.keys()
-		for glyphName in glyphNames:
-			try:
-				gid = ttFont.getGlyphID(glyphName)
-			except:
-				assert 0, "COLR table contains a glyph name not in ttFont.getGlyphNames(): " + str(glyphName)
-			ordered.append([gid, glyphName, self.ColorLayers[glyphName]])
-		ordered.sort()
-		for entry in ordered:
-			writer.begintag("ColorGlyph", name=entry[1])
+		if hasattr(self, "table"):
+			self.table.toXML2(writer, ttFont)
+		else:
+			writer.simpletag("version", value=self.version)
 			writer.newline()
-			for layer in entry[2]:
-				layer.toXML(writer, ttFont)
-			writer.endtag("ColorGlyph")
-			writer.newline()
+			for baseGlyph in sorted(self.ColorLayers.keys(), key=ttFont.getGlyphID):
+				writer.begintag("ColorGlyph", name=baseGlyph)
+				writer.newline()
+				for layer in self.ColorLayers[baseGlyph]:
+					layer.toXML(writer, ttFont)
+				writer.endtag("ColorGlyph")
+				writer.newline()
 
 	def fromXML(self, name, attrs, content, ttFont):
-		if not hasattr(self, "ColorLayers"):
-			self.ColorLayers = {}
-		self.getGlyphName = ttFont.getGlyphName # for use in get/set item functions, for access by GID
-		if name == "ColorGlyph":
+		if name == "version":  # old COLR v0 API
+			setattr(self, name, safeEval(attrs["value"]))
+		elif name == "ColorGlyph":
+			if not hasattr(self, "ColorLayers"):
+				self.ColorLayers = {}
 			glyphName = attrs["name"]
 			for element in content:
-				if isinstance(element, basestring):
+				if isinstance(element, str):
 					continue
 			layers = []
 			for element in content:
-				if isinstance(element, basestring):
+				if isinstance(element, str):
 					continue
 				layer = LayerRecord()
 				layer.fromXML(element[0], element[1], element[2], ttFont)
 				layers.append (layer)
-			operator.setitem(self, glyphName, layers)
-		elif "value" in attrs:
-			setattr(self, name, safeEval(attrs["value"]))
+			self.ColorLayers[glyphName] = layers
+		else:  # new COLR v1 API
+			from . import otTables
 
-	def __getitem__(self, glyphSelector):
-		if isinstance(glyphSelector, int):
-			# its a gid, convert to glyph name
-			glyphSelector = self.getGlyphName(glyphSelector)
+			if not hasattr(self, "table"):
+				tableClass = getattr(otTables, self.tableTag)
+				self.table = tableClass()
+			self.table.fromXML(name, attrs, content, ttFont)
+			self.table.populateDefaults()
+			self.version = self.table.Version
 
-		if glyphSelector not in self.ColorLayers:
-			return None
+	def __getitem__(self, glyphName):
+		if not isinstance(glyphName, str):
+			raise TypeError(f"expected str, found {type(glyphName).__name__}")
+		return self.ColorLayers[glyphName]
 
-		return self.ColorLayers[glyphSelector]
+	def __setitem__(self, glyphName, value):
+		if not isinstance(glyphName, str):
+			raise TypeError(f"expected str, found {type(glyphName).__name__}")
+		if value is not None:
+			self.ColorLayers[glyphName] = value
+		elif glyphName in self.ColorLayers:
+			del self.ColorLayers[glyphName]
 
-	def __setitem__(self, glyphSelector, value):
-		if isinstance(glyphSelector, int):
-			# its a gid, convert to glyph name
-			glyphSelector = self.getGlyphName(glyphSelector)
-
-		if  value:
-			self.ColorLayers[glyphSelector] = value
-		elif glyphSelector in self.ColorLayers:
-			del self.ColorLayers[glyphSelector]
-
-	def __delitem__(self, glyphSelector):
-		del self.ColorLayers[glyphSelector]
+	def __delitem__(self, glyphName):
+		del self.ColorLayers[glyphName]
 
 class LayerRecord(object):
 
@@ -152,8 +153,6 @@
 	def fromXML(self, eltname, attrs, content, ttFont):
 		for (name, value) in attrs.items():
 			if name == "name":
-				if isinstance(value, int):
-					value = ttFont.getGlyphName(value)
 				setattr(self, name, value)
 			else:
 				setattr(self, name, safeEval(value))
diff --git a/Lib/fontTools/ttLib/tables/C_P_A_L_.py b/Lib/fontTools/ttLib/tables/C_P_A_L_.py
index 25d50a5..c095095 100644
--- a/Lib/fontTools/ttLib/tables/C_P_A_L_.py
+++ b/Lib/fontTools/ttLib/tables/C_P_A_L_.py
@@ -2,17 +2,20 @@
 #
 # Google Author(s): Behdad Esfahbod
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
 import array
+from collections import namedtuple
 import struct
 import sys
 
 
 class table_C_P_A_L_(DefaultTable.DefaultTable):
 
+	NO_NAME_ID = 0xFFFF
+	DEFAULT_PALETTE_TYPE = 0
+
 	def __init__(self, tag=None):
 		DefaultTable.DefaultTable.__init__(self, tag)
 		self.palettes = []
@@ -45,28 +48,27 @@
 			offsetToPaletteEntryLabelArray) = (
 				struct.unpack(">LLL", data[pos:pos+12]))
 		self.paletteTypes = self._decompileUInt32Array(
-			data, offsetToPaletteTypeArray, numPalettes)
+			data, offsetToPaletteTypeArray, numPalettes,
+			default=self.DEFAULT_PALETTE_TYPE)
 		self.paletteLabels = self._decompileUInt16Array(
-			data, offsetToPaletteLabelArray, numPalettes)
+			data, offsetToPaletteLabelArray, numPalettes, default=self.NO_NAME_ID)
 		self.paletteEntryLabels = self._decompileUInt16Array(
 			data, offsetToPaletteEntryLabelArray,
-			self.numPaletteEntries)
+			self.numPaletteEntries, default=self.NO_NAME_ID)
 
-	def _decompileUInt16Array(self, data, offset, numElements):
+	def _decompileUInt16Array(self, data, offset, numElements, default=0):
 		if offset == 0:
-			return [0] * numElements
+			return [default] * numElements
 		result = array.array("H", data[offset : offset + 2 * numElements])
-		if sys.byteorder != "big":
-			result.byteswap()
+		if sys.byteorder != "big": result.byteswap()
 		assert len(result) == numElements, result
 		return result.tolist()
 
-	def _decompileUInt32Array(self, data, offset, numElements):
+	def _decompileUInt32Array(self, data, offset, numElements, default=0):
 		if offset == 0:
-			return [0] * numElements
+			return [default] * numElements
 		result = array.array("I", data[offset : offset + 4 * numElements])
-		if sys.byteorder != "big":
-			result.byteswap()
+		if sys.byteorder != "big": result.byteswap()
 		assert len(result) == numElements, result
 		return result.tolist()
 
@@ -138,7 +140,7 @@
 		return result
 
 	def _compilePaletteLabels(self):
-		if self.version == 0 or not any(self.paletteLabels):
+		if self.version == 0 or all(l == self.NO_NAME_ID for l in self.paletteLabels):
 			return b''
 		assert len(self.paletteLabels) == len(self.palettes)
 		result = bytesjoin([struct.pack(">H", label)
@@ -147,7 +149,7 @@
 		return result
 
 	def _compilePaletteEntryLabels(self):
-		if self.version == 0 or not any(self.paletteEntryLabels):
+		if self.version == 0 or all(l == self.NO_NAME_ID for l in self.paletteEntryLabels):
 			return b''
 		assert len(self.paletteEntryLabels) == self.numPaletteEntries
 		result = bytesjoin([struct.pack(">H", label)
@@ -167,15 +169,15 @@
 		writer.newline()
 		for index, palette in enumerate(self.palettes):
 			attrs = {"index": index}
-			paletteType = paletteTypes.get(index)
-			paletteLabel = paletteLabels.get(index)
-			if self.version > 0 and paletteLabel is not None:
+			paletteType = paletteTypes.get(index, self.DEFAULT_PALETTE_TYPE)
+			paletteLabel = paletteLabels.get(index, self.NO_NAME_ID)
+			if self.version > 0 and paletteLabel != self.NO_NAME_ID:
 				attrs["label"] = paletteLabel
-			if self.version > 0 and paletteType is not None:
+			if self.version > 0 and paletteType != self.DEFAULT_PALETTE_TYPE:
 				attrs["type"] = paletteType
 			writer.begintag("palette", **attrs)
 			writer.newline()
-			if (self.version > 0 and paletteLabel and
+			if (self.version > 0 and paletteLabel != self.NO_NAME_ID and
 			    ttFont and "name" in ttFont):
 				name = ttFont["name"].getDebugName(paletteLabel)
 				if name is not None:
@@ -186,11 +188,11 @@
 				color.toXML(writer, ttFont, cindex)
 			writer.endtag("palette")
 			writer.newline()
-		if self.version > 0 and any(self.paletteEntryLabels):
+		if self.version > 0 and not all(l == self.NO_NAME_ID for l in self.paletteEntryLabels):
 			writer.begintag("paletteEntryLabels")
 			writer.newline()
 			for index, label in enumerate(self.paletteEntryLabels):
-				if label:
+				if label != self.NO_NAME_ID:
 					writer.simpletag("label", index=index, value=label)
 					if (self.version > 0 and label and ttFont and "name" in ttFont):
 						name = ttFont["name"].getDebugName(label)
@@ -202,20 +204,20 @@
 
 	def fromXML(self, name, attrs, content, ttFont):
 		if name == "palette":
-			self.paletteLabels.append(int(attrs.get("label", "0")))
-			self.paletteTypes.append(int(attrs.get("type", "0")))
+			self.paletteLabels.append(int(attrs.get("label", self.NO_NAME_ID)))
+			self.paletteTypes.append(int(attrs.get("type", self.DEFAULT_PALETTE_TYPE)))
 			palette = []
 			for element in content:
-				if isinstance(element, basestring):
+				if isinstance(element, str):
 					continue
-				color = Color()
-				color.fromXML(element[0], element[1], element[2], ttFont)
+				attrs = element[1]
+				color = Color.fromHex(attrs["value"])
 				palette.append(color)
 			self.palettes.append(palette)
 		elif name == "paletteEntryLabels":
 			colorLabels = {}
 			for element in content:
-				if isinstance(element, basestring):
+				if isinstance(element, str):
 					continue
 				elementName, elementAttr, _ = element
 				if elementName == "label":
@@ -223,22 +225,16 @@
 					nameID = safeEval(elementAttr["value"])
 					colorLabels[labelIndex] = nameID
 			self.paletteEntryLabels = [
-				colorLabels.get(i, 0)
+				colorLabels.get(i, self.NO_NAME_ID)
 				for i in range(self.numPaletteEntries)]
 		elif "value" in attrs:
 			value = safeEval(attrs["value"])
 			setattr(self, name, value)
 			if name == "numPaletteEntries":
-				self.paletteEntryLabels = [0] * self.numPaletteEntries
+				self.paletteEntryLabels = [self.NO_NAME_ID] * self.numPaletteEntries
 
 
-class Color(object):
-
-	def __init__(self, blue=None, green=None, red=None, alpha=None):
-		self.blue = blue
-		self.green = green
-		self.red = red
-		self.alpha = alpha
+class Color(namedtuple("Color", "blue green red alpha")):
 
 	def hex(self):
 		return "#%02X%02X%02X%02X" % (self.red, self.green, self.blue, self.alpha)
@@ -250,11 +246,16 @@
 		writer.simpletag("color", value=self.hex(), index=index)
 		writer.newline()
 
-	def fromXML(self, eltname, attrs, content, ttFont):
-		value = attrs["value"]
+	@classmethod
+	def fromHex(cls, value):
 		if value[0] == '#':
 			value = value[1:]
-		self.red = int(value[0:2], 16)
-		self.green = int(value[2:4], 16)
-		self.blue = int(value[4:6], 16)
-		self.alpha = int(value[6:8], 16) if len (value) >= 8 else 0xFF
+		red = int(value[0:2], 16)
+		green = int(value[2:4], 16)
+		blue = int(value[4:6], 16)
+		alpha = int(value[6:8], 16) if len (value) >= 8 else 0xFF
+		return cls(red=red, green=green, blue=blue, alpha=alpha)
+
+	@classmethod
+	def fromRGBA(cls, red, green, blue, alpha):
+		return cls(red=red, green=green, blue=blue, alpha=alpha)
diff --git a/Lib/fontTools/ttLib/tables/D_S_I_G_.py b/Lib/fontTools/ttLib/tables/D_S_I_G_.py
index af802b9..1a520ca 100644
--- a/Lib/fontTools/ttLib/tables/D_S_I_G_.py
+++ b/Lib/fontTools/ttLib/tables/D_S_I_G_.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin, strjoin, tobytes, tostr
 from fontTools.misc.textTools import safeEval
 from fontTools.misc import sstruct
 from . import DefaultTable
diff --git a/Lib/fontTools/ttLib/tables/D__e_b_g.py b/Lib/fontTools/ttLib/tables/D__e_b_g.py
new file mode 100644
index 0000000..ff64a9b
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/D__e_b_g.py
@@ -0,0 +1,17 @@
+import json
+
+from . import DefaultTable
+
+
+class table_D__e_b_g(DefaultTable.DefaultTable):
+    def decompile(self, data, ttFont):
+        self.data = json.loads(data)
+
+    def compile(self, ttFont):
+        return json.dumps(self.data).encode("utf-8")
+
+    def toXML(self, writer, ttFont):
+        writer.writecdata(json.dumps(self.data))
+
+    def fromXML(self, name, attrs, content, ttFont):
+        self.data = json.loads(content)
diff --git a/Lib/fontTools/ttLib/tables/DefaultTable.py b/Lib/fontTools/ttLib/tables/DefaultTable.py
index 1a34da3..c70480a 100644
--- a/Lib/fontTools/ttLib/tables/DefaultTable.py
+++ b/Lib/fontTools/ttLib/tables/DefaultTable.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import Tag
 from fontTools.ttLib import getClassTag
 
 class DefaultTable(object):
@@ -17,7 +16,7 @@
 	def compile(self, ttFont):
 		return self.data
 
-	def toXML(self, writer, ttFont, progress=None):
+	def toXML(self, writer, ttFont, **kwargs):
 		if hasattr(self, "ERROR"):
 			writer.comment("An error occurred during the decompilation of this table")
 			writer.newline()
diff --git a/Lib/fontTools/ttLib/tables/E_B_D_T_.py b/Lib/fontTools/ttLib/tables/E_B_D_T_.py
index 3a316e5..5d9e724 100644
--- a/Lib/fontTools/ttLib/tables/E_B_D_T_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_D_T_.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytechr, byteord, bytesjoin, strjoin
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval, readHex, hexStr, deHexStr
 from .BitmapGlyphMetrics import BigGlyphMetrics, bigGlyphMetricsFormat, SmallGlyphMetrics, smallGlyphMetricsFormat
diff --git a/Lib/fontTools/ttLib/tables/E_B_L_C_.py b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
index 0c53e7d..94d40d9 100644
--- a/Lib/fontTools/ttLib/tables/E_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin
 from fontTools.misc import sstruct
 from . import DefaultTable
 from fontTools.misc.textTools import safeEval
@@ -115,7 +114,7 @@
 				indexSubTable.indexFormat = indexFormat
 				indexSubTable.imageFormat = imageFormat
 				indexSubTable.imageDataOffset = imageDataOffset
-				indexSubTable.decompile() # https://github.com/behdad/fonttools/issues/317
+				indexSubTable.decompile() # https://github.com/fonttools/fonttools/issues/317
 				curStrike.indexSubTables.append(indexSubTable)
 
 	def compile(self, ttFont):
@@ -155,7 +154,7 @@
 		# (2) Build each bitmapSizeTable.
 		# (3) Consolidate all the data into the main dataList in the correct order.
 
-		for curStrike in self.strikes:
+		for _ in self.strikes:
 			dataSize += sstruct.calcsize(bitmapSizeTableFormatPart1)
 			dataSize += len(('hori', 'vert')) * sstruct.calcsize(sbitLineMetricsFormat)
 			dataSize += sstruct.calcsize(bitmapSizeTableFormatPart2)
@@ -483,7 +482,7 @@
 			dataList = [EblcIndexSubTable.compile(self, ttFont)]
 			dataList += [struct.pack(dataFormat, offsetValue) for offsetValue in offsetArray]
 			# Take care of any padding issues. Only occurs in format 3.
-			if offsetDataSize * len(dataList) % 4 != 0:
+			if offsetDataSize * len(offsetArray) % 4 != 0:
 				dataList.append(struct.pack(dataFormat, 0))
 			return bytesjoin(dataList)
 
diff --git a/Lib/fontTools/ttLib/tables/F_F_T_M_.py b/Lib/fontTools/ttLib/tables/F_F_T_M_.py
index 3d110bd..2376f2d 100644
--- a/Lib/fontTools/ttLib/tables/F_F_T_M_.py
+++ b/Lib/fontTools/ttLib/tables/F_F_T_M_.py
@@ -1,5 +1,3 @@
-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 fontTools.misc.timeTools import timestampFromString, timestampToString
diff --git a/Lib/fontTools/ttLib/tables/F__e_a_t.py b/Lib/fontTools/ttLib/tables/F__e_a_t.py
index 22be4f6..7e51061 100644
--- a/Lib/fontTools/ttLib/tables/F__e_a_t.py
+++ b/Lib/fontTools/ttLib/tables/F__e_a_t.py
@@ -1,8 +1,6 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import floatToFixedToStr
 from fontTools.misc.textTools import safeEval
-from .otBase import BaseTTXConverter
 from . import DefaultTable
 from . import grUtils
 import struct
@@ -20,6 +18,7 @@
 
     def decompile(self, data, ttFont):
         (_, data) = sstruct.unpack2(Feat_hdr_format, data, self)
+        self.version = float(floatToFixedToStr(self.version, precisionBits=16))
         numFeats, = struct.unpack('>H', data[:2])
         data = data[8:]
         allfeats = []
@@ -58,8 +57,8 @@
                     fobj.default = vid
 
     def compile(self, ttFont):
-        fdat = ""
-        vdat = ""
+        fdat = b""
+        vdat = b""
         offset = 0
         for f, v in sorted(self.features.items(), key=lambda x:x[1].index):
             fnum = grUtils.tag2num(f)
diff --git a/Lib/fontTools/ttLib/tables/G_D_E_F_.py b/Lib/fontTools/ttLib/tables/G_D_E_F_.py
index 08faf62..d4a5741 100644
--- a/Lib/fontTools/ttLib/tables/G_D_E_F_.py
+++ b/Lib/fontTools/ttLib/tables/G_D_E_F_.py
@@ -1,5 +1,3 @@
-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 afa8c4d..5b30dcf 100644
--- a/Lib/fontTools/ttLib/tables/G_M_A_P_.py
+++ b/Lib/fontTools/ttLib/tables/G_M_A_P_.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import tobytes, tostr
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
@@ -116,7 +115,7 @@
 			gmapRecord = GMAPRecord()
 			self.gmapRecords.append(gmapRecord)
 			for element in content:
-				if isinstance(element, basestring):
+				if isinstance(element, str):
 					continue
 				name, attrs, content = element
 				gmapRecord.fromXML(name, attrs, content, ttFont)
diff --git a/Lib/fontTools/ttLib/tables/G_P_K_G_.py b/Lib/fontTools/ttLib/tables/G_P_K_G_.py
index 4e13830..7598a62 100644
--- a/Lib/fontTools/ttLib/tables/G_P_K_G_.py
+++ b/Lib/fontTools/ttLib/tables/G_P_K_G_.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval, readHex
 from . import DefaultTable
@@ -24,9 +23,8 @@
 
 		GMAPoffsets = array.array("I")
 		endPos = (self.numGMAPs+1) * 4
-		GMAPoffsets.fromstring(newData[:endPos])
-		if sys.byteorder != "big":
-			GMAPoffsets.byteswap()
+		GMAPoffsets.frombytes(newData[:endPos])
+		if sys.byteorder != "big": GMAPoffsets.byteswap()
 		self.GMAPs = []
 		for i in range(self.numGMAPs):
 			start = GMAPoffsets[i]
@@ -35,9 +33,8 @@
 		pos = endPos
 		endPos = pos + (self.numGlyplets + 1)*4
 		glyphletOffsets = array.array("I")
-		glyphletOffsets.fromstring(newData[pos:endPos])
-		if sys.byteorder != "big":
-			glyphletOffsets.byteswap()
+		glyphletOffsets.frombytes(newData[pos:endPos])
+		if sys.byteorder != "big": glyphletOffsets.byteswap()
 		self.glyphlets = []
 		for i in range(self.numGlyplets):
 			start = glyphletOffsets[i]
@@ -58,18 +55,16 @@
 			pos += len(self.GMAPs[i-1])
 			GMAPoffsets[i] = pos
 		gmapArray = array.array("I", GMAPoffsets)
-		if sys.byteorder != "big":
-			gmapArray.byteswap()
-		dataList.append(gmapArray.tostring())
+		if sys.byteorder != "big": gmapArray.byteswap()
+		dataList.append(gmapArray.tobytes())
 
 		glyphletOffsets[0] = pos
 		for i in range(1, self.numGlyplets +1):
 			pos += len(self.glyphlets[i-1])
 			glyphletOffsets[i] = pos
 		glyphletArray = array.array("I", glyphletOffsets)
-		if sys.byteorder != "big":
-			glyphletArray.byteswap()
-		dataList.append(glyphletArray.tostring())
+		if sys.byteorder != "big": glyphletArray.byteswap()
+		dataList.append(glyphletArray.tobytes())
 		dataList += self.GMAPs
 		dataList += self.glyphlets
 		data = bytesjoin(dataList)
@@ -111,7 +106,7 @@
 			if not hasattr(self, "GMAPs"):
 				self.GMAPs = []
 			for element in content:
-				if isinstance(element, basestring):
+				if isinstance(element, str):
 					continue
 				itemName, itemAttrs, itemContent = element
 				if itemName == "hexdata":
@@ -120,7 +115,7 @@
 			if not hasattr(self, "glyphlets"):
 				self.glyphlets = []
 			for element in content:
-				if isinstance(element, basestring):
+				if isinstance(element, str):
 					continue
 				itemName, itemAttrs, itemContent = element
 				if itemName == "hexdata":
diff --git a/Lib/fontTools/ttLib/tables/G_P_O_S_.py b/Lib/fontTools/ttLib/tables/G_P_O_S_.py
index 1c36061..013c820 100644
--- a/Lib/fontTools/ttLib/tables/G_P_O_S_.py
+++ b/Lib/fontTools/ttLib/tables/G_P_O_S_.py
@@ -1,5 +1,3 @@
-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 d23e8ba..4403649 100644
--- a/Lib/fontTools/ttLib/tables/G_S_U_B_.py
+++ b/Lib/fontTools/ttLib/tables/G_S_U_B_.py
@@ -1,5 +1,3 @@
-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
index 36ed6df..a4e8e38 100644
--- a/Lib/fontTools/ttLib/tables/G__l_a_t.py
+++ b/Lib/fontTools/ttLib/tables/G__l_a_t.py
@@ -1,16 +1,11 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import floatToFixedToStr
 from fontTools.misc.textTools import safeEval
-from itertools import *
+# from itertools import *
 from functools import partial
 from . import DefaultTable
 from . import grUtils
-import struct, operator, warnings
-try:
-    import lz4
-except: 
-    lz4 = None
+import struct
 
 
 Glat_format_0 = """
@@ -73,6 +68,7 @@
 
     def decompile(self, data, ttFont):
         sstruct.unpack2(Glat_format_0, data, self)
+        self.version = float(floatToFixedToStr(self.version, precisionBits=16))
         if self.version <= 1.9:
             decoder = partial(self.decompileAttributes12,fmt=Glat_format_1_entry)
         elif self.version <= 2.9:   
@@ -139,11 +135,11 @@
         return data
 
     def compileAttributes12(self, attrs, fmt):
-        data = []
+        data = b""
         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)
+            data += sstruct.pack(fmt, {'attNum' : e[0], 'num' : e[1]}) + \
+                    struct.pack(('>%dh' % len(e[2])), *e[2])
+        return data
     
     def compileAttributes3(self, attrs):
         if self.hasOctaboxes:
@@ -168,7 +164,7 @@
                 vals = {}
                 for k in names:
                     if k == 'subboxBitmap': continue
-                    vals[k] = "{:.3f}%".format(getattr(o, k) * 100. / 256)
+                    vals[k] = "{:.3f}%".format(getattr(o, k) * 100. / 255)
                 vals['bitmap'] = "{:0X}".format(o.subboxBitmap)
                 writer.begintag('octaboxes', **vals)
                 writer.newline()
@@ -176,7 +172,7 @@
                 for s in o.subboxes:
                     vals = {}
                     for k in names:
-                        vals[k] = "{:.3f}%".format(getattr(s, k) * 100. / 256)
+                        vals[k] = "{:.3f}%".format(getattr(s, k) * 100. / 255)
                     writer.simpletag('octabox', **vals)
                     writer.newline()
                 writer.endtag('octaboxes')
@@ -190,6 +186,7 @@
     def fromXML(self, name, attrs, content, ttFont):
         if name == 'version' :
             self.version = float(safeEval(attrs['version']))
+            self.scheme = int(safeEval(attrs['compressionScheme']))
         if name != 'glyph' : return
         if not hasattr(self, 'attributes'):
             self.attributes = {}
@@ -209,13 +206,13 @@
                 o.subboxes = []
                 del attrs['bitmap']
                 for k, v in attrs.items():
-                    setattr(o, k, int(float(v[:-1]) * 256. / 100. + 0.5))
+                    setattr(o, k, int(float(v[:-1]) * 255. / 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))
+                        setattr(so, k, int(float(v[:-1]) * 255. / 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
index d77c483..fa114a3 100644
--- a/Lib/fontTools/ttLib/tables/G__l_o_c.py
+++ b/Lib/fontTools/ttLib/tables/G__l_o_c.py
@@ -1,9 +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 . import DefaultTable
 import array
+import sys
+
 
 Gloc_header = '''
     >        # big endian
@@ -29,24 +29,24 @@
         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.locations.frombytes(data[:len(data) - self.numAttribs * (flags & 2)])
+        if sys.byteorder != "big": self.locations.byteswap()
         self.attribIds = array.array('H')
         if flags & 2:
-            self.attribIds.fromstring(data[-self.numAttribs * 2:])
-            self.attribIds.byteswap()
+            self.attribIds.frombytes(data[-self.numAttribs * 2:])
+            if sys.byteorder != "big": 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 sys.byteorder != "big": self.locations.byteswap()
+        data += self.locations.tobytes()
+        if sys.byteorder != "big": self.locations.byteswap()
         if self.attribIds:
-            self.attribIds.byteswap()
-            data += self.attribIds.tostring()
-            self.attribIds.byteswap()
+            if sys.byteorder != "big": self.attribIds.byteswap()
+            data += self.attribIds.tobytes()
+            if sys.byteorder != "big": self.attribIds.byteswap()
         return data
 
     def set(self, locations):
diff --git a/Lib/fontTools/ttLib/tables/H_V_A_R_.py b/Lib/fontTools/ttLib/tables/H_V_A_R_.py
index efab4e7..56992ad 100644
--- a/Lib/fontTools/ttLib/tables/H_V_A_R_.py
+++ b/Lib/fontTools/ttLib/tables/H_V_A_R_.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/J_S_T_F_.py b/Lib/fontTools/ttLib/tables/J_S_T_F_.py
index dffd08b..ddf5405 100644
--- a/Lib/fontTools/ttLib/tables/J_S_T_F_.py
+++ b/Lib/fontTools/ttLib/tables/J_S_T_F_.py
@@ -1,5 +1,3 @@
-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 dd0f195..94c2c22 100644
--- a/Lib/fontTools/ttLib/tables/L_T_S_H_.py
+++ b/Lib/fontTools/ttLib/tables/L_T_S_H_.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
 import struct
@@ -19,7 +17,7 @@
 		# ouch: the assertion is not true in Chicago!
 		#assert numGlyphs == ttFont['maxp'].numGlyphs
 		yPels = array.array("B")
-		yPels.fromstring(data)
+		yPels.frombytes(data)
 		self.yPels = {}
 		for i in range(numGlyphs):
 			self.yPels[ttFont.getGlyphName(i)] = yPels[i]
@@ -34,7 +32,7 @@
 		for name in names:
 			yPels[ttFont.getGlyphID(name)] = self.yPels[name]
 		yPels = array.array("B", yPels)
-		return struct.pack(">HH", version, numGlyphs) + yPels.tostring()
+		return struct.pack(">HH", version, numGlyphs) + yPels.tobytes()
 
 	def toXML(self, writer, ttFont):
 		names = sorted(self.yPels.keys())
diff --git a/Lib/fontTools/ttLib/tables/M_A_T_H_.py b/Lib/fontTools/ttLib/tables/M_A_T_H_.py
index 8c329ba..d894c08 100644
--- a/Lib/fontTools/ttLib/tables/M_A_T_H_.py
+++ b/Lib/fontTools/ttLib/tables/M_A_T_H_.py
@@ -1,5 +1,3 @@
-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 3eb6550..d4f6bc8 100644
--- a/Lib/fontTools/ttLib/tables/M_E_T_A_.py
+++ b/Lib/fontTools/ttLib/tables/M_E_T_A_.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import byteord
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
@@ -174,7 +173,7 @@
 			glyphRec = GlyphRecord()
 			self.glyphRecords.append(glyphRec)
 			for element in content:
-				if isinstance(element, basestring):
+				if isinstance(element, str):
 					continue
 				name, attrs, content = element
 				glyphRec.fromXML(name, attrs, content, ttFont)
@@ -208,7 +207,7 @@
 			stringRec = StringRecord()
 			self.stringRecs.append(stringRec)
 			for element in content:
-				if isinstance(element, basestring):
+				if isinstance(element, str):
 					continue
 				stringRec.fromXML(name, attrs, content, ttFont)
 			stringRec.stringLen = len(stringRec.string)
@@ -230,7 +229,7 @@
 # XXX The following two functions are really broken around UTF-8 vs Unicode
 
 def mapXMLToUTF8(string):
-	uString = unicode()
+	uString = str()
 	strLen = len(string)
 	i = 0
 	while i < strLen:
@@ -246,9 +245,9 @@
 				i = i+1
 			valStr = string[j:i]
 
-			uString = uString + unichr(eval('0x' + valStr))
+			uString = uString + chr(eval('0x' + valStr))
 		else:
-			uString = uString + unichr(byteord(string[i]))
+			uString = uString + chr(byteord(string[i]))
 		i = i +1
 
 	return uString.encode('utf_8')
@@ -282,7 +281,7 @@
 
 	def fromXML(self, name, attrs, content, ttFont):
 		for element in content:
-			if isinstance(element, basestring):
+			if isinstance(element, str):
 				continue
 			name, attrs, content = element
 			value = attrs["value"]
diff --git a/Lib/fontTools/ttLib/tables/M_V_A_R_.py b/Lib/fontTools/ttLib/tables/M_V_A_R_.py
index 8659ae8..34ab20f 100644
--- a/Lib/fontTools/ttLib/tables/M_V_A_R_.py
+++ b/Lib/fontTools/ttLib/tables/M_V_A_R_.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/O_S_2f_2.py b/Lib/fontTools/ttLib/tables/O_S_2f_2.py
index 3e2c30c..a576522 100644
--- a/Lib/fontTools/ttLib/tables/O_S_2f_2.py
+++ b/Lib/fontTools/ttLib/tables/O_S_2f_2.py
@@ -1,8 +1,7 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval, num2binary, binary2num
 from fontTools.ttLib.tables import DefaultTable
+import bisect
 import logging
 
 
@@ -43,7 +42,7 @@
 	xAvgCharWidth:          h       # average character width
 	usWeightClass:          H       # degree of thickness of strokes
 	usWidthClass:           H       # aspect ratio
-	fsType:                 h       # type flags
+	fsType:                 H       # type flags
 	ySubscriptXSize:        h       # subscript horizontal font size
 	ySubscriptYSize:        h       # subscript vertical font size
 	ySubscriptXOffset:      h       # subscript x offset
@@ -476,22 +475,19 @@
 )
 
 
-_unicodeRangeSets = []
+_unicodeStarts = []
+_unicodeValues = [None]
 
-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 _getUnicodeRanges():
+	# build the ranges of codepoints for each unicode range bit, and cache result
+	if not _unicodeStarts:
+		unicodeRanges = [
+			(start, (stop, bit)) for bit, blocks in enumerate(OS2_UNICODE_RANGES)
+			for _, (start, stop) in blocks]
+		for start, (stop, bit) in sorted(unicodeRanges):
+			_unicodeStarts.append(start)
+			_unicodeValues.append((stop, bit))
+	return _unicodeStarts, _unicodeValues
 
 
 def intersectUnicodeRanges(unicodes, inverse=False):
@@ -505,15 +501,22 @@
 	>>> intersectUnicodeRanges([0x0410, 0x1F000]) == {9, 57, 122}
 	True
 	>>> intersectUnicodeRanges([0x0410, 0x1F000], inverse=True) == (
-	...     set(range(123)) - {9, 57, 122})
+	...     set(range(len(OS2_UNICODE_RANGES))) - {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
+	unicodestarts, unicodevalues = _getUnicodeRanges()
+	bits = set()
+	for code in unicodes:
+		stop, bit = unicodevalues[bisect.bisect(unicodestarts, code)]
+		if code <= stop:
+			bits.add(bit)
+	# 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
+	if any(0x10000 <= code < 0x110000 for code in unicodes):
+		bits.add(57)
+	return set(range(len(OS2_UNICODE_RANGES))) - bits if inverse else bits
 
 
 if __name__ == "__main__":
diff --git a/Lib/fontTools/ttLib/tables/S_I_N_G_.py b/Lib/fontTools/ttLib/tables/S_I_N_G_.py
index 413ae48..dd9b63c 100644
--- a/Lib/fontTools/ttLib/tables/S_I_N_G_.py
+++ b/Lib/fontTools/ttLib/tables/S_I_N_G_.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytechr, byteord, tobytes, tostr
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
diff --git a/Lib/fontTools/ttLib/tables/S_T_A_T_.py b/Lib/fontTools/ttLib/tables/S_T_A_T_.py
index 1e044cf..1769de9 100644
--- a/Lib/fontTools/ttLib/tables/S_T_A_T_.py
+++ b/Lib/fontTools/ttLib/tables/S_T_A_T_.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/S_V_G_.py b/Lib/fontTools/ttLib/tables/S_V_G_.py
index 3827c9e..135f271 100644
--- a/Lib/fontTools/ttLib/tables/S_V_G_.py
+++ b/Lib/fontTools/ttLib/tables/S_V_G_.py
@@ -1,13 +1,12 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin, strjoin, tobytes, tostr
 from fontTools.misc import sstruct
 from . import DefaultTable
 try:
 	import xml.etree.cElementTree as ET
 except ImportError:
 	import xml.etree.ElementTree as ET
+from io import BytesIO
 import struct
-import re
 import logging
 
 
@@ -285,7 +284,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:
@@ -343,8 +342,7 @@
 
 	def fromXML(self, name, attrs, content, ttFont):
 		for element in content:
-			element = element.strip()
-			if not element:
+			if not isinstance(element, tuple):
 				continue
 			name, attrib, content = element
 			if name == "colorParamUINameID":
@@ -353,7 +351,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)
diff --git a/Lib/fontTools/ttLib/tables/S__i_l_f.py b/Lib/fontTools/ttLib/tables/S__i_l_f.py
index 2afd71e..95880b0 100644
--- a/Lib/fontTools/ttLib/tables/S__i_l_f.py
+++ b/Lib/fontTools/ttLib/tables/S__i_l_f.py
@@ -1,12 +1,13 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import byteord
 from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import floatToFixedToStr
 from fontTools.misc.textTools import safeEval
-from itertools import *
+# from itertools import *
 from . import DefaultTable
 from . import grUtils
 from array import array
-import struct, operator, warnings, re, sys
+from functools import reduce
+import struct, re, sys
 
 Silf_hdr_format = '''
     >
@@ -81,6 +82,12 @@
     nPseudo:            H
 '''
 
+Silf_pseudomap_format_h = '''
+    >
+    unicode:            H
+    nPseudo:            H
+'''
+
 Silf_classmap_format = '''
     >
     numClass:           H
@@ -218,25 +225,25 @@
         pc += struct.calcsize(fmt)
     return res
 
-instre = re.compile("^\s*([^(]+)\s*(?:\(([^)]+)\))?")
+instre = re.compile(r"^\s*([^(]+)\s*(?:\(([^)]+)\))?")
 def assemble(instrs):
-    res = []
+    res = b""
     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))
+        res += struct.pack("B", opcode)
         if m.group(2):
             if parmfmt == 0:
                 continue
-            parms = [int(x) for x in re.split(",\s*", m.group(2))]
+            parms = [int(x) for x in re.split(r",\s*", m.group(2))]
             if parmfmt == -1:
                 l = len(parms)
-                res.append(struct.pack(("%dB" % (l+1)), l, *parms))
+                res += struct.pack(("%dB" % (l+1)), l, *parms)
             else:
-                res.append(struct.pack(parmfmt, *parms))
-    return b"".join(res)
+                res += struct.pack(parmfmt, *parms)
+    return res
 
 def writecode(tag, writer, instrs):
     writer.begintag(tag)
@@ -306,6 +313,7 @@
 
     def decompile(self, data, ttFont):
         sstruct.unpack2(Silf_hdr_format, data, self)
+        self.version = float(floatToFixedToStr(self.version, precisionBits=16))
         if self.version >= 5.0:
             (data, self.scheme) = grUtils.decompress(data)
             sstruct.unpack2(Silf_hdr_format_3, data, self)
@@ -334,7 +342,7 @@
         else:
             hdr = sstruct.pack(Silf_hdr_format_3, self)
         offset = len(hdr) + 4 * self.numSilf
-        data = ""
+        data = b""
         for s in self.silfs:
             hdr += struct.pack(">L", offset)
             subdata = s.compile(ttFont, self.version)
@@ -384,6 +392,7 @@
     def decompile(self, data, ttFont, version=2.0):
         if version >= 3.0 :
             _, data = sstruct.unpack2(Silf_part1_format_v3, data, self)
+            self.ruleVersion = float(floatToFixedToStr(self.ruleVersion, precisionBits=16))
         _, data = sstruct.unpack2(Silf_part1_format, data, self)
         for jlevel in range(self.numJLevels):
             j, data = sstruct.unpack2(Silf_justify_format, data, _Object())
@@ -394,7 +403,7 @@
         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)]
+            self.scriptTags = [struct.unpack("4s", data[x:x+4])[0].decode("ascii") for x in range(1, 1 + 4 * numScriptTag, 4)]
         data = data[1 + 4 * numScriptTag:]
         (self.lbGID,) = struct.unpack('>H', data[:2])
         if self.numPasses:
@@ -405,7 +414,7 @@
             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())
+                pseudo = sstruct.unpack(Silf_pseudomap_format_h, 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)
@@ -427,7 +436,7 @@
         self.numJLevels = len(self.jLevels)
         self.numCritFeatures = len(self.critFeatures)
         numPseudo = len(self.pMap)
-        data = ""
+        data = b""
         if version >= 3.0:
             hdroffset = sstruct.calcsize(Silf_part1_format_v3)
         else:
@@ -440,8 +449,8 @@
             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)
+            tdata = [struct.pack("4s", x.encode("ascii")) for x in self.scriptTags]
+            data += b"".join(tdata)
         data += struct.pack(">H", self.lbGID)
         self.passOffset = len(data)
 
@@ -453,8 +462,8 @@
                                 u, ttFont.getGlyphID(p))
         data1 += self.classes.compile(ttFont, version)
         currpos += len(data1)
-        data2 = ""
-        datao = ""
+        data2 = b""
+        datao = b""
         for i, p in enumerate(self.passes):
             base = currpos + len(data2)
             datao += struct.pack(">L", base)
@@ -464,7 +473,7 @@
         if version >= 3.0:
             data3 = sstruct.pack(Silf_part1_format_v3, self)
         else:
-            data3 = ""
+            data3 = b""
         return data3 + data + datao + data1 + data2
 
 
@@ -592,8 +601,8 @@
             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])))
+            self.linear.append(ttFont.getGlyphName(x) for x in
+                                   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)]
@@ -601,7 +610,7 @@
             self.nonLinear.append(nonLin)
 
     def compile(self, ttFont, version=2.0):
-        data = ""
+        data = b""
         oClasses = []
         if version >= 4.0:
             offset = 8 + 4 * (len(self.linear) + len(self.nonLinear))
@@ -609,13 +618,13 @@
             offset = 6 + 2 * (len(self.linear) + len(self.nonLinear))
         for l in self.linear:
             oClasses.append(len(data) + offset)
-            gs = map(ttFont.getGlyphID, l)
+            gs = [ttFont.getGlyphID(x) for x in 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)])
+            data += b"".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)
@@ -680,7 +689,7 @@
         self.rulePreContexts = []
         self.ruleSortKeys = []
         self.ruleConstraints = []
-        self.passConstraints = ""
+        self.passConstraints = b""
         self.actions = []
         self.stateTrans = []
         self.startStates = []
@@ -717,7 +726,7 @@
         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()
+            if sys.byteorder != "big": a.byteswap()
             self.stateTrans.append(a)
         data = data[self.numTransitional * self.numColumns * 2 + 1:]
         self.passConstraints = data[:pConstraint]
@@ -725,7 +734,7 @@
         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:])]
+        self.ruleConstraints = [(data[s:e] if (e-s > 1) else b"") 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]:]
@@ -733,14 +742,14 @@
 
     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)
+        oActions = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.actions + [b""], (0, []))[1]
+        oConstraints = reduce(lambda a, x: (a[0]+len(x), a[1]+[a[0]]), self.ruleConstraints + [b""], (1, []))[1]
+        constraintCode = b"\000" + b"".join(self.ruleConstraints)
         transes = []
         for t in self.stateTrans:
-            t.byteswap()
-            transes.append(t.tostring())
-            t.byteswap()
+            if sys.byteorder != "big": t.byteswap()
+            transes.append(t.tobytes())
+            if sys.byteorder != "big": 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]
@@ -761,7 +770,7 @@
         # 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 += b"".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)
@@ -772,8 +781,8 @@
         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)
+        return data + b"".join(transes) + struct.pack("B", 0) + \
+                self.passConstraints + constraintCode + b"".join(self.actions)
 
     def toXML(self, writer, ttFont, version=2.0):
         writesimple('info', self, writer, *pass_attrs_info)
@@ -839,7 +848,7 @@
                 if not isinstance(e, tuple): continue
                 tag, a, c = e
                 if tag == 'state':
-                    self.rules.append(map(int, a['rules'].split(" ")))
+                    self.rules.append([int(x) for x in a['rules'].split(" ")])
         elif name == 'rules':
             for element in content:
                 if not isinstance(element, tuple): continue
@@ -847,8 +856,8 @@
                 if tag != 'rule': continue
                 self.rulePreContexts.append(int(a['precontext']))
                 self.ruleSortKeys.append(int(a['sortkey']))
-                con = ""
-                act = ""
+                con = b""
+                act = b""
                 for e in c:
                     if not isinstance(e, tuple): continue
                     tag, a, subc = e
diff --git a/Lib/fontTools/ttLib/tables/S__i_l_l.py b/Lib/fontTools/ttLib/tables/S__i_l_l.py
index 7acef1d..5ab9ee3 100644
--- a/Lib/fontTools/ttLib/tables/S__i_l_l.py
+++ b/Lib/fontTools/ttLib/tables/S__i_l_l.py
@@ -1,6 +1,5 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import floatToFixedToStr
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
 from . import grUtils
@@ -19,6 +18,7 @@
 
     def decompile(self, data, ttFont):
         (_, data) = sstruct.unpack2(Sill_hdr, data, self)
+        self.version = float(floatToFixedToStr(self.version, precisionBits=16))
         numLangs, = struct.unpack('>H', data[:2])
         data = data[8:]
         maxsetting = 0
@@ -28,7 +28,7 @@
                                                         data[i * 8:(i+1) * 8])
             offset = int(offset / 8) - (numLangs + 1)
             langcode = langcode.replace(b'\000', b'')
-            langinfo.append((langcode, numsettings, offset))
+            langinfo.append((langcode.decode("utf-8"), numsettings, offset))
             maxsetting = max(maxsetting, offset + numsettings)
         data = data[numLangs * 8:]
         finfo = []
@@ -42,14 +42,15 @@
                 self.langs[c].append(finfo[i])
 
     def compile(self, ttFont):
-        ldat = ""
-        fdat = ""
-        offset = 0
+        ldat = b""
+        fdat = b""
+        offset = len(self.langs)
         for c, inf in sorted(self.langs.items()):
-            ldat += struct.pack(">4sHH", c.encode('utf8'), len(inf), 8 * (offset + len(self.langs) + 1))
+            ldat += struct.pack(">4sHH", c.encode('utf8'), len(inf), 8 * offset + 20)
             for fid, val in inf:
                 fdat += struct.pack(">LHH", fid, val, 0)
             offset += len(inf)
+        ldat += struct.pack(">LHH", 0x80808080, 0, 8 * offset + 20)
         return sstruct.pack(Sill_hdr, self) + grUtils.bininfo(len(self.langs)) + \
                 ldat + fdat
 
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_B_.py b/Lib/fontTools/ttLib/tables/T_S_I_B_.py
index 7d47e3a..25d4310 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_B_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_B_.py
@@ -1,5 +1,3 @@
-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_(table_T_S_I_V_):
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_C_.py b/Lib/fontTools/ttLib/tables/T_S_I_C_.py
new file mode 100644
index 0000000..573b3f9
--- /dev/null
+++ b/Lib/fontTools/ttLib/tables/T_S_I_C_.py
@@ -0,0 +1,5 @@
+from .otBase import BaseTTXConverter
+
+
+class table_T_S_I_C_(BaseTTXConverter):
+    pass
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_D_.py b/Lib/fontTools/ttLib/tables/T_S_I_D_.py
index f989c9e..310eb17 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_D_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_D_.py
@@ -1,5 +1,3 @@
-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_(table_T_S_I_V_):
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_J_.py b/Lib/fontTools/ttLib/tables/T_S_I_J_.py
index ca538ba..c1a46ba 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_J_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_J_.py
@@ -1,5 +1,3 @@
-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_(table_T_S_I_V_):
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_P_.py b/Lib/fontTools/ttLib/tables/T_S_I_P_.py
index 062d332..778974c 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_P_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_P_.py
@@ -1,5 +1,3 @@
-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_(table_T_S_I_V_):
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_S_.py b/Lib/fontTools/ttLib/tables/T_S_I_S_.py
index 68e22ce..61c9f76 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_S_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_S_.py
@@ -1,5 +1,3 @@
-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_(table_T_S_I_V_):
diff --git a/Lib/fontTools/ttLib/tables/T_S_I_V_.py b/Lib/fontTools/ttLib/tables/T_S_I_V_.py
index 46b2182..8021445 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I_V_.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I_V_.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import strjoin, tobytes, tostr
 from . import asciiTable
 
 class table_T_S_I_V_(asciiTable.asciiTable):
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__0.py b/Lib/fontTools/ttLib/tables/T_S_I__0.py
index 81808ee..b187f42 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__0.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__0.py
@@ -5,8 +5,6 @@
 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
 
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__1.py b/Lib/fontTools/ttLib/tables/T_S_I__1.py
index 5ef944a..9ae7acd 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__1.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__1.py
@@ -4,8 +4,7 @@
 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 fontTools.misc.py23 import strjoin, tobytes, tostr
 from . import DefaultTable
 from fontTools.misc.loggingTools import LogMixin
 
@@ -69,7 +68,7 @@
 						"%r textLength (%d) must not be > 32768" % (name, textLength))
 				text = data[textOffset:textOffset+textLength]
 				assert len(text) == textLength
-				text = tounicode(text, encoding='utf-8')
+				text = tostr(text, encoding='utf-8')
 				if text:
 					programs[name] = text
 			if isExtra:
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__2.py b/Lib/fontTools/ttLib/tables/T_S_I__2.py
index 7d41ee8..036c981 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__2.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__2.py
@@ -5,8 +5,6 @@
 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")
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__3.py b/Lib/fontTools/ttLib/tables/T_S_I__3.py
index ee95d06..a249014 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__3.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__3.py
@@ -3,8 +3,6 @@
 
 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")
diff --git a/Lib/fontTools/ttLib/tables/T_S_I__5.py b/Lib/fontTools/ttLib/tables/T_S_I__5.py
index dbf9e5a..7be09f9 100644
--- a/Lib/fontTools/ttLib/tables/T_S_I__5.py
+++ b/Lib/fontTools/ttLib/tables/T_S_I__5.py
@@ -3,8 +3,6 @@
 
 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
 from . import DefaultTable
 import sys
@@ -17,9 +15,8 @@
 		numGlyphs = ttFont['maxp'].numGlyphs
 		assert len(data) == 2 * numGlyphs
 		a = array.array("H")
-		a.fromstring(data)
-		if sys.byteorder != "big":
-			a.byteswap()
+		a.frombytes(data)
+		if sys.byteorder != "big": a.byteswap()
 		self.glyphGrouping = {}
 		for i in range(numGlyphs):
 			self.glyphGrouping[ttFont.getGlyphName(i)] = a[i]
@@ -29,9 +26,8 @@
 		a = array.array("H")
 		for i in range(len(glyphNames)):
 			a.append(self.glyphGrouping.get(glyphNames[i], 0))
-		if sys.byteorder != "big":
-			a.byteswap()
-		return a.tostring()
+		if sys.byteorder != "big": a.byteswap()
+		return a.tobytes()
 
 	def toXML(self, writer, ttFont):
 		names = sorted(self.glyphGrouping.keys())
diff --git a/Lib/fontTools/ttLib/tables/T_T_F_A_.py b/Lib/fontTools/ttLib/tables/T_T_F_A_.py
index 8ff8d1b..8446dfc 100644
--- a/Lib/fontTools/ttLib/tables/T_T_F_A_.py
+++ b/Lib/fontTools/ttLib/tables/T_T_F_A_.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from . import asciiTable
 
 class table_T_T_F_A_(asciiTable.asciiTable):
diff --git a/Lib/fontTools/ttLib/tables/TupleVariation.py b/Lib/fontTools/ttLib/tables/TupleVariation.py
index b94cfd4..9c2895e 100644
--- a/Lib/fontTools/ttLib/tables/TupleVariation.py
+++ b/Lib/fontTools/ttLib/tables/TupleVariation.py
@@ -1,8 +1,13 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.misc.fixedTools import fixedToFloat, floatToFixed
+from fontTools.misc.fixedTools import (
+    fixedToFloat as fi2fl,
+    floatToFixed as fl2fi,
+    floatToFixedToStr as fl2str,
+    strToFixedToFloat as str2fl,
+    otRound,
+)
 from fontTools.misc.textTools import safeEval
 import array
+from collections import Counter, defaultdict
 import io
 import logging
 import struct
@@ -30,9 +35,10 @@
 
 
 class TupleVariation(object):
+
 	def __init__(self, axes, coordinates):
 		self.axes = axes.copy()
-		self.coordinates = coordinates[:]
+		self.coordinates = list(coordinates)
 
 	def __repr__(self):
 		axes = ",".join(sorted(["%s=%s" % (name, value) for (name, value) in self.axes.items()]))
@@ -42,11 +48,12 @@
 		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
+		# Empty set means "all points used".
+		if None not in self.coordinates:
+			return frozenset()
+		used = frozenset([i for i,p in enumerate(self.coordinates) if p is not None])
+		# Return None if no points used.
+		return used if used else None
 
 	def hasImpact(self):
 		"""Returns True if this TupleVariation has any visible impact.
@@ -54,10 +61,7 @@
 		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
+		return any(c is not None for c in self.coordinates)
 
 	def toXML(self, writer, axisTags):
 		writer.begintag("tuple")
@@ -65,13 +69,19 @@
 		for axis in axisTags:
 			value = self.axes.get(axis)
 			if value is not None:
-				minValue, value, maxValue = (float(v) for v in value)
+				minValue, value, maxValue = 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)
+					writer.simpletag("coord", axis=axis, value=fl2str(value, 14))
 				else:
-					writer.simpletag("coord", axis=axis, value=value, min=minValue, max=maxValue)
+					attrs = [
+						("axis", axis),
+						("min", fl2str(minValue, 14)),
+						("value", fl2str(value, 14)),
+						("max", fl2str(maxValue, 14)),
+				        ]
+					writer.simpletag("coord", attrs)
 				writer.newline()
 		wrote_any_deltas = False
 		for i, delta in enumerate(self.coordinates):
@@ -97,11 +107,11 @@
 	def fromXML(self, name, attrs, _content):
 		if name == "coord":
 			axis = attrs["axis"]
-			value = float(attrs["value"])
+			value = str2fl(attrs["value"], 14)
 			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))
+			minValue = str2fl(attrs.get("min", defaultMinValue), 14)
+			maxValue = str2fl(attrs.get("max", defaultMaxValue), 14)
 			self.axes[axis] = (minValue, value, maxValue)
 		elif name == "delta":
 			if "pt" in attrs:
@@ -117,15 +127,21 @@
 				log.warning("bad delta format: %s" %
 				            ", ".join(sorted(attrs.keys())))
 
-	def compile(self, axisTags, sharedCoordIndices, sharedPoints):
-		tupleData = []
+	def compile(self, axisTags, sharedCoordIndices={}, pointData=None):
+		assert set(self.axes.keys()) <= set(axisTags), ("Unknown axis tag found.", self.axes.keys(), axisTags)
 
-		assert all(tag in axisTags for tag in self.axes.keys()), ("Unknown axis tag found.", self.axes.keys(), axisTags)
+		tupleData = []
+		auxData = []
+
+		if pointData is None:
+			usedPoints = self.getUsedPoints()
+			if usedPoints is None: # Nothing to encode
+				return b'', b''
+			pointData = self.compilePoints(usedPoints)
 
 		coord = self.compileCoord(axisTags)
-		if coord in sharedCoordIndices:
-			flags = sharedCoordIndices[coord]
-		else:
+		flags = sharedCoordIndices.get(coord)
+		if flags is None:
 			flags = EMBEDDED_PEAK_TUPLE
 			tupleData.append(coord)
 
@@ -134,26 +150,27 @@
 			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:
+		# pointData of b'' implies "use shared points".
+		if pointData:
 			flags |= PRIVATE_POINT_NUMBERS
-			numPointsInGlyph = len(self.coordinates)
-			auxData = self.compilePoints(points, numPointsInGlyph) + self.compileDeltas(points)
-			usesSharedPoints = False
+			auxData.append(pointData)
 
-		tupleData = struct.pack('>HH', len(auxData), flags) + bytesjoin(tupleData)
-		return (tupleData, auxData, usesSharedPoints)
+		auxData.append(self.compileDeltas())
+		auxData = b''.join(auxData)
+
+		tupleData.insert(0, struct.pack('>HH', len(auxData), flags))
+		return b''.join(tupleData), auxData
 
 	def compileCoord(self, axisTags):
-		result = []
+		result = bytearray()
+		axes = self.axes
 		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)
+			triple = axes.get(axis)
+			if triple is None:
+				result.extend(b'\0\0')
+			else:
+				result.extend(struct.pack(">h", fl2fi(triple[1], 14)))
+		return bytes(result)
 
 	def compileIntermediateCoord(self, axisTags):
 		needed = False
@@ -166,29 +183,33 @@
 				break
 		if not needed:
 			return None
-		minCoords = []
-		maxCoords = []
+		minCoords = bytearray()
+		maxCoords = bytearray()
 		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)
+			minCoords.extend(struct.pack(">h", fl2fi(minValue, 14)))
+			maxCoords.extend(struct.pack(">h", fl2fi(maxValue, 14)))
+		return 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)
+			coord[axis] = fi2fl(struct.unpack(">h", data[pos:pos+2])[0], 14)
 			pos += 2
 		return coord, pos
 
 	@staticmethod
-	def compilePoints(points, numPointsInGlyph):
+	def compilePoints(points):
 		# 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"
+		#
+		# To use this optimization, points passed in must be empty set.
+		# The following two lines are not strictly necessary as the main code
+		# below would emit the same. But this is most common and faster.
+		if not points:
+			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.
@@ -200,19 +221,24 @@
 		points.sort()
 		numPoints = len(points)
 
+		result = bytearray()
 		# 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)]
+			result.append(numPoints)
 		else:
-			result = [bytechr((numPoints >> 8) | 0x80) + bytechr(numPoints & 0xff)]
+			result.append((numPoints >> 8) | 0x80)
+			result.append(numPoints & 0xff)
 
 		MAX_RUN_LENGTH = 127
 		pos = 0
 		lastValue = 0
 		while pos < numPoints:
-			run = io.BytesIO()
 			runLength = 0
+
+			headerPos = len(result)
+			result.append(0)
+
 			useByteEncoding = None
 			while pos < numPoints and runLength <= MAX_RUN_LENGTH:
 				curValue = points[pos]
@@ -225,38 +251,36 @@
 				# TODO This never switches back to a byte-encoding from a short-encoding.
 				# That's suboptimal.
 				if useByteEncoding:
-					run.write(bytechr(delta))
+					result.append(delta)
 				else:
-					run.write(bytechr(delta >> 8))
-					run.write(bytechr(delta & 0xff))
+					result.append(delta >> 8)
+					result.append(delta & 0xff)
 				lastValue = curValue
 				pos += 1
 				runLength += 1
 			if useByteEncoding:
-				runHeader = bytechr(runLength - 1)
+				result[headerPos] = runLength - 1
 			else:
-				runHeader = bytechr((runLength - 1) | POINTS_ARE_WORDS)
-			result.append(runHeader)
-			result.append(run.getvalue())
+				result[headerPos] = (runLength - 1) | POINTS_ARE_WORDS
 
-		return bytesjoin(result)
+		return 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])
+		numPointsInData = data[pos]
 		pos += 1
 		if (numPointsInData & POINTS_ARE_WORDS) != 0:
-			numPointsInData = (numPointsInData & POINT_RUN_COUNT_MASK) << 8 | byteord(data[pos])
+			numPointsInData = (numPointsInData & POINT_RUN_COUNT_MASK) << 8 | data[pos]
 			pos += 1
 		if numPointsInData == 0:
 			return (range(numPoints), pos)
 
 		result = []
 		while len(result) < numPointsInData:
-			runHeader = byteord(data[pos])
+			runHeader = data[pos]
 			pos += 1
 			numPointsInRun = (runHeader & POINT_RUN_COUNT_MASK) + 1
 			point = 0
@@ -266,9 +290,8 @@
 			else:
 				points = array.array("B")
 				pointsSize = numPointsInRun
-			points.fromstring(data[pos:pos+pointsSize])
-			if sys.byteorder != "big":
-				points.byteswap()
+			points.frombytes(data[pos:pos+pointsSize])
+			if sys.byteorder != "big": points.byteswap()
 
 			assert len(points) == numPointsInRun
 			pos += pointsSize
@@ -290,23 +313,28 @@
 			            (",".join(sorted(badPoints)), tableTag))
 		return (result, pos)
 
-	def compileDeltas(self, points):
+	def compileDeltas(self):
 		deltaX = []
 		deltaY = []
-		for p in sorted(list(points)):
-			c = self.coordinates[p]
-			if type(c) is tuple and len(c) == 2:
+		if self.getCoordWidth() == 2:
+			for c in self.coordinates:
+				if c is None:
+					continue
 				deltaX.append(c[0])
 				deltaY.append(c[1])
-			elif type(c) is int:
+		else:
+			for c in self.coordinates:
+				if c is None:
+					continue
 				deltaX.append(c)
-			elif c is not None:
-				raise ValueError("invalid type of delta: %s" % type(c))
-		return self.compileDeltaValues_(deltaX) + self.compileDeltaValues_(deltaY)
+		bytearr = bytearray()
+		self.compileDeltaValues_(deltaX, bytearr)
+		self.compileDeltaValues_(deltaY, bytearr)
+		return bytearr
 
 	@staticmethod
-	def compileDeltaValues_(deltas):
-		"""[value1, value2, value3, ...] --> bytestring
+	def compileDeltaValues_(deltas, bytearr=None):
+		"""[value1, value2, value3, ...] --> bytearray
 
 		Emits a sequence of runs. Each run starts with a
 		byte-sized header whose 6 least significant bits
@@ -321,38 +349,41 @@
 		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()
+		if bytearr is None:
+			bytearr = bytearray()
 		pos = 0
-		while pos < len(deltas):
+		numDeltas = len(deltas)
+		while pos < numDeltas:
 			value = deltas[pos]
 			if value == 0:
-				pos = TupleVariation.encodeDeltaRunAsZeroes_(deltas, pos, stream)
-			elif value >= -128 and value <= 127:
-				pos = TupleVariation.encodeDeltaRunAsBytes_(deltas, pos, stream)
+				pos = TupleVariation.encodeDeltaRunAsZeroes_(deltas, pos, bytearr)
+			elif -128 <= value <= 127:
+				pos = TupleVariation.encodeDeltaRunAsBytes_(deltas, pos, bytearr)
 			else:
-				pos = TupleVariation.encodeDeltaRunAsWords_(deltas, pos, stream)
-		return stream.getvalue()
+				pos = TupleVariation.encodeDeltaRunAsWords_(deltas, pos, bytearr)
+		return bytearr
 
 	@staticmethod
-	def encodeDeltaRunAsZeroes_(deltas, offset, stream):
-		runLength = 0
+	def encodeDeltaRunAsZeroes_(deltas, offset, bytearr):
 		pos = offset
 		numDeltas = len(deltas)
-		while pos < numDeltas and runLength < 64 and deltas[pos] == 0:
+		while pos < numDeltas and deltas[pos] == 0:
 			pos += 1
-			runLength += 1
-		assert runLength >= 1 and runLength <= 64
-		stream.write(bytechr(DELTAS_ARE_ZERO | (runLength - 1)))
+		runLength = pos - offset
+		while runLength >= 64:
+			bytearr.append(DELTAS_ARE_ZERO | 63)
+			runLength -= 64
+		if runLength:
+			bytearr.append(DELTAS_ARE_ZERO | (runLength - 1))
 		return pos
 
 	@staticmethod
-	def encodeDeltaRunAsBytes_(deltas, offset, stream):
-		runLength = 0
+	def encodeDeltaRunAsBytes_(deltas, offset, bytearr):
 		pos = offset
 		numDeltas = len(deltas)
-		while pos < numDeltas and runLength < 64:
+		while pos < numDeltas:
 			value = deltas[pos]
-			if value < -128 or value > 127:
+			if not (-128 <= value <= 127):
 				break
 			# Within a byte-encoded run of deltas, a single zero
 			# is best stored literally as 0x00 value. However,
@@ -365,19 +396,22 @@
 			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', round(deltas[i])))
+		runLength = pos - offset
+		while runLength >= 64:
+			bytearr.append(63)
+			bytearr.extend(array.array('b', deltas[offset:offset+64]))
+			offset += 64
+			runLength -= 64
+		if runLength:
+			bytearr.append(runLength - 1)
+			bytearr.extend(array.array('b', deltas[offset:pos]))
 		return pos
 
 	@staticmethod
-	def encodeDeltaRunAsWords_(deltas, offset, stream):
-		runLength = 0
+	def encodeDeltaRunAsWords_(deltas, offset, bytearr):
 		pos = offset
 		numDeltas = len(deltas)
-		while pos < numDeltas and runLength < 64:
+		while pos < numDeltas:
 			value = deltas[pos]
 			# Within a word-encoded run of deltas, it is easiest
 			# to start a new run (with a different encoding)
@@ -395,15 +429,22 @@
 			# [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]):
+			if (-128 <= value <= 127) and pos+1 < numDeltas and (-128 <= deltas[pos+1] <= 127):
 				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', round(deltas[i])))
+		runLength = pos - offset
+		while runLength >= 64:
+			bytearr.append(DELTAS_ARE_WORDS | 63)
+			a = array.array('h', deltas[offset:offset+64])
+			if sys.byteorder != "big": a.byteswap()
+			bytearr.extend(a)
+			offset += 64
+			runLength -= 64
+		if runLength:
+			bytearr.append(DELTAS_ARE_WORDS | (runLength - 1))
+			a = array.array('h', deltas[offset:pos])
+			if sys.byteorder != "big": a.byteswap()
+			bytearr.extend(a)
 		return pos
 
 	@staticmethod
@@ -412,7 +453,7 @@
 		result = []
 		pos = offset
 		while len(result) < numDeltas:
-			runHeader = byteord(data[pos])
+			runHeader = data[pos]
 			pos += 1
 			numDeltasInRun = (runHeader & DELTA_RUN_COUNT_MASK) + 1
 			if (runHeader & DELTAS_ARE_ZERO) != 0:
@@ -424,9 +465,8 @@
 				else:
 					deltas = array.array("b")
 					deltasSize = numDeltasInRun
-				deltas.fromstring(data[pos:pos+deltasSize])
-				if sys.byteorder != "big":
-					deltas.byteswap()
+				deltas.frombytes(data[pos:pos+deltasSize])
+				if sys.byteorder != "big": deltas.byteswap()
 				assert len(deltas) == numDeltasInRun
 				pos += deltasSize
 				result.extend(deltas)
@@ -442,6 +482,125 @@
 			size += axisCount * 4
 		return size
 
+	def getCoordWidth(self):
+		""" Return 2 if coordinates are (x, y) as in gvar, 1 if single values
+		as in cvar, or 0 if empty.
+		"""
+		firstDelta = next((c for c in self.coordinates if c is not None), None)
+		if firstDelta is None:
+			return 0  # empty or has no impact
+		if type(firstDelta) in (int, float):
+			return 1
+		if type(firstDelta) is tuple and len(firstDelta) == 2:
+			return 2
+		raise TypeError(
+			"invalid type of delta; expected (int or float) number, or "
+			"Tuple[number, number]: %r" % firstDelta
+		)
+
+	def scaleDeltas(self, scalar):
+		if scalar == 1.0:
+			return  # no change
+		coordWidth = self.getCoordWidth()
+		self.coordinates = [
+			None
+			if d is None
+			else d * scalar
+			if coordWidth == 1
+			else (d[0] * scalar, d[1] * scalar)
+			for d in self.coordinates
+		]
+
+	def roundDeltas(self):
+		coordWidth = self.getCoordWidth()
+		self.coordinates = [
+			None
+			if d is None
+			else otRound(d)
+			if coordWidth == 1
+			else (otRound(d[0]), otRound(d[1]))
+			for d in self.coordinates
+		]
+
+	def calcInferredDeltas(self, origCoords, endPts):
+		from fontTools.varLib.iup import iup_delta
+
+		if self.getCoordWidth() == 1:
+			raise TypeError(
+				"Only 'gvar' TupleVariation can have inferred deltas"
+			)
+		if None in self.coordinates:
+			if len(self.coordinates) != len(origCoords):
+				raise ValueError(
+					"Expected len(origCoords) == %d; found %d"
+					% (len(self.coordinates), len(origCoords))
+				)
+			self.coordinates = iup_delta(self.coordinates, origCoords, endPts)
+
+	def optimize(self, origCoords, endPts, tolerance=0.5, isComposite=False):
+		from fontTools.varLib.iup import iup_delta_optimize
+
+		if None in self.coordinates:
+			return  # already optimized
+
+		deltaOpt = iup_delta_optimize(
+		    self.coordinates, origCoords, endPts, tolerance=tolerance
+		)
+		if None in deltaOpt:
+			if isComposite and all(d is None for d in deltaOpt):
+				# Fix for macOS composites
+				# https://github.com/fonttools/fonttools/issues/1381
+				deltaOpt = [(0, 0)] + [None] * (len(deltaOpt) - 1)
+			# Use "optimized" version only if smaller...
+			varOpt = TupleVariation(self.axes, deltaOpt)
+
+			# Shouldn't matter that this is different from fvar...?
+			axisTags = sorted(self.axes.keys())
+			tupleData, auxData = self.compile(axisTags)
+			unoptimizedLength = len(tupleData) + len(auxData)
+			tupleData, auxData = varOpt.compile(axisTags)
+			optimizedLength = len(tupleData) + len(auxData)
+
+			if optimizedLength < unoptimizedLength:
+				self.coordinates = varOpt.coordinates
+
+	def __iadd__(self, other):
+		if not isinstance(other, TupleVariation):
+			return NotImplemented
+		deltas1 = self.coordinates
+		length = len(deltas1)
+		deltas2 = other.coordinates
+		if len(deltas2) != length:
+			raise ValueError(
+				"cannot sum TupleVariation deltas with different lengths"
+			)
+		# 'None' values have different meanings in gvar vs cvar TupleVariations:
+		# within the gvar, when deltas are not provided explicitly for some points,
+		# they need to be inferred; whereas for the 'cvar' table, if deltas are not
+		# provided for some CVT values, then no adjustments are made (i.e. None == 0).
+		# Thus, we cannot sum deltas for gvar TupleVariations if they contain
+		# inferred inferred deltas (the latter need to be computed first using
+		# 'calcInferredDeltas' method), but we can treat 'None' values in cvar
+		# deltas as if they are zeros.
+		if self.getCoordWidth() == 2:
+			for i, d2 in zip(range(length), deltas2):
+				d1 = deltas1[i]
+				try:
+					deltas1[i] = (d1[0] + d2[0], d1[1] + d2[1])
+				except TypeError:
+					raise ValueError(
+						"cannot sum gvar deltas with inferred points"
+					)
+		else:
+			for i, d2 in zip(range(length), deltas2):
+				d1 = deltas1[i]
+				if d1 is not None and d2 is not None:
+					deltas1[i] = d1 + d2
+				elif d1 is None and d2 is not None:
+					deltas1[i] = d2
+				# elif d2 is None do nothing
+		return self
+
 
 def decompileSharedTuples(axisTags, sharedTupleCount, data, offset):
 	result = []
@@ -451,87 +610,77 @@
 	return result
 
 
-def compileSharedTuples(axisTags, variations):
-	coordCount = {}
+def compileSharedTuples(axisTags, variations,
+			MAX_NUM_SHARED_COORDS = TUPLE_INDEX_MASK + 1):
+	coordCount = Counter()
 	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.
+		coordCount[coord] += 1
+	# In python < 3.7, most_common() ordering is non-deterministic
+	# so apply a sort to make sure the ordering is consistent.
+	sharedCoords = sorted(
+		coordCount.most_common(MAX_NUM_SHARED_COORDS),
+		key=lambda item: (-item[1], item[0]),
+	)
+	return [c[0] for c in sharedCoords if c[1] > 1]
 
 
 def compileTupleVariationStore(variations, pointCount,
                                axisTags, sharedTupleIndices,
                                useSharedPoints=True):
-	variations = [v for v in variations if v.hasImpact()]
-	if len(variations) == 0:
+	newVariations = []
+	pointDatas = []
+	# Compile all points and figure out sharing if desired
+	sharedPoints = None
+
+	# Collect, count, and compile point-sets for all variation sets
+	pointSetCount = defaultdict(int)
+	for v in variations:
+		points = v.getUsedPoints()
+		if points is None: # Empty variations
+			continue
+		pointSetCount[points] += 1
+		newVariations.append(v)
+		pointDatas.append(points)
+	variations = newVariations
+	del newVariations
+
+	if not variations:
 		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).
-	#
+	n = len(variations[0].coordinates)
+	assert all(len(v.coordinates) == n for v in variations), "Variation sets have different sizes"
 
-	# 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()
+	compiledPoints = {pointSet:TupleVariation.compilePoints(pointSet)
+			  for pointSet in pointSetCount}
+
+	tupleVariationCount = len(variations)
 	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)
+
+	if useSharedPoints:
+		# Find point-set which saves most bytes.
+		def key(pn):
+			pointSet = pn[0]
+			count = pn[1]
+			return len(compiledPoints[pointSet]) * (count - 1)
+		sharedPoints = max(pointSetCount.items(), key=key)[0]
+
+		data.append(compiledPoints[sharedPoints])
+		tupleVariationCount |= TUPLES_SHARE_POINT_NUMBERS
+
+	# b'' implies "use shared points"
+	pointDatas = [compiledPoints[points] if points != sharedPoints else b''
+		     for points in pointDatas]
+
+	for v,p in zip(variations, pointDatas):
+		thisTuple, thisData = v.compile(axisTags, sharedTupleIndices, pointData=p)
+
+		tuples.append(thisTuple)
+		data.append(thisData)
+
+	tuples = b''.join(tuples)
+	data = b''.join(data)
 	return tupleVariationCount, tuples, data
 
 
diff --git a/Lib/fontTools/ttLib/tables/V_D_M_X_.py b/Lib/fontTools/ttLib/tables/V_D_M_X_.py
index abca3bf..ba8593f 100644
--- a/Lib/fontTools/ttLib/tables/V_D_M_X_.py
+++ b/Lib/fontTools/ttLib/tables/V_D_M_X_.py
@@ -1,5 +1,3 @@
-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
diff --git a/Lib/fontTools/ttLib/tables/V_O_R_G_.py b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
index 8b2c317..0b7fe95 100644
--- a/Lib/fontTools/ttLib/tables/V_O_R_G_.py
+++ b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
@@ -1,8 +1,6 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
-import operator
 import struct
 
 
@@ -30,27 +28,27 @@
 		self.VOriginRecords = vOrig = {}
 		glyphOrder = ttFont.getGlyphOrder()
 		try:
-			names = map(operator.getitem, [glyphOrder]*self.numVertOriginYMetrics, gids)
+			names = [glyphOrder[gid] for gid in gids]
 		except IndexError:
 			getGlyphName = self.getGlyphName
-			names = map(getGlyphName, gids )
+			names = map(getGlyphName, gids)
 
-		list(map(operator.setitem, [vOrig]*self.numVertOriginYMetrics, names, vids))
+		for name, vid in zip(names, vids):
+			vOrig[name] = vid
 
 	def compile(self, ttFont):
 		vorgs = list(self.VOriginRecords.values())
 		names = list(self.VOriginRecords.keys())
 		nameMap = ttFont.getReverseGlyphMap()
-		lenRecords = len(vorgs)
 		try:
-			gids = map(operator.getitem, [nameMap]*lenRecords, names)
+			gids = [nameMap[name] for name in names]
 		except KeyError:
 			nameMap = ttFont.getReverseGlyphMap(rebuild=True)
-			gids = map(operator.getitem, [nameMap]*lenRecords, names)
+			gids = [nameMap[name] for name in names]
 		vOriginTable = list(zip(gids, vorgs))
-		self.numVertOriginYMetrics = lenRecords
+		self.numVertOriginYMetrics = len(vorgs)
 		vOriginTable.sort() # must be in ascending GID order
-		dataList = [ struct.pack(">Hh", rec[0], rec[1]) for rec in vOriginTable]
+		dataList = [struct.pack(">Hh", rec[0], rec[1]) for rec in vOriginTable]
 		header = struct.pack(">HHhH", self.majorVersion, self.minorVersion, self.defaultVertOriginY, self.numVertOriginYMetrics)
 		dataList.insert(0, header)
 		data = bytesjoin(dataList)
@@ -85,7 +83,7 @@
 		if name == "VOriginRecord":
 			vOriginRec = VOriginRecord()
 			for element in content:
-				if isinstance(element, basestring):
+				if isinstance(element, str):
 					continue
 				name, attrs, content = element
 				vOriginRec.fromXML(name, attrs, content, ttFont)
diff --git a/Lib/fontTools/ttLib/tables/V_V_A_R_.py b/Lib/fontTools/ttLib/tables/V_V_A_R_.py
index 530f0e3..88f3055 100644
--- a/Lib/fontTools/ttLib/tables/V_V_A_R_.py
+++ b/Lib/fontTools/ttLib/tables/V_V_A_R_.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/__init__.py b/Lib/fontTools/ttLib/tables/__init__.py
index 79a50fd..bbfb8b7 100644
--- a/Lib/fontTools/ttLib/tables/__init__.py
+++ b/Lib/fontTools/ttLib/tables/__init__.py
@@ -1,7 +1,4 @@
 
-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
@@ -17,6 +14,7 @@
 	from . import C_O_L_R_
 	from . import C_P_A_L_
 	from . import D_S_I_G_
+	from . import D__e_b_g
 	from . import E_B_D_T_
 	from . import E_B_L_C_
 	from . import F_F_T_M_
@@ -41,6 +39,7 @@
 	from . import S__i_l_f
 	from . import S__i_l_l
 	from . import T_S_I_B_
+	from . import T_S_I_C_
 	from . import T_S_I_D_
 	from . import T_S_I_J_
 	from . import T_S_I_P_
diff --git a/Lib/fontTools/ttLib/tables/_a_n_k_r.py b/Lib/fontTools/ttLib/tables/_a_n_k_r.py
index 3a1aa08..1f2946c 100644
--- a/Lib/fontTools/ttLib/tables/_a_n_k_r.py
+++ b/Lib/fontTools/ttLib/tables/_a_n_k_r.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/_a_v_a_r.py b/Lib/fontTools/ttLib/tables/_a_v_a_r.py
index eb774ad..2b6a40e 100644
--- a/Lib/fontTools/ttLib/tables/_a_v_a_r.py
+++ b/Lib/fontTools/ttLib/tables/_a_v_a_r.py
@@ -1,12 +1,13 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools import ttLib
+from fontTools.misc.py23 import bytesjoin
 from fontTools.misc import sstruct
-from fontTools.misc.fixedTools import fixedToFloat, floatToFixed
-from fontTools.misc.textTools import safeEval
+from fontTools.misc.fixedTools import (
+    fixedToFloat as fi2fl,
+    floatToFixed as fl2fi,
+    floatToFixedToStr as fl2str,
+    strToFixedToFloat as str2fl,
+)
 from fontTools.ttLib import TTLibError
 from . import DefaultTable
-import array
 import struct
 import logging
 
@@ -47,8 +48,8 @@
             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)
+                fixedKey = fl2fi(key, 14)
+                fixedValue = fl2fi(value, 14)
                 result.append(struct.pack(">hh", fixedKey, fixedValue))
         return bytesjoin(result)
 
@@ -67,19 +68,17 @@
             pos = pos + 2
             for _ in range(numPairs):
                 fromValue, toValue = struct.unpack(">hh", data[pos:pos+4])
-                segments[fixedToFloat(fromValue, 14)] = fixedToFloat(toValue, 14)
+                segments[fi2fl(fromValue, 14)] = fi2fl(toValue, 14)
                 pos = pos + 4
 
-    def toXML(self, writer, ttFont, progress=None):
+    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)
+                key = fl2str(key, 14)
+                value = fl2str(value, 14)
                 writer.simpletag("mapping", **{"from": key, "to": value})
                 writer.newline()
             writer.endtag("segment")
@@ -93,8 +92,8 @@
                 if isinstance(element, tuple):
                     elementName, elementAttrs, _ = element
                     if elementName == "mapping":
-                        fromValue = safeEval(elementAttrs["from"])
-                        toValue = safeEval(elementAttrs["to"])
+                        fromValue = str2fl(elementAttrs["from"], 14)
+                        toValue = str2fl(elementAttrs["to"], 14)
                         if fromValue in segment:
                             log.warning("duplicate entry for %s in axis '%s'",
                                         fromValue, axis)
diff --git a/Lib/fontTools/ttLib/tables/_b_s_l_n.py b/Lib/fontTools/ttLib/tables/_b_s_l_n.py
index 31d8399..8e266fa 100644
--- a/Lib/fontTools/ttLib/tables/_b_s_l_n.py
+++ b/Lib/fontTools/ttLib/tables/_b_s_l_n.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/_c_i_d_g.py b/Lib/fontTools/ttLib/tables/_c_i_d_g.py
index 1d6c502..de83d4d 100644
--- a/Lib/fontTools/ttLib/tables/_c_i_d_g.py
+++ b/Lib/fontTools/ttLib/tables/_c_i_d_g.py
@@ -1,6 +1,4 @@
 # coding: utf-8
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/_c_m_a_p.py b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
index 888eb26..a65a0c2 100644
--- a/Lib/fontTools/ttLib/tables/_c_m_a_p.py
+++ b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin
 from fontTools.misc.textTools import safeEval, readHex
 from fontTools.misc.encodingTools import getEncoding
 from fontTools.ttLib import getSearchRange
@@ -8,7 +7,6 @@
 import sys
 import struct
 import array
-import operator
 import logging
 
 
@@ -20,7 +18,7 @@
 	cmap = {}
 	glyphOrder = font.getGlyphOrder()
 	for char,gid in zip(chars,gids):
-		if gid is 0:
+		if gid == 0:
 			continue
 		try:
 			name = glyphOrder[gid]
@@ -104,7 +102,7 @@
 			tables.append(table)
 
 	def compile(self, ttFont):
-		self.tables.sort()    # sort according to the spec; see CmapSubtable.__lt__()
+		self.tables.sort()  # sort according to the spec; see CmapSubtable.__lt__()
 		numSubTables = len(self.tables)
 		totalOffset = 4 + 8 * numSubTables
 		data = struct.pack(">HH", self.tableVersion, numSubTables)
@@ -246,7 +244,7 @@
 
 	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 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, ttFont)
 		else:
@@ -254,7 +252,7 @@
 		data = self.data # decompileHeader assigns the data after the header to self.data
 		assert 262 == self.length, "Format 0 cmap subtable not 262 bytes"
 		gids = array.array("B")
-		gids.fromstring(self.data)
+		gids.frombytes(self.data)
 		charCodes = list(range(len(gids)))
 		self.cmap = _make_map(self.ttFont, charCodes, gids)
 
@@ -268,7 +266,7 @@
 		valueList = [getGlyphID(cmap[i]) if i in cmap else 0 for i in range(256)]
 
 		gids = array.array("B", valueList)
-		data = struct.pack(">HHH", 0, 262, self.language) + gids.tostring()
+		data = struct.pack(">HHH", 0, 262, self.language) + gids.tobytes()
 		assert len(data) == 262
 		return data
 
@@ -310,15 +308,15 @@
 		# 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),
+		#    (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.
 
-		if  (minGI > 1):
-			if  minGI > 0x7FFF:
+		if (minGI > 1):
+			if minGI > 0x7FFF:
 				subHeader.idDelta = -(0x10000 - minGI) -1
 			else:
-				subHeader.idDelta =  minGI -1
+				subHeader.idDelta = minGI -1
 			idDelta = subHeader.idDelta
 			for i in range(subHeader.entryCount):
 				gid = subHeader.glyphIndexArray[i]
@@ -327,7 +325,7 @@
 
 	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 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, ttFont)
 		else:
@@ -338,10 +336,9 @@
 		maxSubHeaderindex = 0
 		# get the key array, and determine the number of subHeaders.
 		allKeys = array.array("H")
-		allKeys.fromstring(data[:512])
+		allKeys.frombytes(data[:512])
 		data = data[512:]
-		if sys.byteorder != "big":
-			allKeys.byteswap()
+		if sys.byteorder != "big": allKeys.byteswap()
 		subHeaderKeys = [ key//8 for key in allKeys]
 		maxSubHeaderindex = max(subHeaderKeys)
 
@@ -355,14 +352,13 @@
 			pos += 8
 			giDataPos = pos + subHeader.idRangeOffset-2
 			giList = array.array("H")
-			giList.fromstring(data[giDataPos:giDataPos + subHeader.entryCount*2])
-			if sys.byteorder != "big":
-				giList.byteswap()
+			giList.frombytes(data[giDataPos:giDataPos + subHeader.entryCount*2])
+			if sys.byteorder != "big": giList.byteswap()
 			subHeader.glyphIndexArray = giList
 			subHeaderList.append(subHeader)
 		# How this gets processed.
 		# Charcodes may be one or two bytes.
-		# The first byte of a charcode is mapped through the  subHeaderKeys, to select
+		# 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
 		# mapped through the subheader, and there is no second byte.
@@ -444,13 +440,12 @@
 		charCodes = [item[0] for item in items]
 		names = [item[1] for item in items]
 		nameMap = ttFont.getReverseGlyphMap()
-		lenCharCodes = len(charCodes)
 		try:
-			gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+			gids = [nameMap[name] for name in names]
 		except KeyError:
 			nameMap = ttFont.getReverseGlyphMap(rebuild=True)
 			try:
-				gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+				gids = [nameMap[name] for name in names]
 			except KeyError:
 				# allow virtual GIDs in format 2 tables
 				gids = []
@@ -460,7 +455,7 @@
 					except KeyError:
 						try:
 							if (name[:3] == 'gid'):
-								gid = eval(name[3:])
+								gid = int(name[3:])
 							else:
 								gid = ttFont.getGlyphID(name)
 						except:
@@ -468,17 +463,17 @@
 
 					gids.append(gid)
 
-		# Process the (char code to gid) item list  in char code order.
+		# 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,
 		# 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.
 
-		subHeaderKeys = [ kEmptyTwoCharCodeRange for x in  range(256)] # list of indices into subHeaderList.
+		subHeaderKeys = [kEmptyTwoCharCodeRange for x in range(256)] # list of indices into subHeaderList.
 		subHeaderList = []
 
-		# We force this subheader entry 0  to exist in the subHeaderList in the case where some one comes up
+		# We force this subheader entry 0 to exist in the subHeaderList in the case where some one comes up
 		# with a cmap where all the one byte char codes map to notdef,
 		# with the result that the subhead 0 would not get created just by processing the item list.
 		charCode = charCodes[0]
@@ -551,7 +546,7 @@
 		for index in range(subheadRangeLen):
 			subHeader = subHeaderList[index]
 			subHeader.idRangeOffset = 0
-			for j  in range(index):
+			for j in range(index):
 				prevSubhead = subHeaderList[j]
 				if prevSubhead.glyphIndexArray == subHeader.glyphIndexArray: # use the glyphIndexArray subarray
 					subHeader.idRangeOffset = prevSubhead.idRangeOffset - (index-j)*8
@@ -686,7 +681,7 @@
 
 	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 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, ttFont)
 		else:
@@ -699,11 +694,10 @@
 		segCount = segCountX2 // 2
 
 		allCodes = array.array("H")
-		allCodes.fromstring(data)
+		allCodes.frombytes(data)
 		self.data = data = None
 
-		if sys.byteorder != "big":
-			allCodes.byteswap()
+		if sys.byteorder != "big": allCodes.byteswap()
 
 		# divide the data
 		endCode = allCodes[:segCount]
@@ -733,7 +727,7 @@
 			else:
 				for charCode in rangeCharCodes:
 					index = charCode + partial
-					assert (index < lenGIArray), "In format 4 cmap, range (%d), the calculated index (%d) into the glyph index array  is not less than the length of the array (%d) !" % (i, index, lenGIArray)
+					assert (index < lenGIArray), "In format 4 cmap, range (%d), the calculated index (%d) into the glyph index array is not less than the length of the array (%d) !" % (i, index, lenGIArray)
 					if glyphIndexArray[index] != 0:  # if not missing glyph
 						glyphID = glyphIndexArray[index] + delta
 					else:
@@ -747,20 +741,19 @@
 			return struct.pack(">HHH", self.format, self.length, self.language) + self.data
 
 		charCodes = list(self.cmap.keys())
-		lenCharCodes = len(charCodes)
-		if lenCharCodes == 0:
+		if not charCodes:
 			startCode = [0xffff]
 			endCode = [0xffff]
 		else:
 			charCodes.sort()
-			names = list(map(operator.getitem, [self.cmap]*lenCharCodes, charCodes))
+			names = [self.cmap[code] for code in charCodes]
 			nameMap = ttFont.getReverseGlyphMap()
 			try:
-				gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+				gids = [nameMap[name] for name in names]
 			except KeyError:
 				nameMap = ttFont.getReverseGlyphMap(rebuild=True)
 				try:
-					gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+					gids = [nameMap[name] for name in names]
 				except KeyError:
 					# allow virtual GIDs in format 4 tables
 					gids = []
@@ -770,7 +763,7 @@
 						except KeyError:
 							try:
 								if (name[:3] == 'gid'):
-									gid = eval(name[3:])
+									gid = int(name[3:])
 								else:
 									gid = ttFont.getGlyphID(name)
 							except:
@@ -778,7 +771,8 @@
 
 						gids.append(gid)
 			cmap = {}  # code:glyphID mapping
-			list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
+			for code, gid in zip(charCodes, gids):
+				cmap[code] = gid
 
 			# Build startCode and endCode lists.
 			# Split the char codes in ranges of consecutive char codes, then split
@@ -810,7 +804,7 @@
 			indices = []
 			for charCode in range(startCode[i], endCode[i] + 1):
 				indices.append(cmap[charCode])
-			if  (indices == list(range(indices[0], indices[0] + len(indices)))):
+			if (indices == list(range(indices[0], indices[0] + len(indices)))):
 				idDelta.append((indices[0] - startCode[i]) % 0x10000)
 				idRangeOffset.append(0)
 			else:
@@ -829,11 +823,10 @@
 		charCodeArray = array.array("H", endCode + [0] + startCode)
 		idDeltaArray = array.array("H", idDelta)
 		restArray = array.array("H", idRangeOffset + glyphIndexArray)
-		if sys.byteorder != "big":
-			charCodeArray.byteswap()
-			idDeltaArray.byteswap()
-			restArray.byteswap()
-		data = charCodeArray.tostring() + idDeltaArray.tostring() + restArray.tostring()
+		if sys.byteorder != "big": charCodeArray.byteswap()
+		if sys.byteorder != "big": idDeltaArray.byteswap()
+		if sys.byteorder != "big": restArray.byteswap()
+		data = charCodeArray.tobytes() + idDeltaArray.tobytes() + restArray.tobytes()
 
 		length = struct.calcsize(cmap_format_4_format) + len(data)
 		header = struct.pack(cmap_format_4_format, self.format, length, self.language,
@@ -859,7 +852,7 @@
 
 	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 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, ttFont)
 		else:
@@ -871,9 +864,8 @@
 		data = data[4:]
 		#assert len(data) == 2 * entryCount  # XXX not true in Apple's Helvetica!!!
 		gids = array.array("H")
-		gids.fromstring(data[:2 * int(entryCount)])
-		if sys.byteorder != "big":
-			gids.byteswap()
+		gids.frombytes(data[:2 * int(entryCount)])
+		if sys.byteorder != "big": gids.byteswap()
 		self.data = data = None
 
 		charCodes = list(range(firstCode, firstCode + len(gids)))
@@ -887,12 +879,13 @@
 		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)
+			valueList = [
+				ttFont.getGlyphID(cmap[code]) if code in cmap else 0
+				for code in codes
+			]
 			gids = array.array("H", valueList)
-			if sys.byteorder != "big":
-				gids.byteswap()
-			data = gids.tostring()
+			if sys.byteorder != "big": gids.byteswap()
+			data = gids.tobytes()
 		else:
 			data = b""
 			firstCode = 0
@@ -936,7 +929,7 @@
 
 	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 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, ttFont)
 		else:
@@ -959,15 +952,14 @@
 		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)
 		names = list(self.cmap.values())
 		nameMap = ttFont.getReverseGlyphMap()
 		try:
-			gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+			gids = [nameMap[name] for name in names]
 		except KeyError:
 			nameMap = ttFont.getReverseGlyphMap(rebuild=True)
 			try:
-				gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+				gids = [nameMap[name] for name in names]
 			except KeyError:
 				# allow virtual GIDs in format 12 tables
 				gids = []
@@ -977,7 +969,7 @@
 					except KeyError:
 						try:
 							if (name[:3] == 'gid'):
-								gid = eval(name[3:])
+								gid = int(name[3:])
 							else:
 								gid = ttFont.getGlyphID(name)
 						except:
@@ -986,7 +978,8 @@
 					gids.append(gid)
 
 		cmap = {}  # code:glyphID mapping
-		list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
+		for code, gid in zip(charCodes, gids):
+			cmap[code] = gid
 
 		charCodes.sort()
 		index = 0
@@ -995,7 +988,7 @@
 		lastGlyphID = startGlyphID - self._format_step
 		lastCharCode = startCharCode - 1
 		nGroups = 0
-		dataList =  []
+		dataList = []
 		maxIndex = len(charCodes)
 		for index in range(maxIndex):
 			charCode = charCodes[index]
@@ -1077,12 +1070,12 @@
 		return (glyphID == lastGlyphID) and (charCode == 1 + lastCharCode)
 
 
-def  cvtToUVS(threeByteString):
+def cvtToUVS(threeByteString):
 	data = b"\0" + threeByteString
 	val, = struct.unpack(">L", data)
 	return val
 
-def  cvtFromUVS(val):
+def cvtFromUVS(val):
 	assert 0 <= val < 0x1000000
 	fourByteString = struct.pack(">L", val)
 	return fourByteString[1:]
@@ -1109,7 +1102,7 @@
 		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:
@@ -1139,7 +1132,7 @@
 					startOffset += 5
 					uv = cvtToUVS(uv)
 					glyphName = self.ttFont.getGlyphName(gid)
-					localUVList.append( [uv, glyphName] )
+					localUVList.append((uv, glyphName))
 				try:
 					uvsDict[varUVS].extend(localUVList)
 				except KeyError:
@@ -1151,9 +1144,6 @@
 		writer.begintag(self.__class__.__name__, [
 				("platformID", self.platformID),
 				("platEncID", self.platEncID),
-				("format", self.format),
-				("length", self.length),
-				("numVarSelectorRecords", self.numVarSelectorRecords),
 				])
 		writer.newline()
 		uvsDict = self.uvsDict
@@ -1162,25 +1152,27 @@
 			uvList = uvsDict[uvs]
 			uvList.sort(key=lambda item: (item[1] is not None, item[0], item[1]))
 			for uv, gname in uvList:
-				if gname is None:
-					gname = "None"
-				# I use the arg rather than th keyword syntax in order to preserve the attribute order.
-				writer.simpletag("map", [ ("uvs",hex(uvs)), ("uv",hex(uv)), ("name", gname)]  )
+				attrs = [("uv", hex(uv)), ("uvs", hex(uvs))]
+				if gname is not None:
+					attrs.append(("name", gname))
+				writer.simpletag("map", attrs)
 				writer.newline()
 		writer.endtag(self.__class__.__name__)
 		writer.newline()
 
 	def fromXML(self, name, attrs, content, ttFont):
-		self.format = safeEval(attrs["format"])
-		self.length = safeEval(attrs["length"])
-		self.numVarSelectorRecords = safeEval(attrs["numVarSelectorRecords"])
-		self.language = 0xFF # provide a value so that  CmapSubtable.__lt__() won't fail
+		self.language = 0xFF # provide a value so that CmapSubtable.__lt__() won't fail
 		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
 
+		# For backwards compatibility reasons we accept "None" as an indicator
+		# for "default mapping", unless the font actually has a glyph named
+		# "None".
+		_hasGlyphNamedNone = None
+
 		for element in content:
 			if not isinstance(element, tuple):
 				continue
@@ -1189,13 +1181,16 @@
 				continue
 			uvs = safeEval(attrs["uvs"])
 			uv = safeEval(attrs["uv"])
-			gname = attrs["name"]
+			gname = attrs.get("name")
 			if gname == "None":
-				gname = None
+				if _hasGlyphNamedNone is None:
+					_hasGlyphNamedNone = "None" in ttFont.getGlyphOrder()
+				if not _hasGlyphNamedNone:
+					gname = None
 			try:
-				uvsDict[uvs].append( [uv, gname])
+				uvsDict[uvs].append((uv, gname))
 			except KeyError:
-				uvsDict[uvs] = [ [uv, gname] ]
+				uvsDict[uvs] = [(uv, gname)]
 
 	def compile(self, ttFont):
 		if self.data:
@@ -1285,7 +1280,7 @@
 
 	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 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, ttFont)
 		else:
diff --git a/Lib/fontTools/ttLib/tables/_c_v_a_r.py b/Lib/fontTools/ttLib/tables/_c_v_a_r.py
index d7e5553..a5f1acf 100644
--- a/Lib/fontTools/ttLib/tables/_c_v_a_r.py
+++ b/Lib/fontTools/ttLib/tables/_c_v_a_r.py
@@ -1,6 +1,4 @@
-from __future__ import \
-    print_function, division, absolute_import, unicode_literals
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin
 from . import DefaultTable
 from fontTools.misc import sstruct
 from fontTools.ttLib.tables.TupleVariation import \
@@ -43,7 +41,7 @@
             "tupleVariationCount": tupleVariationCount,
             "offsetToData": CVAR_HEADER_SIZE + len(tuples),
         }
-        return bytesjoin([
+        return b''.join([
             sstruct.pack(CVAR_HEADER_FORMAT, header),
             tuples,
             data
@@ -75,7 +73,7 @@
                     tupleName, tupleAttrs, tupleContent = tupleElement
                     var.fromXML(tupleName, tupleAttrs, tupleContent)
 
-    def toXML(self, writer, ttFont, progress=None):
+    def toXML(self, writer, ttFont):
         axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
         writer.simpletag("version",
                          major=self.majorVersion, minor=self.minorVersion)
diff --git a/Lib/fontTools/ttLib/tables/_c_v_t.py b/Lib/fontTools/ttLib/tables/_c_v_t.py
index 4fbee7b..26395c9 100644
--- a/Lib/fontTools/ttLib/tables/_c_v_t.py
+++ b/Lib/fontTools/ttLib/tables/_c_v_t.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
 import sys
@@ -9,16 +7,14 @@
 
 	def decompile(self, data, ttFont):
 		values = array.array("h")
-		values.fromstring(data)
-		if sys.byteorder != "big":
-			values.byteswap()
+		values.frombytes(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()
+		if sys.byteorder != "big": values.byteswap()
+		return values.tobytes()
 
 	def toXML(self, writer, ttFont):
 		for i in range(len(self.values)):
diff --git a/Lib/fontTools/ttLib/tables/_f_e_a_t.py b/Lib/fontTools/ttLib/tables/_f_e_a_t.py
index 3715271..eb03f8b 100644
--- a/Lib/fontTools/ttLib/tables/_f_e_a_t.py
+++ b/Lib/fontTools/ttLib/tables/_f_e_a_t.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/_f_p_g_m.py b/Lib/fontTools/ttLib/tables/_f_p_g_m.py
index 6536dba..ec3576c 100644
--- a/Lib/fontTools/ttLib/tables/_f_p_g_m.py
+++ b/Lib/fontTools/ttLib/tables/_f_p_g_m.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from . import DefaultTable
 from . import ttProgram
 
diff --git a/Lib/fontTools/ttLib/tables/_f_v_a_r.py b/Lib/fontTools/ttLib/tables/_f_v_a_r.py
index 50ae736..7487da6 100644
--- a/Lib/fontTools/ttLib/tables/_f_v_a_r.py
+++ b/Lib/fontTools/ttLib/tables/_f_v_a_r.py
@@ -1,8 +1,12 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import Tag, bytesjoin
 from fontTools.misc import sstruct
-from fontTools.misc.fixedTools import fixedToFloat, floatToFixed
-from fontTools.misc.textTools import safeEval, num2binary, binary2num
+from fontTools.misc.fixedTools import (
+    fixedToFloat as fi2fl,
+    floatToFixed as fl2fi,
+    floatToFixedToStr as fl2str,
+    strToFixedToFloat as str2fl,
+)
+from fontTools.misc.textTools import safeEval
 from fontTools.ttLib import TTLibError
 from . import DefaultTable
 import struct
@@ -89,7 +93,7 @@
             self.instances.append(instance)
             pos += instanceSize
 
-    def toXML(self, writer, ttFont, progress=None):
+    def toXML(self, writer, ttFont):
         for axis in self.axes:
             axis.toXML(writer, ttFont)
         for instance in self.instances:
@@ -130,9 +134,9 @@
         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)),
+                           ("MinValue", fl2str(self.minValue, 16)),
+                           ("DefaultValue", fl2str(self.defaultValue, 16)),
+                           ("MaxValue", fl2str(self.maxValue, 16)),
                            ("AxisNameID", str(self.axisNameID))]:
             writer.begintag(tag)
             writer.write(value)
@@ -149,7 +153,11 @@
                 self.axisTag = Tag(value)
             elif tag in {"Flags", "MinValue", "DefaultValue", "MaxValue",
                          "AxisNameID"}:
-                setattr(self, tag[0].lower() + tag[1:], safeEval(value))
+                setattr(
+                    self,
+                    tag[0].lower() + tag[1:],
+                    str2fl(value, 16) if tag.endswith("Value") else safeEval(value)
+                )
 
 
 class NamedInstance(object):
@@ -162,7 +170,7 @@
     def compile(self, axisTags, includePostScriptName):
         result = [sstruct.pack(FVAR_INSTANCE_FORMAT, self)]
         for axis in axisTags:
-            fixedCoord = floatToFixed(self.coordinates[axis], 16)
+            fixedCoord = fl2fi(self.coordinates[axis], 16)
             result.append(struct.pack(">l", fixedCoord))
         if includePostScriptName:
             result.append(struct.pack(">H", self.postscriptNameID))
@@ -173,7 +181,7 @@
         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)
+            self.coordinates[axis] = fi2fl(value, 16)
             pos += 4
         if pos + 2 <= len(data):
           self.postscriptNameID = struct.unpack(">H", data[pos : pos + 2])[0]
@@ -200,7 +208,7 @@
         writer.newline()
         for axis in ttFont["fvar"].axes:
             writer.simpletag("coord", axis=axis.axisTag,
-                             value=self.coordinates[axis.axisTag])
+                             value=fl2str(self.coordinates[axis.axisTag], 16))
             writer.newline()
         writer.endtag("NamedInstance")
         writer.newline()
@@ -216,4 +224,5 @@
 
         for tag, elementAttrs, _ in filter(lambda t: type(t) is tuple, content):
             if tag == "coord":
-                self.coordinates[elementAttrs["axis"]] = safeEval(elementAttrs["value"])
+                value = str2fl(elementAttrs["value"], 16)
+                self.coordinates[elementAttrs["axis"]] = value
diff --git a/Lib/fontTools/ttLib/tables/_g_a_s_p.py b/Lib/fontTools/ttLib/tables/_g_a_s_p.py
index dce3569..2c80913 100644
--- a/Lib/fontTools/ttLib/tables/_g_a_s_p.py
+++ b/Lib/fontTools/ttLib/tables/_g_a_s_p.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
 import struct
diff --git a/Lib/fontTools/ttLib/tables/_g_c_i_d.py b/Lib/fontTools/ttLib/tables/_g_c_i_d.py
index f8b57e2..2e746c8 100644
--- a/Lib/fontTools/ttLib/tables/_g_c_i_d.py
+++ b/Lib/fontTools/ttLib/tables/_g_c_i_d.py
@@ -1,5 +1,3 @@
-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_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
index 7acf677..e3bbddb 100644
--- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -1,15 +1,20 @@
 """_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.py23 import tostr
 from fontTools.misc import sstruct
 from fontTools import ttLib
 from fontTools import version
 from fontTools.misc.textTools import safeEval, pad
-from fontTools.misc.arrayTools import calcBounds, calcIntBounds, pointInRect
+from fontTools.misc.arrayTools import 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,
+	floatToFixedToStr as fl2str,
+	strToFixedToFloat as str2fl,
+	otRound,
+)
 from numbers import Number
 from . import DefaultTable
 from . import ttProgram
@@ -19,7 +24,8 @@
 import logging
 import os
 from fontTools.misc import xmlWriter
-from fontTools.ttLib import nameToIdentifier
+from fontTools.misc.filenames import userNameToFileName
+from fontTools.misc.loggingTools import deprecateFunction
 
 log = logging.getLogger(__name__)
 
@@ -51,7 +57,8 @@
 
 	def decompile(self, data, ttFont):
 		loca = ttFont['loca']
-		last = int(loca[0])
+		pos = int(loca[0])
+		nextPos = 0
 		noname = 0
 		self.glyphs = {}
 		self.glyphOrder = glyphOrder = ttFont.getGlyphOrder()
@@ -61,17 +68,17 @@
 			except IndexError:
 				noname = noname + 1
 				glyphName = 'ttxautoglyph%s' % i
-			next = int(loca[i+1])
-			glyphdata = data[last:next]
-			if len(glyphdata) != (next - last):
+			nextPos = int(loca[i+1])
+			glyphdata = data[pos:nextPos]
+			if len(glyphdata) != (nextPos - pos):
 				raise ttLib.TTLibError("not enough 'glyf' table data")
 			glyph = Glyph(glyphdata)
 			self.glyphs[glyphName] = glyph
-			last = next
-		if len(data) - next >= 4:
+			pos = nextPos
+		if len(data) - nextPos >= 4:
 			log.warning(
 				"too much 'glyf' table data: expected %d, received %d bytes",
-				next, len(data))
+				nextPos, len(data))
 		if noname:
 			log.warning('%s glyphs have no name', noname)
 		if ttFont.lazy is False: # Be lazy for None and True
@@ -111,14 +118,20 @@
 					currentLocation += len(glyphData)
 				locations[len(dataList)] = currentLocation
 
-		data = bytesjoin(dataList)
+		data = b''.join(dataList)
 		if 'loca' in ttFont:
 			ttFont['loca'].set(locations)
 		if 'maxp' in ttFont:
 			ttFont['maxp'].numGlyphs = len(self.glyphs)
+		if not data:
+		# As a special case when all glyph in the font are empty, add a zero byte
+		# to the table, so that OTS doesn’t reject it, and to make the table work
+		# on Windows as well.
+		# See https://github.com/khaledhosny/ots/issues/52
+			data = b"\0"
 		return data
 
-	def toXML(self, writer, ttFont, progress=None, splitGlyphs=False):
+	def toXML(self, writer, ttFont, splitGlyphs=False):
 		notice = (
 			"The xMin, yMin, xMax and yMax values\n"
 			"will be recalculated by the compiler.")
@@ -128,20 +141,23 @@
 			writer.comment(notice)
 			writer.newline()
 			writer.newline()
-		counter = 0
-		progressStep = 10
 		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]
+			glyph = self.get(glyphName)
+			if glyph is None:
+				log.warning("glyph '%s' does not exist in glyf table", glyphName)
+				continue
 			if glyph.numberOfContours:
 				if splitGlyphs:
-					path, ext = os.path.splitext(writer.file.name)
-					fileNameTemplate = path + ".%s" + ext
-					glyphPath = fileNameTemplate % nameToIdentifier(glyphName)
+					glyphPath = userNameToFileName(
+						tostr(glyphName, 'utf-8'),
+						existingGlyphFiles,
+						prefix=path + ".",
+						suffix=ext)
+					existingGlyphFiles.add(glyphPath.lower())
 					glyphWriter = xmlWriter.XMLWriter(
 						glyphPath, idlefunc=writer.idlefunc,
 						newlinestr=writer.newlinestr)
@@ -221,6 +237,12 @@
 
 	__contains__ = has_key
 
+	def get(self, glyphName, default=None):
+		glyph = self.glyphs.get(glyphName, default)
+		if glyph is not None:
+			glyph.expand(self)
+		return glyph
+
 	def __getitem__(self, glyphName):
 		glyph = self.glyphs[glyphName]
 		glyph.expand(self)
@@ -239,6 +261,193 @@
 		assert len(self.glyphOrder) == len(self.glyphs)
 		return len(self.glyphs)
 
+	def _getPhantomPoints(self, glyphName, hMetrics, vMetrics=None):
+		"""Compute the four "phantom points" for the given glyph from its bounding box
+		and the horizontal and vertical advance widths and sidebearings stored in the
+		ttFont's "hmtx" and "vmtx" tables.
+
+		'hMetrics' should be ttFont['hmtx'].metrics.
+
+		'vMetrics' should be ttFont['vmtx'].metrics if there is "vmtx" or None otherwise.
+		If there is no vMetrics passed in, vertical phantom points are set to the zero coordinate.
+
+		https://docs.microsoft.com/en-us/typography/opentype/spec/tt_instructing_glyphs#phantoms
+		"""
+		glyph = self[glyphName]
+		if not hasattr(glyph, 'xMin'):
+			glyph.recalcBounds(self)
+
+		horizontalAdvanceWidth, leftSideBearing = hMetrics[glyphName]
+		leftSideX = glyph.xMin - leftSideBearing
+		rightSideX = leftSideX + horizontalAdvanceWidth
+
+		if vMetrics:
+			verticalAdvanceWidth, topSideBearing = vMetrics[glyphName]
+			topSideY = topSideBearing + glyph.yMax
+			bottomSideY = topSideY - verticalAdvanceWidth
+		else:
+			bottomSideY = topSideY = 0
+
+		return [
+			(leftSideX, 0),
+			(rightSideX, 0),
+			(0, topSideY),
+			(0, bottomSideY),
+		]
+
+	def _getCoordinatesAndControls(self, glyphName, hMetrics, vMetrics=None):
+		"""Return glyph coordinates and controls as expected by "gvar" table.
+
+		The coordinates includes four "phantom points" for the glyph metrics,
+		as mandated by the "gvar" spec.
+
+		The glyph controls is a namedtuple with the following attributes:
+			- numberOfContours: -1 for composite glyphs.
+			- endPts: list of indices of end points for each contour in simple
+			glyphs, or component indices in composite glyphs (used for IUP
+			optimization).
+			- flags: array of contour point flags for simple glyphs (None for
+			composite glyphs).
+			- components: list of base glyph names (str) for each component in
+			composite glyphs (None for simple glyphs).
+
+		The "hMetrics" and vMetrics are used to compute the "phantom points" (see
+		the "_getPhantomPoints" method).
+
+		Return None if the requested glyphName is not present.
+		"""
+		glyph = self.get(glyphName)
+		if glyph is None:
+			return None
+		if glyph.isComposite():
+			coords = GlyphCoordinates(
+				[(getattr(c, 'x', 0), getattr(c, 'y', 0)) for c in glyph.components]
+			)
+			controls = _GlyphControls(
+				numberOfContours=glyph.numberOfContours,
+				endPts=list(range(len(glyph.components))),
+				flags=None,
+				components=[c.glyphName for c in glyph.components],
+			)
+		else:
+			coords, endPts, flags = glyph.getCoordinates(self)
+			coords = coords.copy()
+			controls = _GlyphControls(
+				numberOfContours=glyph.numberOfContours,
+				endPts=endPts,
+				flags=flags,
+				components=None,
+			)
+		# Add phantom points for (left, right, top, bottom) positions.
+		phantomPoints = self._getPhantomPoints(glyphName, hMetrics, vMetrics)
+		coords.extend(phantomPoints)
+		return coords, controls
+
+	def _setCoordinates(self, glyphName, coord, hMetrics, vMetrics=None):
+		"""Set coordinates and metrics for the given glyph.
+
+		"coord" is an array of GlyphCoordinates which must include the "phantom
+		points" as the last four coordinates.
+
+		Both the horizontal/vertical advances and left/top sidebearings in "hmtx"
+		and "vmtx" tables (if any) are updated from four phantom points and
+		the glyph's bounding boxes.
+
+		The "hMetrics" and vMetrics are used to propagate "phantom points"
+		into "hmtx" and "vmtx" tables if desired.  (see the "_getPhantomPoints"
+		method).
+		"""
+		glyph = self[glyphName]
+
+		# Handle phantom points for (left, right, top, bottom) positions.
+		assert len(coord) >= 4
+		leftSideX = coord[-4][0]
+		rightSideX = coord[-3][0]
+		topSideY = coord[-2][1]
+		bottomSideY = coord[-1][1]
+
+		coord = coord[:-4]
+
+		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 == 0:
+			assert len(coord) == 0
+		else:
+			assert len(coord) == len(glyph.coordinates)
+			glyph.coordinates = GlyphCoordinates(coord)
+
+		glyph.recalcBounds(self)
+
+		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)
+		hMetrics[glyphName] = horizontalAdvanceWidth, leftSideBearing
+
+		if vMetrics is not None:
+			verticalAdvanceWidth = otRound(topSideY - bottomSideY)
+			if verticalAdvanceWidth < 0:  # unlikely but do the same as horizontal
+				verticalAdvanceWidth = 0
+			topSideBearing = otRound(topSideY - glyph.yMax)
+			vMetrics[glyphName] = verticalAdvanceWidth, topSideBearing
+
+
+	# Deprecated
+
+	def _synthesizeVMetrics(self, glyphName, ttFont, defaultVerticalOrigin):
+		"""This method is wrong and deprecated.
+		For rationale see:
+		https://github.com/fonttools/fonttools/pull/2266/files#r613569473
+		"""
+		vMetrics = getattr(ttFont.get('vmtx'), 'metrics', None)
+		if vMetrics is None:
+			verticalAdvanceWidth = ttFont["head"].unitsPerEm
+			topSideY = getattr(ttFont.get('hhea'), 'ascent', None)
+			if topSideY is None:
+				if defaultVerticalOrigin is not None:
+					topSideY = defaultVerticalOrigin
+				else:
+					topSideY = verticalAdvanceWidth
+			glyph = self[glyphName]
+			glyph.recalcBounds(self)
+			topSideBearing = otRound(topSideY - glyph.yMax)
+			vMetrics = {glyphName: (verticalAdvanceWidth, topSideBearing)}
+		return vMetrics
+
+	@deprecateFunction("use '_getPhantomPoints' instead", category=DeprecationWarning)
+	def getPhantomPoints(self, glyphName, ttFont, defaultVerticalOrigin=None):
+		"""Old public name for self._getPhantomPoints().
+		See: https://github.com/fonttools/fonttools/pull/2266"""
+		hMetrics = ttFont['hmtx'].metrics
+		vMetrics = self._synthesizeVMetrics(glyphName, ttFont, defaultVerticalOrigin)
+		return self._getPhantomPoints(glyphName, hMetrics, vMetrics)
+
+	@deprecateFunction("use '_getCoordinatesAndControls' instead", category=DeprecationWarning)
+	def getCoordinatesAndControls(self, glyphName, ttFont, defaultVerticalOrigin=None):
+		"""Old public name for self._getCoordinatesAndControls().
+		See: https://github.com/fonttools/fonttools/pull/2266"""
+		hMetrics = ttFont['hmtx'].metrics
+		vMetrics = self._synthesizeVMetrics(glyphName, ttFont, defaultVerticalOrigin)
+		return self._getCoordinatesAndControls(glyphName, hMetrics, vMetrics)
+
+	@deprecateFunction("use '_setCoordinates' instead", category=DeprecationWarning)
+	def setCoordinates(self, glyphName, ttFont):
+		"""Old public name for self._setCoordinates().
+		See: https://github.com/fonttools/fonttools/pull/2266"""
+		hMetrics = ttFont['hmtx'].metrics
+		vMetrics = getattr(ttFont.get('vmtx'), 'metrics', None)
+		self._setCoordinates(glyphName, hMetrics, vMetrics)
+
+
+_GlyphControls = namedtuple(
+	"_GlyphControls", "numberOfContours endPts flags components"
+)
+
 
 glyphHeaderFormat = """
 		>	# big endian
@@ -256,8 +465,11 @@
 flagRepeat = 0x08
 flagXsame =  0x10
 flagYsame = 0x20
-flagReserved1 = 0x40
-flagReserved2 = 0x80
+flagOverlapSimple = 0x40
+flagReserved = 0x80
+
+# These flags are kept for XML output after decompiling the coordinates
+keepFlags = flagOnCurve + flagOverlapSimple
 
 _flagSignBytes = {
 	0: 2,
@@ -314,8 +526,7 @@
 	elif byteCount == -1:
 		coordBytes.append(-coord)
 	elif byteCount == 2:
-		coordBytes.append((coord >> 8) & 0xFF)
-		coordBytes.append(coord & 0xFF)
+		coordBytes.extend(struct.pack('>h', coord))
 
 def flagEncodeCoords(flag, x, y, xBytes, yBytes):
 	flagEncodeCoord(flag, flagXsame|flagXShort, x, xBytes)
@@ -342,7 +553,7 @@
 
 class Glyph(object):
 
-	def __init__(self, data=""):
+	def __init__(self, data=b""):
 		if not data:
 			# empty char
 			self.numberOfContours = 0
@@ -383,7 +594,7 @@
 			else:
 				return self.data
 		if self.numberOfContours == 0:
-			return ""
+			return b''
 		if recalcBBoxes:
 			self.recalcBounds(glyfTable)
 		data = sstruct.pack(glyphHeaderFormat, self)
@@ -404,10 +615,15 @@
 				writer.begintag("contour")
 				writer.newline()
 				for j in range(last, self.endPtsOfContours[i] + 1):
-					writer.simpletag("pt", [
+					attrs = [
 							("x", self.coordinates[j][0]),
 							("y", self.coordinates[j][1]),
-							("on", self.flags[j] & flagOnCurve)])
+							("on", self.flags[j] & flagOnCurve),
+						]
+					if self.flags[j] & flagOverlapSimple:
+						# Apple's rasterizer uses flagOverlapSimple in the first contour/first pt to flag glyphs that contain overlapping contours
+						attrs.append(("overlap", 1))
+					writer.simpletag("pt", attrs)
 					writer.newline()
 				last = self.endPtsOfContours[i] + 1
 				writer.endtag("contour")
@@ -429,7 +645,7 @@
 				raise ttLib.TTLibError("can't mix composites and contours in glyph")
 			self.numberOfContours = self.numberOfContours + 1
 			coordinates = GlyphCoordinates()
-			flags = []
+			flags = bytearray()
 			for element in content:
 				if not isinstance(element, tuple):
 					continue
@@ -437,8 +653,10 @@
 				if name != "pt":
 					continue  # ignore anything but "pt"
 				coordinates.append((safeEval(attrs["x"]), safeEval(attrs["y"])))
-				flags.append(not not safeEval(attrs["on"]))
-			flags = array.array("B", flags)
+				flag = bool(safeEval(attrs["on"]))
+				if "overlap" in attrs and bool(safeEval(attrs["overlap"])):
+					flag |= flagOverlapSimple
+				flags.append(flag)
 			if not hasattr(self, "coordinates"):
 				self.coordinates = coordinates
 				self.flags = flags
@@ -468,6 +686,7 @@
 		assert self.isComposite()
 		nContours = 0
 		nPoints = 0
+		initialMaxComponentDepth = maxComponentDepth
 		for compo in self.components:
 			baseGlyph = glyfTable[compo.glyphName]
 			if baseGlyph.numberOfContours == 0:
@@ -475,8 +694,9 @@
 			elif baseGlyph.numberOfContours > 0:
 				nP, nC = baseGlyph.getMaxpValues()
 			else:
-				nP, nC, maxComponentDepth = baseGlyph.getCompositeMaxpValues(
-						glyfTable, maxComponentDepth + 1)
+				nP, nC, componentDepth = baseGlyph.getCompositeMaxpValues(
+						glyfTable, initialMaxComponentDepth + 1)
+				maxComponentDepth = max(maxComponentDepth, componentDepth)
 			nPoints = nPoints + nP
 			nContours = nContours + nC
 		return CompositeMaxpValues(nPoints, nContours, maxComponentDepth)
@@ -507,21 +727,18 @@
 
 	def decompileCoordinates(self, data):
 		endPtsOfContours = array.array("h")
-		endPtsOfContours.fromstring(data[:2*self.numberOfContours])
-		if sys.byteorder != "big":
-			endPtsOfContours.byteswap()
+		endPtsOfContours.frombytes(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:]
+		pos = 2*self.numberOfContours
+		instructionLength, = struct.unpack(">h", data[pos:pos+2])
 		self.program = ttProgram.Program()
-		self.program.fromBytecode(data[:instructionLength])
-		data = data[instructionLength:]
+		self.program.fromBytecode(data[pos+2:pos+2+instructionLength])
+		pos += 2 + instructionLength
 		nCoordinates = self.endPtsOfContours[-1] + 1
 		flags, xCoordinates, yCoordinates = \
-				self.decompileCoordinatesRaw(nCoordinates, data)
+				self.decompileCoordinatesRaw(nCoordinates, data, pos)
 
 		# fill in repetitions and apply signs
 		self.coordinates = coordinates = GlyphCoordinates.zeros(nCoordinates)
@@ -557,25 +774,27 @@
 		assert xIndex == len(xCoordinates)
 		assert yIndex == len(yCoordinates)
 		coordinates.relativeToAbsolute()
-		# discard all flags but for "flagOnCurve"
-		self.flags = array.array("B", (f & flagOnCurve for f in flags))
+		# discard all flags except "keepFlags"
+		for i in range(len(flags)):
+			flags[i] &= keepFlags
+		self.flags = flags
 
-	def decompileCoordinatesRaw(self, nCoordinates, data):
+	def decompileCoordinatesRaw(self, nCoordinates, data, pos=0):
 		# unpack flags and prepare unpacking of coordinates
-		flags = array.array("B", [0] * nCoordinates)
+		flags = bytearray(nCoordinates)
 		# Warning: deep Python trickery going on. We use the struct module to unpack
 		# the coordinates. We build a format string based on the flags, so we can
 		# unpack the coordinates in one struct.unpack() call.
 		xFormat = ">" # big endian
 		yFormat = ">" # big endian
-		i = j = 0
+		j = 0
 		while True:
-			flag = byteord(data[i])
-			i = i + 1
+			flag = data[pos]
+			pos += 1
 			repeat = 1
 			if flag & flagRepeat:
-				repeat = byteord(data[i]) + 1
-				i = i + 1
+				repeat = data[pos] + 1
+				pos += 1
 			for k in range(repeat):
 				if flag & flagXShort:
 					xFormat = xFormat + 'B'
@@ -590,15 +809,14 @@
 			if j >= nCoordinates:
 				break
 		assert j == nCoordinates, "bad glyph flags"
-		data = data[i:]
 		# unpack raw coordinates, krrrrrr-tching!
 		xDataLen = struct.calcsize(xFormat)
 		yDataLen = struct.calcsize(yFormat)
-		if len(data) - (xDataLen + yDataLen) >= 4:
+		if len(data) - pos - (xDataLen + yDataLen) >= 4:
 			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])
+				"too much glyph data: %d excess bytes", len(data) - pos - (xDataLen + yDataLen))
+		xCoordinates = struct.unpack(xFormat, data[pos:pos+xDataLen])
+		yCoordinates = struct.unpack(yFormat, data[pos+xDataLen:pos+xDataLen+yDataLen])
 		return flags, xCoordinates, yCoordinates
 
 	def compileComponents(self, glyfTable):
@@ -621,17 +839,14 @@
 		assert len(self.coordinates) == len(self.flags)
 		data = []
 		endPtsOfContours = array.array("h", self.endPtsOfContours)
-		if sys.byteorder != "big":
-			endPtsOfContours.byteswap()
-		data.append(endPtsOfContours.tostring())
+		if sys.byteorder != "big": endPtsOfContours.byteswap()
+		data.append(endPtsOfContours.tobytes())
 		instructions = self.program.getBytecode()
 		data.append(struct.pack(">h", len(instructions)))
 		data.append(instructions)
 
 		deltas = self.coordinates.copy()
-		if deltas.isFloat():
-			# Warn?
-			deltas.toInt()
+		deltas.toInt()
 		deltas.absoluteToRelative()
 
 		# TODO(behdad): Add a configuration option for this?
@@ -639,14 +854,14 @@
 		#deltas = self.compileDeltasOptimal(self.flags, deltas)
 
 		data.extend(deltas)
-		return bytesjoin(data)
+		return b''.join(data)
 
 	def compileDeltasGreedy(self, flags, deltas):
 		# Implements greedy algorithm for packing coordinate deltas:
 		# uses shortest representation one coordinate at a time.
-		compressedflags = []
-		xPoints = []
-		yPoints = []
+		compressedFlags = bytearray()
+		compressedXs = bytearray()
+		compressedYs = bytearray()
 		lastflag = None
 		repeat = 0
 		for flag,(x,y) in zip(flags, deltas):
@@ -660,9 +875,9 @@
 					flag = flag | flagXsame
 				else:
 					x = -x
-				xPoints.append(bytechr(x))
+				compressedXs.append(x)
 			else:
-				xPoints.append(struct.pack(">h", x))
+				compressedXs.extend(struct.pack('>h', x))
 			# do y
 			if y == 0:
 				flag = flag | flagYsame
@@ -672,24 +887,21 @@
 					flag = flag | flagYsame
 				else:
 					y = -y
-				yPoints.append(bytechr(y))
+				compressedYs.append(y)
 			else:
-				yPoints.append(struct.pack(">h", y))
+				compressedYs.extend(struct.pack('>h', y))
 			# handle repeating flags
 			if flag == lastflag and repeat != 255:
 				repeat = repeat + 1
 				if repeat == 1:
-					compressedflags.append(flag)
+					compressedFlags.append(flag)
 				else:
-					compressedflags[-2] = flag | flagRepeat
-					compressedflags[-1] = repeat
+					compressedFlags[-2] = flag | flagRepeat
+					compressedFlags[-1] = repeat
 			else:
 				repeat = 0
-				compressedflags.append(flag)
+				compressedFlags.append(flag)
 			lastflag = flag
-		compressedFlags = array.array("B", compressedflags).tostring()
-		compressedXs = bytesjoin(xPoints)
-		compressedYs = bytesjoin(yPoints)
 		return (compressedFlags, compressedXs, compressedYs)
 
 	def compileDeltasOptimal(self, flags, deltas):
@@ -720,9 +932,9 @@
 			flags.append(flag)
 		flags.reverse()
 
-		compressedFlags = array.array("B")
-		compressedXs = array.array("B")
-		compressedYs = array.array("B")
+		compressedFlags = bytearray()
+		compressedXs = bytearray()
+		compressedYs = bytearray()
 		coords = iter(deltas)
 		ff = []
 		for flag in flags:
@@ -742,69 +954,12 @@
 			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:
-			if 0:
-				# This branch calculates exact glyph outline bounds
-				# analytically, handling cases without on-curve
-				# extremas, etc.  However, the glyf table header
-				# simply says that the bounds should be min/max x/y
-				# "for coordinate data", so I suppose that means no
-				# fancy thing here, just get extremas of all coord
-				# points (on and off).  As such, this branch is
-				# disabled.
-
-				# Collect on-curve points
-				onCurveCoords = [coords[j] for j in range(len(coords))
-								if flags[j] & flagOnCurve]
-				# Add implicit on-curve points
-				start = 0
-				for end in endPts:
-					last = end
-					for j in range(start, end + 1):
-						if not ((flags[j] | flags[last]) & flagOnCurve):
-							x = (coords[last][0] + coords[j][0]) / 2
-							y = (coords[last][1] + coords[j][1]) / 2
-							onCurveCoords.append((x,y))
-						last = j
-					start = end + 1
-				# Add bounds for curves without an explicit extrema
-				start = 0
-				for end in endPts:
-					last = end
-					for j in range(start, end + 1):
-						if not (flags[j] & flagOnCurve):
-							next = j + 1 if j < end else start
-							bbox = calcBounds([coords[last], coords[next]])
-							if not pointInRect(coords[j], bbox):
-								# Ouch!
-								log.warning("Outline has curve with implicit extrema.")
-								# Ouch!  Find analytical curve bounds.
-								pthis = coords[j]
-								plast = coords[last]
-								if not (flags[last] & flagOnCurve):
-									plast = ((pthis[0]+plast[0])/2, (pthis[1]+plast[1])/2)
-								pnext = coords[next]
-								if not (flags[next] & flagOnCurve):
-									pnext = ((pthis[0]+pnext[0])/2, (pthis[1]+pnext[1])/2)
-								bbox = calcQuadraticBounds(plast, pthis, pnext)
-								onCurveCoords.append((bbox[0],bbox[1]))
-								onCurveCoords.append((bbox[2],bbox[3]))
-						last = j
-					start = end + 1
-
-				self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(onCurveCoords)
-			else:
-				self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(coords)
-		else:
-			self.xMin, self.yMin, self.xMax, self.yMax = (0, 0, 0, 0)
+		self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(coords)
 
 	def isComposite(self):
 		"""Can be called on compact or expanded glyph."""
@@ -824,45 +979,52 @@
 		elif self.isComposite():
 			# it's a composite
 			allCoords = GlyphCoordinates()
-			allFlags = array.array("B")
+			allFlags = bytearray()
 			allEndPts = []
 			for compo in self.components:
 				g = glyfTable[compo.glyphName]
-				coordinates, endPts, flags = g.getCoordinates(glyfTable)
+				try:
+					coordinates, endPts, flags = g.getCoordinates(glyfTable)
+				except RecursionError:
+					raise ttLib.TTLibError("glyph '%s' contains a recursive component reference" % compo.glyphName)
+				coordinates = GlyphCoordinates(coordinates)
 				if hasattr(compo, "firstPt"):
-					# move according to two reference points
+					# component uses two reference points: we apply the transform _before_
+					# computing the offset between the points
+					if hasattr(compo, "transform"):
+						coordinates.transform(compo.transform)
 					x1,y1 = allCoords[compo.firstPt]
 					x2,y2 = coordinates[compo.secondPt]
 					move = x1-x2, y1-y2
-				else:
-					move = compo.x, compo.y
-
-				coordinates = GlyphCoordinates(coordinates)
-				if not hasattr(compo, "transform"):
 					coordinates.translate(move)
 				else:
-					apple_way = compo.flags & SCALED_COMPONENT_OFFSET
-					ms_way = compo.flags & UNSCALED_COMPONENT_OFFSET
-					assert not (apple_way and ms_way)
-					if not (apple_way or ms_way):
-						scale_component_offset = SCALE_COMPONENT_OFFSET_DEFAULT  # see top of this file
-					else:
-						scale_component_offset = apple_way
-					if scale_component_offset:
-						# the Apple way: first move, then scale (ie. scale the component offset)
+					# component uses XY offsets
+					move = compo.x, compo.y
+					if not hasattr(compo, "transform"):
 						coordinates.translate(move)
-						coordinates.transform(compo.transform)
 					else:
-						# the MS way: first scale, then move
-						coordinates.transform(compo.transform)
-						coordinates.translate(move)
+						apple_way = compo.flags & SCALED_COMPONENT_OFFSET
+						ms_way = compo.flags & UNSCALED_COMPONENT_OFFSET
+						assert not (apple_way and ms_way)
+						if not (apple_way or ms_way):
+							scale_component_offset = SCALE_COMPONENT_OFFSET_DEFAULT  # see top of this file
+						else:
+							scale_component_offset = apple_way
+						if scale_component_offset:
+							# the Apple way: first move, then scale (ie. scale the component offset)
+							coordinates.translate(move)
+							coordinates.transform(compo.transform)
+						else:
+							# the MS way: first scale, then move
+							coordinates.transform(compo.transform)
+							coordinates.translate(move)
 				offset = len(allCoords)
 				allEndPts.extend(e + offset for e in endPts)
 				allCoords.extend(coordinates)
 				allFlags.extend(flags)
 			return allCoords, allEndPts, allFlags
 		else:
-			return GlyphCoordinates(), [], array.array("B")
+			return GlyphCoordinates(), [], bytearray()
 
 	def getComponentNames(self, glyfTable):
 		if not hasattr(self, "data"):
@@ -901,14 +1063,18 @@
 			expanding it."""
 		if not hasattr(self, "data"):
 			if remove_hinting:
-				self.program = ttProgram.Program()
-				self.program.fromBytecode([])
+				if self.isComposite():
+					if hasattr(self, "program"):
+						del self.program
+				else:
+					self.program = ttProgram.Program()
+					self.program.fromBytecode([])
 			# No padding to trim.
 			return
 		if not self.data:
 			return
 		numContours = struct.unpack(">h", self.data[:2])[0]
-		data = array.array("B", self.data)
+		data = bytearray(self.data)
 		i = 10
 		if numContours >= 0:
 			i += 2 * numContours # endPtsOfContours
@@ -977,7 +1143,7 @@
 			# Remove padding
 			data = data[:i]
 
-		self.data = data.tostring()
+		self.data = data
 
 	def removeHinting(self):
 		self.trim (remove_hinting=True)
@@ -998,7 +1164,7 @@
 		for end in endPts:
 			end = end + 1
 			contour = coordinates[start:end]
-			cFlags = flags[start:end]
+			cFlags = [flagOnCurve & f for f in flags[start:end]]
 			start = end
 			if 1 not in cFlags:
 				# There is not a single on-curve point on the curve,
@@ -1017,13 +1183,49 @@
 				while contour:
 					nextOnCurve = cFlags.index(1) + 1
 					if nextOnCurve == 1:
-						pen.lineTo(contour[0])
+						# Skip a final lineTo(), as it is implied by
+						# pen.closePath()
+						if len(contour) > 1:
+							pen.lineTo(contour[0])
 					else:
 						pen.qCurveTo(*contour[:nextOnCurve])
 					contour = contour[nextOnCurve:]
 					cFlags = cFlags[nextOnCurve:]
 			pen.closePath()
 
+	def drawPoints(self, pen, glyfTable, offset=0):
+		"""Draw the glyph using the supplied pointPen. Opposed to Glyph.draw(),
+		this will not change the point indices.
+		"""
+
+		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
+			pen.beginPath()
+			# Start with the appropriate segment type based on the final segment
+			segmentType = "line" if cFlags[-1] == 1 else "qcurve"
+			for i, pt in enumerate(contour):
+				if cFlags[i] & flagOnCurve == 1:
+					pen.addPoint(pt, segmentType=segmentType)
+					segmentType = "line"
+				else:
+					pen.addPoint(pt)
+					segmentType = "qcurve"
+			pen.endPath()
+
 	def __eq__(self, other):
 		if type(self) != type(other):
 			return NotImplemented
@@ -1114,8 +1316,8 @@
 				data = data + struct.pack(">HH", self.firstPt, self.secondPt)
 				flags = flags | ARG_1_AND_2_ARE_WORDS
 		else:
-			x = round(self.x)
-			y = round(self.y)
+			x = otRound(self.x)
+			y = otRound(self.y)
 			flags = flags | ARGS_ARE_XY_VALUES
 			if (-128 <= x <= 127) and (-128 <= y <= 127):
 				data = data + struct.pack(">bb", x, y)
@@ -1153,15 +1355,18 @@
 			transform = self.transform
 			if transform[0][1] or transform[1][0]:
 				attrs = attrs + [
-						("scalex", transform[0][0]), ("scale01", transform[0][1]),
-						("scale10", transform[1][0]), ("scaley", transform[1][1]),
-						]
+					("scalex", fl2str(transform[0][0], 14)),
+					("scale01", fl2str(transform[0][1], 14)),
+					("scale10", fl2str(transform[1][0], 14)),
+					("scaley", fl2str(transform[1][1], 14)),
+				]
 			elif transform[0][0] != transform[1][1]:
 				attrs = attrs + [
-						("scalex", transform[0][0]), ("scaley", transform[1][1]),
-						]
+					("scalex", fl2str(transform[0][0], 14)),
+					("scaley", fl2str(transform[1][1], 14)),
+				]
 			else:
-				attrs = attrs + [("scale", transform[0][0])]
+				attrs = attrs + [("scale", fl2str(transform[0][0], 14))]
 		attrs = attrs + [("flags", hex(self.flags))]
 		writer.simpletag("component", attrs)
 		writer.newline()
@@ -1175,17 +1380,17 @@
 			self.x = safeEval(attrs["x"])
 			self.y = safeEval(attrs["y"])
 		if "scale01" in attrs:
-			scalex = safeEval(attrs["scalex"])
-			scale01 = safeEval(attrs["scale01"])
-			scale10 = safeEval(attrs["scale10"])
-			scaley = safeEval(attrs["scaley"])
+			scalex = str2fl(attrs["scalex"], 14)
+			scale01 = str2fl(attrs["scale01"], 14)
+			scale10 = str2fl(attrs["scale10"], 14)
+			scaley = str2fl(attrs["scaley"], 14)
 			self.transform = [[scalex, scale01], [scale10, scaley]]
 		elif "scalex" in attrs:
-			scalex = safeEval(attrs["scalex"])
-			scaley = safeEval(attrs["scaley"])
+			scalex = str2fl(attrs["scalex"], 14)
+			scaley = str2fl(attrs["scaley"], 14)
 			self.transform = [[scalex, 0], [0, scaley]]
 		elif "scale" in attrs:
-			scale = safeEval(attrs["scale"])
+			scale = str2fl(attrs["scale"], 14)
 			self.transform = [[scale, 0], [0, scale]]
 		self.flags = safeEval(attrs["flags"])
 
@@ -1200,38 +1405,22 @@
 
 class GlyphCoordinates(object):
 
-	def __init__(self, iterable=[], typecode="h"):
-		self._a = array.array(typecode)
+	def __init__(self, iterable=[]):
+		self._a = array.array('d')
 		self.extend(iterable)
 
 	@property
 	def array(self):
 		return self._a
 
-	def isFloat(self):
-		return self._a.typecode == 'd'
-
-	def _ensureFloat(self):
-		if self.isFloat():
-			return
-		# 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(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):
-				self._ensureFloat()
-		return p
-
 	@staticmethod
 	def zeros(count):
-		return GlyphCoordinates([(0,0)] * count)
+		g = GlyphCoordinates()
+		g._a.frombytes(bytes(count * 2 * g._a.itemsize))
+		return g
 
 	def copy(self):
-		c = GlyphCoordinates(typecode=self._a.typecode)
+		c = GlyphCoordinates()
 		c._a.extend(self._a)
 		return c
 
@@ -1242,7 +1431,11 @@
 		if isinstance(k, slice):
 			indices = range(*k.indices(len(self)))
 			return [self[i] for i in indices]
-		return self._a[2*k],self._a[2*k+1]
+		a = self._a
+		x = a[2*k]
+		y = a[2*k+1]
+		return (int(x) if x.is_integer() else x,
+			int(y) if y.is_integer() else y)
 
 	def __setitem__(self, k, v):
 		if isinstance(k, slice):
@@ -1251,7 +1444,6 @@
 			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):
@@ -1259,76 +1451,75 @@
 		del self._a[i]
 		del self._a[i]
 
-
 	def __repr__(self):
 		return 'GlyphCoordinates(['+','.join(str(c) for c in self)+'])'
 
 	def append(self, p):
-		p = self._checkFloat(p)
 		self._a.extend(tuple(p))
 
 	def extend(self, iterable):
 		for p in iterable:
-			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(round(n))
-		self._a = a
+	def toInt(self, *, round=otRound):
+		a = self._a
+		for i in range(len(a)):
+			a[i] = round(a[i])
 
 	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
+		for i in range(0, len(a), 2):
+			a[i  ] = x = a[i  ] + x
+			a[i+1] = y = a[i+1] + y
 
 	def absoluteToRelative(self):
 		a = self._a
 		x,y = 0,0
-		for i in range(len(a) // 2):
-			dx = a[2*i  ] - x
-			dy = a[2*i+1] - y
-			x = a[2*i  ]
-			y = a[2*i+1]
-			a[2*i  ] = dx
-			a[2*i+1] = dy
+		for i in range(0, len(a), 2):
+			nx = a[i  ]
+			ny = a[i+1]
+			a[i]   = nx - x
+			a[i+1] = ny - y
+			x = nx
+			y = ny
 
 	def translate(self, p):
 		"""
 		>>> GlyphCoordinates([(1,2)]).translate((.5,0))
 		"""
-		(x,y) = self._checkFloat(p)
+		x,y = p
+		if x == 0 and y == 0:
+			return
 		a = self._a
-		for i in range(len(a) // 2):
-			a[2*i  ] += x
-			a[2*i+1] += y
+		for i in range(0, len(a), 2):
+			a[i]   += x
+			a[i+1] += y
 
 	def scale(self, p):
 		"""
 		>>> GlyphCoordinates([(1,2)]).scale((.5,0))
 		"""
-		(x,y) = self._checkFloat(p)
+		x,y = p
+		if x == 1 and y == 1:
+			return
 		a = self._a
-		for i in range(len(a) // 2):
-			a[2*i  ] *= x
-			a[2*i+1] *= y
+		for i in range(0, len(a), 2):
+			a[i]   *= x
+			a[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  ]
-			y = a[2*i+1]
+		for i in range(0, len(a), 2):
+			x = a[i  ]
+			y = a[i+1]
 			px = x * t[0][0] + y * t[1][0]
 			py = x * t[0][1] + y * t[1][1]
-			self[i] = (px, py)
+			a[i]   = px
+			a[i+1] = py
 
 	def __eq__(self, other):
 		"""
@@ -1394,13 +1585,9 @@
 		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. :(
-		"""
+	def __round__(self, *, round=otRound):
 		r = self.copy()
-		r.toInt()
+		r.toInt(round=round)
 		return r
 
 	def __add__(self, other): return self.copy().__iadd__(other)
@@ -1417,18 +1604,17 @@
 		>>> g = GlyphCoordinates([(1,2)])
 		>>> g += (.5,0)
 		>>> g
-		GlyphCoordinates([(1.5, 2.0)])
+		GlyphCoordinates([(1.5, 2)])
 		>>> g2 = GlyphCoordinates([(3,4)])
 		>>> g += g2
 		>>> g
-		GlyphCoordinates([(4.5, 6.0)])
+		GlyphCoordinates([(4.5, 6)])
 		"""
 		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)
@@ -1442,18 +1628,17 @@
 		>>> g = GlyphCoordinates([(1,2)])
 		>>> g -= (.5,0)
 		>>> g
-		GlyphCoordinates([(0.5, 2.0)])
+		GlyphCoordinates([(0.5, 2)])
 		>>> g2 = GlyphCoordinates([(3,4)])
 		>>> g -= g2
 		>>> g
-		GlyphCoordinates([(-2.5, -2.0)])
+		GlyphCoordinates([(-2.5, -2)])
 		"""
 		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)
@@ -1468,20 +1653,23 @@
 		>>> g *= (2,.5)
 		>>> g *= 2
 		>>> g
-		GlyphCoordinates([(4.0, 2.0)])
+		GlyphCoordinates([(4, 2)])
 		>>> 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
+		if isinstance(other, Number):
+			if other == 1:
+				return self
+			a = self._a
+			for i in range(len(a)):
+				a[i] *= other
+			return self
 		return NotImplemented
 
 	def __itruediv__(self, other):
@@ -1490,7 +1678,7 @@
 		>>> g /= (.5,1.5)
 		>>> g /= 2
 		>>> g
-		GlyphCoordinates([(1.0, 1.0)])
+		GlyphCoordinates([(1, 1)])
 		"""
 		if isinstance(other, Number):
 			other = (other, other)
@@ -1522,20 +1710,6 @@
 	__nonzero__ = __bool__
 
 
-def reprflag(flag):
-	bin = ""
-	if isinstance(flag, str):
-		flag = byteord(flag)
-	while flag:
-		if flag & 0x01:
-			bin = "1" + bin
-		else:
-			bin = "0" + bin
-		flag = flag >> 1
-	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
index 85e83ca..bc283cf 100644
--- a/Lib/fontTools/ttLib/tables/_g_v_a_r.py
+++ b/Lib/fontTools/ttLib/tables/_g_v_a_r.py
@@ -1,9 +1,5 @@
-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
@@ -79,12 +75,13 @@
 		result = [compiledHeader, compiledOffsets]
 		result.extend(sharedTuples)
 		result.extend(compiledGlyphs)
-		return bytesjoin(result)
+		return b''.join(result)
 
 	def compileGlyphs_(self, ttFont, axisTags, sharedCoordIndices):
 		result = []
+		glyf = ttFont['glyf']
 		for glyphName in ttFont.getGlyphOrder():
-			glyph = ttFont["glyf"][glyphName]
+			glyph = glyf[glyphName]
 			pointCount = self.getNumPoints_(glyph)
 			variations = self.variations.get(glyphName, [])
 			result.append(compileGlyph_(variations, pointCount,
@@ -102,13 +99,21 @@
 			axisTags, self.sharedTupleCount, data, self.offsetToSharedTuples)
 		self.variations = {}
 		offsetToData = self.offsetToGlyphVariationData
+		glyf = ttFont['glyf']
 		for i in range(self.glyphCount):
 			glyphName = glyphs[i]
-			glyph = ttFont["glyf"][glyphName]
+			glyph = glyf[glyphName]
 			numPointsInGlyph = self.getNumPoints_(glyph)
 			gvarData = data[offsetToData + offsets[i] : offsetToData + offsets[i + 1]]
-			self.variations[glyphName] = decompileGlyph_(
-				numPointsInGlyph, sharedCoords, axisTags, gvarData)
+			try:
+				self.variations[glyphName] = decompileGlyph_(
+					numPointsInGlyph, sharedCoords, axisTags, gvarData)
+			except Exception:
+				log.error(
+					"Failed to decompile deltas for glyph '%s' (%d points)",
+					glyphName, numPointsInGlyph,
+				)
+				raise
 
 	@staticmethod
 	def decompileOffsets_(data, tableFormat, glyphCount):
@@ -120,9 +125,8 @@
 			# Long format: array of UInt32
 			offsets = array.array("I")
 			offsetsSize = (glyphCount + 1) * 4
-		offsets.fromstring(data[0 : offsetsSize])
-		if sys.byteorder != "big":
-			offsets.byteswap()
+		offsets.frombytes(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,
@@ -152,17 +156,16 @@
 		else:
 			packed = array.array("I", offsets)
 			tableFormat = 1
-		if sys.byteorder != "big":
-			packed.byteswap()
-		return (packed.tostring(), tableFormat)
+		if sys.byteorder != "big": packed.byteswap()
+		return (packed.tobytes(), tableFormat)
 
-	def toXML(self, writer, ttFont, progress=None):
+	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():
+		for glyphName in ttFont.getGlyphNames():
 			variations = self.variations.get(glyphName)
 			if not variations:
 				continue
@@ -212,11 +215,14 @@
 		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
+	result = [
+		struct.pack(">HH", tupleVariationCount, 4 + len(tuples)),
+		tuples,
+		data
+	]
+	if (len(tuples) + len(data)) % 2 != 0:
+		result.append(b"\0")  # padding
+	return b''.join(result)
 
 
 def decompileGlyph_(pointCount, sharedTuples, axisTags, data):
@@ -224,6 +230,8 @@
 		return []
 	tupleVariationCount, offsetToData = struct.unpack(">HH", data[:4])
 	dataPos = offsetToData
-	return tv.decompileTupleVariationStore("gvar", axisTags,
-                                           tupleVariationCount, pointCount,
-                                           sharedTuples, data, 4, 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 e073325..954d1bc 100644
--- a/Lib/fontTools/ttLib/tables/_h_d_m_x.py
+++ b/Lib/fontTools/ttLib/tables/_h_d_m_x.py
@@ -1,8 +1,8 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytechr, byteord, strjoin
 from fontTools.misc import sstruct
 from . import DefaultTable
 import array
+from collections.abc import Mapping
 
 hdmxHeaderFormat = """
 	>   # big endian!
@@ -11,11 +11,6 @@
 	recordSize:	l
 """
 
-try:
-	from collections.abc import Mapping
-except:
-	from UserDict import DictMixin as Mapping
-
 class _GlyphnamedList(Mapping):
 
 	def __init__(self, reverseGlyphOrder, data):
diff --git a/Lib/fontTools/ttLib/tables/_h_e_a_d.py b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
index 9275d41..4d19da0 100644
--- a/Lib/fontTools/ttLib/tables/_h_e_a_d.py
+++ b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
@@ -1,9 +1,9 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
+from fontTools.misc.fixedTools import floatToFixedToStr, strToFixedToFloat
 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 fontTools.misc.arrayTools import intRect, unionRect
 from . import DefaultTable
 import logging
 
@@ -33,20 +33,20 @@
 
 class table__h_e_a_d(DefaultTable.DefaultTable):
 
-	dependencies = ['maxp', 'loca', 'CFF ']
+	dependencies = ['maxp', 'loca', 'CFF ', 'CFF2']
 
 	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"
+			assert rest == b"\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
+		# https://github.com/fonttools/fonttools/issues/99#issuecomment-66776810
 		for stamp in 'created', 'modified':
 			value = getattr(self, stamp)
 			if value > 0xFFFFFFFF:
@@ -63,7 +63,20 @@
 			# 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
+				self.xMin, self.yMin, self.xMax, self.yMax = intRect(topDict.FontBBox)
+			elif 'CFF2' in ttFont:
+				topDict = ttFont['CFF2'].cff.topDictIndex[0]
+				charStrings = topDict.CharStrings
+				fontBBox = None
+				for charString in charStrings.values():
+					bounds = charString.calcBounds(charStrings)
+					if bounds is not None:
+						if fontBBox is not None:
+							fontBBox = unionRect(fontBBox, bounds)
+						else:
+							fontBBox = bounds
+				if fontBBox is not None:
+					self.xMin, self.yMin, self.xMax, self.yMax = intRect(fontBBox)
 		if ttFont.recalcTimestamp:
 			self.modified = timestampNow()
 		data = sstruct.pack(headFormat, self)
@@ -72,12 +85,14 @@
 	def toXML(self, writer, ttFont):
 		writer.comment("Most of this table will be recalculated by the compiler")
 		writer.newline()
-		formatstring, names, fixes = sstruct.getformat(headFormat)
+		_, names, fixes = sstruct.getformat(headFormat)
 		for name in names:
 			value = getattr(self, name)
-			if name in ("created", "modified"):
+			if name in fixes:
+				value = floatToFixedToStr(value, precisionBits=fixes[name])
+			elif name in ("created", "modified"):
 				value = timestampToString(value)
-			if name in ("magicNumber", "checkSumAdjustment"):
+			elif name in ("magicNumber", "checkSumAdjustment"):
 				if value < 0:
 					value = value + 0x100000000
 				value = hex(value)
@@ -90,7 +105,10 @@
 
 	def fromXML(self, name, attrs, content, ttFont):
 		value = attrs["value"]
-		if name in ("created", "modified"):
+		fixes = sstruct.getformat(headFormat)[2]
+		if name in fixes:
+			value = strToFixedToFloat(value, precisionBits=fixes[name])
+		elif name in ("created", "modified"):
 			value = timestampFromString(value)
 		elif name in ("macStyle", "flags"):
 			value = binary2num(value)
diff --git a/Lib/fontTools/ttLib/tables/_h_h_e_a.py b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
index 9e9d085..9b8baaa 100644
--- a/Lib/fontTools/ttLib/tables/_h_h_e_a.py
+++ b/Lib/fontTools/ttLib/tables/_h_h_e_a.py
@@ -1,5 +1,3 @@
-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 fontTools.misc.fixedTools import (
@@ -34,13 +32,26 @@
 
 	# Note: Keep in sync with table__v_h_e_a
 
-	dependencies = ['hmtx', 'glyf', 'CFF ']
+	dependencies = ['hmtx', 'glyf', 'CFF ', 'CFF2']
+
+	# OpenType spec renamed these, add aliases for compatibility
+	@property
+	def ascender(self): return self.ascent
+
+	@ascender.setter
+	def ascender(self,value): self.ascent = value
+
+	@property
+	def descender(self): return self.descent
+
+	@descender.setter
+	def descender(self,value): self.descent = value
 
 	def decompile(self, data, ttFont):
 		sstruct.unpack(hheaFormat, data, self)
 
 	def compile(self, ttFont):
-		if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ')):
+		if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ') or ttFont.isLoaded('CFF2')):
 			self.recalc(ttFont)
 		self.tableVersion = fi2ve(self.tableVersion)
 		return sstruct.pack(hheaFormat, self)
@@ -62,11 +73,15 @@
 					# Calculate those.
 					g.recalcBounds(glyfTable)
 				boundsWidthDict[name] = g.xMax - g.xMin
-		elif 'CFF ' in ttFont:
-			topDict = ttFont['CFF '].cff.topDictIndex[0]
+		elif 'CFF ' in ttFont or 'CFF2' in ttFont:
+			if 'CFF ' in ttFont:
+				topDict = ttFont['CFF '].cff.topDictIndex[0]
+			else:
+				topDict = ttFont['CFF2'].cff.topDictIndex[0]
+			charStrings = topDict.CharStrings
 			for name in ttFont.getGlyphOrder():
-				cs = topDict.CharStrings[name]
-				bounds = cs.calcBounds()
+				cs = charStrings[name]
+				bounds = cs.calcBounds(charStrings)
 				if bounds is not None:
 					boundsWidthDict[name] = int(
 						math.ceil(bounds[2]) - math.floor(bounds[0]))
diff --git a/Lib/fontTools/ttLib/tables/_h_m_t_x.py b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
index 6673f4a..6980b8d 100644
--- a/Lib/fontTools/ttLib/tables/_h_m_t_x.py
+++ b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.roundTools import otRound
 from fontTools import ttLib
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
@@ -22,7 +21,11 @@
 
 	def decompile(self, data, ttFont):
 		numGlyphs = ttFont['maxp'].numGlyphs
-		numberOfMetrics = int(getattr(ttFont[self.headerTag], self.numberOfMetricsName))
+		headerTable = ttFont.get(self.headerTag)
+		if headerTable is not None:
+			numberOfMetrics = int(getattr(headerTable, self.numberOfMetricsName))
+		else:
+			numberOfMetrics = numGlyphs
 		if numberOfMetrics > numGlyphs:
 			log.warning("The %s.%s exceeds the maxp.numGlyphs" % (
 				self.headerTag, self.numberOfMetricsName))
@@ -39,8 +42,7 @@
 		sideBearings = array.array("h", data[:2 * numberOfSideBearings])
 		data = data[2 * numberOfSideBearings:]
 
-		if sys.byteorder != "big":
-			sideBearings.byteswap()
+		if sys.byteorder != "big": sideBearings.byteswap()
 		if data:
 			log.warning("too much '%s' table data" % self.tableTag)
 		self.metrics = {}
@@ -69,23 +71,30 @@
 					glyphName, self.advanceName))
 				hasNegativeAdvances = True
 			metrics.append([advanceWidth, sideBearing])
-		lastAdvance = metrics[-1][0]
-		lastIndex = len(metrics)
-		while metrics[lastIndex-2][0] == lastAdvance:
-			lastIndex -= 1
-			if lastIndex <= 1:
-				# all advances are equal
-				lastIndex = 1
-				break
-		additionalMetrics = metrics[lastIndex:]
-		additionalMetrics = [round(sb) for _, sb in additionalMetrics]
-		metrics = metrics[:lastIndex]
-		numberOfMetrics = len(metrics)
-		setattr(ttFont[self.headerTag], self.numberOfMetricsName, numberOfMetrics)
+
+		headerTable = ttFont.get(self.headerTag)
+		if headerTable is not None:
+			lastAdvance = metrics[-1][0]
+			lastIndex = len(metrics)
+			while metrics[lastIndex-2][0] == lastAdvance:
+				lastIndex -= 1
+				if lastIndex <= 1:
+					# all advances are equal
+					lastIndex = 1
+					break
+			additionalMetrics = metrics[lastIndex:]
+			additionalMetrics = [otRound(sb) for _, sb in additionalMetrics]
+			metrics = metrics[:lastIndex]
+			numberOfMetrics = len(metrics)
+			setattr(headerTable, self.numberOfMetricsName, numberOfMetrics)
+		else:
+			# no hhea/vhea, can't store numberOfMetrics; assume == numGlyphs
+			numberOfMetrics = ttFont["maxp"].numGlyphs
+			additionalMetrics = []
 
 		allMetrics = []
 		for advance, sb in metrics:
-			allMetrics.extend([round(advance), round(sb)])
+			allMetrics.extend([otRound(advance), otRound(sb)])
 		metricsFmt = ">" + self.longMetricFormat * numberOfMetrics
 		try:
 			data = struct.pack(metricsFmt, *allMetrics)
@@ -97,9 +106,8 @@
 			else:
 				raise
 		additionalMetrics = array.array("h", additionalMetrics)
-		if sys.byteorder != "big":
-			additionalMetrics.byteswap()
-		data = data + additionalMetrics.tostring()
+		if sys.byteorder != "big": additionalMetrics.byteswap()
+		data = data + additionalMetrics.tobytes()
 		return data
 
 	def toXML(self, writer, ttFont):
diff --git a/Lib/fontTools/ttLib/tables/_k_e_r_n.py b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
index 6e21a4b..f3f714b 100644
--- a/Lib/fontTools/ttLib/tables/_k_e_r_n.py
+++ b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.ttLib import getSearchRange
 from fontTools.misc.textTools import safeEval, readHex
 from fontTools.misc.fixedTools import (
@@ -48,6 +46,19 @@
 				# This "version" is always 0 so we ignore it here
 				_, length, subtableFormat, coverage = struct.unpack(
 					">HHBB", data[:6])
+				if nTables == 1 and subtableFormat == 0:
+					# The "length" value is ignored since some fonts
+					# (like OpenSans and Calibri) have a subtable larger than
+					# its value.
+					nPairs, = struct.unpack(">H", data[6:8])
+					calculated_length = (nPairs * 6) + 14
+					if length != calculated_length:
+						log.warning(
+							"'kern' subtable longer than defined: "
+							"%d bytes instead of %d bytes" %
+							(calculated_length, length)
+						)
+					length = calculated_length
 			if subtableFormat not in kern_classes:
 				subtable = KernTable_format_unkown(subtableFormat)
 			else:
@@ -128,10 +139,8 @@
 			">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()
+		if sys.byteorder != "big": datas.byteswap()
 		it = iter(datas)
 		glyphOrder = ttFont.getGlyphOrder()
 		for k in range(nPairs):
@@ -154,6 +163,7 @@
 	def compile(self, ttFont):
 		nPairs = len(self.kernTable)
 		searchRange, entrySelector, rangeShift = getSearchRange(nPairs, 6)
+		searchRange &= 0xFFFF
 		data = struct.pack(
 			">HHHH", nPairs, searchRange, entrySelector, rangeShift)
 
@@ -176,6 +186,10 @@
 		if not self.apple:
 			version = 0
 			length = len(data) + 6
+			if length >= 0x10000:
+				log.warning('"kern" subtable overflow, '
+							'truncating length value while preserving pairs.')
+				length &= 0xFFFF
 			header = struct.pack(
 				">HHBB", version, length, self.format, self.coverage)
 		else:
diff --git a/Lib/fontTools/ttLib/tables/_l_c_a_r.py b/Lib/fontTools/ttLib/tables/_l_c_a_r.py
index 4b9c854..e63310e 100644
--- a/Lib/fontTools/ttLib/tables/_l_c_a_r.py
+++ b/Lib/fontTools/ttLib/tables/_l_c_a_r.py
@@ -1,5 +1,3 @@
-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_o_c_a.py b/Lib/fontTools/ttLib/tables/_l_o_c_a.py
index 2fcd528..6a8693e 100644
--- a/Lib/fontTools/ttLib/tables/_l_o_c_a.py
+++ b/Lib/fontTools/ttLib/tables/_l_o_c_a.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from . import DefaultTable
 import sys
 import array
@@ -20,9 +18,8 @@
 		else:
 			format = "H"
 		locations = array.array(format)
-		locations.fromstring(data)
-		if sys.byteorder != "big":
-			locations.byteswap()
+		locations.frombytes(data)
+		if sys.byteorder != "big": locations.byteswap()
 		if not longFormat:
 			l = array.array("I")
 			for i in range(len(locations)):
@@ -47,9 +44,8 @@
 		else:
 			locations = array.array("I", self.locations)
 			ttFont['head'].indexToLocFormat = 1
-		if sys.byteorder != "big":
-			locations.byteswap()
-		return locations.tostring()
+		if sys.byteorder != "big": locations.byteswap()
+		return locations.tobytes()
 
 	def set(self, locations):
 		self.locations = array.array("I", locations)
diff --git a/Lib/fontTools/ttLib/tables/_l_t_a_g.py b/Lib/fontTools/ttLib/tables/_l_t_a_g.py
index 59f8e72..caec72a 100644
--- a/Lib/fontTools/ttLib/tables/_l_t_a_g.py
+++ b/Lib/fontTools/ttLib/tables/_l_t_a_g.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin, tobytes
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
 import struct
diff --git a/Lib/fontTools/ttLib/tables/_m_a_x_p.py b/Lib/fontTools/ttLib/tables/_m_a_x_p.py
index a94a9cf..e810806 100644
--- a/Lib/fontTools/ttLib/tables/_m_a_x_p.py
+++ b/Lib/fontTools/ttLib/tables/_m_a_x_p.py
@@ -1,5 +1,3 @@
-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
@@ -107,6 +105,7 @@
 		self.maxContours = maxContours
 		self.maxCompositePoints = maxCompositePoints
 		self.maxCompositeContours = maxCompositeContours
+		self.maxComponentElements = maxComponentElements
 		self.maxComponentDepth = maxComponentDepth
 		if allXMinIsLsb:
 			headTable.flags = headTable.flags | 0x2
diff --git a/Lib/fontTools/ttLib/tables/_m_e_t_a.py b/Lib/fontTools/ttLib/tables/_m_e_t_a.py
index 33067fd..1a125f8 100644
--- a/Lib/fontTools/ttLib/tables/_m_e_t_a.py
+++ b/Lib/fontTools/ttLib/tables/_m_e_t_a.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin, strjoin
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import readHex
 from fontTools.ttLib import TTLibError
@@ -74,7 +73,7 @@
             dataOffset += len(data)
         return bytesjoin([header] + dataMaps + dataBlocks)
 
-    def toXML(self, writer, ttFont, progress=None):
+    def toXML(self, writer, ttFont):
         for tag in sorted(self.data.keys()):
             if tag in ["dlng", "slng"]:
                 writer.begintag("text", tag=tag)
@@ -86,7 +85,11 @@
             else:
                 writer.begintag("hexdata", tag=tag)
                 writer.newline()
-                writer.dumphex(self.data[tag])
+                data = self.data[tag]
+                if min(data) >= 0x20 and max(data) <= 0x7E:
+                    writer.comment("ascii: " + data.decode("ascii"))
+                    writer.newline()
+                writer.dumphex(data)
                 writer.endtag("hexdata")
                 writer.newline()
 
diff --git a/Lib/fontTools/ttLib/tables/_m_o_r_t.py b/Lib/fontTools/ttLib/tables/_m_o_r_t.py
index b87b425..261e593 100644
--- a/Lib/fontTools/ttLib/tables/_m_o_r_t.py
+++ b/Lib/fontTools/ttLib/tables/_m_o_r_t.py
@@ -1,5 +1,3 @@
-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_o_r_x.py b/Lib/fontTools/ttLib/tables/_m_o_r_x.py
index 1619d8d..da299c6 100644
--- a/Lib/fontTools/ttLib/tables/_m_o_r_x.py
+++ b/Lib/fontTools/ttLib/tables/_m_o_r_x.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
index a30291c..206469d 100644
--- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py
+++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
@@ -1,7 +1,5 @@
 # -*- coding: utf-8 -*-
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytechr, byteord, bytesjoin, strjoin, tobytes, tostr
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval
 from fontTools.misc.encodingTools import getEncoding
@@ -135,7 +133,7 @@
 		"""
 		if not hasattr(self, 'names'):
 			self.names = []
-		if not isinstance(string, unicode):
+		if not isinstance(string, str):
 			if isinstance(string, bytes):
 				log.warning(
 					"name string is bytes, ensure it's correctly encoded: %r", string)
@@ -149,6 +147,31 @@
 		else:
 			self.names.append(makeName(string, nameID, platformID, platEncID, langID))
 
+	def removeNames(self, nameID=None, platformID=None, platEncID=None, langID=None):
+		"""Remove any name records identified by the given combination of 'nameID',
+		'platformID', 'platEncID' and 'langID'.
+		"""
+		args = {
+			argName: argValue
+			for argName, argValue in (
+				("nameID", nameID),
+				("platformID", platformID),
+				("platEncID", platEncID),
+				("langID", langID),
+			)
+			if argValue is not None
+		}
+		if not args:
+			# no arguments, nothing to do
+			return
+		self.names = [
+			rec for rec in self.names
+			if any(
+				argValue != getattr(rec, argName)
+				for argName, argValue in args.items()
+			)
+		]
+
 	def _findUnusedNameID(self, minNameID=256):
 		"""Finds an unused name id.
 
@@ -161,7 +184,65 @@
 			raise ValueError("nameID must be less than 32768")
 		return nameID
 
-	def addMultilingualName(self, names, ttFont=None, nameID=None):
+	def findMultilingualName(self, names, windows=True, mac=True, minNameID=0):
+		"""Return the name ID of an existing multilingual name that
+		matches the 'names' dictionary, or None if not found.
+
+		'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.
+
+		If 'windows' is True, the returned name ID is guaranteed
+		exist for all requested languages for platformID=3 and
+		platEncID=1.
+		If 'mac' is True, the returned name ID is guaranteed to exist
+		for all requested languages for platformID=1 and platEncID=0.
+
+		The returned name ID will not be less than the 'minNameID'
+		argument.
+		"""
+		# Gather the set of requested
+		#   (string, platformID, platEncID, langID)
+		# tuples
+		reqNameSet = set()
+		for lang, name in sorted(names.items()):
+			if windows:
+				windowsName = _makeWindowsName(name, None, lang)
+				if windowsName is not None:
+					reqNameSet.add((windowsName.string,
+					                windowsName.platformID,
+					                windowsName.platEncID,
+					                windowsName.langID))
+			if mac:
+				macName = _makeMacName(name, None, lang)
+				if macName is not None:
+					reqNameSet.add((macName.string,
+				                    macName.platformID,
+				                    macName.platEncID,
+				                    macName.langID))
+
+		# Collect matching name IDs
+		matchingNames = dict()
+		for name in self.names:
+			try:
+				key = (name.toUnicode(), name.platformID,
+				       name.platEncID, name.langID)
+			except UnicodeDecodeError:
+				continue
+			if key in reqNameSet and name.nameID >= minNameID:
+				nameSet = matchingNames.setdefault(name.nameID, set())
+				nameSet.add(key)
+
+		# Return the first name ID that defines all requested strings
+		for nameID, nameSet in sorted(matchingNames.items()):
+			if nameSet == reqNameSet:
+				return nameID
+
+		return None  # not found
+
+	def addMultilingualName(self, names, ttFont=None, nameID=None,
+	                        windows=True, mac=True, minNameID=0):
 		"""Add a multilingual name, returning its name ID
 
 		'names' is a dictionary with the name in multiple languages,
@@ -175,24 +256,37 @@
 		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.
+		find an existing set of name records that match, or pick an
+		unused name ID.
+
+		If 'windows' is True, a platformID=3 name record will be added.
+		If 'mac' is True, a platformID=1 name record will be added.
+
+		If the 'nameID' argument is None, the created nameID will not
+		be less than the 'minNameID' argument.
 		"""
 		if not hasattr(self, 'names'):
 			self.names = []
 		if nameID is None:
+			# Reuse nameID if possible
+			nameID = self.findMultilingualName(
+				names, windows=windows, mac=mac, minNameID=minNameID)
+			if nameID is not None:
+				return nameID
 			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:
+			if windows:
+				windowsName = _makeWindowsName(name, nameID, lang)
+				if windowsName is not None:
+					self.names.append(windowsName)
+				else:
+					# We cannot not make a Windows name: make sure we add a
+					# Mac name as a fallback. This can happen for exotic
+					# BCP47 language tags that have no Windows language code.
+					mac = True
+			if mac:
 				macName = _makeMacName(name, nameID, lang, ttFont)
 				if macName is not None:
 					self.names.append(macName)
@@ -216,10 +310,9 @@
 			"'platforms' must contain at least one (platformID, platEncID, langID) tuple"
 		if not hasattr(self, 'names'):
 			self.names = []
-		if not isinstance(string, unicode):
+		if not isinstance(string, str):
 			raise TypeError(
-				"expected %s, found %s: %r" % (
-					unicode.__name__, type(string).__name__,string ))
+				"expected str, found %s: %r" % (type(string).__name__, string))
 		nameID = self._findUnusedNameID(minNameID + 1)
 		for platformID, platEncID, langID in platforms:
 			self.names.append(makeName(string, nameID, platformID, platEncID, langID))
@@ -347,7 +440,7 @@
 		encoding = self.getEncoding()
 		string = self.string
 
-		if encoding == 'utf_16_be' and len(string) % 2 == 1:
+		if isinstance(string, bytes) and 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,
@@ -363,7 +456,7 @@
 			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)
+		string = tostr(string, encoding=encoding, errors=errors)
 
 		# If decoded strings still looks like UTF-16BE, it suggests a double-encoding.
 		# Fix it up.
@@ -387,13 +480,7 @@
 		"""
 		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)
+	toStr = toUnicode
 
 	def toXML(self, writer, ttFont):
 		try:
@@ -437,22 +524,32 @@
 		if type(self) != type(other):
 			return NotImplemented
 
-		# implemented so that list.sort() sorts according to the spec.
-		selfTuple = (
-			getattr(self, "platformID", None),
-			getattr(self, "platEncID", None),
-			getattr(self, "langID", None),
-			getattr(self, "nameID", None),
-			getattr(self, "string", None),
-		)
-		otherTuple = (
-			getattr(other, "platformID", None),
-			getattr(other, "platEncID", None),
-			getattr(other, "langID", None),
-			getattr(other, "nameID", None),
-			getattr(other, "string", None),
-		)
-		return selfTuple < otherTuple
+		try:
+			# implemented so that list.sort() sorts according to the spec.
+			selfTuple = (
+				self.platformID,
+				self.platEncID,
+				self.langID,
+				self.nameID,
+				self.toBytes(),
+			)
+			otherTuple = (
+				other.platformID,
+				other.platEncID,
+				other.langID,
+				other.nameID,
+				other.toBytes(),
+			)
+			return selfTuple < otherTuple
+		except (UnicodeEncodeError, AttributeError):
+			# This can only happen for
+			# 1) an object that is not a NameRecord, or
+			# 2) an unlikely incomplete NameRecord object which has not been
+			#    fully populated, or
+			# 3) when all IDs are identical but the strings can't be encoded
+			#    for their platform encoding.
+			# In all cases it is best to return NotImplemented.
+			return NotImplemented
 
 	def __repr__(self):
 		return "<NameRecord NameID=%d; PlatformID=%d; LanguageID=%d>" % (
diff --git a/Lib/fontTools/ttLib/tables/_o_p_b_d.py b/Lib/fontTools/ttLib/tables/_o_p_b_d.py
index 60bb6c5..b22af21 100644
--- a/Lib/fontTools/ttLib/tables/_o_p_b_d.py
+++ b/Lib/fontTools/ttLib/tables/_o_p_b_d.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/_p_o_s_t.py b/Lib/fontTools/ttLib/tables/_p_o_s_t.py
index ede62da..9bd4944 100644
--- a/Lib/fontTools/ttLib/tables/_p_o_s_t.py
+++ b/Lib/fontTools/ttLib/tables/_p_o_s_t.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytechr, byteord, tobytes, tostr
 from fontTools import ttLib
 from fontTools.ttLib.standardGlyphOrder import standardGlyphOrder
 from fontTools.misc import sstruct
@@ -8,7 +7,9 @@
 import sys
 import struct
 import array
+import logging
 
+log = logging.getLogger(__name__)
 
 postFormat = """
 	>
@@ -83,11 +84,11 @@
 			numGlyphs = ttFont['maxp'].numGlyphs
 		data = data[2:]
 		indices = array.array("H")
-		indices.fromstring(data[:2*numGlyphs])
-		if sys.byteorder != "big":
-			indices.byteswap()
+		indices.frombytes(data[:2*numGlyphs])
+		if sys.byteorder != "big": indices.byteswap()
 		data = data[2*numGlyphs:]
-		self.extraNames = extraNames = unpackPStrings(data)
+		maxIndex = max(indices)
+		self.extraNames = extraNames = unpackPStrings(data, maxIndex-257)
 		self.glyphOrder = glyphOrder = [""] * int(ttFont['maxp'].numGlyphs)
 		for glyphID in range(numGlyphs):
 			index = indices[glyphID]
@@ -133,9 +134,8 @@
 		from fontTools import agl
 		numGlyphs = ttFont['maxp'].numGlyphs
 		indices = array.array("H")
-		indices.fromstring(data)
-		if sys.byteorder != "big":
-			indices.byteswap()
+		indices.frombytes(data)
+		if sys.byteorder != "big": indices.byteswap()
 		# In some older fonts, the size of the post table doesn't match
 		# the number of glyphs. Sometimes it's bigger, sometimes smaller.
 		self.glyphOrder = glyphOrder = [''] * int(numGlyphs)
@@ -173,9 +173,8 @@
 				extraDict[psName] = len(extraNames)
 				extraNames.append(psName)
 			indices.append(index)
-		if sys.byteorder != "big":
-			indices.byteswap()
-		return struct.pack(">H", numGlyphs) + indices.tostring() + packPStrings(extraNames)
+		if sys.byteorder != "big": indices.byteswap()
+		return struct.pack(">H", numGlyphs) + indices.tobytes() + packPStrings(extraNames)
 
 	def encode_format_4_0(self, ttFont):
 		from fontTools import agl
@@ -191,9 +190,8 @@
 				indices.append(int(glyphID[3:],16))
 			else:
 				indices.append(0xFFFF)
-		if sys.byteorder != "big":
-			indices.byteswap()
-		return indices.tostring()
+		if sys.byteorder != "big": indices.byteswap()
+		return indices.tobytes()
 
 	def toXML(self, writer, ttFont):
 		formatstring, names, fixes = sstruct.getformat(postFormat)
@@ -257,14 +255,34 @@
 			self.data = readHex(content)
 
 
-def unpackPStrings(data):
+def unpackPStrings(data, n):
+        # extract n Pascal strings from data.
+        # if there is not enough data, use ""
+
 	strings = []
 	index = 0
 	dataLen = len(data)
-	while index < dataLen:
-		length = byteord(data[index])
-		strings.append(tostr(data[index+1:index+1+length], encoding="latin1"))
-		index = index + 1 + length
+
+	for _ in range(n):
+		if dataLen <= index:
+			length = 0
+		else:
+			length = byteord(data[index])
+		index += 1
+
+		if dataLen <= index + length - 1:
+			name = ""
+		else:
+			name = tostr(data[index:index+length], encoding="latin1")
+		strings.append (name)
+		index += length
+
+	if index < dataLen:
+		log.warning("%d extra bytes in post.stringData array", dataLen - index)
+
+	elif dataLen < index:
+		log.warning("not enough data in post.stringData array")
+
 	return strings
 
 
diff --git a/Lib/fontTools/ttLib/tables/_p_r_e_p.py b/Lib/fontTools/ttLib/tables/_p_r_e_p.py
index f4e8925..7f517fb 100644
--- a/Lib/fontTools/ttLib/tables/_p_r_e_p.py
+++ b/Lib/fontTools/ttLib/tables/_p_r_e_p.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools import ttLib
 
 superclass = ttLib.getTableClass("fpgm")
diff --git a/Lib/fontTools/ttLib/tables/_p_r_o_p.py b/Lib/fontTools/ttLib/tables/_p_r_o_p.py
index 7da18ea..aead9d7 100644
--- a/Lib/fontTools/ttLib/tables/_p_r_o_p.py
+++ b/Lib/fontTools/ttLib/tables/_p_r_o_p.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from .otBase import BaseTTXConverter
 
 
diff --git a/Lib/fontTools/ttLib/tables/_s_b_i_x.py b/Lib/fontTools/ttLib/tables/_s_b_i_x.py
index a8adc0c..c4b2ad3 100644
--- a/Lib/fontTools/ttLib/tables/_s_b_i_x.py
+++ b/Lib/fontTools/ttLib/tables/_s_b_i_x.py
@@ -1,10 +1,7 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc import sstruct
 from fontTools.misc.textTools import safeEval, num2binary, binary2num
 from . import DefaultTable
-from .sbixGlyph import *
-from .sbixStrike import *
+from .sbixStrike import Strike
 
 
 sbixHeaderFormat = """
@@ -69,7 +66,7 @@
 		del self.numStrikes
 
 	def compile(self, ttFont):
-		sbixData = ""
+		sbixData = b""
 		self.numStrikes = len(self.strikes)
 		sbixHeader = sstruct.pack(sbixHeaderFormat, self)
 
diff --git a/Lib/fontTools/ttLib/tables/_t_r_a_k.py b/Lib/fontTools/ttLib/tables/_t_r_a_k.py
index 467375f..7f3227d 100644
--- a/Lib/fontTools/ttLib/tables/_t_r_a_k.py
+++ b/Lib/fontTools/ttLib/tables/_t_r_a_k.py
@@ -1,15 +1,16 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin
 from fontTools.misc import sstruct
-from fontTools.misc.fixedTools import fixedToFloat as fi2fl, floatToFixed as fl2fi
+from fontTools.misc.fixedTools import (
+	fixedToFloat as fi2fl,
+	floatToFixed as fl2fi,
+	floatToFixedToStr as fl2str,
+	strToFixedToFloat as str2fl,
+)
 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
+from collections.abc import MutableMapping
 
 
 # Apple's documentation of 'trak':
@@ -92,7 +93,7 @@
 				trackData.decompile(data, offset)
 			setattr(self, direction + 'Data', trackData)
 
-	def toXML(self, writer, ttFont, progress=None):
+	def toXML(self, writer, ttFont):
 		writer.simpletag('version', value=self.version)
 		writer.newline()
 		writer.simpletag('format', value=self.format)
@@ -194,7 +195,7 @@
 			self[entry.track] = entry
 			offset += TRACK_TABLE_ENTRY_FORMAT_SIZE
 
-	def toXML(self, writer, ttFont, progress=None):
+	def toXML(self, writer, ttFont):
 		nTracks = len(self)
 		nSizes = len(self.sizes())
 		writer.comment("nTracks=%d, nSizes=%d" % (nTracks, nSizes))
@@ -254,23 +255,23 @@
 		self.nameIndex = nameIndex
 		self._map = dict(values)
 
-	def toXML(self, writer, ttFont, progress=None):
+	def toXML(self, writer, ttFont):
 		name = ttFont["name"].getDebugName(self.nameIndex)
 		writer.begintag(
 			"trackEntry",
-			(('value', self.track), ('nameIndex', self.nameIndex)))
+			(('value', fl2str(self.track, 16)), ('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.simpletag("track", size=fl2str(size, 16), value=perSizeValue)
 			writer.newline()
 		writer.endtag("trackEntry")
 		writer.newline()
 
 	def fromXML(self, name, attrs, content, ttFont):
-		self.track = safeEval(attrs['value'])
+		self.track = str2fl(attrs['value'], 16)
 		self.nameIndex = safeEval(attrs['nameIndex'])
 		for element in content:
 			if not isinstance(element, tuple):
@@ -278,7 +279,7 @@
 			name, attrs, _ = element
 			if name != 'track':
 				continue
-			size = safeEval(attrs['size'])
+			size = str2fl(attrs['size'], 16)
 			self[size] = safeEval(attrs['value'])
 
 	def __getitem__(self, size):
diff --git a/Lib/fontTools/ttLib/tables/_v_h_e_a.py b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
index 5176951..2bb2466 100644
--- a/Lib/fontTools/ttLib/tables/_v_h_e_a.py
+++ b/Lib/fontTools/ttLib/tables/_v_h_e_a.py
@@ -1,5 +1,3 @@
-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 fontTools.misc.fixedTools import (
@@ -33,13 +31,13 @@
 
 	# Note: Keep in sync with table__h_h_e_a
 
-	dependencies = ['vmtx', 'glyf', 'CFF ']
+	dependencies = ['vmtx', 'glyf', 'CFF ', 'CFF2']
 
 	def decompile(self, data, ttFont):
 		sstruct.unpack(vheaFormat, data, self)
 
 	def compile(self, ttFont):
-		if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ')):
+		if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ') or ttFont.isLoaded('CFF2')):
 			self.recalc(ttFont)
 		self.tableVersion = fi2ve(self.tableVersion)
 		return sstruct.pack(vheaFormat, self)
@@ -61,11 +59,15 @@
 					# Calculate those.
 					g.recalcBounds(glyfTable)
 				boundsHeightDict[name] = g.yMax - g.yMin
-		elif 'CFF ' in ttFont:
-			topDict = ttFont['CFF '].cff.topDictIndex[0]
+		elif 'CFF ' in ttFont or 'CFF2' in ttFont:
+			if 'CFF ' in ttFont:
+				topDict = ttFont['CFF '].cff.topDictIndex[0]
+			else:
+				topDict = ttFont['CFF2'].cff.topDictIndex[0]
+			charStrings = topDict.CharStrings
 			for name in ttFont.getGlyphOrder():
-				cs = topDict.CharStrings[name]
-				bounds = cs.calcBounds()
+				cs = charStrings[name]
+				bounds = cs.calcBounds(charStrings)
 				if bounds is not None:
 					boundsHeightDict[name] = int(
 						math.ceil(bounds[3]) - math.floor(bounds[1]))
diff --git a/Lib/fontTools/ttLib/tables/_v_m_t_x.py b/Lib/fontTools/ttLib/tables/_v_m_t_x.py
index 5573225..fc818d8 100644
--- a/Lib/fontTools/ttLib/tables/_v_m_t_x.py
+++ b/Lib/fontTools/ttLib/tables/_v_m_t_x.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools import ttLib
 
 superclass = ttLib.getTableClass("hmtx")
diff --git a/Lib/fontTools/ttLib/tables/asciiTable.py b/Lib/fontTools/ttLib/tables/asciiTable.py
index 87266a0..7b036c8 100644
--- a/Lib/fontTools/ttLib/tables/asciiTable.py
+++ b/Lib/fontTools/ttLib/tables/asciiTable.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import strjoin, tobytes, tostr
 from . import DefaultTable
 
 
diff --git a/Lib/fontTools/ttLib/tables/distillery/__init__.py b/Lib/fontTools/ttLib/tables/distillery/__init__.py
index 3eee741..d15706d 100644
--- a/Lib/fontTools/ttLib/tables/distillery/__init__.py
+++ b/Lib/fontTools/ttLib/tables/distillery/__init__.py
@@ -58,7 +58,7 @@
 		#self.awaitS = ObjectSet()
 
 	def _itemPriority(self, item):
-		leashLen = (65536)**2 if item.longOffset else 65536
+		leashLen = (65536)**2 if item.offsetSize == 4 else 65536
 		packedParentsPos = [p.pos for p in item.parents if p in self.packedS]
 		leashStart = min(packedParentsPos) if packedParentsPos else (65536)**2 * 2
 		itemLen = len(item)
diff --git a/Lib/fontTools/ttLib/tables/distillery/dot.py b/Lib/fontTools/ttLib/tables/distillery/dot.py
index 97da367..f89dbf3 100644
--- a/Lib/fontTools/ttLib/tables/distillery/dot.py
+++ b/Lib/fontTools/ttLib/tables/distillery/dot.py
@@ -56,7 +56,7 @@
 			for item in writer.items:
 				if not hasattr(item, "getData"):
 					continue
-				offsetSize = 4 if item.longOffset else 2
+				offsetSize = item.offsetSize
 				rhs = writerId(item)
 				f.write('  '+lhs+' -> '+rhs+';\n')
 
diff --git a/Lib/fontTools/ttLib/tables/grUtils.py b/Lib/fontTools/ttLib/tables/grUtils.py
index 1ce2c9e..a60df23 100644
--- a/Lib/fontTools/ttLib/tables/grUtils.py
+++ b/Lib/fontTools/ttLib/tables/grUtils.py
@@ -1,8 +1,10 @@
 import struct, warnings
 try:
     import lz4
-except: 
+except ImportError:
     lz4 = None
+else:
+    import lz4.block
 
 #old scheme for VERSION < 0.9 otherwise use lz4.block
 
@@ -13,7 +15,7 @@
     if scheme == 0:
         pass
     elif scheme == 1 and lz4:
-        res = lz4.decompress(struct.pack("<L", size) + data[8:])
+        res = lz4.block.decompress(struct.pack("<L", size) + data[8:])
         if len(res) != size:
             warnings.warn("Table decompression failed.")
         else:
@@ -27,8 +29,8 @@
     if scheme == 0 :
         return data
     elif scheme == 1 and lz4:
-        res = lz4.compress(hdr + data)
-        return res
+        res = lz4.block.compress(data, mode='high_compression', compression=16, store_size=False)
+        return hdr + res
     else:
         warnings.warn("Table failed to compress by unsupported compression scheme")
     return data
@@ -47,7 +49,7 @@
     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)
+    g = _entries(sorted(attributes.items(), key=lambda x:int(x[0])), sameval)
     return g
 
 def bininfo(num, size=1):
@@ -59,7 +61,7 @@
         srange *= 2
         select += 1
     select -= 1
-    srange /= 2
+    srange //= 2
     srange *= size
     shift = num * size - srange
     return struct.pack(">4H", num, srange, select, shift)
diff --git a/Lib/fontTools/ttLib/tables/otBase.py b/Lib/fontTools/ttLib/tables/otBase.py
index b20a0f7..fe77196 100644
--- a/Lib/fontTools/ttLib/tables/otBase.py
+++ b/Lib/fontTools/ttLib/tables/otBase.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import Tag, bytesjoin
 from .DefaultTable import DefaultTable
 import sys
 import array
@@ -43,7 +42,7 @@
 		self.table.decompile(reader, font)
 
 	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.
@@ -89,7 +88,13 @@
 					from .otTables import fixSubTableOverFlows
 					ok = fixSubTableOverFlows(font, overflowRecord)
 				if not ok:
-					raise
+					# Try upgrading lookup to Extension and hope
+					# that cross-lookup sharing not happening would
+					# fix overflow...
+					from .otTables import fixLookupOverFlows
+					ok = fixLookupOverFlows(font, overflowRecord)
+					if not ok:
+						raise
 
 	def toXML(self, writer, font):
 		self.table.toXML2(writer, font)
@@ -100,8 +105,13 @@
 			tableClass = getattr(otTables, self.tableTag)
 			self.table = tableClass()
 		self.table.fromXML(name, attrs, content, font)
+		self.table.populateDefaults()
 
 
+# https://github.com/fonttools/fonttools/pull/2285#issuecomment-834652928
+assert len(struct.pack('i', 0)) == 4
+assert array.array('i').itemsize == 4, "Oops, file a bug against fonttools."
+
 class OTTableReader(object):
 
 	"""Helper class to retrieve data from an OpenType table."""
@@ -130,49 +140,49 @@
 		offset = self.offset + offset
 		return self.__class__(self.data, self.localState, offset, self.tableTag)
 
-	def readUShort(self):
+	def readValue(self, typecode, staticSize):
 		pos = self.pos
-		newpos = pos + 2
-		value, = struct.unpack(">H", self.data[pos:newpos])
+		newpos = pos + staticSize
+		value, = struct.unpack(f">{typecode}", self.data[pos:newpos])
 		self.pos = newpos
 		return value
-
-	def readUShortArray(self, count):
+	def readArray(self, typecode, staticSize, count):
 		pos = self.pos
-		newpos = pos + count * 2
-		value = array.array("H", self.data[pos:newpos])
-		if sys.byteorder != "big":
-			value.byteswap()
+		newpos = pos + count * staticSize
+		value = array.array(typecode, self.data[pos:newpos])
+		if sys.byteorder != "big": value.byteswap()
 		self.pos = newpos
-		return value
+		return value.tolist()
 
 	def readInt8(self):
-		pos = self.pos
-		newpos = pos + 1
-		value, = struct.unpack(">b", self.data[pos:newpos])
-		self.pos = newpos
-		return value
+		return self.readValue("b", staticSize=1)
+	def readInt8Array(self, count):
+		return self.readArray("b", staticSize=1, count=count)
 
 	def readShort(self):
-		pos = self.pos
-		newpos = pos + 2
-		value, = struct.unpack(">h", self.data[pos:newpos])
-		self.pos = newpos
-		return value
+		return self.readValue("h", staticSize=2)
+	def readShortArray(self, count):
+		return self.readArray("h", staticSize=2, count=count)
 
 	def readLong(self):
-		pos = self.pos
-		newpos = pos + 4
-		value, = struct.unpack(">l", self.data[pos:newpos])
-		self.pos = newpos
-		return value
+		return self.readValue("i", staticSize=4)
+	def readLongArray(self, count):
+		return self.readArray("i", staticSize=4, count=count)
 
 	def readUInt8(self):
-		pos = self.pos
-		newpos = pos + 1
-		value, = struct.unpack(">B", self.data[pos:newpos])
-		self.pos = newpos
-		return value
+		return self.readValue("B", staticSize=1)
+	def readUInt8Array(self, count):
+		return self.readArray("B", staticSize=1, count=count)
+
+	def readUShort(self):
+		return self.readValue("H", staticSize=2)
+	def readUShortArray(self, count):
+		return self.readArray("H", staticSize=2, count=count)
+
+	def readULong(self):
+		return self.readValue("I", staticSize=4)
+	def readULongArray(self, count):
+		return self.readArray("I", staticSize=4, count=count)
 
 	def readUInt24(self):
 		pos = self.pos
@@ -180,13 +190,8 @@
 		value, = struct.unpack(">l", b'\0'+self.data[pos:newpos])
 		self.pos = newpos
 		return value
-
-	def readULong(self):
-		pos = self.pos
-		newpos = pos + 4
-		value, = struct.unpack(">L", self.data[pos:newpos])
-		self.pos = newpos
-		return value
+	def readUInt24Array(self, count):
+		return [self.readUInt24() for _ in range(count)]
 
 	def readTag(self):
 		pos = self.pos
@@ -219,13 +224,14 @@
 
 	"""Helper class to gather and assemble data for OpenType tables."""
 
-	def __init__(self, localState=None, tableTag=None, name=''):
+	def __init__(self, localState=None, tableTag=None, name='', offsetSize=2):
 		self.localState = localState
 		self.tableTag = tableTag
 		self.name = name
-		self.longOffset = False
 		self.items = []
 		self.pos = None
+		self.offsetSize = offsetSize
+		self.parent = None
 
 	def __setitem__(self, name, value):
 		state = self.localState.copy() if self.localState else dict()
@@ -247,7 +253,7 @@
 			if hasattr(item, "getCountData"):
 				l += item.size
 			elif hasattr(item, "getData"):
-				l += 4 if item.longOffset else 2
+				l += item.offsetSize
 			else:
 				l = l + len(item)
 		return l
@@ -264,9 +270,9 @@
 		for i,item in enumerate(items):
 
 			if hasattr(item, "getData"):
-				if item.longOffset:
+				if item.offsetSize == 4:
 					items[i] = packULong(item.pos - pos)
-				else:
+				elif item.offsetSize == 2:
 					try:
 						items[i] = packUShort(item.pos - pos)
 					except struct.error:
@@ -274,6 +280,10 @@
 						overflowErrorRecord = self.getOverflowErrorRecord(item)
 
 						raise OTLOffsetOverflowError(overflowErrorRecord)
+				elif item.offsetSize == 3:
+					items[i] = packUInt24(item.pos - pos)
+				else:
+					raise ValueError(item.offsetSize)
 
 		return bytesjoin(items)
 
@@ -290,7 +300,9 @@
 	def __eq__(self, other):
 		if type(self) != type(other):
 			return NotImplemented
-		if len(self.items) != len(other.items) or self.longOffset != other.longOffset:
+		if len(self.items) != len(other.items):
+			return False
+		if self.offsetSize != other.offsetSize:
 			return False
 		return all(x is y if hasattr(x, 'getData') else x == y for x,y in zip(self.items, other.items))
 
@@ -311,12 +323,14 @@
 		# 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
+		# See: https://github.com/fonttools/fonttools/issues/518
 		dontShare = hasattr(self, 'DontShare')
 
 		items = self.items
 		for i in range(len(items)):
 			item = items[i]
+			if hasattr(item, "getCountData"):
+				items[i] = item.getCountData()
 			if not hasattr(item, "getData"):
 				continue
 
@@ -327,6 +341,60 @@
 					item = items[i] = internedItem
 				else:
 					internedTables[item] = item
+		self.items = tuple(items)
+
+	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
+		# 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.
+
+		done[id(self)] = True
+
+		numItems = len(self.items)
+		iRange = list(range(numItems))
+		iRange.reverse()
+
+		isExtension = hasattr(self, "Extension")
+
+		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 = False
+		if hasattr(self, "sortCoverageLast"):
+			# Find coverage table
+			for i in range(numItems):
+				item = self.items[i]
+				if getattr(item, 'name', None) == "Coverage":
+					sortCoverageLast = True
+					break
+			if id(item) not in done:
+				item._gatherTables(tables, extTables, done)
+			else:
+				# We're a new parent of item
+				pass
+
+		for i in iRange:
+			item = self.items[i]
+			if not hasattr(item, "getData"):
+				continue
+
+			if sortCoverageLast and (i==1) and getattr(item, 'name', None) == 'Coverage':
+				# we've already 'gathered' it above
+				continue
+
+			if id(item) not in done:
+				item._gatherTables(tables, extTables, done)
+			else:
+				# Item is already written out by other parent
+				pass
 
 		self.items = tuple(items)
 
@@ -340,37 +408,63 @@
 
 	# interface for gathering data, as used by table.compile()
 
-	def getSubWriter(self, longOffset=False, name=''):
-		subwriter = self.__class__(self.localState, self.tableTag, name)
-		subwriter.longOffset = longOffset
+	def getSubWriter(self, offsetSize=2, name=''):
+		subwriter = self.__class__(self.localState, self.tableTag, name, offsetSize=offsetSize)
+		subwriter.offsetSize = offsetSize
+		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, 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 writeValue(self, typecode, value):
+		self.items.append(struct.pack(f">{typecode}", value))
+	def writeArray(self, typecode, values):
+		a = array.array(typecode, values)
+		if sys.byteorder != "big": a.byteswap()
+		self.items.append(a.tobytes())
 
 	def writeInt8(self, value):
 		assert -128 <= value < 128, value
 		self.items.append(struct.pack(">b", value))
+	def writeInt8Array(self, values):
+		self.writeArray('b', values)
+
+	def writeShort(self, value):
+		assert -32768 <= value < 32768, value
+		self.items.append(struct.pack(">h", value))
+	def writeShortArray(self, values):
+		self.writeArray('h', values)
+
+	def writeLong(self, value):
+		self.items.append(struct.pack(">i", value))
+	def writeLongArray(self, values):
+		self.writeArray('i', values)
+
+	def writeUInt8(self, value):
+		assert 0 <= value < 256, value
+		self.items.append(struct.pack(">B", value))
+	def writeUInt8Array(self, values):
+		self.writeArray('B', values)
+
+	def writeUShort(self, value):
+		assert 0 <= value < 0x10000, value
+		self.items.append(struct.pack(">H", value))
+	def writeUShortArray(self, values):
+		self.writeArray('H', values)
+
+	def writeULong(self, value):
+		self.items.append(struct.pack(">I", value))
+	def writeULongArray(self, values):
+		self.writeArray('I', values)
 
 	def writeUInt24(self, value):
 		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 writeUInt24Array(self, values):
+		for value in values:
+			self.writeUInt24(value)
 
 	def writeTag(self, tag):
 		tag = Tag(tag).tobytes()
@@ -441,6 +535,8 @@
 			table[name] = value
 		else:
 			assert table[name] == value, (name, table[name], value)
+	def getValue(self):
+		return self.table[self.name]
 	def getCountData(self):
 		v = self.table[self.name]
 		if v is None: v = 0
@@ -455,7 +551,11 @@
 
 def packULong(value):
 	assert 0 <= value < 0x100000000, value
-	return struct.pack(">L", value)
+	return struct.pack(">I", value)
+
+def packUInt24(value):
+	assert 0 <= value < 0x1000000, value
+	return struct.pack(">I", value)[1:]
 
 
 class BaseTable(object):
@@ -490,7 +590,7 @@
 			countValue = 1
 			if conv.repeat:
 				if conv.repeat in reader:
-					countValue = reader[conv.repeat]
+					countValue = reader[conv.repeat] + conv.aux
 				else:
 					return NotImplemented
 			totalSize += size * countValue
@@ -542,22 +642,27 @@
 			if conv.name == "SubStruct":
 				conv = conv.getConverter(reader.tableTag,
 				                         table["MorphType"])
-			if conv.repeat:
-				if isinstance(conv.repeat, int):
-					countValue = conv.repeat
-				elif conv.repeat in table:
-					countValue = table[conv.repeat]
+			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:
-					# 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]
+					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)
@@ -568,11 +673,30 @@
 
 	def compile(self, writer, font):
 		self.ensureDecompiled()
+		# TODO Following hack to be removed by rewriting how FormatSwitching tables
+		# are handled.
+		# https://github.com/fonttools/fonttools/pull/2238#issuecomment-805192631
 		if hasattr(self, 'preWrite'):
+			deleteFormat = not hasattr(self, 'Format')
 			table = self.preWrite(font)
+			deleteFormat = deleteFormat and hasattr(self, 'Format')
 		else:
+			deleteFormat = False
 			table = self.__dict__.copy()
 
+		# some count references may have been initialized in a custom preWrite; we set
+		# these in the writer's state beforehand (instead of sequentially) so they will
+		# be propagated to all nested subtables even if the count appears in the current
+		# table only *after* the offset to the subtable that it is counting.
+		for conv in self.getConverters():
+			if conv.isCount and conv.isPropagated:
+				value = table.get(conv.name)
+				if isinstance(value, CountReference):
+					writer[conv.name] = value
+
+		if hasattr(self, 'sortCoverageLast'):
+			writer.sortCoverageLast = 1
+
 		if hasattr(self, 'DontShare'):
 			writer.DontShare = True
 
@@ -593,14 +717,11 @@
 				else:
 					# conv.repeat is a propagated count
 					writer[conv.repeat].setValue(countValue)
-				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
+				try:
+					conv.writeArray(writer, font, table, value)
+				except Exception as e:
+					e.args = e.args + (conv.name+'[]',)
+					raise
 			elif conv.isCount:
 				# Special-case Count values.
 				# Assumption: a Count field will *always* precede
@@ -609,8 +730,16 @@
 				# 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, conv.staticSize)
-				table[conv.name] = None
+				# We ignore the current count value since it will be recomputed,
+				# unless it's a CountReference that was already initialized in a custom preWrite.
+				if isinstance(value, CountReference):
+					ref = value
+					ref.size = conv.staticSize
+					writer.writeData(ref)
+					table[conv.name] = ref.getValue()
+				else:
+					ref = writer.writeCountReference(table, conv.name, conv.staticSize)
+					table[conv.name] = None
 				if conv.isPropagated:
 					writer[conv.name] = ref
 			elif conv.isLookupType:
@@ -633,6 +762,9 @@
 				if conv.isPropagated:
 					writer[conv.name] = value
 
+		if deleteFormat:
+			del self.Format
+
 	def readFormat(self, reader):
 		pass
 
@@ -707,7 +839,7 @@
 		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]
@@ -722,6 +854,26 @@
 		BaseTable.toXML(self, xmlWriter, font, attrs, name)
 
 
+class UInt8FormatSwitchingBaseTable(FormatSwitchingBaseTable):
+	def readFormat(self, reader):
+		self.Format = reader.readUInt8()
+
+	def writeFormat(self, writer):
+		writer.writeUInt8(self.Format)
+
+
+formatSwitchingBaseTables = {
+	"uint16": FormatSwitchingBaseTable,
+	"uint8": UInt8FormatSwitchingBaseTable,
+}
+
+def getFormatSwitchingBaseTableClass(formatType):
+	try:
+		return formatSwitchingBaseTables[formatType]
+	except KeyError:
+		raise TypeError(f"Unsupported format type: {formatType!r}")
+
+
 #
 # Support for ValueRecords
 #
@@ -822,7 +974,8 @@
 					setattr(self, name, None if isDevice else 0)
 			if src is not None:
 				for key,val in src.__dict__.items():
-					assert hasattr(self, key)
+					if not hasattr(self, key):
+						continue
 					setattr(self, key, val)
 		elif src is not None:
 			self.__dict__ = src.__dict__.copy()
@@ -833,6 +986,13 @@
 			format = format | valueRecordFormatDict[name][0]
 		return format
 
+	def getEffectiveFormat(self):
+		format = 0
+		for name,value in self.__dict__.items():
+			if value:
+				format = format | valueRecordFormatDict[name][0]
+		return format
+
 	def toXML(self, xmlWriter, font, valueName, attrs=None):
 		if attrs is None:
 			simpleItems = []
diff --git a/Lib/fontTools/ttLib/tables/otConverters.py b/Lib/fontTools/ttLib/tables/otConverters.py
index 8509de7..9cd9bfc 100644
--- a/Lib/fontTools/ttLib/tables/otConverters.py
+++ b/Lib/fontTools/ttLib/tables/otConverters.py
@@ -1,15 +1,23 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin, tobytes, tostr
 from fontTools.misc.fixedTools import (
-	fixedToFloat as fi2fl, floatToFixed as fl2fi, ensureVersionIsLong as fi2ve,
-	versionToFixed as ve2fi)
+	fixedToFloat as fi2fl,
+	floatToFixed as fl2fi,
+	floatToFixedToStr as fl2str,
+	strToFixedToFloat as str2fl,
+	ensureVersionIsLong as fi2ve,
+	versionToFixed as ve2fi,
+)
+from fontTools.misc.roundTools import nearestMultipleShortestRepr, otRound
 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)
+                       InsertionMorphAction, MorxSubtable,
+                       ExtendMode as _ExtendMode,
+                       CompositeMode as _CompositeMode)
+from itertools import zip_longest
 from functools import partial
 import struct
 import logging
@@ -52,14 +60,20 @@
 				converterClass = Struct
 			else:
 				converterClass = eval(tp, tableNamespace, converterMapping)
-		if tp in ('MortChain', 'MortSubtable', 'MorxChain'):
+
+		conv = converterClass(name, repeat, aux)
+
+		if conv.tableClass:
+			# A "template" such as OffsetTo(AType) knowss the table class already
+			tableClass = conv.tableClass
+		elif 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 not conv.tableClass:
+			conv.tableClass = tableClass
+
 		if name in ["SubTable", "ExtSubTable", "SubStruct"]:
 			conv.lookupTypes = tableNamespace['lookupTypes']
 			# also create reverse mapping
@@ -130,7 +144,22 @@
 		self.tableClass = tableClass
 		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']
+		self.isPropagated = name in [
+			"ClassCount",
+			"Class2Count",
+			"FeatureTag",
+			"SettingsCount",
+			"VarRegionCount",
+			"MappingCount",
+			"RegionAxisCount",
+			"DesignAxisCount",
+			"DesignAxisRecordSize",
+			"AxisValueCount",
+			"ValueRecordSize",
+			"AxisCount",
+			"BaseGlyphRecordCount",
+			"LayerRecordCount",
+		]
 
 	def readArray(self, reader, font, tableDict, count):
 		"""Read an array of values from the reader."""
@@ -164,8 +193,12 @@
 		raise NotImplementedError(self)
 
 	def writeArray(self, writer, font, tableDict, values):
-		for i, value in enumerate(values):
-			self.write(writer, font, tableDict, value, i)
+		try:
+			for i, value in enumerate(values):
+				self.write(writer, font, tableDict, value, i)
+		except Exception as e:
+			e.args = e.args + (i,)
+			raise
 
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		"""Write a value to the writer."""
@@ -181,62 +214,108 @@
 
 
 class SimpleValue(BaseConverter):
+	@staticmethod
+	def toString(value):
+		return value
+	@staticmethod
+	def fromString(value):
+		return value
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
-		xmlWriter.simpletag(name, attrs + [("value", value)])
+		xmlWriter.simpletag(name, attrs + [("value", self.toString(value))])
 		xmlWriter.newline()
 	def xmlRead(self, attrs, content, font):
-		return attrs["value"]
+		return self.fromString(attrs["value"])
+
+class OptionalValue(SimpleValue):
+	DEFAULT = None
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		if value != self.DEFAULT:
+			attrs.append(("value", self.toString(value)))
+		xmlWriter.simpletag(name, attrs)
+		xmlWriter.newline()
+	def xmlRead(self, attrs, content, font):
+		if "value" in attrs:
+			return self.fromString(attrs["value"])
+		return self.DEFAULT
 
 class IntValue(SimpleValue):
-	def xmlRead(self, attrs, content, font):
-		return int(attrs["value"], 0)
+	@staticmethod
+	def fromString(value):
+		return int(value, 0)
 
 class Long(IntValue):
 	staticSize = 4
 	def read(self, reader, font, tableDict):
 		return reader.readLong()
+	def readArray(self, reader, font, tableDict, count):
+		return reader.readLongArray(count)
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeLong(value)
+	def writeArray(self, writer, font, tableDict, values):
+		writer.writeLongArray(values)
 
 class ULong(IntValue):
 	staticSize = 4
 	def read(self, reader, font, tableDict):
 		return reader.readULong()
+	def readArray(self, reader, font, tableDict, count):
+		return reader.readULongArray(count)
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeULong(value)
+	def writeArray(self, writer, font, tableDict, values):
+		writer.writeULongArray(values)
 
 class Flags32(ULong):
-	def xmlWrite(self, xmlWriter, font, value, name, attrs):
-		xmlWriter.simpletag(name, attrs + [("value", "0x%08X" % value)])
-		xmlWriter.newline()
+	@staticmethod
+	def toString(value):
+		return "0x%08X" % value
+
+class VarIndex(OptionalValue, ULong):
+	DEFAULT = 0xFFFFFFFF
 
 class Short(IntValue):
 	staticSize = 2
 	def read(self, reader, font, tableDict):
 		return reader.readShort()
+	def readArray(self, reader, font, tableDict, count):
+		return reader.readShortArray(count)
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeShort(value)
+	def writeArray(self, writer, font, tableDict, values):
+		writer.writeShortArray(values)
 
 class UShort(IntValue):
 	staticSize = 2
 	def read(self, reader, font, tableDict):
 		return reader.readUShort()
+	def readArray(self, reader, font, tableDict, count):
+		return reader.readUShortArray(count)
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeUShort(value)
+	def writeArray(self, writer, font, tableDict, values):
+		writer.writeUShortArray(values)
 
 class Int8(IntValue):
 	staticSize = 1
 	def read(self, reader, font, tableDict):
 		return reader.readInt8()
+	def readArray(self, reader, font, tableDict, count):
+		return reader.readInt8Array(count)
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeInt8(value)
+	def writeArray(self, writer, font, tableDict, values):
+		writer.writeInt8Array(values)
 
 class UInt8(IntValue):
 	staticSize = 1
 	def read(self, reader, font, tableDict):
 		return reader.readUInt8()
+	def readArray(self, reader, font, tableDict, count):
+		return reader.readUInt8Array(count)
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeUInt8(value)
+	def writeArray(self, writer, font, tableDict, values):
+		writer.writeUInt8Array(values)
 
 class UInt24(IntValue):
 	staticSize = 3
@@ -267,9 +346,10 @@
 
 class GlyphID(SimpleValue):
 	staticSize = 2
+	typecode = "H"
 	def readArray(self, reader, font, tableDict, count):
 		glyphOrder = font.getGlyphOrder()
-		gids = reader.readUShortArray(count)
+		gids = reader.readArray(self.typecode, self.staticSize, count)
 		try:
 			l = [glyphOrder[gid] for gid in gids]
 		except IndexError:
@@ -277,29 +357,56 @@
 			l = [font.getGlyphName(gid) for gid in gids]
 		return l
 	def read(self, reader, font, tableDict):
-		return font.getGlyphName(reader.readUShort())
+		return font.getGlyphName(reader.readValue(self.typecode, self.staticSize))
+	def writeArray(self, writer, font, tableDict, values):
+		glyphMap = font.getReverseGlyphMap()
+		try:
+			values = [glyphMap[glyphname] for glyphname in values]
+		except KeyError:
+			# Slower, but will not throw a KeyError on an out-of-range glyph name.
+			values = [font.getGlyphID(glyphname) for glyphname in values]
+		writer.writeArray(self.typecode, values)
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
-		writer.writeUShort(font.getGlyphID(value))
+		writer.writeValue(self.typecode, font.getGlyphID(value))
+
+
+class GlyphID32(GlyphID):
+	staticSize = 4
+	typecode = "L"
 
 
 class NameID(UShort):
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
 		xmlWriter.simpletag(name, attrs + [("value", value)])
-		nameTable = font.get("name") if font else None
-		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)
+		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 STATFlags(UShort):
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		xmlWriter.simpletag(name, attrs + [("value", value)])
+		flags = []
+		if value & 0x01:
+			flags.append("OlderSiblingFontAttribute")
+		if value & 0x02:
+			flags.append("ElidableAxisValueName")
+		if flags:
+			xmlWriter.write("  ")
+			xmlWriter.comment(" ".join(flags))
+		xmlWriter.newline()
 
 class FloatValue(SimpleValue):
-	def xmlRead(self, attrs, content, font):
-		return float(attrs["value"])
+	@staticmethod
+	def fromString(value):
+		return float(value)
 
 class DeciPoints(FloatValue):
 	staticSize = 2
@@ -315,6 +422,12 @@
 		return  fi2fl(reader.readLong(), 16)
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeLong(fl2fi(value, 16))
+	@staticmethod
+	def fromString(value):
+		return str2fl(value, 16)
+	@staticmethod
+	def toString(value):
+		return fl2str(value, 16)
 
 class F2Dot14(FloatValue):
 	staticSize = 2
@@ -322,8 +435,30 @@
 		return  fi2fl(reader.readShort(), 14)
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		writer.writeShort(fl2fi(value, 14))
+	@staticmethod
+	def fromString(value):
+		return str2fl(value, 14)
+	@staticmethod
+	def toString(value):
+		return fl2str(value, 14)
 
-class Version(BaseConverter):
+class Angle(F2Dot14):
+	# angles are specified in degrees, and encoded as F2Dot14 fractions of half
+	# circle: e.g. 1.0 => 180, -0.5 => -90, -2.0 => -360, etc.
+	factor = 1.0/(1<<14) * 180  # 0.010986328125
+	def read(self, reader, font, tableDict):
+		return super().read(reader, font, tableDict) * 180
+	def write(self, writer, font, tableDict, value, repeatIndex=None):
+		super().write(writer, font, tableDict, value / 180, repeatIndex=repeatIndex)
+	@classmethod
+	def fromString(cls, value):
+		# quantize to nearest multiples of minimum fixed-precision angle
+		return otRound(float(value) / cls.factor) * cls.factor
+	@classmethod
+	def toString(cls, value):
+		return nearestMultipleShortestRepr(value, cls.factor)
+
+class Version(SimpleValue):
 	staticSize = 4
 	def read(self, reader, font, tableDict):
 		value = reader.readLong()
@@ -333,16 +468,12 @@
 		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 fromString(value):
+		return ve2fi(value)
+	@staticmethod
+	def toString(value):
+		return "0x%08x" % value
 	@staticmethod
 	def fromFloat(v):
 		return fl2fi(v, 16)
@@ -361,8 +492,8 @@
 		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"):
+		s = tostr(data, encoding="ascii", errors="replace")
+		if s != tostr(data, encoding="ascii", errors="ignore"):
 			log.warning('replaced non-ASCII characters in "%s"' %
 			            s)
 		return s
@@ -481,17 +612,13 @@
 
 class Table(Struct):
 
-	longOffset = False
 	staticSize = 2
 
 	def readOffset(self, reader):
 		return reader.readUShort()
 
 	def writeNullOffset(self, writer):
-		if self.longOffset:
-			writer.writeULong(0)
-		else:
-			writer.writeUShort(0)
+		writer.writeUShort(0)
 
 	def read(self, reader, font, tableDict):
 		offset = self.readOffset(reader)
@@ -510,7 +637,7 @@
 		if value is None:
 			self.writeNullOffset(writer)
 		else:
-			subWriter = writer.getSubWriter(self.longOffset, self.name)
+			subWriter = writer.getSubWriter(offsetSize=self.staticSize, name = self.name)
 			if repeatIndex is not None:
 				subWriter.repeatIndex = repeatIndex
 			writer.writeSubTable(subWriter)
@@ -518,12 +645,26 @@
 
 class LTable(Table):
 
-	longOffset = True
 	staticSize = 4
 
 	def readOffset(self, reader):
 		return reader.readULong()
 
+	def writeNullOffset(self, writer):
+		writer.writeULong(0)
+
+
+# Table pointed to by a 24-bit, 3-byte long offset
+class Table24(Table):
+
+	staticSize = 3
+
+	def readOffset(self, reader):
+		return reader.readUInt24()
+
+	def writeNullOffset(self, writer):
+		writer.writeUInt24(0)
+
 
 # TODO Clean / merge the SubTable and SubStruct
 
@@ -859,11 +1000,12 @@
 			offsetByGlyph[glyph] = offset
 		# For calculating the offsets to our AATLookup and data table,
 		# we can use the regular OTTableWriter infrastructure.
-		lookupWriter = writer.getSubWriter(True)
+		lookupWriter = writer.getSubWriter(offsetSize=4)
 		lookup = AATLookup('DataOffsets', None, None, UShort)
 		lookup.write(lookupWriter, font, tableDict, offsetByGlyph, None)
 
-		dataWriter = writer.getSubWriter(True)
+		dataWriter = writer.getSubWriter(offsetSize=4)
+
 		writer.writeSubTable(lookupWriter)
 		writer.writeSubTable(dataWriter)
 		for d in compiledData:
@@ -1050,6 +1192,9 @@
 			table.LigComponents = \
 				ligComponentReader.readUShortArray(numLigComponents)
 			table.Ligatures = self._readLigatures(ligaturesReader, font)
+		elif issubclass(self.tableClass, InsertionMorphAction):
+			actionReader = reader.getSubReader(0)
+			actionReader.seek(pos + reader.readULong())
 		table.GlyphClasses = self.classLookup.read(classTableReader,
 		                                           font, tableDict)
 		numStates = int((entryTableReader.pos - stateArrayReader.pos)
@@ -1114,19 +1259,15 @@
 		glyphClassWriter = OTTableWriter()
 		self.classLookup.write(glyphClassWriter, font, tableDict,
 		                       value.GlyphClasses, repeatIndex=None)
-		glyphClassData = pad(glyphClassWriter.getAllData(), 4)
+		glyphClassData = pad(glyphClassWriter.getAllData(), 2)
 		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)
-
+		glyphClassTableOffset += self.tableClass.actionHeaderSize
+		actionData, actionIndex = \
+			self.tableClass.compileActions(font, value.States)
 		stateArrayData, entryTableData = self._compileStates(
 			font, value.States, glyphClassCount, actionIndex)
 		stateArrayOffset = glyphClassTableOffset + len(glyphClassData)
@@ -1134,17 +1275,19 @@
 		perGlyphOffset = entryTableOffset + len(entryTableData)
 		perGlyphData = \
 			pad(self._compilePerGlyphLookups(value, font), 4)
+		if actionData is not None:
+			actionOffset = entryTableOffset + len(entryTableData)
+		else:
+			actionOffset = None
+
+		ligaturesOffset, ligComponentsOffset = None, None
 		ligComponentsData = self._compileLigComponents(value, font)
 		ligaturesData = self._compileLigatures(value, font)
-		if actionData is None:
-			actionOffset = None
-			ligComponentsOffset = None
-			ligaturesOffset = None
-		else:
+		if ligComponentsData is not None:
 			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)
@@ -1153,6 +1296,7 @@
 			writer.writeULong(perGlyphOffset)
 		if actionOffset is not None:
 			writer.writeULong(actionOffset)
+		if ligComponentsOffset is not None:
 			writer.writeULong(ligComponentsOffset)
 			writer.writeULong(ligaturesOffset)
 		writer.writeData(glyphClassData)
@@ -1202,41 +1346,12 @@
 				(len(table.PerGlyphLookups), numLookups))
 		writer = OTTableWriter()
 		for lookup in table.PerGlyphLookups:
-			lookupWriter = writer.getSubWriter(True)
+			lookupWriter = writer.getSubWriter(offsetSize=4)
 			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
-		assert len(result) % self.tableClass.staticSize == 0
-		return (result, actionIndex)
-
 	def _compileLigComponents(self, table, font):
 		if not hasattr(table, "LigComponents"):
 			return None
@@ -1504,20 +1619,15 @@
 		outerShift = 16 - innerBits
 
 		entrySize = 1 + ((fmt & 0x0030) >> 4)
-		read = {
-			1: reader.readUInt8,
-			2: reader.readUShort,
-			3: reader.readUInt24,
-			4: reader.readULong,
+		readArray = {
+			1: reader.readUInt8Array,
+			2: reader.readUShortArray,
+			3: reader.readUInt24Array,
+			4: reader.readULongArray,
 		}[entrySize]
 
-		mapping = []
-		for i in range(nItems):
-			raw = read()
-			idx = ((raw & outerMask) << outerShift) | (raw & innerMask)
-			mapping.append(idx)
-
-		return mapping
+		return [(((raw & outerMask) << outerShift) | (raw & innerMask))
+			for raw in readArray(nItems)]
 
 	def write(self, writer, font, tableDict, value, repeatIndex=None):
 		fmt = tableDict['EntryFormat']
@@ -1529,16 +1639,15 @@
 		outerShift = 16 - innerBits
 
 		entrySize = 1 + ((fmt & 0x0030) >> 4)
-		write = {
-			1: writer.writeUInt8,
-			2: writer.writeUShort,
-			3: writer.writeUInt24,
-			4: writer.writeULong,
+		writeArray = {
+			1: writer.writeUInt8Array,
+			2: writer.writeUShortArray,
+			3: writer.writeUInt24Array,
+			4: writer.writeULongArray,
 		}[entrySize]
 
-		for idx in mapping:
-			raw = ((idx & 0xFFFF0000) >> outerShift) | (idx & innerMask)
-			write(raw)
+		writeArray([(((idx & 0xFFFF0000) >> outerShift) | (idx & innerMask))
+			    for idx in mapping])
 
 
 class VarDataValue(BaseConverter):
@@ -1547,27 +1656,43 @@
 		values = []
 
 		regionCount = tableDict["VarRegionCount"]
-		shortCount = tableDict["NumShorts"]
+		wordCount = 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()
+		# https://github.com/fonttools/fonttools/issues/2279
+		longWords = bool(wordCount & 0x8000)
+		wordCount = wordCount & 0x7FFF
+
+		if longWords:
+			readBigArray, readSmallArray = reader.readLongArray, reader.readShortArray
+		else:
+			readBigArray, readSmallArray = reader.readShortArray, reader.readInt8Array
+
+		n1, n2 = min(regionCount, wordCount), max(regionCount, wordCount)
+		values.extend(readBigArray(n1))
+		values.extend(readSmallArray(n2 - n1))
+		if n2 > regionCount: # Padding
+			del values[regionCount:]
 
 		return values
 
-	def write(self, writer, font, tableDict, value, repeatIndex=None):
+	def write(self, writer, font, tableDict, values, repeatIndex=None):
 		regionCount = tableDict["VarRegionCount"]
-		shortCount = tableDict["NumShorts"]
+		wordCount = 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)
+		# https://github.com/fonttools/fonttools/issues/2279
+		longWords = bool(wordCount & 0x8000)
+		wordCount = wordCount & 0x7FFF
+
+		(writeBigArray, writeSmallArray) = {
+			False: (writer.writeShortArray, writer.writeInt8Array),
+			True:  (writer.writeLongArray,  writer.writeShortArray),
+		}[longWords]
+
+		n1, n2 = min(regionCount, wordCount), max(regionCount, wordCount)
+		writeBigArray(values[:n1])
+		writeSmallArray(values[n1:regionCount])
+		if n2 > regionCount: # Padding
+			writer.writeSmallArray([0] * (n2 - regionCount))
 
 	def xmlWrite(self, xmlWriter, font, value, name, attrs):
 		xmlWriter.simpletag(name, attrs + [("value", value)])
@@ -1576,32 +1701,74 @@
 	def xmlRead(self, attrs, content, font):
 		return safeEval(attrs["value"])
 
+class LookupFlag(UShort):
+	def xmlWrite(self, xmlWriter, font, value, name, attrs):
+		xmlWriter.simpletag(name, attrs + [("value", value)])
+		flags = []
+		if value & 0x01: flags.append("rightToLeft")
+		if value & 0x02: flags.append("ignoreBaseGlyphs")
+		if value & 0x04: flags.append("ignoreLigatures")
+		if value & 0x08: flags.append("ignoreMarks")
+		if value & 0x10: flags.append("useMarkFilteringSet")
+		if value & 0xff00: flags.append("markAttachmentType[%i]" % (value >> 8))
+		if flags:
+			xmlWriter.comment(" ".join(flags))
+		xmlWriter.newline()
+
+
+class _UInt8Enum(UInt8):
+	enumClass = NotImplemented
+
+	def read(self, reader, font, tableDict):
+		return self.enumClass(super().read(reader, font, tableDict))
+	@classmethod
+	def fromString(cls, value):
+		return getattr(cls.enumClass, value.upper())
+	@classmethod
+	def toString(cls, value):
+		return cls.enumClass(value).name.lower()
+
+
+class ExtendMode(_UInt8Enum):
+	enumClass = _ExtendMode
+
+
+class CompositeMode(_UInt8Enum):
+	enumClass = _CompositeMode
+
 
 converterMapping = {
 	# type		class
 	"int8":		Int8,
 	"int16":	Short,
 	"uint8":	UInt8,
-	"uint8":	UInt8,
 	"uint16":	UShort,
 	"uint24":	UInt24,
 	"uint32":	ULong,
 	"char64":	Char64,
 	"Flags32":	Flags32,
+	"VarIndex":	VarIndex,
 	"Version":	Version,
 	"Tag":		Tag,
 	"GlyphID":	GlyphID,
+	"GlyphID32":	GlyphID32,
 	"NameID":	NameID,
 	"DeciPoints":	DeciPoints,
 	"Fixed":	Fixed,
 	"F2Dot14":	F2Dot14,
+	"Angle":	Angle,
 	"struct":	Struct,
 	"Offset":	Table,
 	"LOffset":	LTable,
+	"Offset24":	Table24,
 	"ValueRecord":	ValueRecord,
 	"DeltaValue":	DeltaValue,
 	"VarIdxMapValue":	VarIdxMapValue,
 	"VarDataValue":	VarDataValue,
+	"LookupFlag": LookupFlag,
+	"ExtendMode": ExtendMode,
+	"CompositeMode": CompositeMode,
+	"STATFlags": STATFlags,
 
 	# AAT
 	"CIDGlyphMap":	CIDGlyphMap,
@@ -1617,4 +1784,5 @@
 	"STXHeader":	lambda C: partial(STXHeader, tableClass=C),
 	"OffsetTo":	lambda C: partial(Table, tableClass=C),
 	"LOffsetTo":	lambda C: partial(LTable, tableClass=C),
+	"LOffset24To":	lambda C: partial(Table24, tableClass=C),
 }
diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py
index cf80098..dd4033e 100755
--- a/Lib/fontTools/ttLib/tables/otData.py
+++ b/Lib/fontTools/ttLib/tables/otData.py
@@ -1,7 +1,3 @@
-# coding: utf-8
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-
 otData = [
 
 	#
@@ -88,7 +84,7 @@
 
 	('Lookup', [
 		('uint16', 'LookupType', None, None, 'Different enumerations for GSUB and GPOS'),
-		('uint16', 'LookupFlag', None, None, 'Lookup qualifiers'),
+		('LookupFlag', 'LookupFlag', None, None, 'Lookup qualifiers'),
 		('uint16', 'SubTableCount', None, None, 'Number of SubTables for this lookup'),
 		('Offset', 'SubTable', 'SubTableCount', 0, 'Array of offsets to SubTables-from beginning of Lookup table'),
 		('uint16', 'MarkFilteringSet', None, 'LookupFlag & 0x0010', 'If set, indicates that the lookup table structure is followed by a MarkFilteringSet field. The layout engine skips over all mark glyphs not in the mark filtering set indicated.'),
@@ -397,16 +393,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'),
@@ -703,6 +699,7 @@
 		('Version', 'Version', None, None, 'Version of the BASE table-initially 0x00010000'),
 		('Offset', 'HorizAxis', None, None, 'Offset to horizontal Axis table-from beginning of BASE table-may be NULL'),
 		('Offset', 'VertAxis', None, None, 'Offset to vertical Axis table-from beginning of BASE table-may be NULL'),
+		('LOffset', 'VarStore', None, 'Version >= 0x00010001', 'Offset to variation store (may be NULL)'),
 	]),
 
 	('Axis', [
@@ -872,7 +869,7 @@
 	('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.'),
+		('STATFlags', 'Flags', None, None, 'Flags.'),
 		('NameID', 'ValueNameID', None, None, ''),
 		('Fixed', 'Value', None, None, ''),
 	]),
@@ -880,7 +877,7 @@
 	('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.'),
+		('STATFlags', 'Flags', None, None, 'Flags.'),
 		('NameID', 'ValueNameID', None, None, ''),
 		('Fixed', 'NominalValue', None, None, ''),
 		('Fixed', 'RangeMinValue', None, None, ''),
@@ -890,7 +887,7 @@
 	('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.'),
+		('STATFlags', 'Flags', None, None, 'Flags.'),
 		('NameID', 'ValueNameID', None, None, ''),
 		('Fixed', 'Value', None, None, ''),
 		('Fixed', 'LinkedValue', None, None, ''),
@@ -899,7 +896,7 @@
 	('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.'),
+		('STATFlags', '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. '),
 	]),
@@ -970,7 +967,7 @@
 
 	('VarData', [
 		('uint16', 'ItemCount', None, None, ''),
-		('uint16', 'NumShorts', None, None, ''), # Automatically computed
+		('uint16', 'NumShorts', None, None, ''),
 		('uint16', 'VarRegionCount', None, None, ''),
 		('uint16', 'VarRegionIndex', 'VarRegionCount', 0, ''),
 		('VarDataValue', 'Item', 'ItemCount', 0, ''),
@@ -991,6 +988,20 @@
 		('VarIdxMapValue', 'mapping', '', 0, 'Array of compressed data'),
 	]),
 
+	('DeltaSetIndexMapFormat0', [
+		('uint8', 'Format', None, None, 'Format of the DeltaSetIndexMap = 0'),
+		('uint8', 'EntryFormat', None, None, ''), # Automatically computed
+		('uint16', 'MappingCount', None, None, ''), # Automatically computed
+		('VarIdxMapValue', 'mapping', '', 0, 'Array of compressed data'),
+	]),
+
+	('DeltaSetIndexMapFormat1', [
+		('uint8', 'Format', None, None, 'Format of the DeltaSetIndexMap = 1'),
+		('uint8', 'EntryFormat', None, None, ''), # Automatically computed
+		('uint32', 'MappingCount', None, None, ''), # Automatically computed
+		('VarIdxMapValue', 'mapping', '', 0, 'Array of compressed data'),
+	]),
+
 	# Glyph advance variations
 
 	('HVAR', [
@@ -1445,8 +1456,7 @@
         ]),
 
 	('InsertionMorph', [
-		('struct', 'StateHeader', None, None, 'Header.'),
-		# TODO: Add missing parts.
+		('STXHeader(InsertionMorphAction)', 'StateTable', None, None, 'Finite-state transducer for glyph insertion.'),
 	]),
 
 	('MorphClass', [
@@ -1513,4 +1523,435 @@
 		('int16', 'Bottom', None, None, 'Control point index for the bottom-side optical edge, or -1 if this glyph has none.'),
 	]),
 
+	#
+	# TSIC
+	#
+	('TSIC', [
+		('Version', 'Version', None, None, 'Version of table initially set to 0x00010000.'),
+		('uint16', 'Flags', None, None, 'TSIC flags - set to 0'),
+		('uint16', 'AxisCount', None, None, 'Axis count from fvar'),
+		('uint16', 'RecordCount', None, None, 'TSIC record count'),
+		('uint16', 'Reserved', None, None, 'Set to 0'),
+		('Tag', 'AxisArray', 'AxisCount', 0, 'Array of axis tags in fvar order'),
+		('LocationRecord', 'RecordLocations', 'RecordCount', 0, 'Location in variation space of TSIC record'),
+		('TSICRecord', 'Record', 'RecordCount', 0, 'Array of TSIC records'),
+	]),
+
+	('LocationRecord', [
+		('F2Dot14', 'Axis', 'AxisCount', 0, 'Axis record'),
+	]),
+
+	('TSICRecord', [
+		('uint16', 'Flags', None, None, 'Record flags - set to 0'),
+		('uint16', 'NumCVTEntries', None, None, 'Number of CVT number value pairs'),
+		('uint16', 'NameLength', None, None, 'Length of optional user record name'),
+		('uint16', 'NameArray', 'NameLength', 0, 'Unicode 16 name'),
+		('uint16', 'CVTArray', 'NumCVTEntries', 0, 'CVT number array'),
+		('int16', 'CVTValueArray', 'NumCVTEntries', 0, 'CVT value'),
+	]),
+
+	#
+	# COLR
+	#
+
+	('COLR', [
+		('uint16', 'Version', None, None, 'Table version number (starts at 0).'),
+		('uint16', 'BaseGlyphRecordCount', None, None, 'Number of Base Glyph Records.'),
+		('LOffset', 'BaseGlyphRecordArray', None, None, 'Offset (from beginning of COLR table) to Base Glyph records.'),
+		('LOffset', 'LayerRecordArray', None, None, 'Offset (from beginning of COLR table) to Layer Records.'),
+		('uint16', 'LayerRecordCount', None, None, 'Number of Layer Records.'),
+		('LOffset', 'BaseGlyphList', None, 'Version >= 1', 'Offset (from beginning of COLR table) to array of Version-1 Base Glyph records.'),
+		('LOffset', 'LayerList', None, 'Version >= 1', 'Offset (from beginning of COLR table) to LayerList.'),
+		('LOffset', 'ClipList', None, 'Version >= 1', 'Offset to ClipList table (may be NULL)'),
+		('LOffsetTo(DeltaSetIndexMap)', 'VarIndexMap', None, 'Version >= 1', 'Offset to DeltaSetIndexMap table (may be NULL)'),
+		('LOffset', 'VarStore', None, 'Version >= 1', 'Offset to variation store (may be NULL)'),
+	]),
+
+	('BaseGlyphRecordArray', [
+		('BaseGlyphRecord', 'BaseGlyphRecord', 'BaseGlyphRecordCount', 0, 'Base Glyph records.'),
+	]),
+
+	('BaseGlyphRecord', [
+		('GlyphID', 'BaseGlyph', None, None, 'Glyph ID of reference glyph. This glyph is for reference only and is not rendered for color.'),
+		('uint16', 'FirstLayerIndex', None, None, 'Index (from beginning of the Layer Records) to the layer record. There will be numLayers consecutive entries for this base glyph.'),
+		('uint16', 'NumLayers', None, None, 'Number of color layers associated with this glyph.'),
+	]),
+
+	('LayerRecordArray', [
+		('LayerRecord', 'LayerRecord', 'LayerRecordCount', 0, 'Layer records.'),
+	]),
+
+	('LayerRecord', [
+		('GlyphID', 'LayerGlyph', None, None, 'Glyph ID of layer glyph (must be in z-order from bottom to top).'),
+		('uint16', 'PaletteIndex', None, None, 'Index value to use with a selected color palette.'),
+	]),
+
+	('BaseGlyphList', [
+		('uint32', 'BaseGlyphCount', None, None, 'Number of Version-1 Base Glyph records'),
+		('struct', 'BaseGlyphPaintRecord', 'BaseGlyphCount', 0, 'Array of Version-1 Base Glyph records'),
+	]),
+
+	('BaseGlyphPaintRecord', [
+		('GlyphID', 'BaseGlyph', None, None, 'Glyph ID of reference glyph.'),
+		('LOffset', 'Paint', None, None, 'Offset (from beginning of BaseGlyphPaintRecord) to Paint, typically a PaintColrLayers.'),
+	]),
+
+	('LayerList', [
+		('uint32', 'LayerCount', None, None, 'Number of Version-1 Layers'),
+		('LOffset', 'Paint', 'LayerCount', 0, 'Array of offsets to Paint tables, from the start of the LayerList table.'),
+	]),
+
+	('ClipListFormat1', [
+		('uint8', 'Format', None, None, 'Format for ClipList with 16bit glyph IDs: 1'),
+		('uint32', 'ClipCount', None, None, 'Number of Clip records.'),
+		('struct', 'ClipRecord', 'ClipCount', 0, 'Array of Clip records sorted by glyph ID.'),
+	]),
+
+	('ClipRecord', [
+		('uint16', 'StartGlyphID', None, None, 'First glyph ID in the range.'),
+		('uint16', 'EndGlyphID', None, None, 'Last glyph ID in the range.'),
+		('Offset24', 'ClipBox', None, None, 'Offset to a ClipBox table.'),
+	]),
+
+	('ClipBoxFormat1', [
+		('uint8', 'Format', None, None, 'Format for ClipBox without variation: set to 1.'),
+		('int16', 'xMin', None, None, 'Minimum x of clip box.'),
+		('int16', 'yMin', None, None, 'Minimum y of clip box.'),
+		('int16', 'xMax', None, None, 'Maximum x of clip box.'),
+		('int16', 'yMax', None, None, 'Maximum y of clip box.'),
+	]),
+
+	('ClipBoxFormat2', [
+		('uint8', 'Format', None, None, 'Format for variable ClipBox: set to 2.'),
+		('int16', 'xMin', None, None, 'Minimum x of clip box.'),
+		('int16', 'yMin', None, None, 'Minimum y of clip box.'),
+		('int16', 'xMax', None, None, 'Maximum x of clip box.'),
+		('int16', 'yMax', None, None, 'Maximum y of clip box.'),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# COLRv1 Affine2x3 uses the same column-major order to serialize a 2D
+	# Affine Transformation as the one used by fontTools.misc.transform.
+	# However, for historical reasons, the labels 'xy' and 'yx' are swapped.
+	# Their fundamental meaning is the same though.
+	# COLRv1 Affine2x3 follows the names found in FreeType and Cairo.
+	# In all case, the second element in the 6-tuple correspond to the
+	# y-part of the x basis vector, and the third to the x-part of the y
+	# basis vector.
+	# See https://github.com/googlefonts/colr-gradients-spec/pull/85
+	('Affine2x3', [
+		('Fixed', 'xx', None, None, 'x-part of x basis vector'),
+		('Fixed', 'yx', None, None, 'y-part of x basis vector'),
+		('Fixed', 'xy', None, None, 'x-part of y basis vector'),
+		('Fixed', 'yy', None, None, 'y-part of y basis vector'),
+		('Fixed', 'dx', None, None, 'Translation in x direction'),
+		('Fixed', 'dy', None, None, 'Translation in y direction'),
+	]),
+	('VarAffine2x3', [
+		('Fixed', 'xx', None, None, 'x-part of x basis vector'),
+		('Fixed', 'yx', None, None, 'y-part of x basis vector'),
+		('Fixed', 'xy', None, None, 'x-part of y basis vector'),
+		('Fixed', 'yy', None, None, 'y-part of y basis vector'),
+		('Fixed', 'dx', None, None, 'Translation in x direction'),
+		('Fixed', 'dy', None, None, 'Translation in y direction'),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	('ColorStop', [
+		('F2Dot14', 'StopOffset', None, None, ''),
+		('uint16', 'PaletteIndex', None, None, 'Index for a CPAL palette entry.'),
+		('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved'),
+	]),
+	('VarColorStop', [
+		('F2Dot14', 'StopOffset', None, None, 'VarIndexBase + 0'),
+		('uint16', 'PaletteIndex', None, None, 'Index for a CPAL palette entry.'),
+		('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved. VarIndexBase + 1'),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	('ColorLine', [
+		('ExtendMode', 'Extend', None, None, 'Enum {PAD = 0, REPEAT = 1, REFLECT = 2}'),
+		('uint16', 'StopCount', None, None, 'Number of Color stops.'),
+		('ColorStop', 'ColorStop', 'StopCount', 0, 'Array of Color stops.'),
+	]),
+	('VarColorLine', [
+		('ExtendMode', 'Extend', None, None, 'Enum {PAD = 0, REPEAT = 1, REFLECT = 2}'),
+		('uint16', 'StopCount', None, None, 'Number of Color stops.'),
+		('VarColorStop', 'ColorStop', 'StopCount', 0, 'Array of Color stops.'),
+	]),
+
+	# PaintColrLayers
+	('PaintFormat1', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 1'),
+		('uint8', 'NumLayers', None, None, 'Number of offsets to Paint to read from LayerList.'),
+		('uint32', 'FirstLayerIndex', None, None, 'Index into LayerList.'),
+	]),
+
+	# PaintSolid
+	('PaintFormat2', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 2'),
+		('uint16', 'PaletteIndex', None, None, 'Index for a CPAL palette entry.'),
+		('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved'),
+	]),
+	# PaintVarSolid
+	('PaintFormat3', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 3'),
+		('uint16', 'PaletteIndex', None, None, 'Index for a CPAL palette entry.'),
+		('F2Dot14', 'Alpha', None, None, 'Values outsided [0.,1.] reserved. VarIndexBase + 0'),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintLinearGradient
+	('PaintFormat4', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 4'),
+		('Offset24', 'ColorLine', None, None, 'Offset (from beginning of PaintLinearGradient table) to ColorLine subtable.'),
+		('int16', 'x0', None, None, ''),
+		('int16', 'y0', None, None, ''),
+		('int16', 'x1', None, None, ''),
+		('int16', 'y1', None, None, ''),
+		('int16', 'x2', None, None, ''),
+		('int16', 'y2', None, None, ''),
+	]),
+	# PaintVarLinearGradient
+	('PaintFormat5', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 5'),
+		('LOffset24To(VarColorLine)', 'ColorLine', None, None, 'Offset (from beginning of PaintVarLinearGradient table) to VarColorLine subtable.'),
+		('int16', 'x0', None, None, ''),
+		('int16', 'y0', None, None, ''),
+		('int16', 'x1', None, None, ''),
+		('int16', 'y1', None, None, ''),
+		('int16', 'x2', None, None, ''),
+		('int16', 'y2', None, None, ''),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintRadialGradient
+	('PaintFormat6', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 6'),
+		('Offset24', 'ColorLine', None, None, 'Offset (from beginning of PaintRadialGradient table) to ColorLine subtable.'),
+		('int16', 'x0', None, None, ''),
+		('int16', 'y0', None, None, ''),
+		('uint16', 'r0', None, None, ''),
+		('int16', 'x1', None, None, ''),
+		('int16', 'y1', None, None, ''),
+		('uint16', 'r1', None, None, ''),
+	]),
+	# PaintVarRadialGradient
+	('PaintFormat7', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 7'),
+		('LOffset24To(VarColorLine)', 'ColorLine', None, None, 'Offset (from beginning of PaintVarRadialGradient table) to VarColorLine subtable.'),
+		('int16', 'x0', None, None, ''),
+		('int16', 'y0', None, None, ''),
+		('uint16', 'r0', None, None, ''),
+		('int16', 'x1', None, None, ''),
+		('int16', 'y1', None, None, ''),
+		('uint16', 'r1', None, None, ''),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintSweepGradient
+	('PaintFormat8', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 8'),
+		('Offset24', 'ColorLine', None, None, 'Offset (from beginning of PaintSweepGradient table) to ColorLine subtable.'),
+		('int16', 'centerX', None, None, 'Center x coordinate.'),
+		('int16', 'centerY', None, None, 'Center y coordinate.'),
+		('Angle', 'startAngle', None, None, 'Start of the angular range of the gradient.'),
+		('Angle', 'endAngle', None, None, 'End of the angular range of the gradient.'),
+	]),
+	# PaintVarSweepGradient
+	('PaintFormat9', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 9'),
+		('LOffset24To(VarColorLine)', 'ColorLine', None, None, 'Offset (from beginning of PaintVarSweepGradient table) to VarColorLine subtable.'),
+		('int16', 'centerX', None, None, 'Center x coordinate.'),
+		('int16', 'centerY', None, None, 'Center y coordinate.'),
+		('Angle', 'startAngle', None, None, 'Start of the angular range of the gradient.'),
+		('Angle', 'endAngle', None, None, 'End of the angular range of the gradient.'),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintGlyph
+	('PaintFormat10', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 10'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintGlyph table) to Paint subtable.'),
+		('GlyphID', 'Glyph', None, None, 'Glyph ID for the source outline.'),
+	]),
+
+	# PaintColrGlyph
+	('PaintFormat11', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 11'),
+		('GlyphID', 'Glyph', None, None, 'Virtual glyph ID for a BaseGlyphList base glyph.'),
+	]),
+
+	# PaintTransform
+	('PaintFormat12', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 12'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintTransform table) to Paint subtable.'),
+		('LOffset24To(Affine2x3)', 'Transform', None, None, '2x3 matrix for 2D affine transformations.'),
+	]),
+	# PaintVarTransform
+	('PaintFormat13', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 13'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarTransform table) to Paint subtable.'),
+		('LOffset24To(VarAffine2x3)', 'Transform', None, None, '2x3 matrix for 2D affine transformations.'),
+	]),
+
+	# PaintTranslate
+	('PaintFormat14', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 14'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintTranslate table) to Paint subtable.'),
+		('int16', 'dx', None, None, 'Translation in x direction.'),
+		('int16', 'dy', None, None, 'Translation in y direction.'),
+	]),
+	# PaintVarTranslate
+	('PaintFormat15', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 15'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarTranslate table) to Paint subtable.'),
+		('int16', 'dx', None, None, 'Translation in x direction.'),
+		('int16', 'dy', None, None, 'Translation in y direction.'),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintScale
+	('PaintFormat16', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 16'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintScale table) to Paint subtable.'),
+		('F2Dot14', 'scaleX', None, None, ''),
+		('F2Dot14', 'scaleY', None, None, ''),
+	]),
+	# PaintVarScale
+	('PaintFormat17', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 17'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScale table) to Paint subtable.'),
+		('F2Dot14', 'scaleX', None, None, ''),
+		('F2Dot14', 'scaleY', None, None, ''),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintScaleAroundCenter
+	('PaintFormat18', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 18'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintScaleAroundCenter table) to Paint subtable.'),
+		('F2Dot14', 'scaleX', None, None, ''),
+		('F2Dot14', 'scaleY', None, None, ''),
+		('int16', 'centerX', None, None, ''),
+		('int16', 'centerY', None, None, ''),
+	]),
+	# PaintVarScaleAroundCenter
+	('PaintFormat19', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 19'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScaleAroundCenter table) to Paint subtable.'),
+		('F2Dot14', 'scaleX', None, None, ''),
+		('F2Dot14', 'scaleY', None, None, ''),
+		('int16', 'centerX', None, None, ''),
+		('int16', 'centerY', None, None, ''),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintScaleUniform
+	('PaintFormat20', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 20'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintScaleUniform table) to Paint subtable.'),
+		('F2Dot14', 'scale', None, None, ''),
+	]),
+	# PaintVarScaleUniform
+	('PaintFormat21', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 21'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScaleUniform table) to Paint subtable.'),
+		('F2Dot14', 'scale', None, None, ''),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintScaleUniformAroundCenter
+	('PaintFormat22', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 22'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintScaleUniformAroundCenter table) to Paint subtable.'),
+		('F2Dot14', 'scale', None, None, ''),
+		('int16', 'centerX', None, None, ''),
+		('int16', 'centerY', None, None, ''),
+	]),
+	# PaintVarScaleUniformAroundCenter
+	('PaintFormat23', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 23'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarScaleUniformAroundCenter table) to Paint subtable.'),
+		('F2Dot14', 'scale', None, None, ''),
+		('int16', 'centerX', None, None, ''),
+		('int16', 'centerY', None, None, ''),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintRotate
+	('PaintFormat24', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 24'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintRotate table) to Paint subtable.'),
+		('Angle', 'angle', None, None, ''),
+	]),
+	# PaintVarRotate
+	('PaintFormat25', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 25'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarRotate table) to Paint subtable.'),
+		('Angle', 'angle', None, None, ''),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintRotateAroundCenter
+	('PaintFormat26', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 26'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintRotateAroundCenter table) to Paint subtable.'),
+		('Angle', 'angle', None, None, ''),
+		('int16', 'centerX', None, None, ''),
+		('int16', 'centerY', None, None, ''),
+	]),
+	# PaintVarRotateAroundCenter
+	('PaintFormat27', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 27'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarRotateAroundCenter table) to Paint subtable.'),
+		('Angle', 'angle', None, None, ''),
+		('int16', 'centerX', None, None, ''),
+		('int16', 'centerY', None, None, ''),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintSkew
+	('PaintFormat28', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 28'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintSkew table) to Paint subtable.'),
+		('Angle', 'xSkewAngle', None, None, ''),
+		('Angle', 'ySkewAngle', None, None, ''),
+	]),
+	# PaintVarSkew
+	('PaintFormat29', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 29'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarSkew table) to Paint subtable.'),
+		('Angle', 'xSkewAngle', None, None, ''),
+		('Angle', 'ySkewAngle', None, None, ''),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintSkewAroundCenter
+	('PaintFormat30', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 30'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintSkewAroundCenter table) to Paint subtable.'),
+		('Angle', 'xSkewAngle', None, None, ''),
+		('Angle', 'ySkewAngle', None, None, ''),
+		('int16', 'centerX', None, None, ''),
+		('int16', 'centerY', None, None, ''),
+	]),
+	# PaintVarSkewAroundCenter
+	('PaintFormat31', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 31'),
+		('Offset24', 'Paint', None, None, 'Offset (from beginning of PaintVarSkewAroundCenter table) to Paint subtable.'),
+		('Angle', 'xSkewAngle', None, None, ''),
+		('Angle', 'ySkewAngle', None, None, ''),
+		('int16', 'centerX', None, None, ''),
+		('int16', 'centerY', None, None, ''),
+		('VarIndex', 'VarIndexBase', None, None, 'Base index into DeltaSetIndexMap.'),
+	]),
+
+	# PaintComposite
+	('PaintFormat32', [
+		('uint8', 'PaintFormat', None, None, 'Format identifier-format = 32'),
+		('LOffset24To(Paint)', 'SourcePaint', None, None, 'Offset (from beginning of PaintComposite table) to source Paint subtable.'),
+		('CompositeMode', 'CompositeMode', None, None, 'A CompositeMode enumeration value.'),
+		('LOffset24To(Paint)', 'BackdropPaint', None, None, 'Offset (from beginning of PaintComposite table) to backdrop Paint subtable.'),
+	]),
 ]
diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py
index 78db748..c2d53d1 100644
--- a/Lib/fontTools/ttLib/tables/otTables.py
+++ b/Lib/fontTools/ttLib/tables/otTables.py
@@ -5,11 +5,18 @@
 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, unicode_literals
-from fontTools.misc.py23 import *
-from fontTools.misc.textTools import safeEval
-from .otBase import BaseTable, FormatSwitchingBaseTable
-import operator
+import copy
+from enum import IntEnum
+import itertools
+from collections import defaultdict, namedtuple
+from fontTools.misc.py23 import bytesjoin
+from fontTools.misc.roundTools import otRound
+from fontTools.misc.textTools import pad, safeEval
+from .otBase import (
+	BaseTable, FormatSwitchingBaseTable, ValueRecord, CountReference,
+	getFormatSwitchingBaseTableClass,
+)
+from fontTools.feaLib.lookupDebugInfo import LookupDebugInfo, LOOKUP_DEBUG_INFO_KEY
 import logging
 import struct
 
@@ -32,6 +39,10 @@
 class AATAction(object):
 	_FLAGS = None
 
+	@staticmethod
+	def compileActions(font, states):
+		return (None, None)
+
 	def _writeFlagsToXML(self, xmlWriter):
 		flags = [f for f in self._FLAGS if self.__dict__[f]]
 		if flags:
@@ -50,6 +61,7 @@
 
 class RearrangementMorphAction(AATAction):
 	staticSize = 4
+	actionHeaderSize = 0
 	_FLAGS = ["MarkFirst", "DontAdvance", "MarkLast"]
 
 	_VERBS = {
@@ -131,6 +143,7 @@
 
 class ContextualMorphAction(AATAction):
 	staticSize = 8
+	actionHeaderSize = 0
 	_FLAGS = ["SetMark", "DontAdvance"]
 
 	def __init__(self):
@@ -209,6 +222,10 @@
 
 class LigatureMorphAction(AATAction):
 	staticSize = 6
+
+	# 4 bytes for each of {action,ligComponents,ligatures}Offset
+	actionHeaderSize = 12
+
 	_FLAGS = ["SetComponent", "DontAdvance"]
 
 	def __init__(self):
@@ -251,6 +268,34 @@
 		else:
 			self.Actions = []
 
+	@staticmethod
+	def compileActions(font, states):
+		result, actions, actionIndex = b"", set(), {}
+		for state in states:
+			for _glyphClass, trans in state.Transitions.items():
+				actions.add(trans.compileLigActions())
+		# 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
+		result = pad(result, 4)
+		return (result, actionIndex)
+
 	def compileLigActions(self):
 		result = []
 		for i, action in enumerate(self.Actions):
@@ -319,7 +364,7 @@
 
 class InsertionMorphAction(AATAction):
 	staticSize = 8
-
+	actionHeaderSize = 4  # 4 bytes for actionOffset
 	_FLAGS = ["SetMark", "DontAdvance",
 	          "CurrentIsKashidaLike", "MarkedIsKashidaLike",
 	          "CurrentInsertBefore", "MarkedInsertBefore"]
@@ -417,6 +462,37 @@
 			else:
 				assert False, eltName
 
+	@staticmethod
+	def compileActions(font, states):
+		actions, actionIndex, result = set(), {}, b""
+		for state in states:
+			for _glyphClass, trans in state.Transitions.items():
+				if trans.CurrentInsertionAction is not None:
+					actions.add(tuple(trans.CurrentInsertionAction))
+				if trans.MarkedInsertionAction is not None:
+					actions.add(tuple(trans.MarkedInsertionAction))
+		# Sort the compiled actions in decreasing order of
+		# length, so that the longer sequence come before the
+		# shorter ones.
+		for action in sorted(actions, key=lambda x:(-len(x), x)):
+			# We insert all sub-sequences of the action glyph sequence
+			# into actionIndex. For example, if one action triggers on
+			# glyph sequence [A, B, C, D, E] and another action triggers
+			# on [C, D], we return result=[A, B, C, D, E] (as list of
+			# encoded glyph IDs), and actionIndex={('A','B','C','D','E'): 0,
+			# ('C','D'): 2}.
+			if action in actionIndex:
+				continue
+			for start in range(0, len(action)):
+				startIndex = (len(result) // 2) + start
+				for limit in range(start, len(action)):
+					glyphs = action[start : limit + 1]
+					actionIndex.setdefault(glyphs, startIndex)
+			for glyph in action:
+				glyphID = font.getGlyphID(glyph)
+				result += struct.pack(">H", glyphID)
+		return result, actionIndex
+
 
 class FeatureParams(BaseTable):
 
@@ -481,7 +557,9 @@
 					endID = len(glyphOrder)
 				glyphs.extend(glyphOrder[glyphID] for glyphID in range(startID, endID))
 		else:
-			assert 0, "unknown format: %s" % self.Format
+			self.glyphs = []
+			log.warning("Unknown Coverage format: %s", self.Format)
+		del self.Format # Don't need this anymore
 
 	def preWrite(self, font):
 		glyphs = getattr(self, "glyphs", None)
@@ -541,7 +619,7 @@
 		glyphs.append(attrs["value"])
 
 
-class VarIdxMap(BaseTable):
+class DeltaSetIndexMap(getFormatSwitchingBaseTableClass("uint8")):
 
 	def populateDefaults(self, propagator=None):
 		if not hasattr(self, 'mapping'):
@@ -551,15 +629,8 @@
 		assert (rawTable['EntryFormat'] & 0xFFC0) == 0
 		self.mapping = rawTable['mapping']
 
-	def preWrite(self, font):
-		mapping = getattr(self, "mapping", None)
-		if mapping is None:
-			mapping = self.mapping = []
-		rawTable = { 'mapping': mapping }
-		rawTable['MappingCount'] = len(mapping)
-
-		# TODO Remove this abstraction/optimization and move it varLib.builder?
-
+	@staticmethod
+	def getEntryFormat(mapping):
 		ored = 0
 		for idx in mapping:
 			ored |= idx
@@ -582,9 +653,16 @@
 		else:
 			entrySize = 4
 
-		entryFormat = ((entrySize - 1) << 4) | (innerBits - 1)
+		return ((entrySize - 1) << 4) | (innerBits - 1)
 
-		rawTable['EntryFormat'] = entryFormat
+	def preWrite(self, font):
+		mapping = getattr(self, "mapping", None)
+		if mapping is None:
+			mapping = self.mapping = []
+		self.Format = 1 if len(mapping) > 0xFFFF else 0
+		rawTable = self.__dict__.copy()
+		rawTable['MappingCount'] = len(mapping)
+		rawTable['EntryFormat'] = self.getEntryFormat(mapping)
 		return rawTable
 
 	def toXML2(self, xmlWriter, font):
@@ -600,12 +678,84 @@
 	def fromXML(self, name, attrs, content, font):
 		mapping = getattr(self, "mapping", None)
 		if mapping is None:
-			mapping = []
-			self.mapping = mapping
+			self.mapping = mapping = []
+		index = safeEval(attrs['index'])
 		outer = safeEval(attrs['outer'])
 		inner = safeEval(attrs['inner'])
 		assert inner <= 0xFFFF
-		mapping.append((outer << 16) | inner)
+		mapping.insert(index, (outer << 16) | inner)
+
+
+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)
+		rawTable['EntryFormat'] = DeltaSetIndexMap.getEntryFormat(mapping)
+		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 VarRegionList(BaseTable):
+
+	def preWrite(self, font):
+		# The OT spec says VarStore.VarRegionList.RegionAxisCount should always
+		# be equal to the fvar.axisCount, and OTS < v8.0.0 enforces this rule
+		# even when the VarRegionList is empty. We can't treat RegionAxisCount
+		# like a normal propagated count (== len(Region[i].VarRegionAxis)),
+		# otherwise it would default to 0 if VarRegionList is empty.
+		# Thus, we force it to always be equal to fvar.axisCount.
+		# https://github.com/khaledhosny/ots/pull/192
+		fvarTable = font.get("fvar")
+		if fvarTable:
+			self.RegionAxisCount = len(fvarTable.axes)
+		return {
+			**self.__dict__,
+			"RegionAxisCount": CountReference(self.__dict__, "RegionAxisCount")
+		}
 
 
 class SingleSubst(FormatSwitchingBaseTable):
@@ -617,21 +767,23 @@
 	def postRead(self, rawTable, font):
 		mapping = {}
 		input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
-		lenMapping = len(input)
 		if self.Format == 1:
 			delta = rawTable["DeltaGlyphID"]
 			inputGIDS =  [ font.getGlyphID(name) for name in input ]
 			outGIDS = [ (glyphID + delta) % 65536 for glyphID in inputGIDS ]
 			outNames = [ font.getGlyphName(glyphID) for glyphID in outGIDS ]
-			list(map(operator.setitem, [mapping]*lenMapping, input, outNames))
+			for inp, out in zip(input, outNames):
+				mapping[inp] = out
 		elif self.Format == 2:
 			assert len(input) == rawTable["GlyphCount"], \
 					"invalid SingleSubstFormat2 table"
 			subst = rawTable["Substitute"]
-			list(map(operator.setitem, [mapping]*lenMapping, input, subst))
+			for inp, sub in zip(input, subst):
+				mapping[inp] = sub
 		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)
@@ -702,6 +854,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)
@@ -818,8 +971,9 @@
 					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 _getClassRanges(self, font):
 		classDefs = getattr(self, "classDefs", None)
@@ -908,6 +1062,7 @@
 		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
@@ -972,6 +1127,7 @@
 		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
@@ -1039,9 +1195,390 @@
 			lig.LigGlyph = attrs["glyph"]
 			components = attrs["components"]
 			lig.Component = components.split(",") if components else []
+			lig.CompCount = len(lig.Component)
 			ligs.append(lig)
 
 
+class COLR(BaseTable):
+
+	def decompile(self, reader, font):
+		# COLRv0 is exceptional in that LayerRecordCount appears *after* the
+		# LayerRecordArray it counts, but the parser logic expects Count fields
+		# to always precede the arrays. Here we work around this by parsing the
+		# LayerRecordCount before the rest of the table, and storing it in
+		# the reader's local state.
+		subReader = reader.getSubReader(offset=0)
+		for conv in self.getConverters():
+			if conv.name != "LayerRecordCount":
+				subReader.advance(conv.staticSize)
+				continue
+			conv = self.getConverterByName("LayerRecordCount")
+			reader[conv.name] = conv.read(subReader, font, tableDict={})
+			break
+		else:
+			raise AssertionError("LayerRecordCount converter not found")
+		return BaseTable.decompile(self, reader, font)
+
+	def preWrite(self, font):
+		# The writer similarly assumes Count values precede the things counted,
+		# thus here we pre-initialize a CountReference; the actual count value
+		# will be set to the lenght of the array by the time this is assembled.
+		self.LayerRecordCount = None
+		return {
+			**self.__dict__,
+			"LayerRecordCount": CountReference(self.__dict__, "LayerRecordCount")
+		}
+
+
+class LookupList(BaseTable):
+	@property
+	def table(self):
+		for l in self.Lookup:
+			for st in l.SubTable:
+				if type(st).__name__.endswith("Subst"):
+					return "GSUB"
+				if type(st).__name__.endswith("Pos"):
+					return "GPOS"
+		raise ValueError
+
+	def toXML2(self, xmlWriter, font):
+		if not font or "Debg" not in font or LOOKUP_DEBUG_INFO_KEY not in font["Debg"].data:
+			return super().toXML2(xmlWriter, font)
+		debugData = font["Debg"].data[LOOKUP_DEBUG_INFO_KEY][self.table]
+		for conv in self.getConverters():
+			if conv.repeat:
+				value = getattr(self, conv.name, [])
+				for lookupIndex, item in enumerate(value):
+					if str(lookupIndex) in debugData:
+						info = LookupDebugInfo(*debugData[str(lookupIndex)])
+						tag = info.location
+						if info.name:
+							tag = f'{info.name}: {tag}'
+						if info.feature:
+							script,language,feature = info.feature
+							tag = f'{tag} in {feature} ({script}/{language})'
+						xmlWriter.comment(tag)
+						xmlWriter.newline()
+
+					conv.xmlWrite(xmlWriter, font, item, conv.name,
+							[("index", lookupIndex)])
+			else:
+				if conv.aux and not eval(conv.aux, None, vars(self)):
+					continue
+				value = getattr(self, conv.name, None) # TODO Handle defaults instead of defaulting to None!
+				conv.xmlWrite(xmlWriter, font, value, conv.name, [])
+
+class BaseGlyphRecordArray(BaseTable):
+
+	def preWrite(self, font):
+		self.BaseGlyphRecord = sorted(
+			self.BaseGlyphRecord,
+			key=lambda rec: font.getGlyphID(rec.BaseGlyph)
+		)
+		return self.__dict__.copy()
+
+
+class BaseGlyphList(BaseTable):
+
+	def preWrite(self, font):
+		self.BaseGlyphPaintRecord = sorted(
+			self.BaseGlyphPaintRecord,
+			key=lambda rec: font.getGlyphID(rec.BaseGlyph)
+		)
+		return self.__dict__.copy()
+
+
+class ClipBox(getFormatSwitchingBaseTableClass("uint8")):
+
+	def as_tuple(self):
+		return tuple(getattr(self, conv.name) for conv in self.getConverters())
+
+	def __repr__(self):
+		return f"{self.__class__.__name__}{self.as_tuple()}"
+
+
+class ClipList(getFormatSwitchingBaseTableClass("uint8")):
+
+	def populateDefaults(self, propagator=None):
+		if not hasattr(self, "clips"):
+			self.clips = {}
+
+	def postRead(self, rawTable, font):
+		clips = {}
+		glyphOrder = font.getGlyphOrder()
+		for i, rec in enumerate(rawTable["ClipRecord"]):
+			if rec.StartGlyphID > rec.EndGlyphID:
+				log.warning(
+					"invalid ClipRecord[%i].StartGlyphID (%i) > "
+					"EndGlyphID (%i); skipped",
+					i,
+					rec.StartGlyphID,
+					rec.EndGlyphID,
+				)
+				continue
+			redefinedGlyphs = []
+			missingGlyphs = []
+			for glyphID in range(rec.StartGlyphID, rec.EndGlyphID + 1):
+				try:
+					glyph = glyphOrder[glyphID]
+				except IndexError:
+					missingGlyphs.append(glyphID)
+					continue
+				if glyph not in clips:
+					clips[glyph] = copy.copy(rec.ClipBox)
+				else:
+					redefinedGlyphs.append(glyphID)
+			if redefinedGlyphs:
+				log.warning(
+					"ClipRecord[%i] overlaps previous records; "
+					"ignoring redefined clip boxes for the "
+					"following glyph ID range: [%i-%i]",
+					i,
+					min(redefinedGlyphs),
+					max(redefinedGlyphs),
+				)
+			if missingGlyphs:
+				log.warning(
+					"ClipRecord[%i] range references missing "
+					"glyph IDs: [%i-%i]",
+					i,
+					min(missingGlyphs),
+					max(missingGlyphs),
+				)
+		self.clips = clips
+
+	def groups(self):
+		glyphsByClip = defaultdict(list)
+		uniqueClips = {}
+		for glyphName, clipBox in self.clips.items():
+			key = clipBox.as_tuple()
+			glyphsByClip[key].append(glyphName)
+			if key not in uniqueClips:
+				uniqueClips[key] = clipBox
+		return {
+			frozenset(glyphs): uniqueClips[key]
+			for key, glyphs in glyphsByClip.items()
+		}
+
+	def preWrite(self, font):
+		if not hasattr(self, "clips"):
+			self.clips = {}
+		clipBoxRanges = {}
+		glyphMap = font.getReverseGlyphMap()
+		for glyphs, clipBox in self.groups().items():
+			glyphIDs = sorted(
+				glyphMap[glyphName] for glyphName in glyphs
+				if glyphName in glyphMap
+			)
+			if not glyphIDs:
+				continue
+			last = glyphIDs[0]
+			ranges = [[last]]
+			for glyphID in glyphIDs[1:]:
+				if glyphID != last + 1:
+					ranges[-1].append(last)
+					ranges.append([glyphID])
+				last = glyphID
+			ranges[-1].append(last)
+			for start, end in ranges:
+				assert (start, end) not in clipBoxRanges
+				clipBoxRanges[(start, end)] = clipBox
+
+		clipRecords = []
+		for (start, end), clipBox in sorted(clipBoxRanges.items()):
+			record = ClipRecord()
+			record.StartGlyphID = start
+			record.EndGlyphID = end
+			record.ClipBox = clipBox
+			clipRecords.append(record)
+		rawTable = {
+			"ClipCount": len(clipRecords),
+			"ClipRecord": clipRecords,
+		}
+		return rawTable
+
+	def toXML(self, xmlWriter, font, attrs=None, name=None):
+		tableName = name if name else self.__class__.__name__
+		if attrs is None:
+			attrs = []
+		if hasattr(self, "Format"):
+			attrs.append(("Format", self.Format))
+		xmlWriter.begintag(tableName, attrs)
+		xmlWriter.newline()
+		# sort clips alphabetically to ensure deterministic XML dump
+		for glyphs, clipBox in sorted(
+			self.groups().items(), key=lambda item: min(item[0])
+		):
+			xmlWriter.begintag("Clip")
+			xmlWriter.newline()
+			for glyphName in sorted(glyphs):
+				xmlWriter.simpletag("Glyph", value=glyphName)
+				xmlWriter.newline()
+			xmlWriter.begintag("ClipBox", [("Format", clipBox.Format)])
+			xmlWriter.newline()
+			clipBox.toXML2(xmlWriter, font)
+			xmlWriter.endtag("ClipBox")
+			xmlWriter.newline()
+			xmlWriter.endtag("Clip")
+			xmlWriter.newline()
+		xmlWriter.endtag(tableName)
+		xmlWriter.newline()
+
+	def fromXML(self, name, attrs, content, font):
+		clips = getattr(self, "clips", None)
+		if clips is None:
+			self.clips = clips = {}
+		assert name == "Clip"
+		glyphs = []
+		clipBox = None
+		for elem in content:
+			if not isinstance(elem, tuple):
+				continue
+			name, attrs, content = elem
+			if name == "Glyph":
+				glyphs.append(attrs["value"])
+			elif name == "ClipBox":
+				clipBox = ClipBox()
+				clipBox.Format = safeEval(attrs["Format"])
+				for elem in content:
+					if not isinstance(elem, tuple):
+						continue
+					name, attrs, content = elem
+					clipBox.fromXML(name, attrs, content, font)
+		if clipBox:
+			for glyphName in glyphs:
+				clips[glyphName] = clipBox
+
+
+class ExtendMode(IntEnum):
+	PAD = 0
+	REPEAT = 1
+	REFLECT = 2
+
+
+# Porter-Duff modes for COLRv1 PaintComposite:
+# https://github.com/googlefonts/colr-gradients-spec/tree/off_sub_1#compositemode-enumeration
+class CompositeMode(IntEnum):
+	CLEAR = 0
+	SRC = 1
+	DEST = 2
+	SRC_OVER = 3
+	DEST_OVER = 4
+	SRC_IN = 5
+	DEST_IN = 6
+	SRC_OUT = 7
+	DEST_OUT = 8
+	SRC_ATOP = 9
+	DEST_ATOP = 10
+	XOR = 11
+	PLUS = 12
+	SCREEN = 13
+	OVERLAY = 14
+	DARKEN = 15
+	LIGHTEN = 16
+	COLOR_DODGE = 17
+	COLOR_BURN = 18
+	HARD_LIGHT = 19
+	SOFT_LIGHT = 20
+	DIFFERENCE = 21
+	EXCLUSION = 22
+	MULTIPLY = 23
+	HSL_HUE = 24
+	HSL_SATURATION = 25
+	HSL_COLOR = 26
+	HSL_LUMINOSITY = 27
+
+
+class PaintFormat(IntEnum):
+	PaintColrLayers = 1
+	PaintSolid = 2
+	PaintVarSolid = 3,
+	PaintLinearGradient = 4
+	PaintVarLinearGradient = 5
+	PaintRadialGradient = 6
+	PaintVarRadialGradient = 7
+	PaintSweepGradient = 8
+	PaintVarSweepGradient = 9
+	PaintGlyph = 10
+	PaintColrGlyph = 11
+	PaintTransform = 12
+	PaintVarTransform = 13
+	PaintTranslate = 14
+	PaintVarTranslate = 15
+	PaintScale = 16
+	PaintVarScale = 17
+	PaintScaleAroundCenter = 18
+	PaintVarScaleAroundCenter = 19
+	PaintScaleUniform = 20
+	PaintVarScaleUniform = 21
+	PaintScaleUniformAroundCenter = 22
+	PaintVarScaleUniformAroundCenter = 23
+	PaintRotate = 24
+	PaintVarRotate = 25
+	PaintRotateAroundCenter = 26
+	PaintVarRotateAroundCenter = 27
+	PaintSkew = 28
+	PaintVarSkew = 29
+	PaintSkewAroundCenter = 30
+	PaintVarSkewAroundCenter = 31
+	PaintComposite = 32
+
+
+class Paint(getFormatSwitchingBaseTableClass("uint8")):
+
+	def getFormatName(self):
+		try:
+			return PaintFormat(self.Format).name
+		except ValueError:
+			raise NotImplementedError(f"Unknown Paint format: {self.Format}")
+
+	def toXML(self, xmlWriter, font, attrs=None, name=None):
+		tableName = name if name else self.__class__.__name__
+		if attrs is None:
+			attrs = []
+		attrs.append(("Format", self.Format))
+		xmlWriter.begintag(tableName, attrs)
+		xmlWriter.comment(self.getFormatName())
+		xmlWriter.newline()
+		self.toXML2(xmlWriter, font)
+		xmlWriter.endtag(tableName)
+		xmlWriter.newline()
+
+	def getChildren(self, colr):
+		if self.Format == PaintFormat.PaintColrLayers:
+			return colr.LayerList.Paint[
+				self.FirstLayerIndex : self.FirstLayerIndex + self.NumLayers
+			]
+
+		if self.Format == PaintFormat.PaintColrGlyph:
+			for record in colr.BaseGlyphList.BaseGlyphPaintRecord:
+				if record.BaseGlyph == self.Glyph:
+					return [record.Paint]
+			else:
+				raise KeyError(f"{self.Glyph!r} not in colr.BaseGlyphList")
+
+		children = []
+		for conv in self.getConverters():
+			if conv.tableClass is not None and issubclass(conv.tableClass, type(self)):
+				children.append(getattr(self, conv.name))
+
+		return children
+
+	def traverse(self, colr: COLR, callback):
+		"""Depth-first traversal of graph rooted at self, callback on each node."""
+		if not callable(callback):
+			raise TypeError("callback must be callable")
+		stack = [self]
+		visited = set()
+		while stack:
+			current = stack.pop()
+			if id(current) in visited:
+				continue
+			callback(current)
+			visited.add(id(current))
+			stack.extend(reversed(current.getChildren(colr)))
+
+
 # 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
@@ -1135,9 +1672,36 @@
 	ok = 1
 	return ok
 
+def splitMultipleSubst(oldSubTable, newSubTable, overflowRecord):
+	ok = 1
+	oldMapping = sorted(oldSubTable.mapping.items())
+	oldLen = len(oldMapping)
+
+	if overflowRecord.itemName in ['Coverage', 'RangeRecord']:
+		# Coverage table is written last. Overflow is to or within the
+		# the coverage table. We will just cut the subtable in half.
+		newLen = oldLen // 2
+
+	elif overflowRecord.itemName == 'Sequence':
+		# We just need to back up by two items from the overflowed
+		# Sequence index to make sure the offset to the Coverage table
+		# doesn't overflow.
+		newLen = overflowRecord.itemIndex - 1
+
+	newSubTable.mapping = {}
+	for i in range(newLen, oldLen):
+		item = oldMapping[i]
+		key = item[0]
+		newSubTable.mapping[key] = item[1]
+		del oldSubTable.mapping[key]
+
+	return ok
+
 def splitAlternateSubst(oldSubTable, newSubTable, overflowRecord):
 	ok = 1
 	newSubTable.Format = oldSubTable.Format
+	if hasattr(oldSubTable, 'sortCoverageLast'):
+		newSubTable.sortCoverageLast = oldSubTable.sortCoverageLast
 
 	oldAlts = sorted(oldSubTable.alternates.items())
 	oldLen = len(oldAlts)
@@ -1165,7 +1729,6 @@
 
 def splitLigatureSubst(oldSubTable, newSubTable, overflowRecord):
 	ok = 1
-	newSubTable.Format = oldSubTable.Format
 	oldLigs = sorted(oldSubTable.ligatures.items())
 	oldLen = len(oldLigs)
 
@@ -1257,9 +1820,70 @@
 	return ok
 
 
+def splitMarkBasePos(oldSubTable, newSubTable, overflowRecord):
+	# split half of the mark classes to the new subtable
+	classCount = oldSubTable.ClassCount
+	if classCount < 2:
+		# oh well, not much left to split...
+		return False
+
+	oldClassCount = classCount // 2
+	newClassCount = classCount - oldClassCount
+
+	oldMarkCoverage, oldMarkRecords = [], []
+	newMarkCoverage, newMarkRecords = [], []
+	for glyphName, markRecord in zip(
+		oldSubTable.MarkCoverage.glyphs,
+		oldSubTable.MarkArray.MarkRecord
+	):
+		if markRecord.Class < oldClassCount:
+			oldMarkCoverage.append(glyphName)
+			oldMarkRecords.append(markRecord)
+		else:
+			markRecord.Class -= oldClassCount
+			newMarkCoverage.append(glyphName)
+			newMarkRecords.append(markRecord)
+
+	oldBaseRecords, newBaseRecords = [], []
+	for rec in oldSubTable.BaseArray.BaseRecord:
+		oldBaseRecord, newBaseRecord = rec.__class__(), rec.__class__()
+		oldBaseRecord.BaseAnchor = rec.BaseAnchor[:oldClassCount]
+		newBaseRecord.BaseAnchor = rec.BaseAnchor[oldClassCount:]
+		oldBaseRecords.append(oldBaseRecord)
+		newBaseRecords.append(newBaseRecord)
+
+	newSubTable.Format = oldSubTable.Format
+
+	oldSubTable.MarkCoverage.glyphs = oldMarkCoverage
+	newSubTable.MarkCoverage = oldSubTable.MarkCoverage.__class__()
+	newSubTable.MarkCoverage.glyphs = newMarkCoverage
+
+	# share the same BaseCoverage in both halves
+	newSubTable.BaseCoverage = oldSubTable.BaseCoverage
+
+	oldSubTable.ClassCount = oldClassCount
+	newSubTable.ClassCount = newClassCount
+
+	oldSubTable.MarkArray.MarkRecord = oldMarkRecords
+	newSubTable.MarkArray = oldSubTable.MarkArray.__class__()
+	newSubTable.MarkArray.MarkRecord = newMarkRecords
+
+	oldSubTable.MarkArray.MarkCount = len(oldMarkRecords)
+	newSubTable.MarkArray.MarkCount = len(newMarkRecords)
+
+	oldSubTable.BaseArray.BaseRecord = oldBaseRecords
+	newSubTable.BaseArray = oldSubTable.BaseArray.__class__()
+	newSubTable.BaseArray.BaseRecord = newBaseRecords
+
+	oldSubTable.BaseArray.BaseCount = len(oldBaseRecords)
+	newSubTable.BaseArray.BaseCount = len(newBaseRecords)
+
+	return True
+
+
 splitTable = {	'GSUB': {
 #					1: splitSingleSubst,
-#					2: splitMultipleSubst,
+					2: splitMultipleSubst,
 					3: splitAlternateSubst,
 					4: splitLigatureSubst,
 #					5: splitContextSubst,
@@ -1271,7 +1895,7 @@
 #					1: splitSinglePos,
 					2: splitPairPos,
 #					3: splitCursivePos,
-#					4: splitMarkBasePos,
+					4: splitMarkBasePos,
 #					5: splitMarkLigPos,
 #					6: splitMarkMarkPos,
 #					7: splitContextPos,
@@ -1285,7 +1909,6 @@
 	"""
 	An offset has overflowed within a sub-table. We need to divide this subtable into smaller parts.
 	"""
-	ok = 0
 	table = ttf[overflowRecord.tableType].table
 	lookup = table.LookupList.Lookup[overflowRecord.LookupListIndex]
 	subIndex = overflowRecord.SubTableIndex
@@ -1306,7 +1929,7 @@
 		newExtSubTableClass = lookupTypes[overflowRecord.tableType][extSubTable.__class__.LookupType]
 		newExtSubTable = newExtSubTableClass()
 		newExtSubTable.Format = extSubTable.Format
-		lookup.SubTable.insert(subIndex + 1, newExtSubTable)
+		toInsert = newExtSubTable
 
 		newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
 		newSubTable = newSubTableClass()
@@ -1315,7 +1938,7 @@
 		subTableType = subtable.__class__.LookupType
 		newSubTableClass = lookupTypes[overflowRecord.tableType][subTableType]
 		newSubTable = newSubTableClass()
-		lookup.SubTable.insert(subIndex + 1, newSubTable)
+		toInsert = newSubTable
 
 	if hasattr(lookup, 'SubTableCount'): # may not be defined yet.
 		lookup.SubTableCount = lookup.SubTableCount + 1
@@ -1323,9 +1946,16 @@
 	try:
 		splitFunc = splitTable[overflowRecord.tableType][subTableType]
 	except KeyError:
-		return ok
+		log.error(
+			"Don't know how to split %s lookup type %s",
+			overflowRecord.tableType,
+			subTableType,
+		)
+		return False
 
 	ok = splitFunc(subtable, newSubTable, overflowRecord)
+	if ok:
+		lookup.SubTable.insert(subIndex + 1, toInsert)
 	return ok
 
 # End of OverFlow logic
@@ -1335,7 +1965,7 @@
 	import re
 	from .otData import otData
 
-	formatPat = re.compile("([A-Za-z0-9]+)Format(\d+)$")
+	formatPat = re.compile(r"([A-Za-z0-9]+)Format(\d+)$")
 	namespace = globals()
 
 	# populate module with classes
@@ -1345,7 +1975,11 @@
 		if m:
 			# XxxFormatN subtable, we only add the "base" table
 			name = m.group(1)
-			baseClass = FormatSwitchingBaseTable
+			# the first row of a format-switching otData table describes the Format;
+			# the first column defines the type of the Format field.
+			# Currently this can be either 'uint16' or 'uint8'.
+			formatType = table[0][0]
+			baseClass = getFormatSwitchingBaseTableClass(formatType)
 		if name not in namespace:
 			# the class doesn't exist yet, so the base implementation is used.
 			cls = type(name, (baseClass,), {})
@@ -1390,7 +2024,7 @@
 			2: LigatureMorph,
 			# 3: Reserved,
 			4: NoncontextualMorph,
-			# 5: InsertionMorph,
+			5: InsertionMorph,
 		},
 	}
 	lookupTypes['JSTF'] = lookupTypes['GPOS']  # JSTF contains GPOS
diff --git a/Lib/fontTools/ttLib/tables/sbixGlyph.py b/Lib/fontTools/ttLib/tables/sbixGlyph.py
index 7c27306..fe29c09 100644
--- a/Lib/fontTools/ttLib/tables/sbixGlyph.py
+++ b/Lib/fontTools/ttLib/tables/sbixGlyph.py
@@ -1,5 +1,3 @@
-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
@@ -75,7 +73,7 @@
 			# (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 = ""
+			self.rawdata = b""
 		else:
 			self.rawdata = sstruct.pack(sbixGlyphHeaderFormat, self) + self.imageData
 
diff --git a/Lib/fontTools/ttLib/tables/sbixStrike.py b/Lib/fontTools/ttLib/tables/sbixStrike.py
index 746b938..b367a99 100644
--- a/Lib/fontTools/ttLib/tables/sbixStrike.py
+++ b/Lib/fontTools/ttLib/tables/sbixStrike.py
@@ -1,8 +1,6 @@
-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 *
+from fontTools.misc.textTools import safeEval
+from .sbixGlyph import Glyph
 import struct
 
 sbixStrikeHeaderFormat = """
@@ -65,8 +63,8 @@
 		del self.data
 
 	def compile(self, ttFont):
-		self.glyphDataOffsets = ""
-		self.bitmapData = ""
+		self.glyphDataOffsets = b""
+		self.bitmapData = b""
 
 		glyphOrder = ttFont.getGlyphOrder()
 
diff --git a/Lib/fontTools/ttLib/tables/ttProgram.py b/Lib/fontTools/ttLib/tables/ttProgram.py
index 182982f..a1dfa3c 100644
--- a/Lib/fontTools/ttLib/tables/ttProgram.py
+++ b/Lib/fontTools/ttLib/tables/ttProgram.py
@@ -1,9 +1,9 @@
 """ttLib.tables.ttProgram.py -- Assembler/disassembler for TrueType bytecode programs."""
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import strjoin
 from fontTools.misc.textTools import num2binary, binary2num, readHex
 import array
+from io import StringIO
 import re
 import logging
 
@@ -63,6 +63,7 @@
 	(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
+        (0x91,  'GETVARIATION', 0,      'GetVariation',         0, -1), #                                    -        a1,..,an
 	(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
@@ -158,7 +159,7 @@
 	return s
 
 
-_mnemonicPat = re.compile("[A-Z][A-Z0-9]*$")
+_mnemonicPat = re.compile(r"[A-Z][A-Z0-9]*$")
 
 def _makeDict(instructionList):
 	opcodeDict = {}
@@ -194,8 +195,8 @@
 
 _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.+")
+_indentRE = re.compile(r"^FDEF|IF|ELSE\[ \]\t.+")
+_unindentRE = re.compile(r"^ELSE|ENDF|EIF\[ \]\t.+")
 
 def _skipWhite(data, pos):
 	m = _whiteRE.match(data, pos)
@@ -222,7 +223,7 @@
 	def getBytecode(self):
 		if not hasattr(self, "bytecode"):
 			self._assemble()
-		return self.bytecode.tostring()
+		return self.bytecode.tobytes()
 
 	def getAssembly(self, preserve=True):
 		if not hasattr(self, "assembly"):
diff --git a/Lib/fontTools/ttLib/ttCollection.py b/Lib/fontTools/ttLib/ttCollection.py
new file mode 100644
index 0000000..0cc11be
--- /dev/null
+++ b/Lib/fontTools/ttLib/ttCollection.py
@@ -0,0 +1,116 @@
+from fontTools.ttLib.ttFont import TTFont
+from fontTools.ttLib.sfnt import readTTCHeader, writeTTCHeader
+from io import BytesIO
+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 __enter__(self):
+		return self
+	
+	def __exit__(self, type, value, traceback):
+		self.close()
+		
+	def close(self):
+		for font in self.fonts:
+			font.close()
+
+	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="\n", 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] = value
+
+	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..5ccd182
--- /dev/null
+++ b/Lib/fontTools/ttLib/ttFont.py
@@ -0,0 +1,1068 @@
+from fontTools.misc import xmlWriter
+from fontTools.misc.py23 import Tag, byteord, tostr
+from fontTools.misc.loggingTools import deprecateArgument
+from fontTools.ttLib import TTLibError
+from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
+from io import BytesIO, StringIO
+import os
+import logging
+import traceback
+
+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=0,
+			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")
+			createStream = True
+		else:
+			# assume "file" is a writable file object
+			createStream = False
+
+		tmp = BytesIO()
+
+		writer_reordersTables = self._save(tmp)
+
+		if not (reorderTables is None or writer_reordersTables or
+				(reorderTables is False and self.reader is None)):
+			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)
+			tmp.close()
+			tmp = tmp2
+
+		if createStream:
+			# "file" is a path
+			with open(file, "wb") as file:
+				file.write(tmp.getvalue())
+		else:
+			file.write(tmp.getvalue())
+
+		tmp.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="\n", **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)
+		table = self.tables.get(tag)
+		if table is None:
+			if tag == "GlyphOrder":
+				table = GlyphOrder(tag)
+				self.tables[tag] = table
+			elif self.reader is not None:
+				table = self._readTable(tag)
+			else:
+				raise KeyError("'%s' table not found" % tag)
+		return table
+
+	def _readTable(self, tag):
+		log.debug("Reading '%s' table from disk", tag)
+		data = self.reader[tag]
+		if self._tableCache is not None:
+			table = self._tableCache.get((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 Exception:
+			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, data)] = table
+		return table
+
+	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):
+		"""Construct a new glyphset.
+
+		Args:
+			font (TTFont): The font object (used to get metrics).
+			glyphs (dict): A dictionary mapping glyph names to ``_TTGlyph`` objects.
+			glyphType (class): Either ``_TTGlyphCFF`` or ``_TTGlyphGlyf``.
+		"""
+		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 .draw() and .drawPoints() methods that take a pen object as
+	their 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):
+		"""Construct a new _TTGlyph.
+
+		Args:
+			glyphset (_TTGlyphSet): A glyphset object used to resolve components.
+			glyph (ttLib.tables._g_l_y_f.Glyph): The glyph object.
+			horizontalMetrics (int, int): The glyph's width and left sidebearing.
+		"""
+		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)
+
+	def drawPoints(self, pen):
+		# drawPoints is only implemented for _TTGlyphGlyf at this time.
+		raise NotImplementedError()
+
+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)
+
+	def drawPoints(self, pen):
+		"""Draw the glyph onto PointPen. See fontTools.pens.pointPen
+		for details how that works.
+		"""
+		glyfTable = self._glyphset._glyphs
+		glyph = self._glyph
+		offset = self.lsb - glyph.xMin if hasattr(glyph, "xMin") else 0
+		glyph.drawPoints(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)
+
+
+# Registry for custom table packer/unpacker classes. Keys are table
+# tags, values are (moduleName, className) tuples.
+# See registerCustomTableClass() and getCustomTableClass()
+_customTableRegistry = {}
+
+
+def registerCustomTableClass(tag, moduleName, className=None):
+	"""Register a custom packer/unpacker class for a table.
+	The 'moduleName' must be an importable module. If no 'className'
+	is given, it is derived from the tag, for example it will be
+	table_C_U_S_T_ for a 'CUST' tag.
+
+	The registered table class should be a subclass of
+	fontTools.ttLib.tables.DefaultTable.DefaultTable
+	"""
+	if className is None:
+		className = "table_" + tagToIdentifier(tag)
+	_customTableRegistry[tag] = (moduleName, className)
+
+
+def unregisterCustomTableClass(tag):
+	"""Unregister the custom packer/unpacker class for a table."""
+	del _customTableRegistry[tag]
+
+
+def getCustomTableClass(tag):
+	"""Return the custom table class for tag, if one has been registered
+	with 'registerCustomTableClass()'. Else return None.
+	"""
+	if tag not in _customTableRegistry:
+		return None
+	import importlib
+	moduleName, className = _customTableRegistry[tag]
+	module = importlib.import_module(moduleName)
+	return getattr(module, className)
+
+
+def getTableClass(tag):
+	"""Fetch the packer/unpacker class for a table."""
+	tableClass = getCustomTableClass(tag)
+	if tableClass is not None:
+		return tableClass
+	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
index 1952682..cc58afa 100644
--- a/Lib/fontTools/ttLib/woff2.py
+++ b/Lib/fontTools/ttLib/woff2.py
@@ -1,5 +1,5 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import Tag, bytechr, byteord, bytesjoin
+from io import BytesIO
 import sys
 import array
 import struct
@@ -12,15 +12,18 @@
 from fontTools.ttLib.sfnt import (SFNTReader, SFNTWriter, DirectoryEntry,
 	WOFFFlavorData, sfntDirectoryFormat, sfntDirectorySize, SFNTDirectoryEntry,
 	sfntDirectoryEntrySize, calcChecksum)
-from fontTools.ttLib.tables import ttProgram
+from fontTools.ttLib.tables import ttProgram, _g_l_y_f
 import logging
 
 
-log = logging.getLogger(__name__)
+log = logging.getLogger("fontTools.ttLib.woff2")
 
 haveBrotli = False
 try:
-	import brotli
+	try:
+		import brotlicffi as brotli
+	except ImportError:
+		import brotli
 	haveBrotli = True
 except ImportError:
 	pass
@@ -30,7 +33,7 @@
 
 	flavor = "woff2"
 
-	def __init__(self, file, checkChecksums=1, fontNumber=-1):
+	def __init__(self, file, checkChecksums=0, fontNumber=-1):
 		if not haveBrotli:
 			log.error(
 				'The WOFF2 decoder requires the Brotli Python extension, available at: '
@@ -82,7 +85,7 @@
 		"""Fetch the raw table data. Reconstruct transformed tables."""
 		entry = self.tables[Tag(tag)]
 		if not hasattr(entry, 'data'):
-			if tag in woff2TransformedTableTags:
+			if entry.transformed:
 				entry.data = self.reconstructTable(tag)
 			else:
 				entry.data = entry.loadData(self.transformBuffer)
@@ -90,8 +93,6 @@
 
 	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':
@@ -100,8 +101,10 @@
 			data = self._reconstructGlyf(rawData, padding)
 		elif tag == 'loca':
 			data = self._reconstructLoca()
+		elif tag == 'hmtx':
+			data = self._reconstructHmtx(rawData)
 		else:
-			raise NotImplementedError
+			raise TTLibError("transform for table '%s' is unknown" % tag)
 		return data
 
 	def _reconstructGlyf(self, data, padding=None):
@@ -130,6 +133,34 @@
 				% (self.tables['loca'].origLength, len(data)))
 		return data
 
+	def _reconstructHmtx(self, data):
+		""" Return reconstructed hmtx table data. """
+		# Before reconstructing 'hmtx' table we need to parse other tables:
+		# 'glyf' is required for reconstructing the sidebearings from the glyphs'
+		# bounding box; 'hhea' is needed for the numberOfHMetrics field.
+		if "glyf" in self.flavorData.transformedTables:
+			# transformed 'glyf' table is self-contained, thus 'loca' not needed
+			tableDependencies = ("maxp", "hhea", "glyf")
+		else:
+			# decompiling untransformed 'glyf' requires 'loca', which requires 'head'
+			tableDependencies = ("maxp", "head", "hhea", "loca", "glyf")
+		for tag in tableDependencies:
+			self._decompileTable(tag)
+		hmtxTable = self.ttFont["hmtx"] = WOFF2HmtxTable()
+		hmtxTable.reconstruct(data, self.ttFont)
+		data = hmtxTable.compile(self.ttFont)
+		return data
+
+	def _decompileTable(self, tag):
+		"""Decompile table data and store it inside self.ttFont."""
+		data = self[tag]
+		if self.ttFont.isLoaded(tag):
+			return self.ttFont[tag]
+		tableClass = getTableClass(tag)
+		table = tableClass(tag)
+		self.ttFont.tables[tag] = table
+		table.decompile(data, self.ttFont)
+
 
 class WOFF2Writer(SFNTWriter):
 
@@ -146,7 +177,7 @@
 		self.file = file
 		self.numTables = numTables
 		self.sfntVersion = Tag(sfntVersion)
-		self.flavorData = flavorData or WOFF2FlavorData()
+		self.flavorData = WOFF2FlavorData(data=flavorData)
 
 		self.directoryFormat = woff2DirectoryFormat
 		self.directorySize = woff2DirectorySize
@@ -199,7 +230,11 @@
 		# See:
 		# https://github.com/khaledhosny/ots/issues/60
 		# https://github.com/google/woff2/issues/15
-		if isTrueType:
+		if (
+			isTrueType
+			and "glyf" in self.flavorData.transformedTables
+			and "glyf" in self.tables
+		):
 			self._normaliseGlyfAndLoca(padding=4)
 		self._setHeadTransformFlag()
 
@@ -234,13 +269,7 @@
 		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'):
+		for tag in ('maxp', 'head', 'loca', 'glyf'):
 			self._decompileTable(tag)
 		self.ttFont['glyf'].padding = padding
 		for tag in ('glyf', 'loca'):
@@ -265,6 +294,8 @@
 			tableClass = WOFF2LocaTable
 		elif tag == 'glyf':
 			tableClass = WOFF2GlyfTable
+		elif tag == 'hmtx':
+			tableClass = WOFF2HmtxTable
 		else:
 			tableClass = getTableClass(tag)
 		table = tableClass(tag)
@@ -293,11 +324,17 @@
 
 	def _transformTables(self):
 		"""Return transformed font data."""
+		transformedTables = self.flavorData.transformedTables
 		for tag, entry in self.tables.items():
-			if tag in woff2TransformedTableTags:
+			data = None
+			if tag in transformedTables:
 				data = self.transformTable(tag)
-			else:
+				if data is not None:
+					entry.transformed = True
+			if data is None:
+				# pass-through the table data without transformation
 				data = entry.data
+				entry.transformed = False
 			entry.offset = self.nextTableOffset
 			entry.saveData(self.transformBuffer, data)
 			self.nextTableOffset += entry.length
@@ -306,9 +343,9 @@
 		return fontData
 
 	def transformTable(self, tag):
-		"""Return transformed table data."""
-		if tag not in woff2TransformedTableTags:
-			raise TTLibError("Transform for table '%s' is unknown" % tag)
+		"""Return transformed table data, or None if some pre-conditions aren't
+		met -- in which case, the non-transformed table data will be used.
+		"""
 		if tag == "loca":
 			data = b""
 		elif tag == "glyf":
@@ -316,8 +353,15 @@
 				self._decompileTable(tag)
 			glyfTable = self.ttFont['glyf']
 			data = glyfTable.transform(self.ttFont)
+		elif tag == "hmtx":
+			if "glyf" not in self.tables:
+				return
+			for tag in ("maxp", "head", "hhea", "loca", "glyf", "hmtx"):
+				self._decompileTable(tag)
+			hmtxTable = self.ttFont["hmtx"]
+			data = hmtxTable.transform(self.ttFont)  # can be None
 		else:
-			raise NotImplementedError
+			raise TTLibError("Transform for table '%s' is unknown" % tag)
 		return data
 
 	def _calcMasterChecksum(self):
@@ -533,11 +577,9 @@
 			# 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:
+		if self.transformed:
 			self.length, data = unpackBase128(data)
 			if self.tag == 'loca' and self.length != 0:
 				raise TTLibError(
@@ -550,10 +592,44 @@
 		if (self.flags & 0x3F) == 0x3F:
 			data += struct.pack('>4s', self.tag.tobytes())
 		data += packBase128(self.origLength)
-		if self.tag in woff2TransformedTableTags:
+		if self.transformed:
 			data += packBase128(self.length)
 		return data
 
+	@property
+	def transformVersion(self):
+		"""Return bits 6-7 of table entry's flags, which indicate the preprocessing
+		transformation version number (between 0 and 3).
+		"""
+		return self.flags >> 6
+
+	@transformVersion.setter
+	def transformVersion(self, value):
+		assert 0 <= value <= 3
+		self.flags |= value << 6
+
+	@property
+	def transformed(self):
+		"""Return True if the table has any transformation, else return False."""
+		# For all tables in a font, except for 'glyf' and 'loca', the transformation
+		# version 0 indicates the null transform (where the original table data is
+		# passed directly to the Brotli compressor). For 'glyf' and 'loca' tables,
+		# transformation version 3 indicates the null transform
+		if self.tag in {"glyf", "loca"}:
+			return self.transformVersion != 3
+		else:
+			return self.transformVersion != 0
+
+	@transformed.setter
+	def transformed(self, booleanValue):
+		# here we assume that a non-null transform means version 0 for 'glyf' and
+		# 'loca' and 1 for every other table (e.g. hmtx); but that may change as
+		# new transformation formats are introduced in the future (if ever).
+		if self.tag in {"glyf", "loca"}:
+			self.transformVersion = 3 if not booleanValue else 0
+		else:
+			self.transformVersion = int(booleanValue)
+
 
 class WOFF2LocaTable(getTableClass('loca')):
 	"""Same as parent class. The only difference is that it attempts to preserve
@@ -582,9 +658,8 @@
 					locations.append(self.locations[i] // 2)
 			else:
 				locations = array.array("I", self.locations)
-			if sys.byteorder != "big":
-				locations.byteswap()
-			data = locations.tostring()
+			if sys.byteorder != "big": locations.byteswap()
+			data = locations.tobytes()
 		else:
 			# use the most compact indexFormat given the current glyph offsets
 			data = super(WOFF2LocaTable, self).compile(ttFont)
@@ -627,8 +702,7 @@
 		self.bboxStream = self.bboxStream[bboxBitmapSize:]
 
 		self.nContourStream = array.array("h", self.nContourStream)
-		if sys.byteorder != "big":
-			self.nContourStream.byteswap()
+		if sys.byteorder != "big": self.nContourStream.byteswap()
 		assert len(self.nContourStream) == self.numGlyphs
 
 		if 'head' in ttFont:
@@ -654,19 +728,7 @@
 	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))
-
+		assert len(self.glyphOrder) == self.numGlyphs
 		if 'maxp' in ttFont:
 			ttFont['maxp'].numGlyphs = self.numGlyphs
 		self.indexFormat = ttFont['head'].indexToLocFormat
@@ -679,7 +741,7 @@
 		for glyphID in range(self.numGlyphs):
 			self._encodeGlyph(glyphID)
 
-		self.bboxStream = self.bboxBitmap.tostring() + self.bboxStream
+		self.bboxStream = self.bboxBitmap.tobytes() + self.bboxStream
 		for stream in self.subStreams:
 			setattr(self, stream + 'Size', len(getattr(self, stream)))
 		self.version = 0
@@ -873,7 +935,7 @@
 		flags = array.array('B')
 		triplets = array.array('B')
 		for i in range(len(coordinates)):
-			onCurve = glyph.flags[i]
+			onCurve = glyph.flags[i] & _g_l_y_f.flagOnCurve
 			x, y = coordinates[i]
 			absX = abs(x)
 			absY = abs(y)
@@ -907,36 +969,231 @@
 				triplets.append(absY >> 8)
 				triplets.append(absY & 0xff)
 
-		self.flagStream += flags.tostring()
-		self.glyphStream += triplets.tostring()
+		self.flagStream += flags.tobytes()
+		self.glyphStream += triplets.tobytes()
+
+
+class WOFF2HmtxTable(getTableClass("hmtx")):
+
+	def __init__(self, tag=None):
+		self.tableTag = Tag(tag or 'hmtx')
+
+	def reconstruct(self, data, ttFont):
+		flags, = struct.unpack(">B", data[:1])
+		data = data[1:]
+		if flags & 0b11111100 != 0:
+			raise TTLibError("Bits 2-7 of '%s' flags are reserved" % self.tableTag)
+
+		# When bit 0 is _not_ set, the lsb[] array is present
+		hasLsbArray = flags & 1 == 0
+		# When bit 1 is _not_ set, the leftSideBearing[] array is present
+		hasLeftSideBearingArray = flags & 2 == 0
+		if hasLsbArray and hasLeftSideBearingArray:
+			raise TTLibError(
+				"either bits 0 or 1 (or both) must set in transformed '%s' flags"
+				% self.tableTag
+			)
+
+		glyfTable = ttFont["glyf"]
+		headerTable = ttFont["hhea"]
+		glyphOrder = glyfTable.glyphOrder
+		numGlyphs = len(glyphOrder)
+		numberOfHMetrics = min(int(headerTable.numberOfHMetrics), numGlyphs)
+
+		assert len(data) >= 2 * numberOfHMetrics
+		advanceWidthArray = array.array("H", data[:2 * numberOfHMetrics])
+		if sys.byteorder != "big":
+			advanceWidthArray.byteswap()
+		data = data[2 * numberOfHMetrics:]
+
+		if hasLsbArray:
+			assert len(data) >= 2 * numberOfHMetrics
+			lsbArray = array.array("h", data[:2 * numberOfHMetrics])
+			if sys.byteorder != "big":
+				lsbArray.byteswap()
+			data = data[2 * numberOfHMetrics:]
+		else:
+			# compute (proportional) glyphs' lsb from their xMin
+			lsbArray = array.array("h")
+			for i, glyphName in enumerate(glyphOrder):
+				if i >= numberOfHMetrics:
+					break
+				glyph = glyfTable[glyphName]
+				xMin = getattr(glyph, "xMin", 0)
+				lsbArray.append(xMin)
+
+		numberOfSideBearings = numGlyphs - numberOfHMetrics
+		if hasLeftSideBearingArray:
+			assert len(data) >= 2 * numberOfSideBearings
+			leftSideBearingArray = array.array("h", data[:2 * numberOfSideBearings])
+			if sys.byteorder != "big":
+				leftSideBearingArray.byteswap()
+			data = data[2 * numberOfSideBearings:]
+		else:
+			# compute (monospaced) glyphs' leftSideBearing from their xMin
+			leftSideBearingArray = array.array("h")
+			for i, glyphName in enumerate(glyphOrder):
+				if i < numberOfHMetrics:
+					continue
+				glyph = glyfTable[glyphName]
+				xMin = getattr(glyph, "xMin", 0)
+				leftSideBearingArray.append(xMin)
+
+		if data:
+			raise TTLibError("too much '%s' table data" % self.tableTag)
+
+		self.metrics = {}
+		for i in range(numberOfHMetrics):
+			glyphName = glyphOrder[i]
+			advanceWidth, lsb = advanceWidthArray[i], lsbArray[i]
+			self.metrics[glyphName] = (advanceWidth, lsb)
+		lastAdvance = advanceWidthArray[-1]
+		for i in range(numberOfSideBearings):
+			glyphName = glyphOrder[i + numberOfHMetrics]
+			self.metrics[glyphName] = (lastAdvance, leftSideBearingArray[i])
+
+	def transform(self, ttFont):
+		glyphOrder = ttFont.getGlyphOrder()
+		glyf = ttFont["glyf"]
+		hhea = ttFont["hhea"]
+		numberOfHMetrics = hhea.numberOfHMetrics
+
+		# check if any of the proportional glyphs has left sidebearings that
+		# differ from their xMin bounding box values.
+		hasLsbArray = False
+		for i in range(numberOfHMetrics):
+			glyphName = glyphOrder[i]
+			lsb = self.metrics[glyphName][1]
+			if lsb != getattr(glyf[glyphName], "xMin", 0):
+				hasLsbArray = True
+				break
+
+		# do the same for the monospaced glyphs (if any) at the end of hmtx table
+		hasLeftSideBearingArray = False
+		for i in range(numberOfHMetrics, len(glyphOrder)):
+			glyphName = glyphOrder[i]
+			lsb = self.metrics[glyphName][1]
+			if lsb != getattr(glyf[glyphName], "xMin", 0):
+				hasLeftSideBearingArray = True
+				break
+
+		# if we need to encode both sidebearings arrays, then no transformation is
+		# applicable, and we must use the untransformed hmtx data
+		if hasLsbArray and hasLeftSideBearingArray:
+			return
+
+		# set bit 0 and 1 when the respective arrays are _not_ present
+		flags = 0
+		if not hasLsbArray:
+			flags |= 1 << 0
+		if not hasLeftSideBearingArray:
+			flags |= 1 << 1
+
+		data = struct.pack(">B", flags)
+
+		advanceWidthArray = array.array(
+			"H",
+			[
+				self.metrics[glyphName][0]
+				for i, glyphName in enumerate(glyphOrder)
+				if i < numberOfHMetrics
+			]
+		)
+		if sys.byteorder != "big":
+			advanceWidthArray.byteswap()
+		data += advanceWidthArray.tobytes()
+
+		if hasLsbArray:
+			lsbArray = array.array(
+				"h",
+				[
+					self.metrics[glyphName][1]
+					for i, glyphName in enumerate(glyphOrder)
+					if i < numberOfHMetrics
+				]
+			)
+			if sys.byteorder != "big":
+				lsbArray.byteswap()
+			data += lsbArray.tobytes()
+
+		if hasLeftSideBearingArray:
+			leftSideBearingArray = array.array(
+				"h",
+				[
+					self.metrics[glyphOrder[i]][1]
+					for i in range(numberOfHMetrics, len(glyphOrder))
+				]
+			)
+			if sys.byteorder != "big":
+				leftSideBearingArray.byteswap()
+			data += leftSideBearingArray.tobytes()
+
+		return data
 
 
 class WOFF2FlavorData(WOFFFlavorData):
 
 	Flavor = 'woff2'
 
-	def __init__(self, reader=None):
+	def __init__(self, reader=None, data=None, transformedTables=None):
+		"""Data class that holds the WOFF2 header major/minor version, any
+		metadata or private data (as bytes strings), and the set of
+		table tags that have transformations applied (if reader is not None),
+		or will have once the WOFF2 font is compiled.
+
+		Args:
+			reader: an SFNTReader (or subclass) object to read flavor data from.
+			data: another WOFFFlavorData object to initialise data from.
+			transformedTables: set of strings containing table tags to be transformed.
+
+		Raises:
+			ImportError if the brotli module is not installed.
+
+		NOTE: The 'reader' argument, on the one hand, and the 'data' and
+		'transformedTables' arguments, on the other hand, are mutually exclusive.
+		"""
 		if not haveBrotli:
 			raise ImportError("No module named brotli")
-		self.majorVersion = None
-		self.minorVersion = None
-		self.metaData = None
-		self.privData = None
+
+		if reader is not None:
+			if data is not None:
+				raise TypeError(
+					"'reader' and 'data' arguments are mutually exclusive"
+				)
+			if transformedTables is not None:
+				raise TypeError(
+					"'reader' and 'transformedTables' arguments are mutually exclusive"
+				)
+
+		if transformedTables is not None and (
+				"glyf" in transformedTables and "loca" not in transformedTables
+				or "loca" in transformedTables and "glyf" not in transformedTables
+			):
+				raise ValueError(
+					"'glyf' and 'loca' must be transformed (or not) together"
+				)
+		super(WOFF2FlavorData, self).__init__(reader=reader)
 		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
+			transformedTables = [
+				tag
+				for tag, entry in reader.tables.items()
+				if entry.transformed
+			]
+		elif data:
+			self.majorVersion = data.majorVersion
+			self.majorVersion = data.minorVersion
+			self.metaData = data.metaData
+			self.privData = data.privData
+			if transformedTables is None and hasattr(data, "transformedTables"):
+				 transformedTables = data.transformedTables
+
+		if transformedTables is None:
+			transformedTables = woff2TransformedTableTags
+
+		self.transformedTables = set(transformedTables)
+
+	def _decompress(self, rawData):
+		return brotli.decompress(rawData)
 
 
 def unpackBase128(data):
@@ -1093,6 +1350,184 @@
 		return struct.pack(">BH", 253, value)
 
 
+def compress(input_file, output_file, transform_tables=None):
+	"""Compress OpenType font to WOFF2.
+
+	Args:
+		input_file: a file path, file or file-like object (open in binary mode)
+			containing an OpenType font (either CFF- or TrueType-flavored).
+		output_file: a file path, file or file-like object where to save the
+			compressed WOFF2 font.
+		transform_tables: Optional[Iterable[str]]: a set of table tags for which
+			to enable preprocessing transformations. By default, only 'glyf'
+			and 'loca' tables are transformed. An empty set means disable all
+			transformations.
+	"""
+	log.info("Processing %s => %s" % (input_file, output_file))
+
+	font = TTFont(input_file, recalcBBoxes=False, recalcTimestamp=False)
+	font.flavor = "woff2"
+
+	if transform_tables is not None:
+		font.flavorData = WOFF2FlavorData(
+			data=font.flavorData, transformedTables=transform_tables
+		)
+
+	font.save(output_file, reorderTables=False)
+
+
+def decompress(input_file, output_file):
+	"""Decompress WOFF2 font to OpenType font.
+
+	Args:
+		input_file: a file path, file or file-like object (open in binary mode)
+			containing a compressed WOFF2 font.
+		output_file: a file path, file or file-like object where to save the
+			decompressed OpenType font.
+	"""
+	log.info("Processing %s => %s" % (input_file, output_file))
+
+	font = TTFont(input_file, recalcBBoxes=False, recalcTimestamp=False)
+	font.flavor = None
+	font.flavorData = None
+	font.save(output_file, reorderTables=True)
+
+
+def main(args=None):
+	"""Compress and decompress WOFF2 fonts"""
+	import argparse
+	from fontTools import configLogger
+	from fontTools.ttx import makeOutputFileName
+
+	class _HelpAction(argparse._HelpAction):
+
+		def __call__(self, parser, namespace, values, option_string=None):
+			subparsers_actions = [
+					action for action in parser._actions
+					if isinstance(action, argparse._SubParsersAction)]
+			for subparsers_action in subparsers_actions:
+					for choice, subparser in subparsers_action.choices.items():
+							print(subparser.format_help())
+			parser.exit()
+
+	class _NoGlyfTransformAction(argparse.Action):
+		def __call__(self, parser, namespace, values, option_string=None):
+			namespace.transform_tables.difference_update({"glyf", "loca"})
+
+	class _HmtxTransformAction(argparse.Action):
+		def __call__(self, parser, namespace, values, option_string=None):
+			namespace.transform_tables.add("hmtx")
+
+	parser = argparse.ArgumentParser(
+		prog="fonttools ttLib.woff2",
+		description=main.__doc__,
+		add_help = False
+	)
+
+	parser.add_argument('-h', '--help', action=_HelpAction,
+		help='show this help message and exit')
+
+	parser_group = parser.add_subparsers(title="sub-commands")
+	parser_compress = parser_group.add_parser("compress",
+		description = "Compress a TTF or OTF font to WOFF2")
+	parser_decompress = parser_group.add_parser("decompress",
+		description = "Decompress a WOFF2 font to OTF")
+
+	for subparser in (parser_compress, parser_decompress):
+		group = subparser.add_mutually_exclusive_group(required=False)
+		group.add_argument(
+			"-v",
+			"--verbose",
+			action="store_true",
+			help="print more messages to console",
+		)
+		group.add_argument(
+			"-q",
+			"--quiet",
+			action="store_true",
+			help="do not print messages to console",
+		)
+
+	parser_compress.add_argument(
+		"input_file",
+		metavar="INPUT",
+		help="the input OpenType font (.ttf or .otf)",
+	)
+	parser_decompress.add_argument(
+		"input_file",
+		metavar="INPUT",
+		help="the input WOFF2 font",
+	)
+
+	parser_compress.add_argument(
+		"-o",
+		"--output-file",
+		metavar="OUTPUT",
+		help="the output WOFF2 font",
+	)
+	parser_decompress.add_argument(
+		"-o",
+		"--output-file",
+		metavar="OUTPUT",
+		help="the output OpenType font",
+	)
+
+	transform_group = parser_compress.add_argument_group()
+	transform_group.add_argument(
+		"--no-glyf-transform",
+		dest="transform_tables",
+		nargs=0,
+		action=_NoGlyfTransformAction,
+		help="Do not transform glyf (and loca) tables",
+	)
+	transform_group.add_argument(
+		"--hmtx-transform",
+		dest="transform_tables",
+		nargs=0,
+		action=_HmtxTransformAction,
+		help="Enable optional transformation for 'hmtx' table",
+	)
+
+	parser_compress.set_defaults(
+		subcommand=compress,
+		transform_tables={"glyf", "loca"},
+	)
+	parser_decompress.set_defaults(subcommand=decompress)
+
+	options = vars(parser.parse_args(args))
+
+	subcommand = options.pop("subcommand", None)
+	if not subcommand:
+		parser.print_help()
+		return
+
+	quiet = options.pop("quiet")
+	verbose = options.pop("verbose")
+	configLogger(
+		level=("ERROR" if quiet else "DEBUG" if verbose else "INFO"),
+	)
+
+	if not options["output_file"]:
+		if subcommand is compress:
+			extension = ".woff2"
+		elif subcommand is decompress:
+			# choose .ttf/.otf file extension depending on sfntVersion
+			with open(options["input_file"], "rb") as f:
+				f.seek(4)  # skip 'wOF2' signature
+				sfntVersion = f.read(4)
+			assert len(sfntVersion) == 4, "not enough data"
+			extension = ".otf" if sfntVersion == b"OTTO" else ".ttf"
+		else:
+			raise AssertionError(subcommand)
+		options["output_file"] = makeOutputFileName(
+			options["input_file"], outputDir=None, extension=extension
+		)
+
+	try:
+		subcommand(**options)
+	except TTLibError as e:
+		parser.error(e)
+
+
 if __name__ == "__main__":
-	import doctest
-	sys.exit(doctest.testmod().failed)
+	sys.exit(main())
diff --git a/Lib/fontTools/ttx.py b/Lib/fontTools/ttx.py
index f695437..55b2751 100644
--- a/Lib/fontTools/ttx.py
+++ b/Lib/fontTools/ttx.py
@@ -41,6 +41,7 @@
     -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
@@ -76,6 +77,7 @@
        file as-is.
     --recalc-timestamp Set font 'modified' timestamp to current time.
        By default, the modification time of the TTX file will be used.
+    --no-recalc-timestamp Keep the original font 'modified' timestamp.
     --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
@@ -84,8 +86,7 @@
 """
 
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import Tag, tostr
 from fontTools.ttLib import TTFont, TTLibError
 from fontTools.misc.macCreatorType import getMacCreatorAndType
 from fontTools.unicode import setUnicodeData
@@ -121,8 +122,8 @@
 	ignoreDecompileErrors = True
 	bitmapGlyphDataFormat = 'raw'
 	unicodedata = None
-	newlinestr = None
-	recalcTimestamp = False
+	newlinestr = "\n"
+	recalcTimestamp = None
 	flavor = None
 	useZopfli = False
 
@@ -165,7 +166,9 @@
 			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":
@@ -201,6 +204,8 @@
 						% (value, ", ".join(map(repr, validOptions))))
 			elif option == "--recalc-timestamp":
 				self.recalcTimestamp = True
+			elif option == "--no-recalc-timestamp":
+				self.recalcTimestamp = False
 			elif option == "--flavor":
 				self.flavor = value
 			elif option == "--with-zopfli":
@@ -215,7 +220,6 @@
 			self.logLevel = logging.INFO
 		if self.mergeFile and self.flavor:
 			raise getopt.GetoptError("-m and --flavor options are mutually exclusive")
-			sys.exit(2)
 		if self.onlyTables and self.skipTables:
 			raise getopt.GetoptError("-t and -x options are mutually exclusive")
 		if self.mergeFile and numFiles > 1:
@@ -229,9 +233,9 @@
 	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]
 		if ttf.flavor == "woff2":
@@ -280,7 +284,7 @@
 			allowVID=options.allowVID)
 	ttf.importXML(input)
 
-	if not options.recalcTimestamp and 'head' in ttf:
+	if options.recalcTimestamp is None and 'head' in ttf:
 		# use TTX file modification time for head "modified" timestamp
 		mtime = os.path.getmtime(input)
 		ttf['head'].modified = timestampSinceEpoch(mtime)
@@ -291,11 +295,11 @@
 def guessFileType(fileName):
 	base, ext = os.path.splitext(fileName)
 	try:
-		f = open(fileName, "rb")
+		with open(fileName, "rb") as f:
+			header = f.read(256)
 	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)
@@ -326,8 +330,8 @@
 
 def parseOptions(args):
 	rawOptions, files = getopt.getopt(args, "ld:o:fvqht:x:sgim:z:baey:",
-			['unicodedata=', "recalc-timestamp", 'flavor=', 'version',
-			 'with-zopfli', 'newline='])
+			['unicodedata=', "recalc-timestamp", "no-recalc-timestamp",
+			 'flavor=', 'version', 'with-zopfli', 'newline='])
 
 	options = Options(rawOptions, len(files))
 	jobs = []
@@ -380,6 +384,7 @@
 
 
 def main(args=None):
+	"""Convert OpenType fonts to XML and back"""
 	from fontTools import configLogger
 
 	if args is None:
diff --git a/Lib/fontTools/ufoLib/__init__.py b/Lib/fontTools/ufoLib/__init__.py
new file mode 100755
index 0000000..e846d08
--- /dev/null
+++ b/Lib/fontTools/ufoLib/__init__.py
@@ -0,0 +1,2308 @@
+import os
+from copy import deepcopy
+from os import fsdecode
+import logging
+import zipfile
+import enum
+from collections import OrderedDict
+import fs
+import fs.base
+import fs.subfs
+import fs.errors
+import fs.copy
+import fs.osfs
+import fs.zipfs
+import fs.tempfs
+import fs.tools
+from fontTools.misc import plistlib
+from fontTools.ufoLib.validators import *
+from fontTools.ufoLib.filenames import userNameToFileName
+from fontTools.ufoLib.converters import convertUFO1OrUFO2KerningToUFO3Kerning
+from fontTools.ufoLib.errors import UFOLibError
+from fontTools.ufoLib.utils import numberTypes, _VersionTupleEnumMixin
+
+"""
+A library for importing .ufo files and their descendants.
+Refer to http://unifiedfontobject.com for the UFO specification.
+
+The UFOReader and UFOWriter classes support versions 1, 2 and 3
+of the specification.
+
+Sets that list the font info attribute names for the fontinfo.plist
+formats are available for external use. These are:
+	fontInfoAttributesVersion1
+	fontInfoAttributesVersion2
+	fontInfoAttributesVersion3
+
+A set listing the fontinfo.plist attributes that were deprecated
+in version 2 is available for external use:
+	deprecatedFontInfoAttributesVersion2
+
+Functions that do basic validation on values for fontinfo.plist
+are available for external use. These are
+	validateFontInfoVersion2ValueForAttribute
+	validateFontInfoVersion3ValueForAttribute
+
+Value conversion functions are available for converting
+fontinfo.plist values between the possible format versions.
+	convertFontInfoValueForAttributeFromVersion1ToVersion2
+	convertFontInfoValueForAttributeFromVersion2ToVersion1
+	convertFontInfoValueForAttributeFromVersion2ToVersion3
+	convertFontInfoValueForAttributeFromVersion3ToVersion2
+"""
+
+__all__ = [
+	"makeUFOPath",
+	"UFOLibError",
+	"UFOReader",
+	"UFOWriter",
+	"UFOReaderWriter",
+	"UFOFileStructure",
+	"fontInfoAttributesVersion1",
+	"fontInfoAttributesVersion2",
+	"fontInfoAttributesVersion3",
+	"deprecatedFontInfoAttributesVersion2",
+	"validateFontInfoVersion2ValueForAttribute",
+	"validateFontInfoVersion3ValueForAttribute",
+	"convertFontInfoValueForAttributeFromVersion1ToVersion2",
+	"convertFontInfoValueForAttributeFromVersion2ToVersion1"
+]
+
+__version__ = "3.0.0"
+
+
+logger = logging.getLogger(__name__)
+
+
+# ---------
+# Constants
+# ---------
+
+DEFAULT_GLYPHS_DIRNAME = "glyphs"
+DATA_DIRNAME = "data"
+IMAGES_DIRNAME = "images"
+METAINFO_FILENAME = "metainfo.plist"
+FONTINFO_FILENAME = "fontinfo.plist"
+LIB_FILENAME = "lib.plist"
+GROUPS_FILENAME = "groups.plist"
+KERNING_FILENAME = "kerning.plist"
+FEATURES_FILENAME = "features.fea"
+LAYERCONTENTS_FILENAME = "layercontents.plist"
+LAYERINFO_FILENAME = "layerinfo.plist"
+
+DEFAULT_LAYER_NAME = "public.default"
+
+
+class UFOFormatVersion(tuple, _VersionTupleEnumMixin, enum.Enum):
+	FORMAT_1_0 = (1, 0)
+	FORMAT_2_0 = (2, 0)
+	FORMAT_3_0 = (3, 0)
+
+
+class UFOFileStructure(enum.Enum):
+	ZIP = "zip"
+	PACKAGE = "package"
+
+
+# --------------
+# Shared Methods
+# --------------
+
+
+class _UFOBaseIO:
+
+	def getFileModificationTime(self, path):
+		"""
+		Returns the modification time for the file at the given path, as a
+		floating point number giving the number of seconds since the epoch.
+		The path must be relative to the UFO path.
+		Returns None if the file does not exist.
+		"""
+		try:
+			dt = self.fs.getinfo(fsdecode(path), namespaces=["details"]).modified
+		except (fs.errors.MissingInfoNamespace, fs.errors.ResourceNotFound):
+			return None
+		else:
+			return dt.timestamp()
+
+	def _getPlist(self, fileName, default=None):
+		"""
+		Read a property list relative to the UFO filesystem's root.
+		Raises UFOLibError if the file is missing and default is None,
+		otherwise default is returned.
+
+		The errors that could be raised during the reading of a plist are
+		unpredictable and/or too large to list, so, a blind try: except:
+		is done. If an exception occurs, a UFOLibError will be raised.
+		"""
+		try:
+			with self.fs.open(fileName, "rb") as f:
+				return plistlib.load(f)
+		except fs.errors.ResourceNotFound:
+			if default is None:
+				raise UFOLibError(
+					"'%s' is missing on %s. This file is required"
+					% (fileName, self.fs)
+				)
+			else:
+				return default
+		except Exception as e:
+			# TODO(anthrotype): try to narrow this down a little
+			raise UFOLibError(
+				f"'{fileName}' could not be read on {self.fs}: {e}"
+			)
+
+	def _writePlist(self, fileName, obj):
+		"""
+		Write a property list to a file relative to the UFO filesystem's root.
+
+		Do this sort of atomically, making it harder to corrupt existing files,
+		for example when plistlib encounters an error halfway during write.
+		This also checks to see if text matches the text that is already in the
+		file at path. If so, the file is not rewritten so that the modification
+		date is preserved.
+
+		The errors that could be raised during the writing of a plist are
+		unpredictable and/or too large to list, so, a blind try: except: is done.
+		If an exception occurs, a UFOLibError will be raised.
+		"""
+		if self._havePreviousFile:
+			try:
+				data = plistlib.dumps(obj)
+			except Exception as e:
+				raise UFOLibError(
+					"'%s' could not be written on %s because "
+					"the data is not properly formatted: %s"
+					% (fileName, self.fs, e)
+				)
+			if self.fs.exists(fileName) and data == self.fs.readbytes(fileName):
+				return
+			self.fs.writebytes(fileName, data)
+		else:
+			with self.fs.openbin(fileName, mode="w") as fp:
+				try:
+					plistlib.dump(obj, fp)
+				except Exception as e:
+					raise UFOLibError(
+						"'%s' could not be written on %s because "
+						"the data is not properly formatted: %s"
+						% (fileName, self.fs, e)
+					)
+
+
+# ----------
+# UFO Reader
+# ----------
+
+class UFOReader(_UFOBaseIO):
+
+	"""
+	Read the various components of the .ufo.
+
+	By default read data is validated. Set ``validate`` to
+	``False`` to not validate the data.
+	"""
+
+	def __init__(self, path, validate=True):
+		if hasattr(path, "__fspath__"):  # support os.PathLike objects
+			path = path.__fspath__()
+
+		if isinstance(path, str):
+			structure = _sniffFileStructure(path)
+			try:
+				if structure is UFOFileStructure.ZIP:
+					parentFS = fs.zipfs.ZipFS(path, write=False, encoding="utf-8")
+				else:
+					parentFS = fs.osfs.OSFS(path)
+			except fs.errors.CreateFailed as e:
+				raise UFOLibError(f"unable to open '{path}': {e}")
+
+			if structure is UFOFileStructure.ZIP:
+				# .ufoz zip files must contain a single root directory, with arbitrary
+				# name, containing all the UFO files
+				rootDirs = [
+					p.name for p in parentFS.scandir("/")
+					# exclude macOS metadata contained in zip file
+					if p.is_dir and p.name != "__MACOSX"
+				]
+				if len(rootDirs) == 1:
+					# 'ClosingSubFS' ensures that the parent zip file is closed when
+					# its root subdirectory is closed
+					self.fs = parentFS.opendir(
+						rootDirs[0], factory=fs.subfs.ClosingSubFS
+					)
+				else:
+					raise UFOLibError(
+						"Expected exactly 1 root directory, found %d" % len(rootDirs)
+					)
+			else:
+				# normal UFO 'packages' are just a single folder
+				self.fs = parentFS
+			# when passed a path string, we make sure we close the newly opened fs
+			# upon calling UFOReader.close method or context manager's __exit__
+			self._shouldClose = True
+			self._fileStructure = structure
+		elif isinstance(path, fs.base.FS):
+			filesystem = path
+			try:
+				filesystem.check()
+			except fs.errors.FilesystemClosed:
+				raise UFOLibError("the filesystem '%s' is closed" % path)
+			else:
+				self.fs = filesystem
+			try:
+				path = filesystem.getsyspath("/")
+			except fs.errors.NoSysPath:
+				# network or in-memory FS may not map to the local one
+				path = str(filesystem)
+			# when user passed an already initialized fs instance, it is her
+			# responsibility to close it, thus UFOReader.close/__exit__ are no-op
+			self._shouldClose = False
+			# default to a 'package' structure
+			self._fileStructure = UFOFileStructure.PACKAGE
+		else:
+			raise TypeError(
+				"Expected a path string or fs.base.FS object, found '%s'"
+				% type(path).__name__
+			)
+		self._path = fsdecode(path)
+		self._validate = validate
+		self._upConvertedKerningData = None
+
+		try:
+			self.readMetaInfo(validate=validate)
+		except UFOLibError:
+			self.close()
+			raise
+
+	# properties
+
+	def _get_path(self):
+		import warnings
+
+		warnings.warn(
+			"The 'path' attribute is deprecated; use the 'fs' attribute instead",
+			DeprecationWarning,
+			stacklevel=2,
+		)
+		return self._path
+
+	path = property(_get_path, doc="The path of the UFO (DEPRECATED).")
+
+	def _get_formatVersion(self):
+		import warnings
+
+		warnings.warn(
+			"The 'formatVersion' attribute is deprecated; use the 'formatVersionTuple'",
+			DeprecationWarning,
+			stacklevel=2,
+		)
+		return self._formatVersion.major
+
+	formatVersion = property(
+		_get_formatVersion,
+		doc="The (major) format version of the UFO. DEPRECATED: Use formatVersionTuple"
+	)
+
+	@property
+	def formatVersionTuple(self):
+		"""The (major, minor) format version of the UFO.
+		This is determined by reading metainfo.plist during __init__.
+		"""
+		return self._formatVersion
+
+	def _get_fileStructure(self):
+		return self._fileStructure
+
+	fileStructure = property(
+		_get_fileStructure,
+		doc=(
+			"The file structure of the UFO: "
+			"either UFOFileStructure.ZIP or UFOFileStructure.PACKAGE"
+		)
+	)
+
+	# up conversion
+
+	def _upConvertKerning(self, validate):
+		"""
+		Up convert kerning and groups in UFO 1 and 2.
+		The data will be held internally until each bit of data
+		has been retrieved. The conversion of both must be done
+		at once, so the raw data is cached and an error is raised
+		if one bit of data becomes obsolete before it is called.
+
+		``validate`` will validate the data.
+		"""
+		if self._upConvertedKerningData:
+			testKerning = self._readKerning()
+			if testKerning != self._upConvertedKerningData["originalKerning"]:
+				raise UFOLibError("The data in kerning.plist has been modified since it was converted to UFO 3 format.")
+			testGroups = self._readGroups()
+			if testGroups != self._upConvertedKerningData["originalGroups"]:
+				raise UFOLibError("The data in groups.plist has been modified since it was converted to UFO 3 format.")
+		else:
+			groups = self._readGroups()
+			if validate:
+				invalidFormatMessage = "groups.plist is not properly formatted."
+				if not isinstance(groups, dict):
+					raise UFOLibError(invalidFormatMessage)
+				for groupName, glyphList in groups.items():
+					if not isinstance(groupName, str):
+						raise UFOLibError(invalidFormatMessage)
+					elif not isinstance(glyphList, list):
+						raise UFOLibError(invalidFormatMessage)
+					for glyphName in glyphList:
+						if not isinstance(glyphName, str):
+							raise UFOLibError(invalidFormatMessage)
+			self._upConvertedKerningData = dict(
+				kerning={},
+				originalKerning=self._readKerning(),
+				groups={},
+				originalGroups=groups
+			)
+			# convert kerning and groups
+			kerning, groups, conversionMaps = convertUFO1OrUFO2KerningToUFO3Kerning(
+				self._upConvertedKerningData["originalKerning"],
+				deepcopy(self._upConvertedKerningData["originalGroups"]),
+				self.getGlyphSet()
+			)
+			# store
+			self._upConvertedKerningData["kerning"] = kerning
+			self._upConvertedKerningData["groups"] = groups
+			self._upConvertedKerningData["groupRenameMaps"] = conversionMaps
+
+	# support methods
+
+	def readBytesFromPath(self, path):
+		"""
+		Returns the bytes in the file at the given path.
+		The path must be relative to the UFO's filesystem root.
+		Returns None if the file does not exist.
+		"""
+		try:
+			return self.fs.readbytes(fsdecode(path))
+		except fs.errors.ResourceNotFound:
+			return None
+
+	def getReadFileForPath(self, path, encoding=None):
+		"""
+		Returns a file (or file-like) object for the file at the given path.
+		The path must be relative to the UFO path.
+		Returns None if the file does not exist.
+		By default the file is opened in binary mode (reads bytes).
+		If encoding is passed, the file is opened in text mode (reads str).
+
+		Note: The caller is responsible for closing the open file.
+		"""
+		path = fsdecode(path)
+		try:
+			if encoding is None:
+				return self.fs.openbin(path)
+			else:
+				return self.fs.open(path, mode="r", encoding=encoding)
+		except fs.errors.ResourceNotFound:
+			return None
+	# metainfo.plist
+
+	def _readMetaInfo(self, validate=None):
+		"""
+		Read metainfo.plist and return raw data. Only used for internal operations.
+
+		``validate`` will validate the read data, by default it is set
+		to the class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		data = self._getPlist(METAINFO_FILENAME)
+		if validate and not isinstance(data, dict):
+			raise UFOLibError("metainfo.plist is not properly formatted.")
+		try:
+			formatVersionMajor = data["formatVersion"]
+		except KeyError:
+			raise UFOLibError(
+				f"Missing required formatVersion in '{METAINFO_FILENAME}' on {self.fs}"
+			)
+		formatVersionMinor = data.setdefault("formatVersionMinor", 0)
+
+		try:
+			formatVersion = UFOFormatVersion((formatVersionMajor, formatVersionMinor))
+		except ValueError as e:
+			unsupportedMsg = (
+				f"Unsupported UFO format ({formatVersionMajor}.{formatVersionMinor}) "
+				f"in '{METAINFO_FILENAME}' on {self.fs}"
+			)
+			if validate:
+				from fontTools.ufoLib.errors import UnsupportedUFOFormat
+
+				raise UnsupportedUFOFormat(unsupportedMsg) from e
+
+			formatVersion = UFOFormatVersion.default()
+			logger.warning(
+				"%s. Assuming the latest supported version (%s). "
+				"Some data may be skipped or parsed incorrectly",
+				unsupportedMsg, formatVersion
+			)
+		data["formatVersionTuple"] = formatVersion
+		return data
+
+	def readMetaInfo(self, validate=None):
+		"""
+		Read metainfo.plist and set formatVersion. Only used for internal operations.
+
+		``validate`` will validate the read data, by default it is set
+		to the class's validate value, can be overridden.
+		"""
+		data = self._readMetaInfo(validate=validate)
+		self._formatVersion = data["formatVersionTuple"]
+
+	# groups.plist
+
+	def _readGroups(self):
+		groups = self._getPlist(GROUPS_FILENAME, {})
+		# remove any duplicate glyphs in a kerning group
+		for groupName, glyphList in groups.items():
+			if groupName.startswith(('public.kern1.', 'public.kern2.')):
+				groups[groupName] = list(OrderedDict.fromkeys(glyphList))
+		return groups
+
+	def readGroups(self, validate=None):
+		"""
+		Read groups.plist. Returns a dict.
+		``validate`` will validate the read data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		# handle up conversion
+		if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+			self._upConvertKerning(validate)
+			groups = self._upConvertedKerningData["groups"]
+		# normal
+		else:
+			groups = self._readGroups()
+		if validate:
+			valid, message = groupsValidator(groups)
+			if not valid:
+				raise UFOLibError(message)
+		return groups
+
+	def getKerningGroupConversionRenameMaps(self, validate=None):
+		"""
+		Get maps defining the renaming that was done during any
+		needed kerning group conversion. This method returns a
+		dictionary of this form:
+
+			{
+				"side1" : {"old group name" : "new group name"},
+				"side2" : {"old group name" : "new group name"}
+			}
+
+		When no conversion has been performed, the side1 and side2
+		dictionaries will be empty.
+
+		``validate`` will validate the groups, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		if self._formatVersion >= UFOFormatVersion.FORMAT_3_0:
+			return dict(side1={}, side2={})
+		# use the public group reader to force the load and
+		# conversion of the data if it hasn't happened yet.
+		self.readGroups(validate=validate)
+		return self._upConvertedKerningData["groupRenameMaps"]
+
+	# fontinfo.plist
+
+	def _readInfo(self, validate):
+		data = self._getPlist(FONTINFO_FILENAME, {})
+		if validate and not isinstance(data, dict):
+			raise UFOLibError("fontinfo.plist is not properly formatted.")
+		return data
+
+	def readInfo(self, info, validate=None):
+		"""
+		Read fontinfo.plist. It requires an object that allows
+		setting attributes with names that follow the fontinfo.plist
+		version 3 specification. This will write the attributes
+		defined in the file into the object.
+
+		``validate`` will validate the read data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		infoDict = self._readInfo(validate)
+		infoDataToSet = {}
+		# version 1
+		if self._formatVersion == UFOFormatVersion.FORMAT_1_0:
+			for attr in fontInfoAttributesVersion1:
+				value = infoDict.get(attr)
+				if value is not None:
+					infoDataToSet[attr] = value
+			infoDataToSet = _convertFontInfoDataVersion1ToVersion2(infoDataToSet)
+			infoDataToSet = _convertFontInfoDataVersion2ToVersion3(infoDataToSet)
+		# version 2
+		elif self._formatVersion == UFOFormatVersion.FORMAT_2_0:
+			for attr, dataValidationDict in list(fontInfoAttributesVersion2ValueData.items()):
+				value = infoDict.get(attr)
+				if value is None:
+					continue
+				infoDataToSet[attr] = value
+			infoDataToSet = _convertFontInfoDataVersion2ToVersion3(infoDataToSet)
+		# version 3.x
+		elif self._formatVersion.major == UFOFormatVersion.FORMAT_3_0.major:
+			for attr, dataValidationDict in list(fontInfoAttributesVersion3ValueData.items()):
+				value = infoDict.get(attr)
+				if value is None:
+					continue
+				infoDataToSet[attr] = value
+		# unsupported version
+		else:
+			raise NotImplementedError(self._formatVersion)
+		# validate data
+		if validate:
+			infoDataToSet = validateInfoVersion3Data(infoDataToSet)
+		# populate the object
+		for attr, value in list(infoDataToSet.items()):
+			try:
+				setattr(info, attr, value)
+			except AttributeError:
+				raise UFOLibError("The supplied info object does not support setting a necessary attribute (%s)." % attr)
+
+	# kerning.plist
+
+	def _readKerning(self):
+		data = self._getPlist(KERNING_FILENAME, {})
+		return data
+
+	def readKerning(self, validate=None):
+		"""
+		Read kerning.plist. Returns a dict.
+
+		``validate`` will validate the kerning data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		# handle up conversion
+		if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+			self._upConvertKerning(validate)
+			kerningNested = self._upConvertedKerningData["kerning"]
+		# normal
+		else:
+			kerningNested = self._readKerning()
+		if validate:
+			valid, message = kerningValidator(kerningNested)
+			if not valid:
+				raise UFOLibError(message)
+		# flatten
+		kerning = {}
+		for left in kerningNested:
+			for right in kerningNested[left]:
+				value = kerningNested[left][right]
+				kerning[left, right] = value
+		return kerning
+
+	# lib.plist
+
+	def readLib(self, validate=None):
+		"""
+		Read lib.plist. Returns a dict.
+
+		``validate`` will validate the data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		data = self._getPlist(LIB_FILENAME, {})
+		if validate:
+			valid, message = fontLibValidator(data)
+			if not valid:
+				raise UFOLibError(message)
+		return data
+
+	# features.fea
+
+	def readFeatures(self):
+		"""
+		Read features.fea. Return a string.
+		The returned string is empty if the file is missing.
+		"""
+		try:
+			with self.fs.open(FEATURES_FILENAME, "r", encoding="utf-8") as f:
+				return f.read()
+		except fs.errors.ResourceNotFound:
+			return ""
+
+	# glyph sets & layers
+
+	def _readLayerContents(self, validate):
+		"""
+		Rebuild the layer contents list by checking what glyphsets
+		are available on disk.
+
+		``validate`` will validate the layer contents.
+		"""
+		if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+			return [(DEFAULT_LAYER_NAME, DEFAULT_GLYPHS_DIRNAME)]
+		contents = self._getPlist(LAYERCONTENTS_FILENAME)
+		if validate:
+			valid, error = layerContentsValidator(contents, self.fs)
+			if not valid:
+				raise UFOLibError(error)
+		return contents
+
+	def getLayerNames(self, validate=None):
+		"""
+		Get the ordered layer names from layercontents.plist.
+
+		``validate`` will validate the data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		layerContents = self._readLayerContents(validate)
+		layerNames = [layerName for layerName, directoryName in layerContents]
+		return layerNames
+
+	def getDefaultLayerName(self, validate=None):
+		"""
+		Get the default layer name from layercontents.plist.
+
+		``validate`` will validate the data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		layerContents = self._readLayerContents(validate)
+		for layerName, layerDirectory in layerContents:
+			if layerDirectory == DEFAULT_GLYPHS_DIRNAME:
+				return layerName
+		# this will already have been raised during __init__
+		raise UFOLibError("The default layer is not defined in layercontents.plist.")
+
+	def getGlyphSet(self, layerName=None, validateRead=None, validateWrite=None):
+		"""
+		Return the GlyphSet associated with the
+		glyphs directory mapped to layerName
+		in the UFO. If layerName is not provided,
+		the name retrieved with getDefaultLayerName
+		will be used.
+
+		``validateRead`` will validate the read data, by default it is set to the
+		class's validate value, can be overridden.
+		``validateWrite`` will validate the written data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		from fontTools.ufoLib.glifLib import GlyphSet
+
+		if validateRead is None:
+			validateRead = self._validate
+		if validateWrite is None:
+			validateWrite = self._validate
+		if layerName is None:
+			layerName = self.getDefaultLayerName(validate=validateRead)
+		directory = None
+		layerContents = self._readLayerContents(validateRead)
+		for storedLayerName, storedLayerDirectory in layerContents:
+			if layerName == storedLayerName:
+				directory = storedLayerDirectory
+				break
+		if directory is None:
+			raise UFOLibError("No glyphs directory is mapped to \"%s\"." % layerName)
+		try:
+			glyphSubFS = self.fs.opendir(directory)
+		except fs.errors.ResourceNotFound:
+			raise UFOLibError(
+				f"No '{directory}' directory for layer '{layerName}'"
+			)
+		return GlyphSet(
+			glyphSubFS,
+			ufoFormatVersion=self._formatVersion,
+			validateRead=validateRead,
+			validateWrite=validateWrite,
+			expectContentsFile=True
+		)
+
+	def getCharacterMapping(self, layerName=None, validate=None):
+		"""
+		Return a dictionary that maps unicode values (ints) to
+		lists of glyph names.
+		"""
+		if validate is None:
+			validate = self._validate
+		glyphSet = self.getGlyphSet(layerName, validateRead=validate, validateWrite=True)
+		allUnicodes = glyphSet.getUnicodes()
+		cmap = {}
+		for glyphName, unicodes in allUnicodes.items():
+			for code in unicodes:
+				if code in cmap:
+					cmap[code].append(glyphName)
+				else:
+					cmap[code] = [glyphName]
+		return cmap
+
+	# /data
+
+	def getDataDirectoryListing(self):
+		"""
+		Returns a list of all files in the data directory.
+		The returned paths will be relative to the UFO.
+		This will not list directory names, only file names.
+		Thus, empty directories will be skipped.
+		"""
+		try:
+			self._dataFS = self.fs.opendir(DATA_DIRNAME)
+		except fs.errors.ResourceNotFound:
+			return []
+		except fs.errors.DirectoryExpected:
+			raise UFOLibError("The UFO contains a \"data\" file instead of a directory.")
+		try:
+			# fs Walker.files method returns "absolute" paths (in terms of the
+			# root of the 'data' SubFS), so we strip the leading '/' to make
+			# them relative
+			return [
+				p.lstrip("/") for p in self._dataFS.walk.files()
+			]
+		except fs.errors.ResourceError:
+			return []
+
+	def getImageDirectoryListing(self, validate=None):
+		"""
+		Returns a list of all image file names in
+		the images directory. Each of the images will
+		have been verified to have the PNG signature.
+
+		``validate`` will validate the data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+			return []
+		if validate is None:
+			validate = self._validate
+		try:
+			self._imagesFS = imagesFS = self.fs.opendir(IMAGES_DIRNAME)
+		except fs.errors.ResourceNotFound:
+			return []
+		except fs.errors.DirectoryExpected:
+			raise UFOLibError("The UFO contains an \"images\" file instead of a directory.")
+		result = []
+		for path in imagesFS.scandir("/"):
+			if path.is_dir:
+				# silently skip this as version control
+				# systems often have hidden directories
+				continue
+			if validate:
+				with imagesFS.openbin(path.name) as fp:
+					valid, error = pngValidator(fileObj=fp)
+				if valid:
+					result.append(path.name)
+			else:
+				result.append(path.name)
+		return result
+
+	def readData(self, fileName):
+		"""
+		Return bytes for the file named 'fileName' inside the 'data/' directory.
+		"""
+		fileName = fsdecode(fileName)
+		try:
+			try:
+				dataFS = self._dataFS
+			except AttributeError:
+				# in case readData is called before getDataDirectoryListing
+				dataFS = self.fs.opendir(DATA_DIRNAME)
+			data = dataFS.readbytes(fileName)
+		except fs.errors.ResourceNotFound:
+			raise UFOLibError(f"No data file named '{fileName}' on {self.fs}")
+		return data
+
+	def readImage(self, fileName, validate=None):
+		"""
+		Return image data for the file named fileName.
+
+		``validate`` will validate the data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+			raise UFOLibError(
+				f"Reading images is not allowed in UFO {self._formatVersion.major}."
+			)
+		fileName = fsdecode(fileName)
+		try:
+			try:
+				imagesFS = self._imagesFS
+			except AttributeError:
+				# in case readImage is called before getImageDirectoryListing
+				imagesFS = self.fs.opendir(IMAGES_DIRNAME)
+			data = imagesFS.readbytes(fileName)
+		except fs.errors.ResourceNotFound:
+			raise UFOLibError(f"No image file named '{fileName}' on {self.fs}")
+		if validate:
+			valid, error = pngValidator(data=data)
+			if not valid:
+				raise UFOLibError(error)
+		return data
+
+	def close(self):
+		if self._shouldClose:
+			self.fs.close()
+
+	def __enter__(self):
+		return self
+
+	def __exit__(self, exc_type, exc_value, exc_tb):
+		self.close()
+
+
+# ----------
+# UFO Writer
+# ----------
+
+class UFOWriter(UFOReader):
+
+	"""
+	Write the various components of the .ufo.
+
+	By default, the written data will be validated before writing. Set ``validate`` to
+	``False`` if you do not want to validate the data. Validation can also be overriden
+	on a per method level if desired.
+
+	The ``formatVersion`` argument allows to specify the UFO format version as a tuple
+	of integers (major, minor), or as a single integer for the major digit only (minor
+	is implied as 0). By default the latest formatVersion will be used; currently it's
+	3.0, which is equivalent to formatVersion=(3, 0).
+
+	An UnsupportedUFOFormat exception is raised if the requested UFO formatVersion is
+	not supported.
+	"""
+
+	def __init__(
+		self,
+		path,
+		formatVersion=None,
+		fileCreator="com.github.fonttools.ufoLib",
+		structure=None,
+		validate=True,
+	):
+		try:
+			formatVersion = UFOFormatVersion(formatVersion)
+		except ValueError as e:
+			from fontTools.ufoLib.errors import UnsupportedUFOFormat
+
+			raise UnsupportedUFOFormat(f"Unsupported UFO format: {formatVersion!r}") from e
+
+		if hasattr(path, "__fspath__"):  # support os.PathLike objects
+			path = path.__fspath__()
+
+		if isinstance(path, str):
+			# normalize path by removing trailing or double slashes
+			path = os.path.normpath(path)
+			havePreviousFile = os.path.exists(path)
+			if havePreviousFile:
+				# ensure we use the same structure as the destination
+				existingStructure = _sniffFileStructure(path)
+				if structure is not None:
+					try:
+						structure = UFOFileStructure(structure)
+					except ValueError:
+						raise UFOLibError(
+							"Invalid or unsupported structure: '%s'" % structure
+						)
+					if structure is not existingStructure:
+						raise UFOLibError(
+							"A UFO with a different structure (%s) already exists "
+							"at the given path: '%s'" % (existingStructure, path)
+						)
+				else:
+					structure = existingStructure
+			else:
+				# if not exists, default to 'package' structure
+				if structure is None:
+					structure = UFOFileStructure.PACKAGE
+				dirName = os.path.dirname(path)
+				if dirName and not os.path.isdir(dirName):
+					raise UFOLibError(
+						"Cannot write to '%s': directory does not exist" % path
+					)
+			if structure is UFOFileStructure.ZIP:
+				if havePreviousFile:
+					# we can't write a zip in-place, so we have to copy its
+					# contents to a temporary location and work from there, then
+					# upon closing UFOWriter we create the final zip file
+					parentFS = fs.tempfs.TempFS()
+					with fs.zipfs.ZipFS(path, encoding="utf-8") as origFS:
+						fs.copy.copy_fs(origFS, parentFS)
+					# if output path is an existing zip, we require that it contains
+					# one, and only one, root directory (with arbitrary name), in turn
+					# containing all the existing UFO contents
+					rootDirs = [
+						p.name for p in parentFS.scandir("/")
+						# exclude macOS metadata contained in zip file
+						if p.is_dir and p.name != "__MACOSX"
+					]
+					if len(rootDirs) != 1:
+						raise UFOLibError(
+							"Expected exactly 1 root directory, found %d" % len(rootDirs)
+						)
+					else:
+						# 'ClosingSubFS' ensures that the parent filesystem is closed
+						# when its root subdirectory is closed
+						self.fs = parentFS.opendir(
+							rootDirs[0], factory=fs.subfs.ClosingSubFS
+						)
+				else:
+					# if the output zip file didn't exist, we create the root folder;
+					# we name it the same as input 'path', but with '.ufo' extension
+					rootDir = os.path.splitext(os.path.basename(path))[0] + ".ufo"
+					parentFS = fs.zipfs.ZipFS(path, write=True, encoding="utf-8")
+					parentFS.makedir(rootDir)
+					self.fs = parentFS.opendir(rootDir, factory=fs.subfs.ClosingSubFS)
+			else:
+				self.fs = fs.osfs.OSFS(path, create=True)
+			self._fileStructure = structure
+			self._havePreviousFile = havePreviousFile
+			self._shouldClose = True
+		elif isinstance(path, fs.base.FS):
+			filesystem = path
+			try:
+				filesystem.check()
+			except fs.errors.FilesystemClosed:
+				raise UFOLibError("the filesystem '%s' is closed" % path)
+			else:
+				self.fs = filesystem
+			try:
+				path = filesystem.getsyspath("/")
+			except fs.errors.NoSysPath:
+				# network or in-memory FS may not map to the local one
+				path = str(filesystem)
+			# if passed an FS object, always use 'package' structure
+			if structure and structure is not UFOFileStructure.PACKAGE:
+				import warnings
+
+				warnings.warn(
+					"The 'structure' argument is not used when input is an FS object",
+					UserWarning,
+					stacklevel=2,
+				)
+			self._fileStructure = UFOFileStructure.PACKAGE
+			# if FS contains a "metainfo.plist", we consider it non-empty
+			self._havePreviousFile = filesystem.exists(METAINFO_FILENAME)
+			# the user is responsible for closing the FS object
+			self._shouldClose = False
+		else:
+			raise TypeError(
+				"Expected a path string or fs object, found %s"
+				% type(path).__name__
+			)
+
+		# establish some basic stuff
+		self._path = fsdecode(path)
+		self._formatVersion = formatVersion
+		self._fileCreator = fileCreator
+		self._downConversionKerningData = None
+		self._validate = validate
+		# if the file already exists, get the format version.
+		# this will be needed for up and down conversion.
+		previousFormatVersion = None
+		if self._havePreviousFile:
+			metaInfo = self._readMetaInfo(validate=validate)
+			previousFormatVersion = metaInfo["formatVersionTuple"]
+			# catch down conversion
+			if previousFormatVersion > formatVersion:
+				from fontTools.ufoLib.errors import UnsupportedUFOFormat
+
+				raise UnsupportedUFOFormat(
+					"The UFO located at this path is a higher version "
+					f"({previousFormatVersion}) than the version ({formatVersion}) "
+					"that is trying to be written. This is not supported."
+				)
+		# handle the layer contents
+		self.layerContents = {}
+		if previousFormatVersion is not None and previousFormatVersion.major >= 3:
+			# already exists
+			self.layerContents = OrderedDict(self._readLayerContents(validate))
+		else:
+			# previous < 3
+			# imply the layer contents
+			if self.fs.exists(DEFAULT_GLYPHS_DIRNAME):
+				self.layerContents = {DEFAULT_LAYER_NAME : DEFAULT_GLYPHS_DIRNAME}
+		# write the new metainfo
+		self._writeMetaInfo()
+
+	# properties
+
+	def _get_fileCreator(self):
+		return self._fileCreator
+
+	fileCreator = property(_get_fileCreator, doc="The file creator of the UFO. This is set into metainfo.plist during __init__.")
+
+	# support methods for file system interaction
+
+	def copyFromReader(self, reader, sourcePath, destPath):
+		"""
+		Copy the sourcePath in the provided UFOReader to destPath
+		in this writer. The paths must be relative. This works with
+		both individual files and directories.
+		"""
+		if not isinstance(reader, UFOReader):
+			raise UFOLibError("The reader must be an instance of UFOReader.")
+		sourcePath = fsdecode(sourcePath)
+		destPath = fsdecode(destPath)
+		if not reader.fs.exists(sourcePath):
+			raise UFOLibError("The reader does not have data located at \"%s\"." % sourcePath)
+		if self.fs.exists(destPath):
+			raise UFOLibError("A file named \"%s\" already exists." % destPath)
+		# create the destination directory if it doesn't exist
+		self.fs.makedirs(fs.path.dirname(destPath), recreate=True)
+		if reader.fs.isdir(sourcePath):
+			fs.copy.copy_dir(reader.fs, sourcePath, self.fs, destPath)
+		else:
+			fs.copy.copy_file(reader.fs, sourcePath, self.fs, destPath)
+
+	def writeBytesToPath(self, path, data):
+		"""
+		Write bytes to a path relative to the UFO filesystem's root.
+		If writing to an existing UFO, check to see if data matches the data
+		that is already in the file at path; if so, the file is not rewritten
+		so that the modification date is preserved.
+		If needed, the directory tree for the given path will be built.
+		"""
+		path = fsdecode(path)
+		if self._havePreviousFile:
+			if self.fs.isfile(path) and data == self.fs.readbytes(path):
+				return
+		try:
+			self.fs.writebytes(path, data)
+		except fs.errors.FileExpected:
+			raise UFOLibError("A directory exists at '%s'" % path)
+		except fs.errors.ResourceNotFound:
+			self.fs.makedirs(fs.path.dirname(path), recreate=True)
+			self.fs.writebytes(path, data)
+
+	def getFileObjectForPath(self, path, mode="w", encoding=None):
+		"""
+		Returns a file (or file-like) object for the
+		file at the given path. The path must be relative
+		to the UFO path. Returns None if the file does
+		not exist and the mode is "r" or "rb.
+		An encoding may be passed if the file is opened in text mode.
+
+		Note: The caller is responsible for closing the open file.
+		"""
+		path = fsdecode(path)
+		try:
+			return self.fs.open(path, mode=mode, encoding=encoding)
+		except fs.errors.ResourceNotFound as e:
+			m = mode[0]
+			if m == "r":
+				# XXX I think we should just let it raise. The docstring,
+				# however, says that this returns None if mode is 'r'
+				return None
+			elif m == "w" or m == "a" or m == "x":
+				self.fs.makedirs(fs.path.dirname(path), recreate=True)
+				return self.fs.open(path, mode=mode, encoding=encoding)
+		except fs.errors.ResourceError as e:
+			return UFOLibError(
+				f"unable to open '{path}' on {self.fs}: {e}"
+			)
+
+	def removePath(self, path, force=False, removeEmptyParents=True):
+		"""
+		Remove the file (or directory) at path. The path
+		must be relative to the UFO.
+		Raises UFOLibError if the path doesn't exist.
+		If force=True, ignore non-existent paths.
+		If the directory where 'path' is located becomes empty, it will
+		be automatically removed, unless 'removeEmptyParents' is False.
+		"""
+		path = fsdecode(path)
+		try:
+			self.fs.remove(path)
+		except fs.errors.FileExpected:
+			self.fs.removetree(path)
+		except fs.errors.ResourceNotFound:
+			if not force:
+				raise UFOLibError(
+					f"'{path}' does not exist on {self.fs}"
+				)
+		if removeEmptyParents:
+			parent = fs.path.dirname(path)
+			if parent:
+				fs.tools.remove_empty(self.fs, parent)
+
+	# alias kept for backward compatibility with old API
+	removeFileForPath = removePath
+
+	# UFO mod time
+
+	def setModificationTime(self):
+		"""
+		Set the UFO modification time to the current time.
+		This is never called automatically. It is up to the
+		caller to call this when finished working on the UFO.
+		"""
+		path = self._path
+		if path is not None and os.path.exists(path):
+			try:
+				# this may fail on some filesystems (e.g. SMB servers)
+				os.utime(path, None)
+			except OSError as e:
+				logger.warning("Failed to set modified time: %s", e)
+
+	# metainfo.plist
+
+	def _writeMetaInfo(self):
+		metaInfo = dict(
+			creator=self._fileCreator,
+			formatVersion=self._formatVersion.major,
+		)
+		if self._formatVersion.minor != 0:
+			metaInfo["formatVersionMinor"] = self._formatVersion.minor
+		self._writePlist(METAINFO_FILENAME, metaInfo)
+
+	# groups.plist
+
+	def setKerningGroupConversionRenameMaps(self, maps):
+		"""
+		Set maps defining the renaming that should be done
+		when writing groups and kerning in UFO 1 and UFO 2.
+		This will effectively undo the conversion done when
+		UFOReader reads this data. The dictionary should have
+		this form:
+
+			{
+				"side1" : {"group name to use when writing" : "group name in data"},
+				"side2" : {"group name to use when writing" : "group name in data"}
+			}
+
+		This is the same form returned by UFOReader's
+		getKerningGroupConversionRenameMaps method.
+		"""
+		if self._formatVersion >= UFOFormatVersion.FORMAT_3_0:
+			return # XXX raise an error here
+		# flip the dictionaries
+		remap = {}
+		for side in ("side1", "side2"):
+			for writeName, dataName in list(maps[side].items()):
+				remap[dataName] = writeName
+		self._downConversionKerningData = dict(groupRenameMap=remap)
+
+	def writeGroups(self, groups, validate=None):
+		"""
+		Write groups.plist. This method requires a
+		dict of glyph groups as an argument.
+
+		``validate`` will validate the data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		# validate the data structure
+		if validate:
+			valid, message = groupsValidator(groups)
+			if not valid:
+				raise UFOLibError(message)
+		# down convert
+		if (
+			self._formatVersion < UFOFormatVersion.FORMAT_3_0
+			and self._downConversionKerningData is not None
+		):
+			remap = self._downConversionKerningData["groupRenameMap"]
+			remappedGroups = {}
+			# there are some edge cases here that are ignored:
+			# 1. if a group is being renamed to a name that
+			#    already exists, the existing group is always
+			#    overwritten. (this is why there are two loops
+			#    below.) there doesn't seem to be a logical
+			#    solution to groups mismatching and overwriting
+			#    with the specifiecd group seems like a better
+			#    solution than throwing an error.
+			# 2. if side 1 and side 2 groups are being renamed
+			#    to the same group name there is no check to
+			#    ensure that the contents are identical. that
+			#    is left up to the caller.
+			for name, contents in list(groups.items()):
+				if name in remap:
+					continue
+				remappedGroups[name] = contents
+			for name, contents in list(groups.items()):
+				if name not in remap:
+					continue
+				name = remap[name]
+				remappedGroups[name] = contents
+			groups = remappedGroups
+		# pack and write
+		groupsNew = {}
+		for key, value in groups.items():
+			groupsNew[key] = list(value)
+		if groupsNew:
+			self._writePlist(GROUPS_FILENAME, groupsNew)
+		elif self._havePreviousFile:
+			self.removePath(GROUPS_FILENAME, force=True, removeEmptyParents=False)
+
+	# fontinfo.plist
+
+	def writeInfo(self, info, validate=None):
+		"""
+		Write info.plist. This method requires an object
+		that supports getting attributes that follow the
+		fontinfo.plist version 2 specification. Attributes
+		will be taken from the given object and written
+		into the file.
+
+		``validate`` will validate the data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		# gather version 3 data
+		infoData = {}
+		for attr in list(fontInfoAttributesVersion3ValueData.keys()):
+			if hasattr(info, attr):
+				try:
+					value = getattr(info, attr)
+				except AttributeError:
+					raise UFOLibError("The supplied info object does not support getting a necessary attribute (%s)." % attr)
+				if value is None:
+					continue
+				infoData[attr] = value
+		# down convert data if necessary and validate
+		if self._formatVersion == UFOFormatVersion.FORMAT_3_0:
+			if validate:
+				infoData = validateInfoVersion3Data(infoData)
+		elif self._formatVersion == UFOFormatVersion.FORMAT_2_0:
+			infoData = _convertFontInfoDataVersion3ToVersion2(infoData)
+			if validate:
+				infoData = validateInfoVersion2Data(infoData)
+		elif self._formatVersion == UFOFormatVersion.FORMAT_1_0:
+			infoData = _convertFontInfoDataVersion3ToVersion2(infoData)
+			if validate:
+				infoData = validateInfoVersion2Data(infoData)
+			infoData = _convertFontInfoDataVersion2ToVersion1(infoData)
+		# write file if there is anything to write
+		if infoData:
+			self._writePlist(FONTINFO_FILENAME, infoData)
+
+	# kerning.plist
+
+	def writeKerning(self, kerning, validate=None):
+		"""
+		Write kerning.plist. This method requires a
+		dict of kerning pairs as an argument.
+
+		This performs basic structural validation of the kerning,
+		but it does not check for compliance with the spec in
+		regards to conflicting pairs. The assumption is that the
+		kerning data being passed is standards compliant.
+
+		``validate`` will validate the data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		# validate the data structure
+		if validate:
+			invalidFormatMessage = "The kerning is not properly formatted."
+			if not isDictEnough(kerning):
+				raise UFOLibError(invalidFormatMessage)
+			for pair, value in list(kerning.items()):
+				if not isinstance(pair, (list, tuple)):
+					raise UFOLibError(invalidFormatMessage)
+				if not len(pair) == 2:
+					raise UFOLibError(invalidFormatMessage)
+				if not isinstance(pair[0], str):
+					raise UFOLibError(invalidFormatMessage)
+				if not isinstance(pair[1], str):
+					raise UFOLibError(invalidFormatMessage)
+				if not isinstance(value, numberTypes):
+					raise UFOLibError(invalidFormatMessage)
+		# down convert
+		if (
+			self._formatVersion < UFOFormatVersion.FORMAT_3_0
+			and self._downConversionKerningData is not None
+		):
+			remap = self._downConversionKerningData["groupRenameMap"]
+			remappedKerning = {}
+			for (side1, side2), value in list(kerning.items()):
+				side1 = remap.get(side1, side1)
+				side2 = remap.get(side2, side2)
+				remappedKerning[side1, side2] = value
+			kerning = remappedKerning
+		# pack and write
+		kerningDict = {}
+		for left, right in kerning.keys():
+			value = kerning[left, right]
+			if left not in kerningDict:
+				kerningDict[left] = {}
+			kerningDict[left][right] = value
+		if kerningDict:
+			self._writePlist(KERNING_FILENAME, kerningDict)
+		elif self._havePreviousFile:
+			self.removePath(KERNING_FILENAME, force=True, removeEmptyParents=False)
+
+	# lib.plist
+
+	def writeLib(self, libDict, validate=None):
+		"""
+		Write lib.plist. This method requires a
+		lib dict as an argument.
+
+		``validate`` will validate the data, by default it is set to the
+		class's validate value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validate
+		if validate:
+			valid, message = fontLibValidator(libDict)
+			if not valid:
+				raise UFOLibError(message)
+		if libDict:
+			self._writePlist(LIB_FILENAME, libDict)
+		elif self._havePreviousFile:
+			self.removePath(LIB_FILENAME, force=True, removeEmptyParents=False)
+
+	# features.fea
+
+	def writeFeatures(self, features, validate=None):
+		"""
+		Write features.fea. This method requires a
+		features string as an argument.
+		"""
+		if validate is None:
+			validate = self._validate
+		if self._formatVersion == UFOFormatVersion.FORMAT_1_0:
+			raise UFOLibError("features.fea is not allowed in UFO Format Version 1.")
+		if validate:
+			if not isinstance(features, str):
+				raise UFOLibError("The features are not text.")
+		if features:
+			self.writeBytesToPath(FEATURES_FILENAME, features.encode("utf8"))
+		elif self._havePreviousFile:
+			self.removePath(FEATURES_FILENAME, force=True, removeEmptyParents=False)
+
+	# glyph sets & layers
+
+	def writeLayerContents(self, layerOrder=None, validate=None):
+		"""
+		Write the layercontents.plist file. This method  *must* be called
+		after all glyph sets have been written.
+		"""
+		if validate is None:
+			validate = self._validate
+		if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+			return
+		if layerOrder is not None:
+			newOrder = []
+			for layerName in layerOrder:
+				if layerName is None:
+					layerName = DEFAULT_LAYER_NAME
+				newOrder.append(layerName)
+			layerOrder = newOrder
+		else:
+			layerOrder = list(self.layerContents.keys())
+		if validate and set(layerOrder) != set(self.layerContents.keys()):
+			raise UFOLibError("The layer order content does not match the glyph sets that have been created.")
+		layerContents = [(layerName, self.layerContents[layerName]) for layerName in layerOrder]
+		self._writePlist(LAYERCONTENTS_FILENAME, layerContents)
+
+	def _findDirectoryForLayerName(self, layerName):
+		foundDirectory = None
+		for existingLayerName, directoryName in list(self.layerContents.items()):
+			if layerName is None and directoryName == DEFAULT_GLYPHS_DIRNAME:
+				foundDirectory = directoryName
+				break
+			elif existingLayerName == layerName:
+				foundDirectory = directoryName
+				break
+		if not foundDirectory:
+			raise UFOLibError("Could not locate a glyph set directory for the layer named %s." % layerName)
+		return foundDirectory
+
+	def getGlyphSet(
+		self,
+		layerName=None,
+		defaultLayer=True,
+		glyphNameToFileNameFunc=None,
+		validateRead=None,
+		validateWrite=None,
+		expectContentsFile=False,
+	):
+		"""
+		Return the GlyphSet object associated with the
+		appropriate glyph directory in the .ufo.
+		If layerName is None, the default glyph set
+		will be used. The defaultLayer flag indictes
+		that the layer should be saved into the default
+		glyphs directory.
+
+		``validateRead`` will validate the read data, by default it is set to the
+		class's validate value, can be overridden.
+		``validateWrte`` will validate the written data, by default it is set to the
+		class's validate value, can be overridden.
+		``expectContentsFile`` will raise a GlifLibError if a contents.plist file is
+		not found on the glyph set file system. This should be set to ``True`` if you
+		are reading an existing UFO and ``False`` if you use ``getGlyphSet`` to create
+		a fresh	glyph set.
+		"""
+		if validateRead is None:
+			validateRead = self._validate
+		if validateWrite is None:
+			validateWrite = self._validate
+		# only default can be written in < 3
+		if (
+			self._formatVersion < UFOFormatVersion.FORMAT_3_0
+			and (not defaultLayer or layerName is not None)
+		):
+			raise UFOLibError(
+				f"Only the default layer can be writen in UFO {self._formatVersion.major}."
+			)
+		# locate a layer name when None has been given
+		if layerName is None and defaultLayer:
+			for existingLayerName, directory in self.layerContents.items():
+				if directory == DEFAULT_GLYPHS_DIRNAME:
+					layerName = existingLayerName
+			if layerName is None:
+				layerName = DEFAULT_LAYER_NAME
+		elif layerName is None and not defaultLayer:
+			raise UFOLibError("A layer name must be provided for non-default layers.")
+		# move along to format specific writing
+		if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+			return self._getDefaultGlyphSet(
+				validateRead,
+				validateWrite,
+				glyphNameToFileNameFunc=glyphNameToFileNameFunc,
+				expectContentsFile=expectContentsFile
+			)
+		elif self._formatVersion.major == UFOFormatVersion.FORMAT_3_0.major:
+			return self._getGlyphSetFormatVersion3(
+				validateRead,
+				validateWrite,
+				layerName=layerName,
+				defaultLayer=defaultLayer,
+				glyphNameToFileNameFunc=glyphNameToFileNameFunc,
+				expectContentsFile=expectContentsFile,
+			)
+		else:
+			raise NotImplementedError(self._formatVersion)
+
+	def _getDefaultGlyphSet(
+		self,
+		validateRead,
+		validateWrite,
+		glyphNameToFileNameFunc=None,
+		expectContentsFile=False,
+	):
+		from fontTools.ufoLib.glifLib import GlyphSet
+
+		glyphSubFS = self.fs.makedir(DEFAULT_GLYPHS_DIRNAME, recreate=True)
+		return GlyphSet(
+			glyphSubFS,
+			glyphNameToFileNameFunc=glyphNameToFileNameFunc,
+			ufoFormatVersion=self._formatVersion,
+			validateRead=validateRead,
+			validateWrite=validateWrite,
+			expectContentsFile=expectContentsFile,
+		)
+
+	def _getGlyphSetFormatVersion3(
+		self,
+		validateRead,
+		validateWrite,
+		layerName=None,
+		defaultLayer=True,
+		glyphNameToFileNameFunc=None,
+		expectContentsFile=False,
+	):
+		from fontTools.ufoLib.glifLib import GlyphSet
+
+		# if the default flag is on, make sure that the default in the file
+		# matches the default being written. also make sure that this layer
+		# name is not already linked to a non-default layer.
+		if defaultLayer:
+			for existingLayerName, directory in self.layerContents.items():
+				if directory == DEFAULT_GLYPHS_DIRNAME:
+					if existingLayerName != layerName:
+						raise UFOLibError(
+							"Another layer ('%s') is already mapped to the default directory."
+							% existingLayerName
+						)
+				elif existingLayerName == layerName:
+					raise UFOLibError("The layer name is already mapped to a non-default layer.")
+		# get an existing directory name
+		if layerName in self.layerContents:
+			directory = self.layerContents[layerName]
+		# get a  new directory name
+		else:
+			if defaultLayer:
+				directory = DEFAULT_GLYPHS_DIRNAME
+			else:
+				# not caching this could be slightly expensive,
+				# but caching it will be cumbersome
+				existing = {d.lower() for d in self.layerContents.values()}
+				directory = userNameToFileName(layerName, existing=existing, prefix="glyphs.")
+		# make the directory
+		glyphSubFS = self.fs.makedir(directory, recreate=True)
+		# store the mapping
+		self.layerContents[layerName] = directory
+		# load the glyph set
+		return GlyphSet(
+			glyphSubFS,
+			glyphNameToFileNameFunc=glyphNameToFileNameFunc,
+			ufoFormatVersion=self._formatVersion,
+			validateRead=validateRead,
+			validateWrite=validateWrite,
+			expectContentsFile=expectContentsFile,
+		)
+
+	def renameGlyphSet(self, layerName, newLayerName, defaultLayer=False):
+		"""
+		Rename a glyph set.
+
+		Note: if a GlyphSet object has already been retrieved for
+		layerName, it is up to the caller to inform that object that
+		the directory it represents has changed.
+		"""
+		if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+			# ignore renaming glyph sets for UFO1 UFO2
+			# just write the data from the default layer
+			return
+		# the new and old names can be the same
+		# as long as the default is being switched
+		if layerName == newLayerName:
+			# if the default is off and the layer is already not the default, skip
+			if self.layerContents[layerName] != DEFAULT_GLYPHS_DIRNAME and not defaultLayer:
+				return
+			# if the default is on and the layer is already the default, skip
+			if self.layerContents[layerName] == DEFAULT_GLYPHS_DIRNAME and defaultLayer:
+				return
+		else:
+			# make sure the new layer name doesn't already exist
+			if newLayerName is None:
+				newLayerName = DEFAULT_LAYER_NAME
+			if newLayerName in self.layerContents:
+				raise UFOLibError("A layer named %s already exists." % newLayerName)
+			# make sure the default layer doesn't already exist
+			if defaultLayer and DEFAULT_GLYPHS_DIRNAME in self.layerContents.values():
+				raise UFOLibError("A default layer already exists.")
+		# get the paths
+		oldDirectory = self._findDirectoryForLayerName(layerName)
+		if defaultLayer:
+			newDirectory = DEFAULT_GLYPHS_DIRNAME
+		else:
+			existing = {name.lower() for name in self.layerContents.values()}
+			newDirectory = userNameToFileName(newLayerName, existing=existing, prefix="glyphs.")
+		# update the internal mapping
+		del self.layerContents[layerName]
+		self.layerContents[newLayerName] = newDirectory
+		# do the file system copy
+		self.fs.movedir(oldDirectory, newDirectory, create=True)
+
+	def deleteGlyphSet(self, layerName):
+		"""
+		Remove the glyph set matching layerName.
+		"""
+		if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+			# ignore deleting glyph sets for UFO1 UFO2 as there are no layers
+			# just write the data from the default layer
+			return
+		foundDirectory = self._findDirectoryForLayerName(layerName)
+		self.removePath(foundDirectory, removeEmptyParents=False)
+		del self.layerContents[layerName]
+
+	def writeData(self, fileName, data):
+		"""
+		Write data to fileName in the 'data' directory.
+		The data must be a bytes string.
+		"""
+		self.writeBytesToPath(f"{DATA_DIRNAME}/{fsdecode(fileName)}", data)
+
+	def removeData(self, fileName):
+		"""
+		Remove the file named fileName from the data directory.
+		"""
+		self.removePath(f"{DATA_DIRNAME}/{fsdecode(fileName)}")
+
+	# /images
+
+	def writeImage(self, fileName, data, validate=None):
+		"""
+		Write data to fileName in the images directory.
+		The data must be a valid PNG.
+		"""
+		if validate is None:
+			validate = self._validate
+		if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+			raise UFOLibError(
+				f"Images are not allowed in UFO {self._formatVersion.major}."
+			)
+		fileName = fsdecode(fileName)
+		if validate:
+			valid, error = pngValidator(data=data)
+			if not valid:
+				raise UFOLibError(error)
+		self.writeBytesToPath(f"{IMAGES_DIRNAME}/{fileName}", data)
+
+	def removeImage(self, fileName, validate=None):  # XXX remove unused 'validate'?
+		"""
+		Remove the file named fileName from the
+		images directory.
+		"""
+		if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+			raise UFOLibError(
+				f"Images are not allowed in UFO {self._formatVersion.major}."
+			)
+		self.removePath(f"{IMAGES_DIRNAME}/{fsdecode(fileName)}")
+
+	def copyImageFromReader(self, reader, sourceFileName, destFileName, validate=None):
+		"""
+		Copy the sourceFileName in the provided UFOReader to destFileName
+		in this writer. This uses the most memory efficient method possible
+		for copying the data possible.
+		"""
+		if validate is None:
+			validate = self._validate
+		if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
+			raise UFOLibError(
+				f"Images are not allowed in UFO {self._formatVersion.major}."
+			)
+		sourcePath = f"{IMAGES_DIRNAME}/{fsdecode(sourceFileName)}"
+		destPath = f"{IMAGES_DIRNAME}/{fsdecode(destFileName)}"
+		self.copyFromReader(reader, sourcePath, destPath)
+
+	def close(self):
+		if self._havePreviousFile and self._fileStructure is UFOFileStructure.ZIP:
+			# if we are updating an existing zip file, we can now compress the
+			# contents of the temporary filesystem in the destination path
+			rootDir = os.path.splitext(os.path.basename(self._path))[0] + ".ufo"
+			with fs.zipfs.ZipFS(self._path, write=True, encoding="utf-8") as destFS:
+				fs.copy.copy_fs(self.fs, destFS.makedir(rootDir))
+		super().close()
+
+
+# just an alias, makes it more explicit
+UFOReaderWriter = UFOWriter
+
+
+# ----------------
+# Helper Functions
+# ----------------
+
+
+def _sniffFileStructure(ufo_path):
+	"""Return UFOFileStructure.ZIP if the UFO at path 'ufo_path' (str)
+	is a zip file, else return UFOFileStructure.PACKAGE if 'ufo_path' is a
+	directory.
+	Raise UFOLibError if it is a file with unknown structure, or if the path
+	does not exist.
+	"""
+	if zipfile.is_zipfile(ufo_path):
+		return UFOFileStructure.ZIP
+	elif os.path.isdir(ufo_path):
+		return UFOFileStructure.PACKAGE
+	elif os.path.isfile(ufo_path):
+		raise UFOLibError(
+			"The specified UFO does not have a known structure: '%s'" % ufo_path
+		)
+	else:
+		raise UFOLibError("No such file or directory: '%s'" % ufo_path)
+
+
+def makeUFOPath(path):
+	"""
+	Return a .ufo pathname.
+
+	>>> makeUFOPath("directory/something.ext") == (
+	... 	os.path.join('directory', 'something.ufo'))
+	True
+	>>> makeUFOPath("directory/something.another.thing.ext") == (
+	... 	os.path.join('directory', 'something.another.thing.ufo'))
+	True
+	"""
+	dir, name = os.path.split(path)
+	name = ".".join([".".join(name.split(".")[:-1]), "ufo"])
+	return os.path.join(dir, name)
+
+# ----------------------
+# fontinfo.plist Support
+# ----------------------
+
+# Version Validators
+
+# There is no version 1 validator and there shouldn't be.
+# The version 1 spec was very loose and there were numerous
+# cases of invalid values.
+
+def validateFontInfoVersion2ValueForAttribute(attr, value):
+	"""
+	This performs very basic validation of the value for attribute
+	following the UFO 2 fontinfo.plist specification. The results
+	of this should not be interpretted as *correct* for the font
+	that they are part of. This merely indicates that the value
+	is of the proper type and, where the specification defines
+	a set range of possible values for an attribute, that the
+	value is in the accepted range.
+	"""
+	dataValidationDict = fontInfoAttributesVersion2ValueData[attr]
+	valueType = dataValidationDict.get("type")
+	validator = dataValidationDict.get("valueValidator")
+	valueOptions = dataValidationDict.get("valueOptions")
+	# have specific options for the validator
+	if valueOptions is not None:
+		isValidValue = validator(value, valueOptions)
+	# no specific options
+	else:
+		if validator == genericTypeValidator:
+			isValidValue = validator(value, valueType)
+		else:
+			isValidValue = validator(value)
+	return isValidValue
+
+def validateInfoVersion2Data(infoData):
+	"""
+	This performs very basic validation of the value for infoData
+	following the UFO 2 fontinfo.plist specification. The results
+	of this should not be interpretted as *correct* for the font
+	that they are part of. This merely indicates that the values
+	are of the proper type and, where the specification defines
+	a set range of possible values for an attribute, that the
+	value is in the accepted range.
+	"""
+	validInfoData = {}
+	for attr, value in list(infoData.items()):
+		isValidValue = validateFontInfoVersion2ValueForAttribute(attr, value)
+		if not isValidValue:
+			raise UFOLibError(f"Invalid value for attribute {attr} ({value!r}).")
+		else:
+			validInfoData[attr] = value
+	return validInfoData
+
+def validateFontInfoVersion3ValueForAttribute(attr, value):
+	"""
+	This performs very basic validation of the value for attribute
+	following the UFO 3 fontinfo.plist specification. The results
+	of this should not be interpretted as *correct* for the font
+	that they are part of. This merely indicates that the value
+	is of the proper type and, where the specification defines
+	a set range of possible values for an attribute, that the
+	value is in the accepted range.
+	"""
+	dataValidationDict = fontInfoAttributesVersion3ValueData[attr]
+	valueType = dataValidationDict.get("type")
+	validator = dataValidationDict.get("valueValidator")
+	valueOptions = dataValidationDict.get("valueOptions")
+	# have specific options for the validator
+	if valueOptions is not None:
+		isValidValue = validator(value, valueOptions)
+	# no specific options
+	else:
+		if validator == genericTypeValidator:
+			isValidValue = validator(value, valueType)
+		else:
+			isValidValue = validator(value)
+	return isValidValue
+
+def validateInfoVersion3Data(infoData):
+	"""
+	This performs very basic validation of the value for infoData
+	following the UFO 3 fontinfo.plist specification. The results
+	of this should not be interpretted as *correct* for the font
+	that they are part of. This merely indicates that the values
+	are of the proper type and, where the specification defines
+	a set range of possible values for an attribute, that the
+	value is in the accepted range.
+	"""
+	validInfoData = {}
+	for attr, value in list(infoData.items()):
+		isValidValue = validateFontInfoVersion3ValueForAttribute(attr, value)
+		if not isValidValue:
+			raise UFOLibError(f"Invalid value for attribute {attr} ({value!r}).")
+		else:
+			validInfoData[attr] = value
+	return validInfoData
+
+# Value Options
+
+fontInfoOpenTypeHeadFlagsOptions = list(range(0, 15))
+fontInfoOpenTypeOS2SelectionOptions = [1, 2, 3, 4, 7, 8, 9]
+fontInfoOpenTypeOS2UnicodeRangesOptions = list(range(0, 128))
+fontInfoOpenTypeOS2CodePageRangesOptions = list(range(0, 64))
+fontInfoOpenTypeOS2TypeOptions = [0, 1, 2, 3, 8, 9]
+
+# Version Attribute Definitions
+# This defines the attributes, types and, in some
+# cases the possible values, that can exist is
+# fontinfo.plist.
+
+fontInfoAttributesVersion1 = {
+	"familyName",
+	"styleName",
+	"fullName",
+	"fontName",
+	"menuName",
+	"fontStyle",
+	"note",
+	"versionMajor",
+	"versionMinor",
+	"year",
+	"copyright",
+	"notice",
+	"trademark",
+	"license",
+	"licenseURL",
+	"createdBy",
+	"designer",
+	"designerURL",
+	"vendorURL",
+	"unitsPerEm",
+	"ascender",
+	"descender",
+	"capHeight",
+	"xHeight",
+	"defaultWidth",
+	"slantAngle",
+	"italicAngle",
+	"widthName",
+	"weightName",
+	"weightValue",
+	"fondName",
+	"otFamilyName",
+	"otStyleName",
+	"otMacName",
+	"msCharSet",
+	"fondID",
+	"uniqueID",
+	"ttVendor",
+	"ttUniqueID",
+	"ttVersion",
+}
+
+fontInfoAttributesVersion2ValueData = {
+	"familyName"							: dict(type=str),
+	"styleName"								: dict(type=str),
+	"styleMapFamilyName"					: dict(type=str),
+	"styleMapStyleName"						: dict(type=str, valueValidator=fontInfoStyleMapStyleNameValidator),
+	"versionMajor"							: dict(type=int),
+	"versionMinor"							: dict(type=int),
+	"year"									: dict(type=int),
+	"copyright"								: dict(type=str),
+	"trademark"								: dict(type=str),
+	"unitsPerEm"							: dict(type=(int, float)),
+	"descender"								: dict(type=(int, float)),
+	"xHeight"								: dict(type=(int, float)),
+	"capHeight"								: dict(type=(int, float)),
+	"ascender"								: dict(type=(int, float)),
+	"italicAngle"							: dict(type=(float, int)),
+	"note"									: dict(type=str),
+	"openTypeHeadCreated"					: dict(type=str, valueValidator=fontInfoOpenTypeHeadCreatedValidator),
+	"openTypeHeadLowestRecPPEM"				: dict(type=(int, float)),
+	"openTypeHeadFlags"						: dict(type="integerList", valueValidator=genericIntListValidator, valueOptions=fontInfoOpenTypeHeadFlagsOptions),
+	"openTypeHheaAscender"					: dict(type=(int, float)),
+	"openTypeHheaDescender"					: dict(type=(int, float)),
+	"openTypeHheaLineGap"					: dict(type=(int, float)),
+	"openTypeHheaCaretSlopeRise"			: dict(type=int),
+	"openTypeHheaCaretSlopeRun"				: dict(type=int),
+	"openTypeHheaCaretOffset"				: dict(type=(int, float)),
+	"openTypeNameDesigner"					: dict(type=str),
+	"openTypeNameDesignerURL"				: dict(type=str),
+	"openTypeNameManufacturer"				: dict(type=str),
+	"openTypeNameManufacturerURL"			: dict(type=str),
+	"openTypeNameLicense"					: dict(type=str),
+	"openTypeNameLicenseURL"				: dict(type=str),
+	"openTypeNameVersion"					: dict(type=str),
+	"openTypeNameUniqueID"					: dict(type=str),
+	"openTypeNameDescription"				: dict(type=str),
+	"openTypeNamePreferredFamilyName"		: dict(type=str),
+	"openTypeNamePreferredSubfamilyName"	: dict(type=str),
+	"openTypeNameCompatibleFullName"		: dict(type=str),
+	"openTypeNameSampleText"				: dict(type=str),
+	"openTypeNameWWSFamilyName"				: dict(type=str),
+	"openTypeNameWWSSubfamilyName"			: dict(type=str),
+	"openTypeOS2WidthClass"					: dict(type=int, valueValidator=fontInfoOpenTypeOS2WidthClassValidator),
+	"openTypeOS2WeightClass"				: dict(type=int, valueValidator=fontInfoOpenTypeOS2WeightClassValidator),
+	"openTypeOS2Selection"					: dict(type="integerList", valueValidator=genericIntListValidator, valueOptions=fontInfoOpenTypeOS2SelectionOptions),
+	"openTypeOS2VendorID"					: dict(type=str),
+	"openTypeOS2Panose"						: dict(type="integerList", valueValidator=fontInfoVersion2OpenTypeOS2PanoseValidator),
+	"openTypeOS2FamilyClass"				: dict(type="integerList", valueValidator=fontInfoOpenTypeOS2FamilyClassValidator),
+	"openTypeOS2UnicodeRanges"				: dict(type="integerList", valueValidator=genericIntListValidator, valueOptions=fontInfoOpenTypeOS2UnicodeRangesOptions),
+	"openTypeOS2CodePageRanges"				: dict(type="integerList", valueValidator=genericIntListValidator, valueOptions=fontInfoOpenTypeOS2CodePageRangesOptions),
+	"openTypeOS2TypoAscender"				: dict(type=(int, float)),
+	"openTypeOS2TypoDescender"				: dict(type=(int, float)),
+	"openTypeOS2TypoLineGap"				: dict(type=(int, float)),
+	"openTypeOS2WinAscent"					: dict(type=(int, float)),
+	"openTypeOS2WinDescent"					: dict(type=(int, float)),
+	"openTypeOS2Type"						: dict(type="integerList", valueValidator=genericIntListValidator, valueOptions=fontInfoOpenTypeOS2TypeOptions),
+	"openTypeOS2SubscriptXSize"				: dict(type=(int, float)),
+	"openTypeOS2SubscriptYSize"				: dict(type=(int, float)),
+	"openTypeOS2SubscriptXOffset"			: dict(type=(int, float)),
+	"openTypeOS2SubscriptYOffset"			: dict(type=(int, float)),
+	"openTypeOS2SuperscriptXSize"			: dict(type=(int, float)),
+	"openTypeOS2SuperscriptYSize"			: dict(type=(int, float)),
+	"openTypeOS2SuperscriptXOffset"			: dict(type=(int, float)),
+	"openTypeOS2SuperscriptYOffset"			: dict(type=(int, float)),
+	"openTypeOS2StrikeoutSize"				: dict(type=(int, float)),
+	"openTypeOS2StrikeoutPosition"			: dict(type=(int, float)),
+	"openTypeVheaVertTypoAscender"			: dict(type=(int, float)),
+	"openTypeVheaVertTypoDescender"			: dict(type=(int, float)),
+	"openTypeVheaVertTypoLineGap"			: dict(type=(int, float)),
+	"openTypeVheaCaretSlopeRise"			: dict(type=int),
+	"openTypeVheaCaretSlopeRun"				: dict(type=int),
+	"openTypeVheaCaretOffset"				: dict(type=(int, float)),
+	"postscriptFontName"					: dict(type=str),
+	"postscriptFullName"					: dict(type=str),
+	"postscriptSlantAngle"					: dict(type=(float, int)),
+	"postscriptUniqueID"					: dict(type=int),
+	"postscriptUnderlineThickness"			: dict(type=(int, float)),
+	"postscriptUnderlinePosition"			: dict(type=(int, float)),
+	"postscriptIsFixedPitch"				: dict(type=bool),
+	"postscriptBlueValues"					: dict(type="integerList", valueValidator=fontInfoPostscriptBluesValidator),
+	"postscriptOtherBlues"					: dict(type="integerList", valueValidator=fontInfoPostscriptOtherBluesValidator),
+	"postscriptFamilyBlues"					: dict(type="integerList", valueValidator=fontInfoPostscriptBluesValidator),
+	"postscriptFamilyOtherBlues"			: dict(type="integerList", valueValidator=fontInfoPostscriptOtherBluesValidator),
+	"postscriptStemSnapH"					: dict(type="integerList", valueValidator=fontInfoPostscriptStemsValidator),
+	"postscriptStemSnapV"					: dict(type="integerList", valueValidator=fontInfoPostscriptStemsValidator),
+	"postscriptBlueFuzz"					: dict(type=(int, float)),
+	"postscriptBlueShift"					: dict(type=(int, float)),
+	"postscriptBlueScale"					: dict(type=(float, int)),
+	"postscriptForceBold"					: dict(type=bool),
+	"postscriptDefaultWidthX"				: dict(type=(int, float)),
+	"postscriptNominalWidthX"				: dict(type=(int, float)),
+	"postscriptWeightName"					: dict(type=str),
+	"postscriptDefaultCharacter"			: dict(type=str),
+	"postscriptWindowsCharacterSet"			: dict(type=int, valueValidator=fontInfoPostscriptWindowsCharacterSetValidator),
+	"macintoshFONDFamilyID"					: dict(type=int),
+	"macintoshFONDName"						: dict(type=str),
+}
+fontInfoAttributesVersion2 = set(fontInfoAttributesVersion2ValueData.keys())
+
+fontInfoAttributesVersion3ValueData = deepcopy(fontInfoAttributesVersion2ValueData)
+fontInfoAttributesVersion3ValueData.update({
+	"versionMinor"							: dict(type=int, valueValidator=genericNonNegativeIntValidator),
+	"unitsPerEm"							: dict(type=(int, float), valueValidator=genericNonNegativeNumberValidator),
+	"openTypeHeadLowestRecPPEM"				: dict(type=int, valueValidator=genericNonNegativeNumberValidator),
+	"openTypeHheaAscender"					: dict(type=int),
+	"openTypeHheaDescender"					: dict(type=int),
+	"openTypeHheaLineGap"					: dict(type=int),
+	"openTypeHheaCaretOffset"				: dict(type=int),
+	"openTypeOS2Panose"						: dict(type="integerList", valueValidator=fontInfoVersion3OpenTypeOS2PanoseValidator),
+	"openTypeOS2TypoAscender"				: dict(type=int),
+	"openTypeOS2TypoDescender"				: dict(type=int),
+	"openTypeOS2TypoLineGap"				: dict(type=int),
+	"openTypeOS2WinAscent"					: dict(type=int, valueValidator=genericNonNegativeNumberValidator),
+	"openTypeOS2WinDescent"					: dict(type=int, valueValidator=genericNonNegativeNumberValidator),
+	"openTypeOS2SubscriptXSize"				: dict(type=int),
+	"openTypeOS2SubscriptYSize"				: dict(type=int),
+	"openTypeOS2SubscriptXOffset"			: dict(type=int),
+	"openTypeOS2SubscriptYOffset"			: dict(type=int),
+	"openTypeOS2SuperscriptXSize"			: dict(type=int),
+	"openTypeOS2SuperscriptYSize"			: dict(type=int),
+	"openTypeOS2SuperscriptXOffset"			: dict(type=int),
+	"openTypeOS2SuperscriptYOffset"			: dict(type=int),
+	"openTypeOS2StrikeoutSize"				: dict(type=int),
+	"openTypeOS2StrikeoutPosition"			: dict(type=int),
+	"openTypeGaspRangeRecords"				: dict(type="dictList", valueValidator=fontInfoOpenTypeGaspRangeRecordsValidator),
+	"openTypeNameRecords"					: dict(type="dictList", valueValidator=fontInfoOpenTypeNameRecordsValidator),
+	"openTypeVheaVertTypoAscender"			: dict(type=int),
+	"openTypeVheaVertTypoDescender"			: dict(type=int),
+	"openTypeVheaVertTypoLineGap"			: dict(type=int),
+	"openTypeVheaCaretOffset"				: dict(type=int),
+	"woffMajorVersion"						: dict(type=int, valueValidator=genericNonNegativeIntValidator),
+	"woffMinorVersion"						: dict(type=int, valueValidator=genericNonNegativeIntValidator),
+	"woffMetadataUniqueID"					: dict(type=dict, valueValidator=fontInfoWOFFMetadataUniqueIDValidator),
+	"woffMetadataVendor"					: dict(type=dict, valueValidator=fontInfoWOFFMetadataVendorValidator),
+	"woffMetadataCredits"					: dict(type=dict, valueValidator=fontInfoWOFFMetadataCreditsValidator),
+	"woffMetadataDescription"				: dict(type=dict, valueValidator=fontInfoWOFFMetadataDescriptionValidator),
+	"woffMetadataLicense"					: dict(type=dict, valueValidator=fontInfoWOFFMetadataLicenseValidator),
+	"woffMetadataCopyright"					: dict(type=dict, valueValidator=fontInfoWOFFMetadataCopyrightValidator),
+	"woffMetadataTrademark"					: dict(type=dict, valueValidator=fontInfoWOFFMetadataTrademarkValidator),
+	"woffMetadataLicensee"					: dict(type=dict, valueValidator=fontInfoWOFFMetadataLicenseeValidator),
+	"woffMetadataExtensions"				: dict(type=list, valueValidator=fontInfoWOFFMetadataExtensionsValidator),
+	"guidelines"							: dict(type=list, valueValidator=guidelinesValidator)
+})
+fontInfoAttributesVersion3 = set(fontInfoAttributesVersion3ValueData.keys())
+
+# insert the type validator for all attrs that
+# have no defined validator.
+for attr, dataDict in list(fontInfoAttributesVersion2ValueData.items()):
+	if "valueValidator" not in dataDict:
+		dataDict["valueValidator"] = genericTypeValidator
+
+for attr, dataDict in list(fontInfoAttributesVersion3ValueData.items()):
+	if "valueValidator" not in dataDict:
+		dataDict["valueValidator"] = genericTypeValidator
+
+# Version Conversion Support
+# These are used from converting from version 1
+# to version 2 or vice-versa.
+
+def _flipDict(d):
+	flipped = {}
+	for key, value in list(d.items()):
+		flipped[value] = key
+	return flipped
+
+fontInfoAttributesVersion1To2 = {
+	"menuName"		: "styleMapFamilyName",
+	"designer"		: "openTypeNameDesigner",
+	"designerURL"	: "openTypeNameDesignerURL",
+	"createdBy"		: "openTypeNameManufacturer",
+	"vendorURL"		: "openTypeNameManufacturerURL",
+	"license"		: "openTypeNameLicense",
+	"licenseURL"	: "openTypeNameLicenseURL",
+	"ttVersion"		: "openTypeNameVersion",
+	"ttUniqueID"	: "openTypeNameUniqueID",
+	"notice"		: "openTypeNameDescription",
+	"otFamilyName"	: "openTypeNamePreferredFamilyName",
+	"otStyleName"	: "openTypeNamePreferredSubfamilyName",
+	"otMacName"		: "openTypeNameCompatibleFullName",
+	"weightName"	: "postscriptWeightName",
+	"weightValue"	: "openTypeOS2WeightClass",
+	"ttVendor"		: "openTypeOS2VendorID",
+	"uniqueID"		: "postscriptUniqueID",
+	"fontName"		: "postscriptFontName",
+	"fondID"		: "macintoshFONDFamilyID",
+	"fondName"		: "macintoshFONDName",
+	"defaultWidth"	: "postscriptDefaultWidthX",
+	"slantAngle"	: "postscriptSlantAngle",
+	"fullName"		: "postscriptFullName",
+	# require special value conversion
+	"fontStyle"		: "styleMapStyleName",
+	"widthName"		: "openTypeOS2WidthClass",
+	"msCharSet"		: "postscriptWindowsCharacterSet"
+}
+fontInfoAttributesVersion2To1 = _flipDict(fontInfoAttributesVersion1To2)
+deprecatedFontInfoAttributesVersion2 = set(fontInfoAttributesVersion1To2.keys())
+
+_fontStyle1To2 = {
+	64 : "regular",
+	1  : "italic",
+	32 : "bold",
+	33 : "bold italic"
+}
+_fontStyle2To1 = _flipDict(_fontStyle1To2)
+# Some UFO 1 files have 0
+_fontStyle1To2[0] = "regular"
+
+_widthName1To2 = {
+	"Ultra-condensed" : 1,
+	"Extra-condensed" : 2,
+	"Condensed"		  : 3,
+	"Semi-condensed"  : 4,
+	"Medium (normal)" : 5,
+	"Semi-expanded"	  : 6,
+	"Expanded"		  : 7,
+	"Extra-expanded"  : 8,
+	"Ultra-expanded"  : 9
+}
+_widthName2To1 = _flipDict(_widthName1To2)
+# FontLab's default width value is "Normal".
+# Many format version 1 UFOs will have this.
+_widthName1To2["Normal"] = 5
+# FontLab has an "All" width value. In UFO 1
+# move this up to "Normal".
+_widthName1To2["All"] = 5
+# "medium" appears in a lot of UFO 1 files.
+_widthName1To2["medium"] = 5
+# "Medium" appears in a lot of UFO 1 files.
+_widthName1To2["Medium"] = 5
+
+_msCharSet1To2 = {
+	0	: 1,
+	1	: 2,
+	2	: 3,
+	77	: 4,
+	128 : 5,
+	129 : 6,
+	130 : 7,
+	134 : 8,
+	136 : 9,
+	161 : 10,
+	162 : 11,
+	163 : 12,
+	177 : 13,
+	178 : 14,
+	186 : 15,
+	200 : 16,
+	204 : 17,
+	222 : 18,
+	238 : 19,
+	255 : 20
+}
+_msCharSet2To1 = _flipDict(_msCharSet1To2)
+
+# 1 <-> 2
+
+def convertFontInfoValueForAttributeFromVersion1ToVersion2(attr, value):
+	"""
+	Convert value from version 1 to version 2 format.
+	Returns the new attribute name and the converted value.
+	If the value is None, None will be returned for the new value.
+	"""
+	# convert floats to ints if possible
+	if isinstance(value, float):
+		if int(value) == value:
+			value = int(value)
+	if value is not None:
+		if attr == "fontStyle":
+			v = _fontStyle1To2.get(value)
+			if v is None:
+				raise UFOLibError(f"Cannot convert value ({value!r}) for attribute {attr}.")
+			value = v
+		elif attr == "widthName":
+			v = _widthName1To2.get(value)
+			if v is None:
+				raise UFOLibError(f"Cannot convert value ({value!r}) for attribute {attr}.")
+			value = v
+		elif attr == "msCharSet":
+			v = _msCharSet1To2.get(value)
+			if v is None:
+				raise UFOLibError(f"Cannot convert value ({value!r}) for attribute {attr}.")
+			value = v
+	attr = fontInfoAttributesVersion1To2.get(attr, attr)
+	return attr, value
+
+def convertFontInfoValueForAttributeFromVersion2ToVersion1(attr, value):
+	"""
+	Convert value from version 2 to version 1 format.
+	Returns the new attribute name and the converted value.
+	If the value is None, None will be returned for the new value.
+	"""
+	if value is not None:
+		if attr == "styleMapStyleName":
+			value = _fontStyle2To1.get(value)
+		elif attr == "openTypeOS2WidthClass":
+			value = _widthName2To1.get(value)
+		elif attr == "postscriptWindowsCharacterSet":
+			value = _msCharSet2To1.get(value)
+	attr = fontInfoAttributesVersion2To1.get(attr, attr)
+	return attr, value
+
+def _convertFontInfoDataVersion1ToVersion2(data):
+	converted = {}
+	for attr, value in list(data.items()):
+		# FontLab gives -1 for the weightValue
+		# for fonts wil no defined value. Many
+		# format version 1 UFOs will have this.
+		if attr == "weightValue" and value == -1:
+			continue
+		newAttr, newValue = convertFontInfoValueForAttributeFromVersion1ToVersion2(attr, value)
+		# skip if the attribute is not part of version 2
+		if newAttr not in fontInfoAttributesVersion2:
+			continue
+		# catch values that can't be converted
+		if value is None:
+			raise UFOLibError(f"Cannot convert value ({value!r}) for attribute {newAttr}.")
+		# store
+		converted[newAttr] = newValue
+	return converted
+
+def _convertFontInfoDataVersion2ToVersion1(data):
+	converted = {}
+	for attr, value in list(data.items()):
+		newAttr, newValue = convertFontInfoValueForAttributeFromVersion2ToVersion1(attr, value)
+		# only take attributes that are registered for version 1
+		if newAttr not in fontInfoAttributesVersion1:
+			continue
+		# catch values that can't be converted
+		if value is None:
+			raise UFOLibError(f"Cannot convert value ({value!r}) for attribute {newAttr}.")
+		# store
+		converted[newAttr] = newValue
+	return converted
+
+# 2 <-> 3
+
+_ufo2To3NonNegativeInt = {
+	"versionMinor",
+	"openTypeHeadLowestRecPPEM",
+	"openTypeOS2WinAscent",
+	"openTypeOS2WinDescent"
+}
+_ufo2To3NonNegativeIntOrFloat = {
+	"unitsPerEm",
+}
+_ufo2To3FloatToInt = {
+	"openTypeHeadLowestRecPPEM",
+	"openTypeHheaAscender",
+	"openTypeHheaDescender",
+	"openTypeHheaLineGap",
+	"openTypeHheaCaretOffset",
+	"openTypeOS2TypoAscender",
+	"openTypeOS2TypoDescender",
+	"openTypeOS2TypoLineGap",
+	"openTypeOS2WinAscent",
+	"openTypeOS2WinDescent",
+	"openTypeOS2SubscriptXSize",
+	"openTypeOS2SubscriptYSize",
+	"openTypeOS2SubscriptXOffset",
+	"openTypeOS2SubscriptYOffset",
+	"openTypeOS2SuperscriptXSize",
+	"openTypeOS2SuperscriptYSize",
+	"openTypeOS2SuperscriptXOffset",
+	"openTypeOS2SuperscriptYOffset",
+	"openTypeOS2StrikeoutSize",
+	"openTypeOS2StrikeoutPosition",
+	"openTypeVheaVertTypoAscender",
+	"openTypeVheaVertTypoDescender",
+	"openTypeVheaVertTypoLineGap",
+	"openTypeVheaCaretOffset"
+}
+
+def convertFontInfoValueForAttributeFromVersion2ToVersion3(attr, value):
+	"""
+	Convert value from version 2 to version 3 format.
+	Returns the new attribute name and the converted value.
+	If the value is None, None will be returned for the new value.
+	"""
+	if attr in _ufo2To3FloatToInt:
+		try:
+			value = round(value)
+		except (ValueError, TypeError):
+			raise UFOLibError("Could not convert value for %s." % attr)
+	if attr in _ufo2To3NonNegativeInt:
+		try:
+			value = int(abs(value))
+		except (ValueError, TypeError):
+			raise UFOLibError("Could not convert value for %s." % attr)
+	elif attr in _ufo2To3NonNegativeIntOrFloat:
+		try:
+			v = float(abs(value))
+		except (ValueError, TypeError):
+			raise UFOLibError("Could not convert value for %s." % attr)
+		if v == int(v):
+			v = int(v)
+		if v != value:
+			value = v
+	return attr, value
+
+def convertFontInfoValueForAttributeFromVersion3ToVersion2(attr, value):
+	"""
+	Convert value from version 3 to version 2 format.
+	Returns the new attribute name and the converted value.
+	If the value is None, None will be returned for the new value.
+	"""
+	return attr, value
+
+def _convertFontInfoDataVersion3ToVersion2(data):
+	converted = {}
+	for attr, value in list(data.items()):
+		newAttr, newValue = convertFontInfoValueForAttributeFromVersion3ToVersion2(attr, value)
+		if newAttr not in fontInfoAttributesVersion2:
+			continue
+		converted[newAttr] = newValue
+	return converted
+
+def _convertFontInfoDataVersion2ToVersion3(data):
+	converted = {}
+	for attr, value in list(data.items()):
+		attr, value = convertFontInfoValueForAttributeFromVersion2ToVersion3(attr, value)
+		converted[attr] = value
+	return converted
+
+if __name__ == "__main__":
+	import doctest
+	doctest.testmod()
diff --git a/Lib/fontTools/ufoLib/converters.py b/Lib/fontTools/ufoLib/converters.py
new file mode 100644
index 0000000..3b8112c
--- /dev/null
+++ b/Lib/fontTools/ufoLib/converters.py
@@ -0,0 +1,335 @@
+"""
+Conversion functions.
+"""
+
+
+
+# adapted from the UFO spec
+
+def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
+    # gather known kerning groups based on the prefixes
+    firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups)
+    # Make lists of groups referenced in kerning pairs.
+    for first, seconds in list(kerning.items()):
+        if first in groups and first not in glyphSet:
+            if not first.startswith("public.kern1."):
+                firstReferencedGroups.add(first)
+        for second in list(seconds.keys()):
+            if second in groups and second not in glyphSet:
+                if not second.startswith("public.kern2."):
+                    secondReferencedGroups.add(second)
+    # Create new names for these groups.
+    firstRenamedGroups = {}
+    for first in firstReferencedGroups:
+        # Make a list of existing group names.
+        existingGroupNames = list(groups.keys()) + list(firstRenamedGroups.keys())
+        # Remove the old prefix from the name
+        newName = first.replace("@MMK_L_", "")
+        # Add the new prefix to the name.
+        newName = "public.kern1." + newName
+        # Make a unique group name.
+        newName = makeUniqueGroupName(newName, existingGroupNames)
+        # Store for use later.
+        firstRenamedGroups[first] = newName
+    secondRenamedGroups = {}
+    for second in secondReferencedGroups:
+        # Make a list of existing group names.
+        existingGroupNames = list(groups.keys()) + list(secondRenamedGroups.keys())
+        # Remove the old prefix from the name
+        newName = second.replace("@MMK_R_", "")
+        # Add the new prefix to the name.
+        newName = "public.kern2." + newName
+        # Make a unique group name.
+        newName = makeUniqueGroupName(newName, existingGroupNames)
+        # Store for use later.
+        secondRenamedGroups[second] = newName
+    # Populate the new group names into the kerning dictionary as needed.
+    newKerning = {}
+    for first, seconds in list(kerning.items()):
+        first = firstRenamedGroups.get(first, first)
+        newSeconds = {}
+        for second, value in list(seconds.items()):
+            second = secondRenamedGroups.get(second, second)
+            newSeconds[second] = value
+        newKerning[first] = newSeconds
+    # Make copies of the referenced groups and store them
+    # under the new names in the overall groups dictionary.
+    allRenamedGroups = list(firstRenamedGroups.items())
+    allRenamedGroups += list(secondRenamedGroups.items())
+    for oldName, newName in allRenamedGroups:
+        group = list(groups[oldName])
+        groups[newName] = group
+    # Return the kerning and the groups.
+    return newKerning, groups, dict(side1=firstRenamedGroups, side2=secondRenamedGroups)
+
+def findKnownKerningGroups(groups):
+    """
+    This will find kerning groups with known prefixes.
+    In some cases not all kerning groups will be referenced
+    by the kerning pairs. The algorithm for locating groups
+    in convertUFO1OrUFO2KerningToUFO3Kerning will miss these
+    unreferenced groups. By scanning for known prefixes
+    this function will catch all of the prefixed groups.
+
+    These are the prefixes and sides that are handled:
+    @MMK_L_ - side 1
+    @MMK_R_ - side 2
+
+    >>> testGroups = {
+    ...     "@MMK_L_1" : None,
+    ...     "@MMK_L_2" : None,
+    ...     "@MMK_L_3" : None,
+    ...     "@MMK_R_1" : None,
+    ...     "@MMK_R_2" : None,
+    ...     "@MMK_R_3" : None,
+    ...     "@MMK_l_1" : None,
+    ...     "@MMK_r_1" : None,
+    ...     "@MMK_X_1" : None,
+    ...     "foo" : None,
+    ... }
+    >>> first, second = findKnownKerningGroups(testGroups)
+    >>> sorted(first) == ['@MMK_L_1', '@MMK_L_2', '@MMK_L_3']
+    True
+    >>> sorted(second) == ['@MMK_R_1', '@MMK_R_2', '@MMK_R_3']
+    True
+    """
+    knownFirstGroupPrefixes = [
+        "@MMK_L_"
+    ]
+    knownSecondGroupPrefixes = [
+        "@MMK_R_"
+    ]
+    firstGroups = set()
+    secondGroups = set()
+    for groupName in list(groups.keys()):
+        for firstPrefix in knownFirstGroupPrefixes:
+            if groupName.startswith(firstPrefix):
+                firstGroups.add(groupName)
+                break
+        for secondPrefix in knownSecondGroupPrefixes:
+            if groupName.startswith(secondPrefix):
+                secondGroups.add(groupName)
+                break
+    return firstGroups, secondGroups
+
+
+def makeUniqueGroupName(name, groupNames, counter=0):
+    # Add a number to the name if the counter is higher than zero.
+    newName = name
+    if counter > 0:
+        newName = "%s%d" % (newName, counter)
+    # If the new name is in the existing group names, recurse.
+    if newName in groupNames:
+        return makeUniqueGroupName(name, groupNames, counter + 1)
+    # Otherwise send back the new name.
+    return newName
+
+def test():
+    """
+    No known prefixes.
+
+    >>> testKerning = {
+    ...     "A" : {
+    ...         "A" : 1,
+    ...         "B" : 2,
+    ...         "CGroup" : 3,
+    ...         "DGroup" : 4
+    ...     },
+    ...     "BGroup" : {
+    ...         "A" : 5,
+    ...         "B" : 6,
+    ...         "CGroup" : 7,
+    ...         "DGroup" : 8
+    ...     },
+    ...     "CGroup" : {
+    ...         "A" : 9,
+    ...         "B" : 10,
+    ...         "CGroup" : 11,
+    ...         "DGroup" : 12
+    ...     },
+    ... }
+    >>> testGroups = {
+    ...     "BGroup" : ["B"],
+    ...     "CGroup" : ["C"],
+    ...     "DGroup" : ["D"],
+    ... }
+    >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
+    ...     testKerning, testGroups, [])
+    >>> expected = {
+    ...     "A" : {
+    ...         "A": 1,
+    ...         "B": 2,
+    ...         "public.kern2.CGroup": 3,
+    ...         "public.kern2.DGroup": 4
+    ...     },
+    ...     "public.kern1.BGroup": {
+    ...         "A": 5,
+    ...         "B": 6,
+    ...         "public.kern2.CGroup": 7,
+    ...         "public.kern2.DGroup": 8
+    ...     },
+    ...     "public.kern1.CGroup": {
+    ...         "A": 9,
+    ...         "B": 10,
+    ...         "public.kern2.CGroup": 11,
+    ...         "public.kern2.DGroup": 12
+    ...     }
+    ... }
+    >>> kerning == expected
+    True
+    >>> expected = {
+    ...     "BGroup": ["B"],
+    ...     "CGroup": ["C"],
+    ...     "DGroup": ["D"],
+    ...     "public.kern1.BGroup": ["B"],
+    ...     "public.kern1.CGroup": ["C"],
+    ...     "public.kern2.CGroup": ["C"],
+    ...     "public.kern2.DGroup": ["D"],
+    ... }
+    >>> groups == expected
+    True
+
+    Known prefixes.
+
+    >>> testKerning = {
+    ...     "A" : {
+    ...         "A" : 1,
+    ...         "B" : 2,
+    ...         "@MMK_R_CGroup" : 3,
+    ...         "@MMK_R_DGroup" : 4
+    ...     },
+    ...     "@MMK_L_BGroup" : {
+    ...         "A" : 5,
+    ...         "B" : 6,
+    ...         "@MMK_R_CGroup" : 7,
+    ...         "@MMK_R_DGroup" : 8
+    ...     },
+    ...     "@MMK_L_CGroup" : {
+    ...         "A" : 9,
+    ...         "B" : 10,
+    ...         "@MMK_R_CGroup" : 11,
+    ...         "@MMK_R_DGroup" : 12
+    ...     },
+    ... }
+    >>> testGroups = {
+    ...     "@MMK_L_BGroup" : ["B"],
+    ...     "@MMK_L_CGroup" : ["C"],
+    ...     "@MMK_L_XGroup" : ["X"],
+    ...     "@MMK_R_CGroup" : ["C"],
+    ...     "@MMK_R_DGroup" : ["D"],
+    ...     "@MMK_R_XGroup" : ["X"],
+    ... }
+    >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
+    ...     testKerning, testGroups, [])
+    >>> expected = {
+    ...     "A" : {
+    ...         "A": 1,
+    ...         "B": 2,
+    ...         "public.kern2.CGroup": 3,
+    ...         "public.kern2.DGroup": 4
+    ...     },
+    ...     "public.kern1.BGroup": {
+    ...         "A": 5,
+    ...         "B": 6,
+    ...         "public.kern2.CGroup": 7,
+    ...         "public.kern2.DGroup": 8
+    ...     },
+    ...     "public.kern1.CGroup": {
+    ...         "A": 9,
+    ...         "B": 10,
+    ...         "public.kern2.CGroup": 11,
+    ...         "public.kern2.DGroup": 12
+    ...     }
+    ... }
+    >>> kerning == expected
+    True
+    >>> expected = {
+    ...     "@MMK_L_BGroup": ["B"],
+    ...     "@MMK_L_CGroup": ["C"],
+    ...     "@MMK_L_XGroup": ["X"],
+    ...     "@MMK_R_CGroup": ["C"],
+    ...     "@MMK_R_DGroup": ["D"],
+    ...     "@MMK_R_XGroup": ["X"],
+    ...     "public.kern1.BGroup": ["B"],
+    ...     "public.kern1.CGroup": ["C"],
+    ...     "public.kern1.XGroup": ["X"],
+    ...     "public.kern2.CGroup": ["C"],
+    ...     "public.kern2.DGroup": ["D"],
+    ...     "public.kern2.XGroup": ["X"],
+    ... }
+    >>> groups == expected
+    True
+
+    >>> from .validators import kerningValidator
+    >>> kerningValidator(kerning)
+    (True, None)
+
+    Mixture of known prefixes and groups without prefixes.
+
+    >>> testKerning = {
+    ...     "A" : {
+    ...         "A" : 1,
+    ...         "B" : 2,
+    ...         "@MMK_R_CGroup" : 3,
+    ...         "DGroup" : 4
+    ...     },
+    ...     "BGroup" : {
+    ...         "A" : 5,
+    ...         "B" : 6,
+    ...         "@MMK_R_CGroup" : 7,
+    ...         "DGroup" : 8
+    ...     },
+    ...     "@MMK_L_CGroup" : {
+    ...         "A" : 9,
+    ...         "B" : 10,
+    ...         "@MMK_R_CGroup" : 11,
+    ...         "DGroup" : 12
+    ...     },
+    ... }
+    >>> testGroups = {
+    ...     "BGroup" : ["B"],
+    ...     "@MMK_L_CGroup" : ["C"],
+    ...     "@MMK_R_CGroup" : ["C"],
+    ...     "DGroup" : ["D"],
+    ... }
+    >>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
+    ...     testKerning, testGroups, [])
+    >>> expected = {
+    ...     "A" : {
+    ...         "A": 1,
+    ...         "B": 2,
+    ...         "public.kern2.CGroup": 3,
+    ...         "public.kern2.DGroup": 4
+    ...     },
+    ...     "public.kern1.BGroup": {
+    ...         "A": 5,
+    ...         "B": 6,
+    ...         "public.kern2.CGroup": 7,
+    ...         "public.kern2.DGroup": 8
+    ...     },
+    ...     "public.kern1.CGroup": {
+    ...         "A": 9,
+    ...         "B": 10,
+    ...         "public.kern2.CGroup": 11,
+    ...         "public.kern2.DGroup": 12
+    ...     }
+    ... }
+    >>> kerning == expected
+    True
+    >>> expected = {
+    ...     "BGroup": ["B"],
+    ...     "@MMK_L_CGroup": ["C"],
+    ...     "@MMK_R_CGroup": ["C"],
+    ...     "DGroup": ["D"],
+    ...     "public.kern1.BGroup": ["B"],
+    ...     "public.kern1.CGroup": ["C"],
+    ...     "public.kern2.CGroup": ["C"],
+    ...     "public.kern2.DGroup": ["D"],
+    ... }
+    >>> groups == expected
+    True
+    """
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
diff --git a/Lib/fontTools/ufoLib/errors.py b/Lib/fontTools/ufoLib/errors.py
new file mode 100644
index 0000000..304345e
--- /dev/null
+++ b/Lib/fontTools/ufoLib/errors.py
@@ -0,0 +1,16 @@
+
+
+class UFOLibError(Exception):
+    pass
+
+
+class UnsupportedUFOFormat(UFOLibError):
+    pass
+
+
+class GlifLibError(UFOLibError):
+    pass
+
+
+class UnsupportedGLIFFormat(GlifLibError):
+    pass
diff --git a/Lib/fontTools/ufoLib/etree.py b/Lib/fontTools/ufoLib/etree.py
new file mode 100644
index 0000000..5054f81
--- /dev/null
+++ b/Lib/fontTools/ufoLib/etree.py
@@ -0,0 +1,5 @@
+"""DEPRECATED - This module is kept here only as a backward compatibility shim
+for the old ufoLib.etree module, which was moved to fontTools.misc.etree.
+Please use the latter instead.
+"""
+from fontTools.misc.etree import *
diff --git a/Lib/fontTools/ufoLib/filenames.py b/Lib/fontTools/ufoLib/filenames.py
new file mode 100644
index 0000000..2815469
--- /dev/null
+++ b/Lib/fontTools/ufoLib/filenames.py
@@ -0,0 +1,211 @@
+"""
+User name to file name conversion.
+This was taken from the UFO 3 spec.
+"""
+
+illegalCharacters = r"\" * + / : < > ? [ \ ] | \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: str, 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 string
+	if not isinstance(userName, str):
+		raise ValueError("The value for userName must be a 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
+	doctest.testmod()
diff --git a/Lib/fontTools/ufoLib/glifLib.py b/Lib/fontTools/ufoLib/glifLib.py
new file mode 100755
index 0000000..3003110
--- /dev/null
+++ b/Lib/fontTools/ufoLib/glifLib.py
@@ -0,0 +1,1768 @@
+"""
+glifLib.py -- Generic module for reading and writing the .glif format.
+
+More info about the .glif format (GLyphInterchangeFormat) can be found here:
+
+	http://unifiedfontobject.org
+
+The main class in this module is GlyphSet. It manages a set of .glif files
+in a folder. It offers two ways to read glyph data, and one way to write
+glyph data. See the class doc string for details.
+"""
+
+import logging
+import enum
+from warnings import warn
+from collections import OrderedDict
+import fs
+import fs.base
+import fs.errors
+import fs.osfs
+import fs.path
+from fontTools.misc.py23 import tobytes
+from fontTools.misc import plistlib
+from fontTools.pens.pointPen import AbstractPointPen, PointToSegmentPen
+from fontTools.ufoLib.errors import GlifLibError
+from fontTools.ufoLib.filenames import userNameToFileName
+from fontTools.ufoLib.validators import (
+	genericTypeValidator,
+	colorValidator,
+	guidelinesValidator,
+	anchorsValidator,
+	identifierValidator,
+	imageValidator,
+	glyphLibValidator,
+)
+from fontTools.misc import etree
+from fontTools.ufoLib import _UFOBaseIO, UFOFormatVersion
+from fontTools.ufoLib.utils import numberTypes, _VersionTupleEnumMixin
+
+
+__all__ = [
+	"GlyphSet",
+	"GlifLibError",
+	"readGlyphFromString", "writeGlyphToString",
+	"glyphNameToFileName"
+]
+
+logger = logging.getLogger(__name__)
+
+
+# ---------
+# Constants
+# ---------
+
+CONTENTS_FILENAME = "contents.plist"
+LAYERINFO_FILENAME = "layerinfo.plist"
+
+
+class GLIFFormatVersion(tuple, _VersionTupleEnumMixin, enum.Enum):
+	FORMAT_1_0 = (1, 0)
+	FORMAT_2_0 = (2, 0)
+
+	@classmethod
+	def default(cls, ufoFormatVersion=None):
+		if ufoFormatVersion is not None:
+			return max(cls.supported_versions(ufoFormatVersion))
+		return super().default()
+
+	@classmethod
+	def supported_versions(cls, ufoFormatVersion=None):
+		if ufoFormatVersion is None:
+			# if ufo format unspecified, return all the supported GLIF formats
+			return super().supported_versions()
+		# else only return the GLIF formats supported by the given UFO format
+		versions = {cls.FORMAT_1_0}
+		if ufoFormatVersion >= UFOFormatVersion.FORMAT_3_0:
+			versions.add(cls.FORMAT_2_0)
+		return frozenset(versions)
+
+
+# ------------
+# Simple Glyph
+# ------------
+
+class Glyph:
+
+	"""
+	Minimal glyph object. It has no glyph attributes until either
+	the draw() or the drawPoints() method has been called.
+	"""
+
+	def __init__(self, glyphName, glyphSet):
+		self.glyphName = glyphName
+		self.glyphSet = glyphSet
+
+	def draw(self, pen):
+		"""
+		Draw this glyph onto a *FontTools* Pen.
+		"""
+		pointPen = PointToSegmentPen(pen)
+		self.drawPoints(pointPen)
+
+	def drawPoints(self, pointPen):
+		"""
+		Draw this glyph onto a PointPen.
+		"""
+		self.glyphSet.readGlyph(self.glyphName, self, pointPen)
+
+
+# ---------
+# Glyph Set
+# ---------
+
+class GlyphSet(_UFOBaseIO):
+
+	"""
+	GlyphSet manages a set of .glif files inside one directory.
+
+	GlyphSet's constructor takes a path to an existing directory as it's
+	first argument. Reading glyph data can either be done through the
+	readGlyph() method, or by using GlyphSet's dictionary interface, where
+	the keys are glyph names and the values are (very) simple glyph objects.
+
+	To write a glyph to the glyph set, you use the writeGlyph() method.
+	The simple glyph objects returned through the dict interface do not
+	support writing, they are just a convenient way to get at the glyph data.
+	"""
+
+	glyphClass = Glyph
+
+	def __init__(
+		self,
+		path,
+		glyphNameToFileNameFunc=None,
+		ufoFormatVersion=None,
+		validateRead=True,
+		validateWrite=True,
+		expectContentsFile=False,
+	):
+		"""
+		'path' should be a path (string) to an existing local directory, or
+		an instance of fs.base.FS class.
+
+		The optional 'glyphNameToFileNameFunc' argument must be a callback
+		function that takes two arguments: a glyph name and a list of all
+		existing filenames (if any exist). It should return a file name
+		(including the .glif extension). The glyphNameToFileName function
+		is called whenever a file name is created for a given glyph name.
+
+		``validateRead`` will validate read operations. Its default is ``True``.
+		``validateWrite`` will validate write operations. Its default is ``True``.
+		``expectContentsFile`` will raise a GlifLibError if a contents.plist file is
+		not found on the glyph set file system. This should be set to ``True`` if you
+		are reading an existing UFO and ``False`` if you create a fresh	glyph set.
+		"""
+		try:
+			ufoFormatVersion = UFOFormatVersion(ufoFormatVersion)
+		except ValueError as e:
+			from fontTools.ufoLib.errors import UnsupportedUFOFormat
+
+			raise UnsupportedUFOFormat(
+				f"Unsupported UFO format: {ufoFormatVersion!r}"
+			) from e
+
+		if hasattr(path, "__fspath__"):  # support os.PathLike objects
+			path = path.__fspath__()
+
+		if isinstance(path, str):
+			try:
+				filesystem = fs.osfs.OSFS(path)
+			except fs.errors.CreateFailed:
+				raise GlifLibError("No glyphs directory '%s'" % path)
+			self._shouldClose = True
+		elif isinstance(path, fs.base.FS):
+			filesystem = path
+			try:
+				filesystem.check()
+			except fs.errors.FilesystemClosed:
+				raise GlifLibError("the filesystem '%s' is closed" % filesystem)
+			self._shouldClose = False
+		else:
+			raise TypeError(
+				"Expected a path string or fs object, found %s"
+				% type(path).__name__
+			)
+		try:
+			path = filesystem.getsyspath("/")
+		except fs.errors.NoSysPath:
+			# network or in-memory FS may not map to the local one
+			path = str(filesystem)
+		# 'dirName' is kept for backward compatibility only, but it's DEPRECATED
+		# as it's not guaranteed that it maps to an existing OSFS directory.
+		# Client could use the FS api via the `self.fs` attribute instead.
+		self.dirName = fs.path.parts(path)[-1]
+		self.fs = filesystem
+		# if glyphSet contains no 'contents.plist', we consider it empty
+		self._havePreviousFile = filesystem.exists(CONTENTS_FILENAME)
+		if expectContentsFile and not self._havePreviousFile:
+			raise GlifLibError(f"{CONTENTS_FILENAME} is missing.")
+		# attribute kept for backward compatibility
+		self.ufoFormatVersion = ufoFormatVersion.major
+		self.ufoFormatVersionTuple = ufoFormatVersion
+		if glyphNameToFileNameFunc is None:
+			glyphNameToFileNameFunc = glyphNameToFileName
+		self.glyphNameToFileName = glyphNameToFileNameFunc
+		self._validateRead = validateRead
+		self._validateWrite = validateWrite
+		self._existingFileNames = None
+		self._reverseContents = None
+
+		self.rebuildContents()
+
+	def rebuildContents(self, validateRead=None):
+		"""
+		Rebuild the contents dict by loading contents.plist.
+
+		``validateRead`` will validate the data, by default it is set to the
+		class's ``validateRead`` value, can be overridden.
+		"""
+		if validateRead is None:
+			validateRead = self._validateRead
+		contents = self._getPlist(CONTENTS_FILENAME, {})
+		# validate the contents
+		if validateRead:
+			invalidFormat = False
+			if not isinstance(contents, dict):
+				invalidFormat = True
+			else:
+				for name, fileName in contents.items():
+					if not isinstance(name, str):
+						invalidFormat = True
+					if not isinstance(fileName, str):
+						invalidFormat = True
+					elif not self.fs.exists(fileName):
+						raise GlifLibError(
+							"%s references a file that does not exist: %s"
+							% (CONTENTS_FILENAME, fileName)
+						)
+			if invalidFormat:
+				raise GlifLibError("%s is not properly formatted" % CONTENTS_FILENAME)
+		self.contents = contents
+		self._existingFileNames = None
+		self._reverseContents = None
+
+	def getReverseContents(self):
+		"""
+		Return a reversed dict of self.contents, mapping file names to
+		glyph names. This is primarily an aid for custom glyph name to file
+		name schemes that want to make sure they don't generate duplicate
+		file names. The file names are converted to lowercase so we can
+		reliably check for duplicates that only differ in case, which is
+		important for case-insensitive file systems.
+		"""
+		if self._reverseContents is None:
+			d = {}
+			for k, v in self.contents.items():
+				d[v.lower()] = k
+			self._reverseContents = d
+		return self._reverseContents
+
+	def writeContents(self):
+		"""
+		Write the contents.plist file out to disk. Call this method when
+		you're done writing glyphs.
+		"""
+		self._writePlist(CONTENTS_FILENAME, self.contents)
+
+	# layer info
+
+	def readLayerInfo(self, info, validateRead=None):
+		"""
+		``validateRead`` will validate the data, by default it is set to the
+		class's ``validateRead`` value, can be overridden.
+		"""
+		if validateRead is None:
+			validateRead = self._validateRead
+		infoDict = self._getPlist(LAYERINFO_FILENAME, {})
+		if validateRead:
+			if not isinstance(infoDict, dict):
+				raise GlifLibError("layerinfo.plist is not properly formatted.")
+			infoDict = validateLayerInfoVersion3Data(infoDict)
+		# populate the object
+		for attr, value in infoDict.items():
+			try:
+				setattr(info, attr, value)
+			except AttributeError:
+				raise GlifLibError("The supplied layer info object does not support setting a necessary attribute (%s)." % attr)
+
+	def writeLayerInfo(self, info, validateWrite=None):
+		"""
+		``validateWrite`` will validate the data, by default it is set to the
+		class's ``validateWrite`` value, can be overridden.
+		"""
+		if validateWrite is None:
+			validateWrite = self._validateWrite
+		if self.ufoFormatVersionTuple.major < 3:
+			raise GlifLibError(
+				"layerinfo.plist is not allowed in UFO %d." % self.ufoFormatVersionTuple.major
+			)
+		# gather data
+		infoData = {}
+		for attr in layerInfoVersion3ValueData.keys():
+			if hasattr(info, attr):
+				try:
+					value = getattr(info, attr)
+				except AttributeError:
+					raise GlifLibError("The supplied info object does not support getting a necessary attribute (%s)." % attr)
+				if value is None or (attr == 'lib' and not value):
+					continue
+				infoData[attr] = value
+		if infoData:
+			# validate
+			if validateWrite:
+				infoData = validateLayerInfoVersion3Data(infoData)
+			# write file
+			self._writePlist(LAYERINFO_FILENAME, infoData)
+		elif self._havePreviousFile and self.fs.exists(LAYERINFO_FILENAME):
+			# data empty, remove existing file
+			self.fs.remove(LAYERINFO_FILENAME)
+
+	def getGLIF(self, glyphName):
+		"""
+		Get the raw GLIF text for a given glyph name. This only works
+		for GLIF files that are already on disk.
+
+		This method is useful in situations when the raw XML needs to be
+		read from a glyph set for a particular glyph before fully parsing
+		it into an object structure via the readGlyph method.
+
+		Raises KeyError if 'glyphName' is not in contents.plist, or
+		GlifLibError if the file associated with can't be found.
+		"""
+		fileName = self.contents[glyphName]
+		try:
+			return self.fs.readbytes(fileName)
+		except fs.errors.ResourceNotFound:
+			raise GlifLibError(
+				"The file '%s' associated with glyph '%s' in contents.plist "
+				"does not exist on %s" % (fileName, glyphName, self.fs)
+			)
+
+	def getGLIFModificationTime(self, glyphName):
+		"""
+		Returns the modification time for the GLIF file with 'glyphName', as
+		a floating point number giving the number of seconds since the epoch.
+		Return None if the associated file does not exist or the underlying
+		filesystem does not support getting modified times.
+		Raises KeyError if the glyphName is not in contents.plist.
+		"""
+		fileName = self.contents[glyphName]
+		return self.getFileModificationTime(fileName)
+
+	# reading/writing API
+
+	def readGlyph(self, glyphName, glyphObject=None, pointPen=None, validate=None):
+		"""
+		Read a .glif file for 'glyphName' from the glyph set. The
+		'glyphObject' argument can be any kind of object (even None);
+		the readGlyph() method will attempt to set the following
+		attributes on it:
+			"width"      the advance width of the glyph
+			"height"     the advance height of the glyph
+			"unicodes"   a list of unicode values for this glyph
+			"note"       a string
+			"lib"        a dictionary containing custom data
+			"image"      a dictionary containing image data
+			"guidelines" a list of guideline data dictionaries
+			"anchors"    a list of anchor data dictionaries
+
+		All attributes are optional, in two ways:
+			1) An attribute *won't* be set if the .glif file doesn't
+			   contain data for it. 'glyphObject' will have to deal
+			   with default values itself.
+			2) If setting the attribute fails with an AttributeError
+			   (for example if the 'glyphObject' attribute is read-
+			   only), readGlyph() will not propagate that exception,
+			   but ignore that attribute.
+
+		To retrieve outline information, you need to pass an object
+		conforming to the PointPen protocol as the 'pointPen' argument.
+		This argument may be None if you don't need the outline data.
+
+		readGlyph() will raise KeyError if the glyph is not present in
+		the glyph set.
+
+		``validate`` will validate the data, by default it is set to the
+		class's ``validateRead`` value, can be overridden.
+		"""
+		if validate is None:
+			validate = self._validateRead
+		text = self.getGLIF(glyphName)
+		tree = _glifTreeFromString(text)
+		formatVersions = GLIFFormatVersion.supported_versions(self.ufoFormatVersionTuple)
+		_readGlyphFromTree(tree, glyphObject, pointPen, formatVersions=formatVersions, validate=validate)
+
+	def writeGlyph(self, glyphName, glyphObject=None, drawPointsFunc=None, formatVersion=None, validate=None):
+		"""
+		Write a .glif file for 'glyphName' to the glyph set. The
+		'glyphObject' argument can be any kind of object (even None);
+		the writeGlyph() method will attempt to get the following
+		attributes from it:
+			"width"      the advance with of the glyph
+			"height"     the advance height of the glyph
+			"unicodes"   a list of unicode values for this glyph
+			"note"       a string
+			"lib"        a dictionary containing custom data
+			"image"      a dictionary containing image data
+			"guidelines" a list of guideline data dictionaries
+			"anchors"    a list of anchor data dictionaries
+
+		All attributes are optional: if 'glyphObject' doesn't
+		have the attribute, it will simply be skipped.
+
+		To write outline data to the .glif file, writeGlyph() needs
+		a function (any callable object actually) that will take one
+		argument: an object that conforms to the PointPen protocol.
+		The function will be called by writeGlyph(); it has to call the
+		proper PointPen methods to transfer the outline to the .glif file.
+
+		The GLIF format version will be chosen based on the ufoFormatVersion
+		passed during the creation of this object. If a particular format
+		version is desired, it can be passed with the formatVersion argument.
+		The formatVersion argument accepts either a tuple of integers for
+		(major, minor), or a single integer for the major digit only (with
+		minor digit implied as 0).
+
+		An UnsupportedGLIFFormat exception is raised if the requested GLIF
+		formatVersion is not supported.
+
+		``validate`` will validate the data, by default it is set to the
+		class's ``validateWrite`` value, can be overridden.
+		"""
+		if formatVersion is None:
+			formatVersion = GLIFFormatVersion.default(self.ufoFormatVersionTuple)
+		else:
+			try:
+				formatVersion = GLIFFormatVersion(formatVersion)
+			except ValueError as e:
+				from fontTools.ufoLib.errors import UnsupportedGLIFFormat
+
+				raise UnsupportedGLIFFormat(
+					f"Unsupported GLIF format version: {formatVersion!r}"
+				) from e
+		if formatVersion not in GLIFFormatVersion.supported_versions(
+			self.ufoFormatVersionTuple
+		):
+			from fontTools.ufoLib.errors import UnsupportedGLIFFormat
+
+			raise UnsupportedGLIFFormat(
+				f"Unsupported GLIF format version ({formatVersion!s}) "
+				f"for UFO format version {self.ufoFormatVersionTuple!s}."
+			)
+		if validate is None:
+			validate = self._validateWrite
+		fileName = self.contents.get(glyphName)
+		if fileName is None:
+			if self._existingFileNames is None:
+				self._existingFileNames = {}
+				for fileName in self.contents.values():
+					self._existingFileNames[fileName] = fileName.lower()
+			fileName = self.glyphNameToFileName(glyphName, self._existingFileNames.values())
+			self.contents[glyphName] = fileName
+			self._existingFileNames[fileName] = fileName.lower()
+			if self._reverseContents is not None:
+				self._reverseContents[fileName.lower()] = glyphName
+		data = _writeGlyphToBytes(
+			glyphName,
+			glyphObject,
+			drawPointsFunc,
+			formatVersion=formatVersion,
+			validate=validate,
+		)
+		if (
+			self._havePreviousFile
+			and self.fs.exists(fileName)
+			and data == self.fs.readbytes(fileName)
+		):
+			return
+		self.fs.writebytes(fileName, data)
+
+	def deleteGlyph(self, glyphName):
+		"""Permanently delete the glyph from the glyph set on disk. Will
+		raise KeyError if the glyph is not present in the glyph set.
+		"""
+		fileName = self.contents[glyphName]
+		self.fs.remove(fileName)
+		if self._existingFileNames is not None:
+			del self._existingFileNames[fileName]
+		if self._reverseContents is not None:
+			del self._reverseContents[self.contents[glyphName].lower()]
+		del self.contents[glyphName]
+
+	# dict-like support
+
+	def keys(self):
+		return list(self.contents.keys())
+
+	def has_key(self, glyphName):
+		return glyphName in self.contents
+
+	__contains__ = has_key
+
+	def __len__(self):
+		return len(self.contents)
+
+	def __getitem__(self, glyphName):
+		if glyphName not in self.contents:
+			raise KeyError(glyphName)
+		return self.glyphClass(glyphName, self)
+
+	# quickly fetch unicode values
+
+	def getUnicodes(self, glyphNames=None):
+		"""
+		Return a dictionary that maps glyph names to lists containing
+		the unicode value[s] for that glyph, if any. This parses the .glif
+		files partially, so it is a lot faster than parsing all files completely.
+		By default this checks all glyphs, but a subset can be passed with glyphNames.
+		"""
+		unicodes = {}
+		if glyphNames is None:
+			glyphNames = self.contents.keys()
+		for glyphName in glyphNames:
+			text = self.getGLIF(glyphName)
+			unicodes[glyphName] = _fetchUnicodes(text)
+		return unicodes
+
+	def getComponentReferences(self, glyphNames=None):
+		"""
+		Return a dictionary that maps glyph names to lists containing the
+		base glyph name of components in the glyph. This parses the .glif
+		files partially, so it is a lot faster than parsing all files completely.
+		By default this checks all glyphs, but a subset can be passed with glyphNames.
+		"""
+		components = {}
+		if glyphNames is None:
+			glyphNames = self.contents.keys()
+		for glyphName in glyphNames:
+			text = self.getGLIF(glyphName)
+			components[glyphName] = _fetchComponentBases(text)
+		return components
+
+	def getImageReferences(self, glyphNames=None):
+		"""
+		Return a dictionary that maps glyph names to the file name of the image
+		referenced by the glyph. This parses the .glif files partially, so it is a
+		lot faster than parsing all files completely.
+		By default this checks all glyphs, but a subset can be passed with glyphNames.
+		"""
+		images = {}
+		if glyphNames is None:
+			glyphNames = self.contents.keys()
+		for glyphName in glyphNames:
+			text = self.getGLIF(glyphName)
+			images[glyphName] = _fetchImageFileName(text)
+		return images
+
+	def close(self):
+		if self._shouldClose:
+			self.fs.close()
+
+	def __enter__(self):
+		return self
+
+	def __exit__(self, exc_type, exc_value, exc_tb):
+		self.close()
+
+
+# -----------------------
+# Glyph Name to File Name
+# -----------------------
+
+def glyphNameToFileName(glyphName, existingFileNames):
+	"""
+	Wrapper around the userNameToFileName function in filenames.py
+	"""
+	if existingFileNames is None:
+		existingFileNames = []
+	return userNameToFileName(glyphName, existing=existingFileNames, suffix=".glif")
+
+# -----------------------
+# GLIF To and From String
+# -----------------------
+
+def readGlyphFromString(
+	aString,
+	glyphObject=None,
+	pointPen=None,
+	formatVersions=None,
+	validate=True,
+):
+	"""
+	Read .glif data from a string into a glyph object.
+
+	The 'glyphObject' argument can be any kind of object (even None);
+	the readGlyphFromString() method will attempt to set the following
+	attributes on it:
+		"width"      the advance with of the glyph
+		"height"     the advance height of the glyph
+		"unicodes"   a list of unicode values for this glyph
+		"note"       a string
+		"lib"        a dictionary containing custom data
+		"image"      a dictionary containing image data
+		"guidelines" a list of guideline data dictionaries
+		"anchors"    a list of anchor data dictionaries
+
+	All attributes are optional, in two ways:
+		1) An attribute *won't* be set if the .glif file doesn't
+		   contain data for it. 'glyphObject' will have to deal
+		   with default values itself.
+		2) If setting the attribute fails with an AttributeError
+		   (for example if the 'glyphObject' attribute is read-
+		   only), readGlyphFromString() will not propagate that
+		   exception, but ignore that attribute.
+
+	To retrieve outline information, you need to pass an object
+	conforming to the PointPen protocol as the 'pointPen' argument.
+	This argument may be None if you don't need the outline data.
+
+	The formatVersions optional argument define the GLIF format versions
+	that are allowed to be read.
+	The type is Optional[Iterable[Tuple[int, int], int]]. It can contain
+	either integers (for the major versions to be allowed, with minor
+	digits defaulting to 0), or tuples of integers to specify both
+	(major, minor) versions.
+	By default when formatVersions is None all the GLIF format versions
+	currently defined are allowed to be read.
+
+	``validate`` will validate the read data. It is set to ``True`` by default.
+	"""
+	tree = _glifTreeFromString(aString)
+
+	if formatVersions is None:
+		validFormatVersions = GLIFFormatVersion.supported_versions()
+	else:
+		validFormatVersions, invalidFormatVersions = set(), set()
+		for v in formatVersions:
+			try:
+				formatVersion = GLIFFormatVersion(v)
+			except ValueError:
+				invalidFormatVersions.add(v)
+			else:
+				validFormatVersions.add(formatVersion)
+		if not validFormatVersions:
+			raise ValueError(
+				"None of the requested GLIF formatVersions are supported: "
+				f"{formatVersions!r}"
+			)
+
+	_readGlyphFromTree(
+		tree, glyphObject, pointPen, formatVersions=validFormatVersions, validate=validate
+	)
+
+
+def _writeGlyphToBytes(
+		glyphName,
+		glyphObject=None,
+		drawPointsFunc=None,
+		writer=None,
+		formatVersion=None,
+		validate=True,
+):
+	"""Return .glif data for a glyph as a UTF-8 encoded bytes string."""
+	try:
+		formatVersion = GLIFFormatVersion(formatVersion)
+	except ValueError:
+		from fontTools.ufoLib.errors import UnsupportedGLIFFormat
+
+		raise UnsupportedGLIFFormat("Unsupported GLIF format version: {formatVersion!r}")
+	# start
+	if validate and not isinstance(glyphName, str):
+		raise GlifLibError("The glyph name is not properly formatted.")
+	if validate and len(glyphName) == 0:
+		raise GlifLibError("The glyph name is empty.")
+	glyphAttrs = OrderedDict([("name", glyphName), ("format", repr(formatVersion.major))])
+	if formatVersion.minor != 0:
+		glyphAttrs["formatMinor"] = repr(formatVersion.minor)
+	root = etree.Element("glyph", glyphAttrs)
+	identifiers = set()
+	# advance
+	_writeAdvance(glyphObject, root, validate)
+	# unicodes
+	if getattr(glyphObject, "unicodes", None):
+		_writeUnicodes(glyphObject, root, validate)
+	# note
+	if getattr(glyphObject, "note", None):
+		_writeNote(glyphObject, root, validate)
+	# image
+	if formatVersion.major >= 2 and getattr(glyphObject, "image", None):
+		_writeImage(glyphObject, root, validate)
+	# guidelines
+	if formatVersion.major >= 2 and getattr(glyphObject, "guidelines", None):
+		_writeGuidelines(glyphObject, root, identifiers, validate)
+	# anchors
+	anchors = getattr(glyphObject, "anchors", None)
+	if formatVersion.major >= 2 and anchors:
+		_writeAnchors(glyphObject, root, identifiers, validate)
+	# outline
+	if drawPointsFunc is not None:
+		outline = etree.SubElement(root, "outline")
+		pen = GLIFPointPen(outline, identifiers=identifiers, validate=validate)
+		drawPointsFunc(pen)
+		if formatVersion.major == 1 and anchors:
+			_writeAnchorsFormat1(pen, anchors, validate)
+		# prevent lxml from writing self-closing tags
+		if not len(outline):
+			outline.text = "\n  "
+	# lib
+	if getattr(glyphObject, "lib", None):
+		_writeLib(glyphObject, root, validate)
+	# return the text
+	data = etree.tostring(
+		root, encoding="UTF-8", xml_declaration=True, pretty_print=True
+	)
+	return data
+
+
+def writeGlyphToString(
+	glyphName,
+	glyphObject=None,
+	drawPointsFunc=None,
+	formatVersion=None,
+	validate=True,
+):
+	"""
+	Return .glif data for a glyph as a string. The XML declaration's
+	encoding is always set to "UTF-8".
+	The 'glyphObject' argument can be any kind of object (even None);
+	the writeGlyphToString() method will attempt to get the following
+	attributes from it:
+		"width"      the advance width of the glyph
+		"height"     the advance height of the glyph
+		"unicodes"   a list of unicode values for this glyph
+		"note"       a string
+		"lib"        a dictionary containing custom data
+		"image"      a dictionary containing image data
+		"guidelines" a list of guideline data dictionaries
+		"anchors"    a list of anchor data dictionaries
+
+	All attributes are optional: if 'glyphObject' doesn't
+	have the attribute, it will simply be skipped.
+
+	To write outline data to the .glif file, writeGlyphToString() needs
+	a function (any callable object actually) that will take one
+	argument: an object that conforms to the PointPen protocol.
+	The function will be called by writeGlyphToString(); it has to call the
+	proper PointPen methods to transfer the outline to the .glif file.
+
+	The GLIF format version can be specified with the formatVersion argument.
+	This accepts either a tuple of integers for (major, minor), or a single
+	integer for the major digit only (with minor digit implied as 0).
+	By default when formatVesion is None the latest GLIF format version will
+	be used; currently it's 2.0, which is equivalent to formatVersion=(2, 0).
+
+	An UnsupportedGLIFFormat exception is raised if the requested UFO
+	formatVersion is not supported.
+
+	``validate`` will validate the written data. It is set to ``True`` by default.
+	"""
+	data = _writeGlyphToBytes(
+		glyphName,
+		glyphObject=glyphObject,
+		drawPointsFunc=drawPointsFunc,
+		formatVersion=formatVersion,
+		validate=validate,
+	)
+	return data.decode("utf-8")
+
+
+def _writeAdvance(glyphObject, element, validate):
+	width = getattr(glyphObject, "width", None)
+	if width is not None:
+		if validate and not isinstance(width, numberTypes):
+			raise GlifLibError("width attribute must be int or float")
+		if width == 0:
+			width = None
+	height = getattr(glyphObject, "height", None)
+	if height is not None:
+		if validate and not isinstance(height, numberTypes):
+			raise GlifLibError("height attribute must be int or float")
+		if height == 0:
+			height = None
+	if width is not None and height is not None:
+		etree.SubElement(element, "advance", OrderedDict([("height", repr(height)), ("width", repr(width))]))
+	elif width is not None:
+		etree.SubElement(element, "advance", dict(width=repr(width)))
+	elif height is not None:
+		etree.SubElement(element, "advance", dict(height=repr(height)))
+
+def _writeUnicodes(glyphObject, element, validate):
+	unicodes = getattr(glyphObject, "unicodes", None)
+	if validate and isinstance(unicodes, int):
+		unicodes = [unicodes]
+	seen = set()
+	for code in unicodes:
+		if validate and not isinstance(code, int):
+			raise GlifLibError("unicode values must be int")
+		if code in seen:
+			continue
+		seen.add(code)
+		hexCode = "%04X" % code
+		etree.SubElement(element, "unicode", dict(hex=hexCode))
+
+def _writeNote(glyphObject, element, validate):
+	note = getattr(glyphObject, "note", None)
+	if validate and not isinstance(note, str):
+		raise GlifLibError("note attribute must be str")
+	note = note.strip()
+	note = "\n" + note + "\n"
+	etree.SubElement(element, "note").text = note
+
+def _writeImage(glyphObject, element, validate):
+	image = getattr(glyphObject, "image", None)
+	if validate and not imageValidator(image):
+		raise GlifLibError("image attribute must be a dict or dict-like object with the proper structure.")
+	attrs = OrderedDict([("fileName", image["fileName"])])
+	for attr, default in _transformationInfo:
+		value = image.get(attr, default)
+		if value != default:
+			attrs[attr] = repr(value)
+	color = image.get("color")
+	if color is not None:
+		attrs["color"] = color
+	etree.SubElement(element, "image", attrs)
+
+def _writeGuidelines(glyphObject, element, identifiers, validate):
+	guidelines = getattr(glyphObject, "guidelines", [])
+	if validate and not guidelinesValidator(guidelines):
+		raise GlifLibError("guidelines attribute does not have the proper structure.")
+	for guideline in guidelines:
+		attrs = OrderedDict()
+		x = guideline.get("x")
+		if x is not None:
+			attrs["x"] = repr(x)
+		y = guideline.get("y")
+		if y is not None:
+			attrs["y"] = repr(y)
+		angle = guideline.get("angle")
+		if angle is not None:
+			attrs["angle"] = repr(angle)
+		name = guideline.get("name")
+		if name is not None:
+			attrs["name"] = name
+		color = guideline.get("color")
+		if color is not None:
+			attrs["color"] = color
+		identifier = guideline.get("identifier")
+		if identifier is not None:
+			if validate and identifier in identifiers:
+				raise GlifLibError("identifier used more than once: %s" % identifier)
+			attrs["identifier"] = identifier
+			identifiers.add(identifier)
+		etree.SubElement(element, "guideline", attrs)
+
+def _writeAnchorsFormat1(pen, anchors, validate):
+	if validate and not anchorsValidator(anchors):
+		raise GlifLibError("anchors attribute does not have the proper structure.")
+	for anchor in anchors:
+		attrs = {}
+		x = anchor["x"]
+		attrs["x"] = repr(x)
+		y = anchor["y"]
+		attrs["y"] = repr(y)
+		name = anchor.get("name")
+		if name is not None:
+			attrs["name"] = name
+		pen.beginPath()
+		pen.addPoint((x, y), segmentType="move", name=name)
+		pen.endPath()
+
+def _writeAnchors(glyphObject, element, identifiers, validate):
+	anchors = getattr(glyphObject, "anchors", [])
+	if validate and not anchorsValidator(anchors):
+		raise GlifLibError("anchors attribute does not have the proper structure.")
+	for anchor in anchors:
+		attrs = OrderedDict()
+		x = anchor["x"]
+		attrs["x"] = repr(x)
+		y = anchor["y"]
+		attrs["y"] = repr(y)
+		name = anchor.get("name")
+		if name is not None:
+			attrs["name"] = name
+		color = anchor.get("color")
+		if color is not None:
+			attrs["color"] = color
+		identifier = anchor.get("identifier")
+		if identifier is not None:
+			if validate and identifier in identifiers:
+				raise GlifLibError("identifier used more than once: %s" % identifier)
+			attrs["identifier"] = identifier
+			identifiers.add(identifier)
+		etree.SubElement(element, "anchor", attrs)
+
+def _writeLib(glyphObject, element, validate):
+	lib = getattr(glyphObject, "lib", None)
+	if not lib:
+		# don't write empty lib
+		return
+	if validate:
+		valid, message = glyphLibValidator(lib)
+		if not valid:
+			raise GlifLibError(message)
+	if not isinstance(lib, dict):
+		lib = dict(lib)
+	# plist inside GLIF begins with 2 levels of indentation
+	e = plistlib.totree(lib, indent_level=2)
+	etree.SubElement(element, "lib").append(e)
+
+# -----------------------
+# layerinfo.plist Support
+# -----------------------
+
+layerInfoVersion3ValueData = {
+	"color"			: dict(type=str, valueValidator=colorValidator),
+	"lib"			: dict(type=dict, valueValidator=genericTypeValidator)
+}
+
+def validateLayerInfoVersion3ValueForAttribute(attr, value):
+	"""
+	This performs very basic validation of the value for attribute
+	following the UFO 3 fontinfo.plist specification. The results
+	of this should not be interpretted as *correct* for the font
+	that they are part of. This merely indicates that the value
+	is of the proper type and, where the specification defines
+	a set range of possible values for an attribute, that the
+	value is in the accepted range.
+	"""
+	if attr not in layerInfoVersion3ValueData:
+		return False
+	dataValidationDict = layerInfoVersion3ValueData[attr]
+	valueType = dataValidationDict.get("type")
+	validator = dataValidationDict.get("valueValidator")
+	valueOptions = dataValidationDict.get("valueOptions")
+	# have specific options for the validator
+	if valueOptions is not None:
+		isValidValue = validator(value, valueOptions)
+	# no specific options
+	else:
+		if validator == genericTypeValidator:
+			isValidValue = validator(value, valueType)
+		else:
+			isValidValue = validator(value)
+	return isValidValue
+
+def validateLayerInfoVersion3Data(infoData):
+	"""
+	This performs very basic validation of the value for infoData
+	following the UFO 3 layerinfo.plist specification. The results
+	of this should not be interpretted as *correct* for the font
+	that they are part of. This merely indicates that the values
+	are of the proper type and, where the specification defines
+	a set range of possible values for an attribute, that the
+	value is in the accepted range.
+	"""
+	for attr, value in infoData.items():
+		if attr not in layerInfoVersion3ValueData:
+			raise GlifLibError("Unknown attribute %s." % attr)
+		isValidValue = validateLayerInfoVersion3ValueForAttribute(attr, value)
+		if not isValidValue:
+			raise GlifLibError(f"Invalid value for attribute {attr} ({value!r}).")
+	return infoData
+
+# -----------------
+# GLIF Tree Support
+# -----------------
+
+def _glifTreeFromFile(aFile):
+	if etree._have_lxml:
+		tree = etree.parse(aFile, parser=etree.XMLParser(remove_comments=True))
+	else:
+		tree = etree.parse(aFile)
+	root = tree.getroot()
+	if root.tag != "glyph":
+		raise GlifLibError("The GLIF is not properly formatted.")
+	if root.text and root.text.strip() != '':
+		raise GlifLibError("Invalid GLIF structure.")
+	return root
+
+
+def _glifTreeFromString(aString):
+	data = tobytes(aString, encoding="utf-8")
+	if etree._have_lxml:
+		root = etree.fromstring(data, parser=etree.XMLParser(remove_comments=True))
+	else:
+		root = etree.fromstring(data)
+	if root.tag != "glyph":
+		raise GlifLibError("The GLIF is not properly formatted.")
+	if root.text and root.text.strip() != '':
+		raise GlifLibError("Invalid GLIF structure.")
+	return root
+
+
+def _readGlyphFromTree(
+	tree,
+	glyphObject=None,
+	pointPen=None,
+	formatVersions=GLIFFormatVersion.supported_versions(),
+	validate=True,
+):
+	# check the format version
+	formatVersionMajor = tree.get("format")
+	if validate and formatVersionMajor is None:
+		raise GlifLibError("Unspecified format version in GLIF.")
+	formatVersionMinor = tree.get("formatMinor", 0)
+	try:
+		formatVersion = GLIFFormatVersion((int(formatVersionMajor), int(formatVersionMinor)))
+	except ValueError as e:
+		msg = "Unsupported GLIF format: %s.%s" % (formatVersionMajor, formatVersionMinor)
+		if validate:
+			from fontTools.ufoLib.errors import UnsupportedGLIFFormat
+
+			raise UnsupportedGLIFFormat(msg) from e
+		# warn but continue using the latest supported format
+		formatVersion = GLIFFormatVersion.default()
+		logger.warning(
+			"%s. Assuming the latest supported version (%s). "
+			"Some data may be skipped or parsed incorrectly.",
+			msg,
+			formatVersion,
+		)
+
+	if validate and formatVersion not in formatVersions:
+		raise GlifLibError(f"Forbidden GLIF format version: {formatVersion!s}")
+
+	try:
+		readGlyphFromTree = _READ_GLYPH_FROM_TREE_FUNCS[formatVersion]
+	except KeyError:
+		raise NotImplementedError(formatVersion)
+
+	readGlyphFromTree(
+		tree=tree,
+		glyphObject=glyphObject,
+		pointPen=pointPen,
+		validate=validate,
+		formatMinor=formatVersion.minor,
+	)
+
+
+def _readGlyphFromTreeFormat1(tree, glyphObject=None, pointPen=None, validate=None, **kwargs):
+	# get the name
+	_readName(glyphObject, tree, validate)
+	# populate the sub elements
+	unicodes = []
+	haveSeenAdvance = haveSeenOutline = haveSeenLib = haveSeenNote = False
+	for element in tree:
+		if element.tag == "outline":
+			if validate:
+				if haveSeenOutline:
+					raise GlifLibError("The outline element occurs more than once.")
+				if element.attrib:
+					raise GlifLibError("The outline element contains unknown attributes.")
+				if element.text and element.text.strip() != '':
+					raise GlifLibError("Invalid outline structure.")
+			haveSeenOutline = True
+			buildOutlineFormat1(glyphObject, pointPen, element, validate)
+		elif glyphObject is None:
+			continue
+		elif element.tag == "advance":
+			if validate and haveSeenAdvance:
+				raise GlifLibError("The advance element occurs more than once.")
+			haveSeenAdvance = True
+			_readAdvance(glyphObject, element)
+		elif element.tag == "unicode":
+			try:
+				v = element.get("hex")
+				v = int(v, 16)
+				if v not in unicodes:
+					unicodes.append(v)
+			except ValueError:
+				raise GlifLibError("Illegal value for hex attribute of unicode element.")
+		elif element.tag == "note":
+			if validate and haveSeenNote:
+				raise GlifLibError("The note element occurs more than once.")
+			haveSeenNote = True
+			_readNote(glyphObject, element)
+		elif element.tag == "lib":
+			if validate and haveSeenLib:
+				raise GlifLibError("The lib element occurs more than once.")
+			haveSeenLib = True
+			_readLib(glyphObject, element, validate)
+		else:
+			raise GlifLibError("Unknown element in GLIF: %s" % element)
+	# set the collected unicodes
+	if unicodes:
+		_relaxedSetattr(glyphObject, "unicodes", unicodes)
+
+def _readGlyphFromTreeFormat2(
+	tree, glyphObject=None, pointPen=None, validate=None, formatMinor=0
+):
+	# get the name
+	_readName(glyphObject, tree, validate)
+	# populate the sub elements
+	unicodes = []
+	guidelines = []
+	anchors = []
+	haveSeenAdvance = haveSeenImage = haveSeenOutline = haveSeenLib = haveSeenNote = False
+	identifiers = set()
+	for element in tree:
+		if element.tag == "outline":
+			if validate:
+				if haveSeenOutline:
+					raise GlifLibError("The outline element occurs more than once.")
+				if element.attrib:
+					raise GlifLibError("The outline element contains unknown attributes.")
+				if element.text and element.text.strip() != '':
+					raise GlifLibError("Invalid outline structure.")
+			haveSeenOutline = True
+			if pointPen is not None:
+				buildOutlineFormat2(glyphObject, pointPen, element, identifiers, validate)
+		elif glyphObject is None:
+			continue
+		elif element.tag == "advance":
+			if validate and haveSeenAdvance:
+				raise GlifLibError("The advance element occurs more than once.")
+			haveSeenAdvance = True
+			_readAdvance(glyphObject, element)
+		elif element.tag == "unicode":
+			try:
+				v = element.get("hex")
+				v = int(v, 16)
+				if v not in unicodes:
+					unicodes.append(v)
+			except ValueError:
+				raise GlifLibError("Illegal value for hex attribute of unicode element.")
+		elif element.tag == "guideline":
+			if validate and len(element):
+				raise GlifLibError("Unknown children in guideline element.")
+			attrib = dict(element.attrib)
+			for attr in ("x", "y", "angle"):
+				if attr in attrib:
+					attrib[attr] = _number(attrib[attr])
+			guidelines.append(attrib)
+		elif element.tag == "anchor":
+			if validate and len(element):
+				raise GlifLibError("Unknown children in anchor element.")
+			attrib = dict(element.attrib)
+			for attr in ("x", "y"):
+				if attr in element.attrib:
+					attrib[attr] = _number(attrib[attr])
+			anchors.append(attrib)
+		elif element.tag == "image":
+			if validate:
+				if haveSeenImage:
+					raise GlifLibError("The image element occurs more than once.")
+				if len(element):
+					raise GlifLibError("Unknown children in image element.")
+			haveSeenImage = True
+			_readImage(glyphObject, element, validate)
+		elif element.tag == "note":
+			if validate and haveSeenNote:
+				raise GlifLibError("The note element occurs more than once.")
+			haveSeenNote = True
+			_readNote(glyphObject, element)
+		elif element.tag == "lib":
+			if validate and haveSeenLib:
+				raise GlifLibError("The lib element occurs more than once.")
+			haveSeenLib = True
+			_readLib(glyphObject, element, validate)
+		else:
+			raise GlifLibError("Unknown element in GLIF: %s" % element)
+	# set the collected unicodes
+	if unicodes:
+		_relaxedSetattr(glyphObject, "unicodes", unicodes)
+	# set the collected guidelines
+	if guidelines:
+		if validate and not guidelinesValidator(guidelines, identifiers):
+			raise GlifLibError("The guidelines are improperly formatted.")
+		_relaxedSetattr(glyphObject, "guidelines", guidelines)
+	# set the collected anchors
+	if anchors:
+		if validate and not anchorsValidator(anchors, identifiers):
+			raise GlifLibError("The anchors are improperly formatted.")
+		_relaxedSetattr(glyphObject, "anchors", anchors)
+
+
+_READ_GLYPH_FROM_TREE_FUNCS = {
+	GLIFFormatVersion.FORMAT_1_0: _readGlyphFromTreeFormat1,
+	GLIFFormatVersion.FORMAT_2_0: _readGlyphFromTreeFormat2,
+}
+
+
+def _readName(glyphObject, root, validate):
+	glyphName = root.get("name")
+	if validate and not glyphName:
+		raise GlifLibError("Empty glyph name in GLIF.")
+	if glyphName and glyphObject is not None:
+		_relaxedSetattr(glyphObject, "name", glyphName)
+
+def _readAdvance(glyphObject, advance):
+	width = _number(advance.get("width", 0))
+	_relaxedSetattr(glyphObject, "width", width)
+	height = _number(advance.get("height", 0))
+	_relaxedSetattr(glyphObject, "height", height)
+
+def _readNote(glyphObject, note):
+	lines = note.text.split("\n")
+	note = "\n".join(line.strip() for line in lines if line.strip())
+	_relaxedSetattr(glyphObject, "note", note)
+
+def _readLib(glyphObject, lib, validate):
+	assert len(lib) == 1
+	child = lib[0]
+	plist = plistlib.fromtree(child)
+	if validate:
+		valid, message = glyphLibValidator(plist)
+		if not valid:
+			raise GlifLibError(message)
+	_relaxedSetattr(glyphObject, "lib", plist)
+
+def _readImage(glyphObject, image, validate):
+	imageData = dict(image.attrib)
+	for attr, default in _transformationInfo:
+		value = imageData.get(attr, default)
+		imageData[attr] = _number(value)
+	if validate and not imageValidator(imageData):
+		raise GlifLibError("The image element is not properly formatted.")
+	_relaxedSetattr(glyphObject, "image", imageData)
+
+# ----------------
+# GLIF to PointPen
+# ----------------
+
+contourAttributesFormat2 = {"identifier"}
+componentAttributesFormat1 = {"base", "xScale", "xyScale", "yxScale", "yScale", "xOffset", "yOffset"}
+componentAttributesFormat2 = componentAttributesFormat1 | {"identifier"}
+pointAttributesFormat1 = {"x", "y", "type", "smooth", "name"}
+pointAttributesFormat2 = pointAttributesFormat1 | {"identifier"}
+pointSmoothOptions = {"no", "yes"}
+pointTypeOptions = {"move", "line", "offcurve", "curve", "qcurve"}
+
+# format 1
+
+def buildOutlineFormat1(glyphObject, pen, outline, validate):
+	anchors = []
+	for element in outline:
+		if element.tag == "contour":
+			if len(element) == 1:
+				point = element[0]
+				if point.tag == "point":
+					anchor = _buildAnchorFormat1(point, validate)
+					if anchor is not None:
+						anchors.append(anchor)
+						continue
+			if pen is not None:
+				_buildOutlineContourFormat1(pen, element, validate)
+		elif element.tag == "component":
+			if pen is not None:
+				_buildOutlineComponentFormat1(pen, element, validate)
+		else:
+			raise GlifLibError("Unknown element in outline element: %s" % element)
+	if glyphObject is not None and anchors:
+		if validate and not anchorsValidator(anchors):
+			raise GlifLibError("GLIF 1 anchors are not properly formatted.")
+		_relaxedSetattr(glyphObject, "anchors", anchors)
+
+def _buildAnchorFormat1(point, validate):
+	if point.get("type") != "move":
+		return None
+	name = point.get("name")
+	if name is None:
+		return None
+	x = point.get("x")
+	y = point.get("y")
+	if validate and x is None:
+		raise GlifLibError("Required x attribute is missing in point element.")
+	if validate and y is None:
+		raise GlifLibError("Required y attribute is missing in point element.")
+	x = _number(x)
+	y = _number(y)
+	anchor = dict(x=x, y=y, name=name)
+	return anchor
+
+def _buildOutlineContourFormat1(pen, contour, validate):
+	if validate and contour.attrib:
+		raise GlifLibError("Unknown attributes in contour element.")
+	pen.beginPath()
+	if len(contour):
+		massaged = _validateAndMassagePointStructures(contour, pointAttributesFormat1, openContourOffCurveLeniency=True, validate=validate)
+		_buildOutlinePointsFormat1(pen, massaged)
+	pen.endPath()
+
+def _buildOutlinePointsFormat1(pen, contour):
+	for point in contour:
+		x = point["x"]
+		y = point["y"]
+		segmentType = point["segmentType"]
+		smooth = point["smooth"]
+		name = point["name"]
+		pen.addPoint((x, y), segmentType=segmentType, smooth=smooth, name=name)
+
+def _buildOutlineComponentFormat1(pen, component, validate):
+	if validate:
+		if len(component):
+			raise GlifLibError("Unknown child elements of component element.")
+		for attr in component.attrib.keys():
+			if attr not in componentAttributesFormat1:
+				raise GlifLibError("Unknown attribute in component element: %s" % attr)
+	baseGlyphName = component.get("base")
+	if validate and baseGlyphName is None:
+		raise GlifLibError("The base attribute is not defined in the component.")
+	transformation = []
+	for attr, default in _transformationInfo:
+		value = component.get(attr)
+		if value is None:
+			value = default
+		else:
+			value = _number(value)
+		transformation.append(value)
+	pen.addComponent(baseGlyphName, tuple(transformation))
+
+# format 2
+
+def buildOutlineFormat2(glyphObject, pen, outline, identifiers, validate):
+	for element in outline:
+		if element.tag == "contour":
+			_buildOutlineContourFormat2(pen, element, identifiers, validate)
+		elif element.tag == "component":
+			_buildOutlineComponentFormat2(pen, element, identifiers, validate)
+		else:
+			raise GlifLibError("Unknown element in outline element: %s" % element.tag)
+
+def _buildOutlineContourFormat2(pen, contour, identifiers, validate):
+	if validate:
+		for attr in contour.attrib.keys():
+			if attr not in contourAttributesFormat2:
+				raise GlifLibError("Unknown attribute in contour element: %s" % attr)
+	identifier = contour.get("identifier")
+	if identifier is not None:
+		if validate:
+			if identifier in identifiers:
+				raise GlifLibError("The identifier %s is used more than once." % identifier)
+			if not identifierValidator(identifier):
+				raise GlifLibError("The contour identifier %s is not valid." % identifier)
+		identifiers.add(identifier)
+	try:
+		pen.beginPath(identifier=identifier)
+	except TypeError:
+		pen.beginPath()
+		warn("The beginPath method needs an identifier kwarg. The contour's identifier value has been discarded.", DeprecationWarning)
+	if len(contour):
+		massaged = _validateAndMassagePointStructures(contour, pointAttributesFormat2, validate=validate)
+		_buildOutlinePointsFormat2(pen, massaged, identifiers, validate)
+	pen.endPath()
+
+def _buildOutlinePointsFormat2(pen, contour, identifiers, validate):
+	for point in contour:
+		x = point["x"]
+		y = point["y"]
+		segmentType = point["segmentType"]
+		smooth = point["smooth"]
+		name = point["name"]
+		identifier = point.get("identifier")
+		if identifier is not None:
+			if validate:
+				if identifier in identifiers:
+					raise GlifLibError("The identifier %s is used more than once." % identifier)
+				if not identifierValidator(identifier):
+					raise GlifLibError("The identifier %s is not valid." % identifier)
+			identifiers.add(identifier)
+		try:
+			pen.addPoint((x, y), segmentType=segmentType, smooth=smooth, name=name, identifier=identifier)
+		except TypeError:
+			pen.addPoint((x, y), segmentType=segmentType, smooth=smooth, name=name)
+			warn("The addPoint method needs an identifier kwarg. The point's identifier value has been discarded.", DeprecationWarning)
+
+def _buildOutlineComponentFormat2(pen, component, identifiers, validate):
+	if validate:
+		if len(component):
+			raise GlifLibError("Unknown child elements of component element.")
+		for attr in component.attrib.keys():
+			if attr not in componentAttributesFormat2:
+				raise GlifLibError("Unknown attribute in component element: %s" % attr)
+	baseGlyphName = component.get("base")
+	if validate and baseGlyphName is None:
+		raise GlifLibError("The base attribute is not defined in the component.")
+	transformation = []
+	for attr, default in _transformationInfo:
+		value = component.get(attr)
+		if value is None:
+			value = default
+		else:
+			value = _number(value)
+		transformation.append(value)
+	identifier = component.get("identifier")
+	if identifier is not None:
+		if validate:
+			if identifier in identifiers:
+				raise GlifLibError("The identifier %s is used more than once." % identifier)
+			if validate and not identifierValidator(identifier):
+				raise GlifLibError("The identifier %s is not valid." % identifier)
+		identifiers.add(identifier)
+	try:
+		pen.addComponent(baseGlyphName, tuple(transformation), identifier=identifier)
+	except TypeError:
+		pen.addComponent(baseGlyphName, tuple(transformation))
+		warn("The addComponent method needs an identifier kwarg. The component's identifier value has been discarded.", DeprecationWarning)
+
+# all formats
+
+def _validateAndMassagePointStructures(contour, pointAttributes, openContourOffCurveLeniency=False, validate=True):
+	if not len(contour):
+		return
+	# store some data for later validation
+	lastOnCurvePoint = None
+	haveOffCurvePoint = False
+	# validate and massage the individual point elements
+	massaged = []
+	for index, element in enumerate(contour):
+		# not <point>
+		if element.tag != "point":
+			raise GlifLibError("Unknown child element (%s) of contour element." % element.tag)
+		point = dict(element.attrib)
+		massaged.append(point)
+		if validate:
+			# unknown attributes
+			for attr in point.keys():
+				if attr not in pointAttributes:
+					raise GlifLibError("Unknown attribute in point element: %s" % attr)
+			# search for unknown children
+			if len(element):
+				raise GlifLibError("Unknown child elements in point element.")
+		# x and y are required
+		for attr in ("x", "y"):
+			try:
+				point[attr] = _number(point[attr])
+			except KeyError as e:
+				raise GlifLibError(f"Required {attr} attribute is missing in point element.") from e
+		# segment type
+		pointType = point.pop("type", "offcurve")
+		if validate and pointType not in pointTypeOptions:
+			raise GlifLibError("Unknown point type: %s" % pointType)
+		if pointType == "offcurve":
+			pointType = None
+		point["segmentType"] = pointType
+		if pointType is None:
+			haveOffCurvePoint = True
+		else:
+			lastOnCurvePoint = index
+		# move can only occur as the first point
+		if validate and pointType == "move" and index != 0:
+			raise GlifLibError("A move point occurs after the first point in the contour.")
+		# smooth is optional
+		smooth = point.get("smooth", "no")
+		if validate and smooth is not None:
+			if smooth not in pointSmoothOptions:
+				raise GlifLibError("Unknown point smooth value: %s" % smooth)
+		smooth = smooth == "yes"
+		point["smooth"] = smooth
+		# smooth can only be applied to curve and qcurve
+		if validate and smooth and pointType is None:
+			raise GlifLibError("smooth attribute set in an offcurve point.")
+		# name is optional
+		if "name" not in element.attrib:
+			point["name"] = None
+	if openContourOffCurveLeniency:
+		# remove offcurves that precede a move. this is technically illegal,
+		# but we let it slide because there are fonts out there in the wild like this.
+		if massaged[0]["segmentType"] == "move":
+			count = 0
+			for point in reversed(massaged):
+				if point["segmentType"] is None:
+					count += 1
+				else:
+					break
+			if count:
+				massaged = massaged[:-count]
+	# validate the off-curves in the segments
+	if validate and haveOffCurvePoint and lastOnCurvePoint is not None:
+		# we only care about how many offCurves there are before an onCurve
+		# filter out the trailing offCurves
+		offCurvesCount = len(massaged) - 1 - lastOnCurvePoint
+		for point in massaged:
+			segmentType = point["segmentType"]
+			if segmentType is None:
+				offCurvesCount += 1
+			else:
+				if offCurvesCount:
+					# move and line can't be preceded by off-curves
+					if segmentType == "move":
+						# this will have been filtered out already
+						raise GlifLibError("move can not have an offcurve.")
+					elif segmentType == "line":
+						raise GlifLibError("line can not have an offcurve.")
+					elif segmentType == "curve":
+						if offCurvesCount > 2:
+							raise GlifLibError("Too many offcurves defined for curve.")
+					elif segmentType == "qcurve":
+						pass
+					else:
+						# unknown segment type. it'll be caught later.
+						pass
+				offCurvesCount = 0
+	return massaged
+
+# ---------------------
+# Misc Helper Functions
+# ---------------------
+
+def _relaxedSetattr(object, attr, value):
+	try:
+		setattr(object, attr, value)
+	except AttributeError:
+		pass
+
+def _number(s):
+	"""
+	Given a numeric string, return an integer or a float, whichever
+	the string indicates. _number("1") will return the integer 1,
+	_number("1.0") will return the float 1.0.
+
+	>>> _number("1")
+	1
+	>>> _number("1.0")
+	1.0
+	>>> _number("a")  # doctest: +IGNORE_EXCEPTION_DETAIL
+	Traceback (most recent call last):
+	    ...
+	GlifLibError: Could not convert a to an int or float.
+	"""
+	try:
+		n = int(s)
+		return n
+	except ValueError:
+		pass
+	try:
+		n = float(s)
+		return n
+	except ValueError:
+		raise GlifLibError("Could not convert %s to an int or float." % s)
+
+# --------------------
+# Rapid Value Fetching
+# --------------------
+
+# base
+
+class _DoneParsing(Exception): pass
+
+class _BaseParser:
+
+	def __init__(self):
+		self._elementStack = []
+
+	def parse(self, text):
+		from xml.parsers.expat import ParserCreate
+		parser = ParserCreate()
+		parser.StartElementHandler = self.startElementHandler
+		parser.EndElementHandler = self.endElementHandler
+		parser.Parse(text)
+
+	def startElementHandler(self, name, attrs):
+		self._elementStack.append(name)
+
+	def endElementHandler(self, name):
+		other = self._elementStack.pop(-1)
+		assert other == name
+
+
+# unicodes
+
+def _fetchUnicodes(glif):
+	"""
+	Get a list of unicodes listed in glif.
+	"""
+	parser = _FetchUnicodesParser()
+	parser.parse(glif)
+	return parser.unicodes
+
+class _FetchUnicodesParser(_BaseParser):
+
+	def __init__(self):
+		self.unicodes = []
+		super().__init__()
+
+	def startElementHandler(self, name, attrs):
+		if name == "unicode" and self._elementStack and self._elementStack[-1] == "glyph":
+			value = attrs.get("hex")
+			if value is not None:
+				try:
+					value = int(value, 16)
+					if value not in self.unicodes:
+						self.unicodes.append(value)
+				except ValueError:
+					pass
+		super().startElementHandler(name, attrs)
+
+# image
+
+def _fetchImageFileName(glif):
+	"""
+	The image file name (if any) from glif.
+	"""
+	parser = _FetchImageFileNameParser()
+	try:
+		parser.parse(glif)
+	except _DoneParsing:
+		pass
+	return parser.fileName
+
+class _FetchImageFileNameParser(_BaseParser):
+
+	def __init__(self):
+		self.fileName = None
+		super().__init__()
+
+	def startElementHandler(self, name, attrs):
+		if name == "image" and self._elementStack and self._elementStack[-1] == "glyph":
+			self.fileName = attrs.get("fileName")
+			raise _DoneParsing
+		super().startElementHandler(name, attrs)
+
+# component references
+
+def _fetchComponentBases(glif):
+	"""
+	Get a list of component base glyphs listed in glif.
+	"""
+	parser = _FetchComponentBasesParser()
+	try:
+		parser.parse(glif)
+	except _DoneParsing:
+		pass
+	return list(parser.bases)
+
+class _FetchComponentBasesParser(_BaseParser):
+
+	def __init__(self):
+		self.bases = []
+		super().__init__()
+
+	def startElementHandler(self, name, attrs):
+		if name == "component" and self._elementStack and self._elementStack[-1] == "outline":
+			base = attrs.get("base")
+			if base is not None:
+				self.bases.append(base)
+		super().startElementHandler(name, attrs)
+
+	def endElementHandler(self, name):
+		if name == "outline":
+			raise _DoneParsing
+		super().endElementHandler(name)
+
+# --------------
+# GLIF Point Pen
+# --------------
+
+_transformationInfo = [
+	# field name, default value
+	("xScale",    1),
+	("xyScale",   0),
+	("yxScale",   0),
+	("yScale",    1),
+	("xOffset",   0),
+	("yOffset",   0),
+]
+
+class GLIFPointPen(AbstractPointPen):
+
+	"""
+	Helper class using the PointPen protocol to write the <outline>
+	part of .glif files.
+	"""
+
+	def __init__(self, element, formatVersion=None, identifiers=None, validate=True):
+		if identifiers is None:
+			identifiers = set()
+		self.formatVersion = GLIFFormatVersion(formatVersion)
+		self.identifiers = identifiers
+		self.outline = element
+		self.contour = None
+		self.prevOffCurveCount = 0
+		self.prevPointTypes = []
+		self.validate = validate
+
+	def beginPath(self, identifier=None, **kwargs):
+		attrs = OrderedDict()
+		if identifier is not None and self.formatVersion.major >= 2:
+			if self.validate:
+				if identifier in self.identifiers:
+					raise GlifLibError("identifier used more than once: %s" % identifier)
+				if not identifierValidator(identifier):
+					raise GlifLibError("identifier not formatted properly: %s" % identifier)
+			attrs["identifier"] = identifier
+			self.identifiers.add(identifier)
+		self.contour = etree.SubElement(self.outline, "contour", attrs)
+		self.prevOffCurveCount = 0
+
+	def endPath(self):
+		if self.prevPointTypes and self.prevPointTypes[0] == "move":
+			if self.validate and self.prevPointTypes[-1] == "offcurve":
+				raise GlifLibError("open contour has loose offcurve point")
+		# prevent lxml from writing self-closing tags
+		if not len(self.contour):
+			self.contour.text = "\n  "
+		self.contour = None
+		self.prevPointType = None
+		self.prevOffCurveCount = 0
+		self.prevPointTypes = []
+
+	def addPoint(self, pt, segmentType=None, smooth=None, name=None, identifier=None, **kwargs):
+		attrs = OrderedDict()
+		# coordinates
+		if pt is not None:
+			if self.validate:
+				for coord in pt:
+					if not isinstance(coord, numberTypes):
+						raise GlifLibError("coordinates must be int or float")
+			attrs["x"] = repr(pt[0])
+			attrs["y"] = repr(pt[1])
+		# segment type
+		if segmentType == "offcurve":
+			segmentType = None
+		if self.validate:
+			if segmentType == "move" and self.prevPointTypes:
+				raise GlifLibError("move occurs after a point has already been added to the contour.")
+			if segmentType in ("move", "line") and self.prevPointTypes and self.prevPointTypes[-1] == "offcurve":
+				raise GlifLibError("offcurve occurs before %s point." % segmentType)
+			if segmentType == "curve" and self.prevOffCurveCount > 2:
+				raise GlifLibError("too many offcurve points before curve point.")
+		if segmentType is not None:
+			attrs["type"] = segmentType
+		else:
+			segmentType = "offcurve"
+		if segmentType == "offcurve":
+			self.prevOffCurveCount += 1
+		else:
+			self.prevOffCurveCount = 0
+		self.prevPointTypes.append(segmentType)
+		# smooth
+		if smooth:
+			if self.validate and segmentType == "offcurve":
+				raise GlifLibError("can't set smooth in an offcurve point.")
+			attrs["smooth"] = "yes"
+		# name
+		if name is not None:
+			attrs["name"] = name
+		# identifier
+		if identifier is not None and self.formatVersion.major >= 2:
+			if self.validate:
+				if identifier in self.identifiers:
+					raise GlifLibError("identifier used more than once: %s" % identifier)
+				if not identifierValidator(identifier):
+					raise GlifLibError("identifier not formatted properly: %s" % identifier)
+			attrs["identifier"] = identifier
+			self.identifiers.add(identifier)
+		etree.SubElement(self.contour, "point", attrs)
+
+	def addComponent(self, glyphName, transformation, identifier=None, **kwargs):
+		attrs = OrderedDict([("base", glyphName)])
+		for (attr, default), value in zip(_transformationInfo, transformation):
+			if self.validate and not isinstance(value, numberTypes):
+				raise GlifLibError("transformation values must be int or float")
+			if value != default:
+				attrs[attr] = repr(value)
+		if identifier is not None and self.formatVersion.major >= 2:
+			if self.validate:
+				if identifier in self.identifiers:
+					raise GlifLibError("identifier used more than once: %s" % identifier)
+				if self.validate and not identifierValidator(identifier):
+					raise GlifLibError("identifier not formatted properly: %s" % identifier)
+			attrs["identifier"] = identifier
+			self.identifiers.add(identifier)
+		etree.SubElement(self.outline, "component", attrs)
+
+if __name__ == "__main__":
+	import doctest
+	doctest.testmod()
diff --git a/Lib/fontTools/ufoLib/kerning.py b/Lib/fontTools/ufoLib/kerning.py
new file mode 100644
index 0000000..947222a
--- /dev/null
+++ b/Lib/fontTools/ufoLib/kerning.py
@@ -0,0 +1,89 @@
+
+
+def lookupKerningValue(pair, kerning, groups, fallback=0, glyphToFirstGroup=None, glyphToSecondGroup=None):
+	"""
+	Note: This expects kerning to be a flat dictionary
+	of kerning pairs, not the nested structure used
+	in kerning.plist.
+
+	>>> groups = {
+	...     "public.kern1.O" : ["O", "D", "Q"],
+	...     "public.kern2.E" : ["E", "F"]
+	... }
+	>>> kerning = {
+	...     ("public.kern1.O", "public.kern2.E") : -100,
+	...     ("public.kern1.O", "F") : -200,
+	...     ("D", "F") : -300
+	... }
+	>>> lookupKerningValue(("D", "F"), kerning, groups)
+	-300
+	>>> lookupKerningValue(("O", "F"), kerning, groups)
+	-200
+	>>> lookupKerningValue(("O", "E"), kerning, groups)
+	-100
+	>>> lookupKerningValue(("O", "O"), kerning, groups)
+	0
+	>>> lookupKerningValue(("E", "E"), kerning, groups)
+	0
+	>>> lookupKerningValue(("E", "O"), kerning, groups)
+	0
+	>>> lookupKerningValue(("X", "X"), kerning, groups)
+	0
+	>>> lookupKerningValue(("public.kern1.O", "public.kern2.E"),
+	...     kerning, groups)
+	-100
+	>>> lookupKerningValue(("public.kern1.O", "F"), kerning, groups)
+	-200
+	>>> lookupKerningValue(("O", "public.kern2.E"), kerning, groups)
+	-100
+	>>> lookupKerningValue(("public.kern1.X", "public.kern2.X"), kerning, groups)
+	0
+	"""
+	# quickly check to see if the pair is in the kerning dictionary
+	if pair in kerning:
+		return kerning[pair]
+	# create glyph to group mapping
+	if glyphToFirstGroup is not None:
+		assert glyphToSecondGroup is not None
+	if glyphToSecondGroup is not None:
+		assert glyphToFirstGroup is not None
+	if glyphToFirstGroup is None:
+		glyphToFirstGroup = {}
+		glyphToSecondGroup = {}
+		for group, groupMembers in groups.items():
+			if group.startswith("public.kern1."):
+				for glyph in groupMembers:
+					glyphToFirstGroup[glyph] = group
+			elif group.startswith("public.kern2."):
+				for glyph in groupMembers:
+					glyphToSecondGroup[glyph] = group
+	# get group names and make sure first and second are glyph names
+	first, second = pair
+	firstGroup = secondGroup = None
+	if first.startswith("public.kern1."):
+		firstGroup = first
+		first = None
+	else:
+		firstGroup = glyphToFirstGroup.get(first)
+	if second.startswith("public.kern2."):
+		secondGroup = second
+		second = None
+	else:
+		secondGroup = glyphToSecondGroup.get(second)
+	# make an ordered list of pairs to look up
+	pairs = [
+		(first, second),
+		(first, secondGroup),
+		(firstGroup, second),
+		(firstGroup, secondGroup)
+	]
+	# look up the pairs and return any matches
+	for pair in pairs:
+		if pair in kerning:
+			return kerning[pair]
+	# use the fallback value
+	return fallback
+
+if __name__ == "__main__":
+	import doctest
+	doctest.testmod()
diff --git a/Lib/fontTools/ufoLib/plistlib.py b/Lib/fontTools/ufoLib/plistlib.py
new file mode 100644
index 0000000..7638168
--- /dev/null
+++ b/Lib/fontTools/ufoLib/plistlib.py
@@ -0,0 +1,46 @@
+"""DEPRECATED - This module is kept here only as a backward compatibility shim
+for the old ufoLib.plistlib module, which was moved to fontTools.misc.plistlib.
+Please use the latter instead.
+"""
+from fontTools.misc.plistlib import dump, dumps, load, loads
+from fontTools.misc.py23 import tobytes
+
+# The following functions were part of the old py2-like ufoLib.plistlib API.
+# They are kept only for backward compatiblity.
+from fontTools.ufoLib.utils import deprecated
+
+
+@deprecated("Use 'fontTools.misc.plistlib.load' instead")
+def readPlist(path_or_file):
+    did_open = False
+    if isinstance(path_or_file, str):
+        path_or_file = open(path_or_file, "rb")
+        did_open = True
+    try:
+        return load(path_or_file, use_builtin_types=False)
+    finally:
+        if did_open:
+            path_or_file.close()
+
+
+@deprecated("Use 'fontTools.misc.plistlib.dump' instead")
+def writePlist(value, path_or_file):
+    did_open = False
+    if isinstance(path_or_file, str):
+        path_or_file = open(path_or_file, "wb")
+        did_open = True
+    try:
+        dump(value, path_or_file, use_builtin_types=False)
+    finally:
+        if did_open:
+            path_or_file.close()
+
+
+@deprecated("Use 'fontTools.misc.plistlib.loads' instead")
+def readPlistFromString(data):
+    return loads(tobytes(data, encoding="utf-8"), use_builtin_types=False)
+
+
+@deprecated("Use 'fontTools.misc.plistlib.dumps' instead")
+def writePlistToString(value):
+    return dumps(value, use_builtin_types=False)
diff --git a/Lib/fontTools/ufoLib/pointPen.py b/Lib/fontTools/ufoLib/pointPen.py
new file mode 100644
index 0000000..3433fdb
--- /dev/null
+++ b/Lib/fontTools/ufoLib/pointPen.py
@@ -0,0 +1,5 @@
+"""DEPRECATED - This module is kept here only as a backward compatibility shim
+for the old ufoLib.pointPen module, which was moved to fontTools.pens.pointPen.
+Please use the latter instead.
+"""
+from fontTools.pens.pointPen import *
diff --git a/Lib/fontTools/ufoLib/utils.py b/Lib/fontTools/ufoLib/utils.py
new file mode 100644
index 0000000..85878b4
--- /dev/null
+++ b/Lib/fontTools/ufoLib/utils.py
@@ -0,0 +1,75 @@
+"""The module contains miscellaneous helpers.
+It's not considered part of the public ufoLib API.
+"""
+import warnings
+import functools
+
+
+numberTypes = (int, float)
+
+
+def deprecated(msg=""):
+    """Decorator factory to mark functions as deprecated with given message.
+
+    >>> @deprecated("Enough!")
+    ... def some_function():
+    ...    "I just print 'hello world'."
+    ...    print("hello world")
+    >>> some_function()
+    hello world
+    >>> some_function.__doc__ == "I just print 'hello world'."
+    True
+    """
+
+    def deprecated_decorator(func):
+        @functools.wraps(func)
+        def wrapper(*args, **kwargs):
+            warnings.warn(
+                f"{func.__name__} function is a deprecated. {msg}",
+                category=DeprecationWarning,
+                stacklevel=2,
+            )
+            return func(*args, **kwargs)
+
+        return wrapper
+
+    return deprecated_decorator
+
+
+# To be mixed with enum.Enum in UFOFormatVersion and GLIFFormatVersion
+class _VersionTupleEnumMixin:
+    @property
+    def major(self):
+        return self.value[0]
+
+    @property
+    def minor(self):
+        return self.value[1]
+
+    @classmethod
+    def _missing_(cls, value):
+        # allow to initialize a version enum from a single (major) integer
+        if isinstance(value, int):
+            return cls((value, 0))
+        # or from None to obtain the current default version
+        if value is None:
+            return cls.default()
+        return super()._missing_(value)
+
+    def __str__(self):
+        return f"{self.major}.{self.minor}"
+
+    @classmethod
+    def default(cls):
+        # get the latest defined version (i.e. the max of all versions)
+        return max(cls.__members__.values())
+
+    @classmethod
+    def supported_versions(cls):
+        return frozenset(cls.__members__.values())
+
+
+if __name__ == "__main__":
+    import doctest
+
+    doctest.testmod()
diff --git a/Lib/fontTools/ufoLib/validators.py b/Lib/fontTools/ufoLib/validators.py
new file mode 100644
index 0000000..49cb0e4
--- /dev/null
+++ b/Lib/fontTools/ufoLib/validators.py
@@ -0,0 +1,1060 @@
+"""Various low level data validators."""
+
+import calendar
+from io import open
+import fs.base
+import fs.osfs
+
+from collections.abc import Mapping
+from fontTools.ufoLib.utils import numberTypes
+
+
+# -------
+# Generic
+# -------
+
+def isDictEnough(value):
+    """
+    Some objects will likely come in that aren't
+    dicts but are dict-ish enough.
+    """
+    if isinstance(value, Mapping):
+        return True
+    for attr in ("keys", "values", "items"):
+        if not hasattr(value, attr):
+            return False
+    return True
+
+def genericTypeValidator(value, typ):
+	"""
+	Generic. (Added at version 2.)
+	"""
+	return isinstance(value, typ)
+
+def genericIntListValidator(values, validValues):
+	"""
+	Generic. (Added at version 2.)
+	"""
+	if not isinstance(values, (list, tuple)):
+		return False
+	valuesSet = set(values)
+	validValuesSet = set(validValues)
+	if valuesSet - validValuesSet:
+		return False
+	for value in values:
+		if not isinstance(value, int):
+			return False
+	return True
+
+def genericNonNegativeIntValidator(value):
+	"""
+	Generic. (Added at version 3.)
+	"""
+	if not isinstance(value, int):
+		return False
+	if value < 0:
+		return False
+	return True
+
+def genericNonNegativeNumberValidator(value):
+	"""
+	Generic. (Added at version 3.)
+	"""
+	if not isinstance(value, numberTypes):
+		return False
+	if value < 0:
+		return False
+	return True
+
+def genericDictValidator(value, prototype):
+	"""
+	Generic. (Added at version 3.)
+	"""
+	# not a dict
+	if not isinstance(value, Mapping):
+		return False
+	# missing required keys
+	for key, (typ, required) in prototype.items():
+		if not required:
+			continue
+		if key not in value:
+			return False
+	# unknown keys
+	for key in value.keys():
+		if key not in prototype:
+			return False
+	# incorrect types
+	for key, v in value.items():
+		prototypeType, required = prototype[key]
+		if v is None and not required:
+			continue
+		if not isinstance(v, prototypeType):
+			return False
+	return True
+
+# --------------
+# fontinfo.plist
+# --------------
+
+# Data Validators
+
+def fontInfoStyleMapStyleNameValidator(value):
+	"""
+	Version 2+.
+	"""
+	options = ["regular", "italic", "bold", "bold italic"]
+	return value in options
+
+def fontInfoOpenTypeGaspRangeRecordsValidator(value):
+	"""
+	Version 3+.
+	"""
+	if not isinstance(value, list):
+		return False
+	if len(value) == 0:
+		return True
+	validBehaviors = [0, 1, 2, 3]
+	dictPrototype = dict(rangeMaxPPEM=(int, True), rangeGaspBehavior=(list, True))
+	ppemOrder = []
+	for rangeRecord in value:
+		if not genericDictValidator(rangeRecord, dictPrototype):
+			return False
+		ppem = rangeRecord["rangeMaxPPEM"]
+		behavior = rangeRecord["rangeGaspBehavior"]
+		ppemValidity = genericNonNegativeIntValidator(ppem)
+		if not ppemValidity:
+			return False
+		behaviorValidity = genericIntListValidator(behavior, validBehaviors)
+		if not behaviorValidity:
+			return False
+		ppemOrder.append(ppem)
+	if ppemOrder != sorted(ppemOrder):
+		return False
+	return True
+
+def fontInfoOpenTypeHeadCreatedValidator(value):
+	"""
+	Version 2+.
+	"""
+	# format: 0000/00/00 00:00:00
+	if not isinstance(value, str):
+		return False
+	# basic formatting
+	if not len(value) == 19:
+		return False
+	if value.count(" ") != 1:
+		return False
+	date, time = value.split(" ")
+	if date.count("/") != 2:
+		return False
+	if time.count(":") != 2:
+		return False
+	# date
+	year, month, day = date.split("/")
+	if len(year) != 4:
+		return False
+	if len(month) != 2:
+		return False
+	if len(day) != 2:
+		return False
+	try:
+		year = int(year)
+		month = int(month)
+		day = int(day)
+	except ValueError:
+		return False
+	if month < 1 or month > 12:
+		return False
+	monthMaxDay = calendar.monthrange(year, month)[1]
+	if day < 1 or day > monthMaxDay:
+		return False
+	# time
+	hour, minute, second = time.split(":")
+	if len(hour) != 2:
+		return False
+	if len(minute) != 2:
+		return False
+	if len(second) != 2:
+		return False
+	try:
+		hour = int(hour)
+		minute = int(minute)
+		second = int(second)
+	except ValueError:
+		return False
+	if hour < 0 or hour > 23:
+		return False
+	if minute < 0 or minute > 59:
+		return False
+	if second < 0 or second > 59:
+		return False
+	# fallback
+	return True
+
+def fontInfoOpenTypeNameRecordsValidator(value):
+	"""
+	Version 3+.
+	"""
+	if not isinstance(value, list):
+		return False
+	dictPrototype = dict(nameID=(int, True), platformID=(int, True), encodingID=(int, True), languageID=(int, True), string=(str, True))
+	for nameRecord in value:
+		if not genericDictValidator(nameRecord, dictPrototype):
+			return False
+	return True
+
+def fontInfoOpenTypeOS2WeightClassValidator(value):
+	"""
+	Version 2+.
+	"""
+	if not isinstance(value, int):
+		return False
+	if value < 0:
+		return False
+	return True
+
+def fontInfoOpenTypeOS2WidthClassValidator(value):
+	"""
+	Version 2+.
+	"""
+	if not isinstance(value, int):
+		return False
+	if value < 1:
+		return False
+	if value > 9:
+		return False
+	return True
+
+def fontInfoVersion2OpenTypeOS2PanoseValidator(values):
+	"""
+	Version 2.
+	"""
+	if not isinstance(values, (list, tuple)):
+		return False
+	if len(values) != 10:
+		return False
+	for value in values:
+		if not isinstance(value, int):
+			return False
+	# XXX further validation?
+	return True
+
+def fontInfoVersion3OpenTypeOS2PanoseValidator(values):
+	"""
+	Version 3+.
+	"""
+	if not isinstance(values, (list, tuple)):
+		return False
+	if len(values) != 10:
+		return False
+	for value in values:
+		if not isinstance(value, int):
+			return False
+		if value < 0:
+			return False
+	# XXX further validation?
+	return True
+
+def fontInfoOpenTypeOS2FamilyClassValidator(values):
+	"""
+	Version 2+.
+	"""
+	if not isinstance(values, (list, tuple)):
+		return False
+	if len(values) != 2:
+		return False
+	for value in values:
+		if not isinstance(value, int):
+			return False
+	classID, subclassID = values
+	if classID < 0 or classID > 14:
+		return False
+	if subclassID < 0 or subclassID > 15:
+		return False
+	return True
+
+def fontInfoPostscriptBluesValidator(values):
+	"""
+	Version 2+.
+	"""
+	if not isinstance(values, (list, tuple)):
+		return False
+	if len(values) > 14:
+		return False
+	if len(values) % 2:
+		return False
+	for value in values:
+		if not isinstance(value, numberTypes):
+			return False
+	return True
+
+def fontInfoPostscriptOtherBluesValidator(values):
+	"""
+	Version 2+.
+	"""
+	if not isinstance(values, (list, tuple)):
+		return False
+	if len(values) > 10:
+		return False
+	if len(values) % 2:
+		return False
+	for value in values:
+		if not isinstance(value, numberTypes):
+			return False
+	return True
+
+def fontInfoPostscriptStemsValidator(values):
+	"""
+	Version 2+.
+	"""
+	if not isinstance(values, (list, tuple)):
+		return False
+	if len(values) > 12:
+		return False
+	for value in values:
+		if not isinstance(value, numberTypes):
+			return False
+	return True
+
+def fontInfoPostscriptWindowsCharacterSetValidator(value):
+	"""
+	Version 2+.
+	"""
+	validValues = list(range(1, 21))
+	if value not in validValues:
+		return False
+	return True
+
+def fontInfoWOFFMetadataUniqueIDValidator(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = dict(id=(str, True))
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	return True
+
+def fontInfoWOFFMetadataVendorValidator(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = {"name" : (str, True), "url" : (str, False), "dir" : (str, False), "class" : (str, False)}
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	if "dir" in value and value.get("dir") not in ("ltr", "rtl"):
+		return False
+	return True
+
+def fontInfoWOFFMetadataCreditsValidator(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = dict(credits=(list, True))
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	if not len(value["credits"]):
+		return False
+	dictPrototype = {"name" : (str, True), "url" : (str, False), "role" : (str, False), "dir" : (str, False), "class" : (str, False)}
+	for credit in value["credits"]:
+		if not genericDictValidator(credit, dictPrototype):
+			return False
+		if "dir" in credit and credit.get("dir") not in ("ltr", "rtl"):
+			return False
+	return True
+
+def fontInfoWOFFMetadataDescriptionValidator(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = dict(url=(str, False), text=(list, True))
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	for text in value["text"]:
+		if not fontInfoWOFFMetadataTextValue(text):
+			return False
+	return True
+
+def fontInfoWOFFMetadataLicenseValidator(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = dict(url=(str, False), text=(list, False), id=(str, False))
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	if "text" in value:
+		for text in value["text"]:
+			if not fontInfoWOFFMetadataTextValue(text):
+				return False
+	return True
+
+def fontInfoWOFFMetadataTrademarkValidator(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = dict(text=(list, True))
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	for text in value["text"]:
+		if not fontInfoWOFFMetadataTextValue(text):
+			return False
+	return True
+
+def fontInfoWOFFMetadataCopyrightValidator(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = dict(text=(list, True))
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	for text in value["text"]:
+		if not fontInfoWOFFMetadataTextValue(text):
+			return False
+	return True
+
+def fontInfoWOFFMetadataLicenseeValidator(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = {"name" : (str, True), "dir" : (str, False), "class" : (str, False)}
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	if "dir" in value and value.get("dir") not in ("ltr", "rtl"):
+		return False
+	return True
+
+def fontInfoWOFFMetadataTextValue(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = {"text" : (str, True), "language" : (str, False), "dir" : (str, False), "class" : (str, False)}
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	if "dir" in value and value.get("dir") not in ("ltr", "rtl"):
+		return False
+	return True
+
+def fontInfoWOFFMetadataExtensionsValidator(value):
+	"""
+	Version 3+.
+	"""
+	if not isinstance(value, list):
+		return False
+	if not value:
+		return False
+	for extension in value:
+		if not fontInfoWOFFMetadataExtensionValidator(extension):
+			return False
+	return True
+
+def fontInfoWOFFMetadataExtensionValidator(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = dict(names=(list, False), items=(list, True), id=(str, False))
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	if "names" in value:
+		for name in value["names"]:
+			if not fontInfoWOFFMetadataExtensionNameValidator(name):
+				return False
+	for item in value["items"]:
+		if not fontInfoWOFFMetadataExtensionItemValidator(item):
+			return False
+	return True
+
+def fontInfoWOFFMetadataExtensionItemValidator(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = dict(id=(str, False), names=(list, True), values=(list, True))
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	for name in value["names"]:
+		if not fontInfoWOFFMetadataExtensionNameValidator(name):
+			return False
+	for val in value["values"]:
+		if not fontInfoWOFFMetadataExtensionValueValidator(val):
+			return False
+	return True
+
+def fontInfoWOFFMetadataExtensionNameValidator(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = {"text" : (str, True), "language" : (str, False), "dir" : (str, False), "class" : (str, False)}
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	if "dir" in value and value.get("dir") not in ("ltr", "rtl"):
+		return False
+	return True
+
+def fontInfoWOFFMetadataExtensionValueValidator(value):
+	"""
+	Version 3+.
+	"""
+	dictPrototype = {"text" : (str, True), "language" : (str, False), "dir" : (str, False), "class" : (str, False)}
+	if not genericDictValidator(value, dictPrototype):
+		return False
+	if "dir" in value and value.get("dir") not in ("ltr", "rtl"):
+		return False
+	return True
+
+# ----------
+# Guidelines
+# ----------
+
+def guidelinesValidator(value, identifiers=None):
+	"""
+	Version 3+.
+	"""
+	if not isinstance(value, list):
+		return False
+	if identifiers is None:
+		identifiers = set()
+	for guide in value:
+		if not guidelineValidator(guide):
+			return False
+		identifier = guide.get("identifier")
+		if identifier is not None:
+			if identifier in identifiers:
+				return False
+			identifiers.add(identifier)
+	return True
+
+_guidelineDictPrototype = dict(
+	x=((int, float), False), y=((int, float), False), angle=((int, float), False),
+	name=(str, False), color=(str, False), identifier=(str, False)
+)
+
+def guidelineValidator(value):
+	"""
+	Version 3+.
+	"""
+	if not genericDictValidator(value, _guidelineDictPrototype):
+		return False
+	x = value.get("x")
+	y = value.get("y")
+	angle = value.get("angle")
+	# x or y must be present
+	if x is None and y is None:
+		return False
+	# if x or y are None, angle must not be present
+	if x is None or y is None:
+		if angle is not None:
+			return False
+	# if x and y are defined, angle must be defined
+	if x is not None and y is not None and angle is None:
+		return False
+	# angle must be between 0 and 360
+	if angle is not None:
+		if angle < 0:
+			return False
+		if angle > 360:
+			return False
+	# identifier must be 1 or more characters
+	identifier = value.get("identifier")
+	if identifier is not None and not identifierValidator(identifier):
+		return False
+	# color must follow the proper format
+	color = value.get("color")
+	if color is not None and not colorValidator(color):
+		return False
+	return True
+
+# -------
+# Anchors
+# -------
+
+def anchorsValidator(value, identifiers=None):
+	"""
+	Version 3+.
+	"""
+	if not isinstance(value, list):
+		return False
+	if identifiers is None:
+		identifiers = set()
+	for anchor in value:
+		if not anchorValidator(anchor):
+			return False
+		identifier = anchor.get("identifier")
+		if identifier is not None:
+			if identifier in identifiers:
+				return False
+			identifiers.add(identifier)
+	return True
+
+_anchorDictPrototype = dict(
+	x=((int, float), False), y=((int, float), False),
+	name=(str, False), color=(str, False),
+	identifier=(str, False)
+)
+
+def anchorValidator(value):
+	"""
+	Version 3+.
+	"""
+	if not genericDictValidator(value, _anchorDictPrototype):
+		return False
+	x = value.get("x")
+	y = value.get("y")
+	# x and y must be present
+	if x is None or y is None:
+		return False
+	# identifier must be 1 or more characters
+	identifier = value.get("identifier")
+	if identifier is not None and not identifierValidator(identifier):
+		return False
+	# color must follow the proper format
+	color = value.get("color")
+	if color is not None and not colorValidator(color):
+		return False
+	return True
+
+# ----------
+# Identifier
+# ----------
+
+def identifierValidator(value):
+	"""
+	Version 3+.
+
+	>>> identifierValidator("a")
+	True
+	>>> identifierValidator("")
+	False
+	>>> identifierValidator("a" * 101)
+	False
+	"""
+	validCharactersMin = 0x20
+	validCharactersMax = 0x7E
+	if not isinstance(value, str):
+		return False
+	if not value:
+		return False
+	if len(value) > 100:
+		return False
+	for c in value:
+		c = ord(c)
+		if c < validCharactersMin or c > validCharactersMax:
+			return False
+	return True
+
+# -----
+# Color
+# -----
+
+def colorValidator(value):
+	"""
+	Version 3+.
+
+	>>> colorValidator("0,0,0,0")
+	True
+	>>> colorValidator(".5,.5,.5,.5")
+	True
+	>>> colorValidator("0.5,0.5,0.5,0.5")
+	True
+	>>> colorValidator("1,1,1,1")
+	True
+
+	>>> colorValidator("2,0,0,0")
+	False
+	>>> colorValidator("0,2,0,0")
+	False
+	>>> colorValidator("0,0,2,0")
+	False
+	>>> colorValidator("0,0,0,2")
+	False
+
+	>>> colorValidator("1r,1,1,1")
+	False
+	>>> colorValidator("1,1g,1,1")
+	False
+	>>> colorValidator("1,1,1b,1")
+	False
+	>>> colorValidator("1,1,1,1a")
+	False
+
+	>>> colorValidator("1 1 1 1")
+	False
+	>>> colorValidator("1 1,1,1")
+	False
+	>>> colorValidator("1,1 1,1")
+	False
+	>>> colorValidator("1,1,1 1")
+	False
+
+	>>> colorValidator("1, 1, 1, 1")
+	True
+	"""
+	if not isinstance(value, str):
+		return False
+	parts = value.split(",")
+	if len(parts) != 4:
+		return False
+	for part in parts:
+		part = part.strip()
+		converted = False
+		try:
+			part = int(part)
+			converted = True
+		except ValueError:
+			pass
+		if not converted:
+			try:
+				part = float(part)
+				converted = True
+			except ValueError:
+				pass
+		if not converted:
+			return False
+		if part < 0:
+			return False
+		if part > 1:
+			return False
+	return True
+
+# -----
+# image
+# -----
+
+pngSignature = b"\x89PNG\r\n\x1a\n"
+
+_imageDictPrototype = dict(
+	fileName=(str, True),
+	xScale=((int, float), False), xyScale=((int, float), False),
+	yxScale=((int, float), False), yScale=((int, float), False),
+	xOffset=((int, float), False), yOffset=((int, float), False),
+	color=(str, False)
+)
+
+def imageValidator(value):
+	"""
+	Version 3+.
+	"""
+	if not genericDictValidator(value, _imageDictPrototype):
+		return False
+	# fileName must be one or more characters
+	if not value["fileName"]:
+		return False
+	# color must follow the proper format
+	color = value.get("color")
+	if color is not None and not colorValidator(color):
+		return False
+	return True
+
+def pngValidator(path=None, data=None, fileObj=None):
+	"""
+	Version 3+.
+
+	This checks the signature of the image data.
+	"""
+	assert path is not None or data is not None or fileObj is not None
+	if path is not None:
+		with open(path, "rb") as f:
+			signature = f.read(8)
+	elif data is not None:
+		signature = data[:8]
+	elif fileObj is not None:
+		pos = fileObj.tell()
+		signature = fileObj.read(8)
+		fileObj.seek(pos)
+	if signature != pngSignature:
+		return False, "Image does not begin with the PNG signature."
+	return True, None
+
+# -------------------
+# layercontents.plist
+# -------------------
+
+def layerContentsValidator(value, ufoPathOrFileSystem):
+	"""
+	Check the validity of layercontents.plist.
+	Version 3+.
+	"""
+	if isinstance(ufoPathOrFileSystem, fs.base.FS):
+		fileSystem = ufoPathOrFileSystem
+	else:
+		fileSystem = fs.osfs.OSFS(ufoPathOrFileSystem)
+
+	bogusFileMessage = "layercontents.plist in not in the correct format."
+	# file isn't in the right format
+	if not isinstance(value, list):
+		return False, bogusFileMessage
+	# work through each entry
+	usedLayerNames = set()
+	usedDirectories = set()
+	contents = {}
+	for entry in value:
+		# layer entry in the incorrect format
+		if not isinstance(entry, list):
+			return False, bogusFileMessage
+		if not len(entry) == 2:
+			return False, bogusFileMessage
+		for i in entry:
+			if not isinstance(i, str):
+				return False, bogusFileMessage
+		layerName, directoryName = entry
+		# check directory naming
+		if directoryName != "glyphs":
+			if not directoryName.startswith("glyphs."):
+				return False, "Invalid directory name (%s) in layercontents.plist." % directoryName
+		if len(layerName) == 0:
+			return False, "Empty layer name in layercontents.plist."
+		# directory doesn't exist
+		if not fileSystem.exists(directoryName):
+			return False, "A glyphset does not exist at %s." % directoryName
+		# default layer name
+		if layerName == "public.default" and directoryName != "glyphs":
+			return False, "The name public.default is being used by a layer that is not the default."
+		# check usage
+		if layerName in usedLayerNames:
+			return False, "The layer name %s is used by more than one layer." % layerName
+		usedLayerNames.add(layerName)
+		if directoryName in usedDirectories:
+			return False, "The directory %s is used by more than one layer." % directoryName
+		usedDirectories.add(directoryName)
+		# store
+		contents[layerName] = directoryName
+	# missing default layer
+	foundDefault = "glyphs" in contents.values()
+	if not foundDefault:
+		return False, "The required default glyph set is not in the UFO."
+	return True, None
+
+# ------------
+# groups.plist
+# ------------
+
+def groupsValidator(value):
+	"""
+	Check the validity of the groups.
+	Version 3+ (though it's backwards compatible with UFO 1 and UFO 2).
+
+	>>> groups = {"A" : ["A", "A"], "A2" : ["A"]}
+	>>> groupsValidator(groups)
+	(True, None)
+
+	>>> groups = {"" : ["A"]}
+	>>> valid, msg = groupsValidator(groups)
+	>>> valid
+	False
+	>>> print(msg)
+	A group has an empty name.
+
+	>>> groups = {"public.awesome" : ["A"]}
+	>>> groupsValidator(groups)
+	(True, None)
+
+	>>> groups = {"public.kern1." : ["A"]}
+	>>> valid, msg = groupsValidator(groups)
+	>>> valid
+	False
+	>>> print(msg)
+	The group data contains a kerning group with an incomplete name.
+	>>> groups = {"public.kern2." : ["A"]}
+	>>> valid, msg = groupsValidator(groups)
+	>>> valid
+	False
+	>>> print(msg)
+	The group data contains a kerning group with an incomplete name.
+
+	>>> groups = {"public.kern1.A" : ["A"], "public.kern2.A" : ["A"]}
+	>>> groupsValidator(groups)
+	(True, None)
+
+	>>> groups = {"public.kern1.A1" : ["A"], "public.kern1.A2" : ["A"]}
+	>>> valid, msg = groupsValidator(groups)
+	>>> valid
+	False
+	>>> print(msg)
+	The glyph "A" occurs in too many kerning groups.
+	"""
+	bogusFormatMessage = "The group data is not in the correct format."
+	if not isDictEnough(value):
+		return False, bogusFormatMessage
+	firstSideMapping = {}
+	secondSideMapping = {}
+	for groupName, glyphList in value.items():
+		if not isinstance(groupName, (str)):
+			return False, bogusFormatMessage
+		if not isinstance(glyphList, (list, tuple)):
+			return False, bogusFormatMessage
+		if not groupName:
+			return False, "A group has an empty name."
+		if groupName.startswith("public."):
+			if not groupName.startswith("public.kern1.") and not groupName.startswith("public.kern2."):
+				# unknown public.* name. silently skip.
+				continue
+			else:
+				if len("public.kernN.") == len(groupName):
+					return False, "The group data contains a kerning group with an incomplete name."
+			if groupName.startswith("public.kern1."):
+				d = firstSideMapping
+			else:
+				d = secondSideMapping
+			for glyphName in glyphList:
+				if not isinstance(glyphName, str):
+					return False, "The group data %s contains an invalid member." % groupName
+				if glyphName in d:
+					return False, "The glyph \"%s\" occurs in too many kerning groups." % glyphName
+				d[glyphName] = groupName
+	return True, None
+
+# -------------
+# kerning.plist
+# -------------
+
+def kerningValidator(data):
+	"""
+	Check the validity of the kerning data structure.
+	Version 3+ (though it's backwards compatible with UFO 1 and UFO 2).
+
+	>>> kerning = {"A" : {"B" : 100}}
+	>>> kerningValidator(kerning)
+	(True, None)
+
+	>>> kerning = {"A" : ["B"]}
+	>>> valid, msg = kerningValidator(kerning)
+	>>> valid
+	False
+	>>> print(msg)
+	The kerning data is not in the correct format.
+
+	>>> kerning = {"A" : {"B" : "100"}}
+	>>> valid, msg = kerningValidator(kerning)
+	>>> valid
+	False
+	>>> print(msg)
+	The kerning data is not in the correct format.
+	"""
+	bogusFormatMessage = "The kerning data is not in the correct format."
+	if not isinstance(data, Mapping):
+		return False, bogusFormatMessage
+	for first, secondDict in data.items():
+		if not isinstance(first, str):
+			return False, bogusFormatMessage
+		elif not isinstance(secondDict, Mapping):
+			return False, bogusFormatMessage
+		for second, value in secondDict.items():
+			if not isinstance(second, str):
+				return False, bogusFormatMessage
+			elif not isinstance(value, numberTypes):
+				return False, bogusFormatMessage
+	return True, None
+
+# -------------
+# lib.plist/lib
+# -------------
+
+_bogusLibFormatMessage = "The lib data is not in the correct format: %s"
+
+def fontLibValidator(value):
+	"""
+	Check the validity of the lib.
+	Version 3+ (though it's backwards compatible with UFO 1 and UFO 2).
+
+	>>> lib = {"foo" : "bar"}
+	>>> fontLibValidator(lib)
+	(True, None)
+
+	>>> lib = {"public.awesome" : "hello"}
+	>>> fontLibValidator(lib)
+	(True, None)
+
+	>>> lib = {"public.glyphOrder" : ["A", "C", "B"]}
+	>>> fontLibValidator(lib)
+	(True, None)
+
+	>>> lib = "hello"
+	>>> valid, msg = fontLibValidator(lib)
+	>>> valid
+	False
+	>>> print(msg)  # doctest: +ELLIPSIS
+	The lib data is not in the correct format: expected a dictionary, ...
+
+	>>> lib = {1: "hello"}
+	>>> valid, msg = fontLibValidator(lib)
+	>>> valid
+	False
+	>>> print(msg)
+	The lib key is not properly formatted: expected str, found int: 1
+
+	>>> lib = {"public.glyphOrder" : "hello"}
+	>>> valid, msg = fontLibValidator(lib)
+	>>> valid
+	False
+	>>> print(msg)  # doctest: +ELLIPSIS
+	public.glyphOrder is not properly formatted: expected list or tuple,...
+
+	>>> lib = {"public.glyphOrder" : ["A", 1, "B"]}
+	>>> valid, msg = fontLibValidator(lib)
+	>>> valid
+	False
+	>>> print(msg)  # doctest: +ELLIPSIS
+	public.glyphOrder is not properly formatted: expected str,...
+	"""
+	if not isDictEnough(value):
+		reason = "expected a dictionary, found %s" % type(value).__name__
+		return False, _bogusLibFormatMessage % reason
+	for key, value in value.items():
+		if not isinstance(key, str):
+			return False, (
+				"The lib key is not properly formatted: expected str, found %s: %r" %
+				(type(key).__name__, key))
+		# public.glyphOrder
+		if key == "public.glyphOrder":
+			bogusGlyphOrderMessage = "public.glyphOrder is not properly formatted: %s"
+			if not isinstance(value, (list, tuple)):
+				reason = "expected list or tuple, found %s" % type(value).__name__
+				return False, bogusGlyphOrderMessage % reason
+			for glyphName in value:
+				if not isinstance(glyphName, str):
+					reason = "expected str, found %s" % type(glyphName).__name__
+					return False, bogusGlyphOrderMessage % reason
+	return True, None
+
+# --------
+# GLIF lib
+# --------
+
+def glyphLibValidator(value):
+	"""
+	Check the validity of the lib.
+	Version 3+ (though it's backwards compatible with UFO 1 and UFO 2).
+
+	>>> lib = {"foo" : "bar"}
+	>>> glyphLibValidator(lib)
+	(True, None)
+
+	>>> lib = {"public.awesome" : "hello"}
+	>>> glyphLibValidator(lib)
+	(True, None)
+
+	>>> lib = {"public.markColor" : "1,0,0,0.5"}
+	>>> glyphLibValidator(lib)
+	(True, None)
+
+	>>> lib = {"public.markColor" : 1}
+	>>> valid, msg = glyphLibValidator(lib)
+	>>> valid
+	False
+	>>> print(msg)
+	public.markColor is not properly formatted.
+	"""
+	if not isDictEnough(value):
+		reason = "expected a dictionary, found %s" % type(value).__name__
+		return False, _bogusLibFormatMessage % reason
+	for key, value in value.items():
+		if not isinstance(key, str):
+			reason = "key (%s) should be a string" % key
+			return False, _bogusLibFormatMessage % reason
+		# public.markColor
+		if key == "public.markColor":
+			if not colorValidator(value):
+				return False, "public.markColor is not properly formatted."
+	return True, None
+
+
+if __name__ == "__main__":
+	import doctest
+	doctest.testmod()
diff --git a/Lib/fontTools/unicode.py b/Lib/fontTools/unicode.py
index 50dfc53..e0867aa 100644
--- a/Lib/fontTools/unicode.py
+++ b/Lib/fontTools/unicode.py
@@ -1,8 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-
 def _makeunicodes(f):
-	import re
 	lines = iter(f.readlines())
 	unicodes = {}
 	for line in lines:
@@ -17,9 +13,12 @@
 class _UnicodeCustom(object):
 
 	def __init__(self, f):
-		if isinstance(f, basestring):
-			f = open(f)
-		self.codes = _makeunicodes(f)
+		if isinstance(f, str):
+			with open(f) as fd:
+				codes = _makeunicodes(fd)
+		else:
+			codes = _makeunicodes(f)
+		self.codes = codes
 
 	def __getitem__(self, charCode):
 		try:
@@ -37,7 +36,7 @@
 		except ImportError: 
 			import unicodedata
 		try:
-			return unicodedata.name(unichr(charCode))
+			return unicodedata.name(chr(charCode))
 		except ValueError:
 			return "????"
 
diff --git a/Lib/fontTools/unicodedata/Blocks.py b/Lib/fontTools/unicodedata/Blocks.py
index cba4e9e..0755074 100644
--- a/Lib/fontTools/unicodedata/Blocks.py
+++ b/Lib/fontTools/unicodedata/Blocks.py
@@ -4,9 +4,9 @@
 # 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.
+# Blocks-13.0.0.txt
+# Date: 2019-07-10, 19:06:00 GMT [KW]
+# © 2019 Unicode®, Inc.
 # For terms of use, see http://www.unicode.org/terms_of_use.html
 #
 # Unicode Character Database
@@ -81,7 +81,7 @@
     0x1C00,  # .. 0x1C4F ; Lepcha
     0x1C50,  # .. 0x1C7F ; Ol Chiki
     0x1C80,  # .. 0x1C8F ; Cyrillic Extended-C
-    0x1C90,  # .. 0x1CBF ; No_Block
+    0x1C90,  # .. 0x1CBF ; Georgian Extended
     0x1CC0,  # .. 0x1CCF ; Sundanese Supplement
     0x1CD0,  # .. 0x1CFF ; Vedic Extensions
     0x1D00,  # .. 0x1D7F ; Phonetic Extensions
@@ -231,9 +231,16 @@
     0x10C00,  # .. 0x10C4F ; Old Turkic
     0x10C50,  # .. 0x10C7F ; No_Block
     0x10C80,  # .. 0x10CFF ; Old Hungarian
-    0x10D00,  # .. 0x10E5F ; No_Block
+    0x10D00,  # .. 0x10D3F ; Hanifi Rohingya
+    0x10D40,  # .. 0x10E5F ; No_Block
     0x10E60,  # .. 0x10E7F ; Rumi Numeral Symbols
-    0x10E80,  # .. 0x10FFF ; No_Block
+    0x10E80,  # .. 0x10EBF ; Yezidi
+    0x10EC0,  # .. 0x10EFF ; No_Block
+    0x10F00,  # .. 0x10F2F ; Old Sogdian
+    0x10F30,  # .. 0x10F6F ; Sogdian
+    0x10F70,  # .. 0x10FAF ; No_Block
+    0x10FB0,  # .. 0x10FDF ; Chorasmian
+    0x10FE0,  # .. 0x10FFF ; Elymaic
     0x11000,  # .. 0x1107F ; Brahmi
     0x11080,  # .. 0x110CF ; Kaithi
     0x110D0,  # .. 0x110FF ; Sora Sompeng
@@ -256,9 +263,13 @@
     0x11680,  # .. 0x116CF ; Takri
     0x116D0,  # .. 0x116FF ; No_Block
     0x11700,  # .. 0x1173F ; Ahom
-    0x11740,  # .. 0x1189F ; No_Block
+    0x11740,  # .. 0x117FF ; No_Block
+    0x11800,  # .. 0x1184F ; Dogra
+    0x11850,  # .. 0x1189F ; No_Block
     0x118A0,  # .. 0x118FF ; Warang Citi
-    0x11900,  # .. 0x119FF ; No_Block
+    0x11900,  # .. 0x1195F ; Dives Akuru
+    0x11960,  # .. 0x1199F ; No_Block
+    0x119A0,  # .. 0x119FF ; Nandinagari
     0x11A00,  # .. 0x11A4F ; Zanabazar Square
     0x11A50,  # .. 0x11AAF ; Soyombo
     0x11AB0,  # .. 0x11ABF ; No_Block
@@ -268,13 +279,19 @@
     0x11C70,  # .. 0x11CBF ; Marchen
     0x11CC0,  # .. 0x11CFF ; No_Block
     0x11D00,  # .. 0x11D5F ; Masaram Gondi
-    0x11D60,  # .. 0x11FFF ; No_Block
+    0x11D60,  # .. 0x11DAF ; Gunjala Gondi
+    0x11DB0,  # .. 0x11EDF ; No_Block
+    0x11EE0,  # .. 0x11EFF ; Makasar
+    0x11F00,  # .. 0x11FAF ; No_Block
+    0x11FB0,  # .. 0x11FBF ; Lisu Supplement
+    0x11FC0,  # .. 0x11FFF ; Tamil Supplement
     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
+    0x13430,  # .. 0x1343F ; Egyptian Hieroglyph Format Controls
+    0x13440,  # .. 0x143FF ; No_Block
     0x14400,  # .. 0x1467F ; Anatolian Hieroglyphs
     0x14680,  # .. 0x167FF ; No_Block
     0x16800,  # .. 0x16A3F ; Bamum Supplement
@@ -282,16 +299,20 @@
     0x16A70,  # .. 0x16ACF ; No_Block
     0x16AD0,  # .. 0x16AFF ; Bassa Vah
     0x16B00,  # .. 0x16B8F ; Pahawh Hmong
-    0x16B90,  # .. 0x16EFF ; No_Block
+    0x16B90,  # .. 0x16E3F ; No_Block
+    0x16E40,  # .. 0x16E9F ; Medefaidrin
+    0x16EA0,  # .. 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
+    0x18B00,  # .. 0x18CFF ; Khitan Small Script
+    0x18D00,  # .. 0x18D8F ; Tangut Supplement
+    0x18D90,  # .. 0x1AFFF ; No_Block
     0x1B000,  # .. 0x1B0FF ; Kana Supplement
     0x1B100,  # .. 0x1B12F ; Kana Extended-A
-    0x1B130,  # .. 0x1B16F ; No_Block
+    0x1B130,  # .. 0x1B16F ; Small Kana Extension
     0x1B170,  # .. 0x1B2FF ; Nushu
     0x1B300,  # .. 0x1BBFF ; No_Block
     0x1BC00,  # .. 0x1BC9F ; Duployan
@@ -300,7 +321,8 @@
     0x1D000,  # .. 0x1D0FF ; Byzantine Musical Symbols
     0x1D100,  # .. 0x1D1FF ; Musical Symbols
     0x1D200,  # .. 0x1D24F ; Ancient Greek Musical Notation
-    0x1D250,  # .. 0x1D2FF ; No_Block
+    0x1D250,  # .. 0x1D2DF ; No_Block
+    0x1D2E0,  # .. 0x1D2FF ; Mayan Numerals
     0x1D300,  # .. 0x1D35F ; Tai Xuan Jing Symbols
     0x1D360,  # .. 0x1D37F ; Counting Rod Numerals
     0x1D380,  # .. 0x1D3FF ; No_Block
@@ -308,11 +330,19 @@
     0x1D800,  # .. 0x1DAAF ; Sutton SignWriting
     0x1DAB0,  # .. 0x1DFFF ; No_Block
     0x1E000,  # .. 0x1E02F ; Glagolitic Supplement
-    0x1E030,  # .. 0x1E7FF ; No_Block
+    0x1E030,  # .. 0x1E0FF ; No_Block
+    0x1E100,  # .. 0x1E14F ; Nyiakeng Puachue Hmong
+    0x1E150,  # .. 0x1E2BF ; No_Block
+    0x1E2C0,  # .. 0x1E2FF ; Wancho
+    0x1E300,  # .. 0x1E7FF ; No_Block
     0x1E800,  # .. 0x1E8DF ; Mende Kikakui
     0x1E8E0,  # .. 0x1E8FF ; No_Block
     0x1E900,  # .. 0x1E95F ; Adlam
-    0x1E960,  # .. 0x1EDFF ; No_Block
+    0x1E960,  # .. 0x1EC6F ; No_Block
+    0x1EC70,  # .. 0x1ECBF ; Indic Siyaq Numbers
+    0x1ECC0,  # .. 0x1ECFF ; No_Block
+    0x1ED00,  # .. 0x1ED4F ; Ottoman Siyaq Numbers
+    0x1ED50,  # .. 0x1EDFF ; No_Block
     0x1EE00,  # .. 0x1EEFF ; Arabic Mathematical Alphabetic Symbols
     0x1EF00,  # .. 0x1EFFF ; No_Block
     0x1F000,  # .. 0x1F02F ; Mahjong Tiles
@@ -328,7 +358,10 @@
     0x1F780,  # .. 0x1F7FF ; Geometric Shapes Extended
     0x1F800,  # .. 0x1F8FF ; Supplemental Arrows-C
     0x1F900,  # .. 0x1F9FF ; Supplemental Symbols and Pictographs
-    0x1FA00,  # .. 0x1FFFF ; No_Block
+    0x1FA00,  # .. 0x1FA6F ; Chess Symbols
+    0x1FA70,  # .. 0x1FAFF ; Symbols and Pictographs Extended-A
+    0x1FB00,  # .. 0x1FBFF ; Symbols for Legacy Computing
+    0x1FC00,  # .. 0x1FFFF ; No_Block
     0x20000,  # .. 0x2A6DF ; CJK Unified Ideographs Extension B
     0x2A6E0,  # .. 0x2A6FF ; No_Block
     0x2A700,  # .. 0x2B73F ; CJK Unified Ideographs Extension C
@@ -337,7 +370,9 @@
     0x2CEB0,  # .. 0x2EBEF ; CJK Unified Ideographs Extension F
     0x2EBF0,  # .. 0x2F7FF ; No_Block
     0x2F800,  # .. 0x2FA1F ; CJK Compatibility Ideographs Supplement
-    0x2FA20,  # .. 0xDFFFF ; No_Block
+    0x2FA20,  # .. 0x2FFFF ; No_Block
+    0x30000,  # .. 0x3134F ; CJK Unified Ideographs Extension G
+    0x31350,  # .. 0xDFFFF ; No_Block
     0xE0000,  # .. 0xE007F ; Tags
     0xE0080,  # .. 0xE00FF ; No_Block
     0xE0100,  # .. 0xE01EF ; Variation Selectors Supplement
@@ -411,7 +446,7 @@
     'Lepcha',                                          # 1C00..1C4F
     'Ol Chiki',                                        # 1C50..1C7F
     'Cyrillic Extended-C',                             # 1C80..1C8F
-    'No_Block',                                        # 1C90..1CBF
+    'Georgian Extended',                               # 1C90..1CBF
     'Sundanese Supplement',                            # 1CC0..1CCF
     'Vedic Extensions',                                # 1CD0..1CFF
     'Phonetic Extensions',                             # 1D00..1D7F
@@ -561,9 +596,16 @@
     'Old Turkic',                                      # 10C00..10C4F
     'No_Block',                                        # 10C50..10C7F
     'Old Hungarian',                                   # 10C80..10CFF
-    'No_Block',                                        # 10D00..10E5F
+    'Hanifi Rohingya',                                 # 10D00..10D3F
+    'No_Block',                                        # 10D40..10E5F
     'Rumi Numeral Symbols',                            # 10E60..10E7F
-    'No_Block',                                        # 10E80..10FFF
+    'Yezidi',                                          # 10E80..10EBF
+    'No_Block',                                        # 10EC0..10EFF
+    'Old Sogdian',                                     # 10F00..10F2F
+    'Sogdian',                                         # 10F30..10F6F
+    'No_Block',                                        # 10F70..10FAF
+    'Chorasmian',                                      # 10FB0..10FDF
+    'Elymaic',                                         # 10FE0..10FFF
     'Brahmi',                                          # 11000..1107F
     'Kaithi',                                          # 11080..110CF
     'Sora Sompeng',                                    # 110D0..110FF
@@ -586,9 +628,13 @@
     'Takri',                                           # 11680..116CF
     'No_Block',                                        # 116D0..116FF
     'Ahom',                                            # 11700..1173F
-    'No_Block',                                        # 11740..1189F
+    'No_Block',                                        # 11740..117FF
+    'Dogra',                                           # 11800..1184F
+    'No_Block',                                        # 11850..1189F
     'Warang Citi',                                     # 118A0..118FF
-    'No_Block',                                        # 11900..119FF
+    'Dives Akuru',                                     # 11900..1195F
+    'No_Block',                                        # 11960..1199F
+    'Nandinagari',                                     # 119A0..119FF
     'Zanabazar Square',                                # 11A00..11A4F
     'Soyombo',                                         # 11A50..11AAF
     'No_Block',                                        # 11AB0..11ABF
@@ -598,13 +644,19 @@
     'Marchen',                                         # 11C70..11CBF
     'No_Block',                                        # 11CC0..11CFF
     'Masaram Gondi',                                   # 11D00..11D5F
-    'No_Block',                                        # 11D60..11FFF
+    'Gunjala Gondi',                                   # 11D60..11DAF
+    'No_Block',                                        # 11DB0..11EDF
+    'Makasar',                                         # 11EE0..11EFF
+    'No_Block',                                        # 11F00..11FAF
+    'Lisu Supplement',                                 # 11FB0..11FBF
+    'Tamil Supplement',                                # 11FC0..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
+    'Egyptian Hieroglyph Format Controls',             # 13430..1343F
+    'No_Block',                                        # 13440..143FF
     'Anatolian Hieroglyphs',                           # 14400..1467F
     'No_Block',                                        # 14680..167FF
     'Bamum Supplement',                                # 16800..16A3F
@@ -612,16 +664,20 @@
     'No_Block',                                        # 16A70..16ACF
     'Bassa Vah',                                       # 16AD0..16AFF
     'Pahawh Hmong',                                    # 16B00..16B8F
-    'No_Block',                                        # 16B90..16EFF
+    'No_Block',                                        # 16B90..16E3F
+    'Medefaidrin',                                     # 16E40..16E9F
+    'No_Block',                                        # 16EA0..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
+    'Khitan Small Script',                             # 18B00..18CFF
+    'Tangut Supplement',                               # 18D00..18D8F
+    'No_Block',                                        # 18D90..1AFFF
     'Kana Supplement',                                 # 1B000..1B0FF
     'Kana Extended-A',                                 # 1B100..1B12F
-    'No_Block',                                        # 1B130..1B16F
+    'Small Kana Extension',                            # 1B130..1B16F
     'Nushu',                                           # 1B170..1B2FF
     'No_Block',                                        # 1B300..1BBFF
     'Duployan',                                        # 1BC00..1BC9F
@@ -630,7 +686,8 @@
     'Byzantine Musical Symbols',                       # 1D000..1D0FF
     'Musical Symbols',                                 # 1D100..1D1FF
     'Ancient Greek Musical Notation',                  # 1D200..1D24F
-    'No_Block',                                        # 1D250..1D2FF
+    'No_Block',                                        # 1D250..1D2DF
+    'Mayan Numerals',                                  # 1D2E0..1D2FF
     'Tai Xuan Jing Symbols',                           # 1D300..1D35F
     'Counting Rod Numerals',                           # 1D360..1D37F
     'No_Block',                                        # 1D380..1D3FF
@@ -638,11 +695,19 @@
     'Sutton SignWriting',                              # 1D800..1DAAF
     'No_Block',                                        # 1DAB0..1DFFF
     'Glagolitic Supplement',                           # 1E000..1E02F
-    'No_Block',                                        # 1E030..1E7FF
+    'No_Block',                                        # 1E030..1E0FF
+    'Nyiakeng Puachue Hmong',                          # 1E100..1E14F
+    'No_Block',                                        # 1E150..1E2BF
+    'Wancho',                                          # 1E2C0..1E2FF
+    'No_Block',                                        # 1E300..1E7FF
     'Mende Kikakui',                                   # 1E800..1E8DF
     'No_Block',                                        # 1E8E0..1E8FF
     'Adlam',                                           # 1E900..1E95F
-    'No_Block',                                        # 1E960..1EDFF
+    'No_Block',                                        # 1E960..1EC6F
+    'Indic Siyaq Numbers',                             # 1EC70..1ECBF
+    'No_Block',                                        # 1ECC0..1ECFF
+    'Ottoman Siyaq Numbers',                           # 1ED00..1ED4F
+    'No_Block',                                        # 1ED50..1EDFF
     'Arabic Mathematical Alphabetic Symbols',          # 1EE00..1EEFF
     'No_Block',                                        # 1EF00..1EFFF
     'Mahjong Tiles',                                   # 1F000..1F02F
@@ -658,7 +723,10 @@
     'Geometric Shapes Extended',                       # 1F780..1F7FF
     'Supplemental Arrows-C',                           # 1F800..1F8FF
     'Supplemental Symbols and Pictographs',            # 1F900..1F9FF
-    'No_Block',                                        # 1FA00..1FFFF
+    'Chess Symbols',                                   # 1FA00..1FA6F
+    'Symbols and Pictographs Extended-A',              # 1FA70..1FAFF
+    'Symbols for Legacy Computing',                    # 1FB00..1FBFF
+    'No_Block',                                        # 1FC00..1FFFF
     'CJK Unified Ideographs Extension B',              # 20000..2A6DF
     'No_Block',                                        # 2A6E0..2A6FF
     'CJK Unified Ideographs Extension C',              # 2A700..2B73F
@@ -667,7 +735,9 @@
     'CJK Unified Ideographs Extension F',              # 2CEB0..2EBEF
     'No_Block',                                        # 2EBF0..2F7FF
     'CJK Compatibility Ideographs Supplement',         # 2F800..2FA1F
-    'No_Block',                                        # 2FA20..DFFFF
+    'No_Block',                                        # 2FA20..2FFFF
+    'CJK Unified Ideographs Extension G',              # 30000..3134F
+    'No_Block',                                        # 31350..DFFFF
     'Tags',                                            # E0000..E007F
     'No_Block',                                        # E0080..E00FF
     'Variation Selectors Supplement',                  # E0100..E01EF
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
index a92cc80..b4e09cd 100644
--- a/Lib/fontTools/unicodedata/ScriptExtensions.py
+++ b/Lib/fontTools/unicodedata/ScriptExtensions.py
@@ -4,9 +4,9 @@
 # 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.
+# ScriptExtensions-13.0.0.txt
+# Date: 2020-01-22, 00:07:43 GMT
+# © 2020 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
 #
@@ -52,42 +52,41 @@
     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'}
+    0x0488,  # .. 0x060B ; None
+    0x060C,  # .. 0x060C ; {'Arab', 'Rohg', 'Syrc', 'Thaa', 'Yezi'}
     0x060D,  # .. 0x061A ; None
-    0x061B,  # .. 0x061C ; {'Arab', 'Syrc', 'Thaa'}
+    0x061B,  # .. 0x061B ; {'Arab', 'Rohg', 'Syrc', 'Thaa', 'Yezi'}
+    0x061C,  # .. 0x061C ; {'Arab', 'Syrc', 'Thaa'}
     0x061D,  # .. 0x061E ; None
-    0x061F,  # .. 0x061F ; {'Arab', 'Syrc', 'Thaa'}
+    0x061F,  # .. 0x061F ; {'Arab', 'Rohg', 'Syrc', 'Thaa', 'Yezi'}
     0x0620,  # .. 0x063F ; None
-    0x0640,  # .. 0x0640 ; {'Adlm', 'Arab', 'Mand', 'Mani', 'Phlp', 'Syrc'}
+    0x0640,  # .. 0x0640 ; {'Adlm', 'Arab', 'Mand', 'Mani', 'Phlp', 'Rohg', 'Sogd', 'Syrc'}
     0x0641,  # .. 0x064A ; None
     0x064B,  # .. 0x0655 ; {'Arab', 'Syrc'}
     0x0656,  # .. 0x065F ; None
-    0x0660,  # .. 0x0669 ; {'Arab', 'Thaa'}
+    0x0660,  # .. 0x0669 ; {'Arab', 'Thaa', 'Yezi'}
     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'}
+    0x0671,  # .. 0x06D3 ; None
+    0x06D4,  # .. 0x06D4 ; {'Arab', 'Rohg'}
+    0x06D5,  # .. 0x0950 ; None
+    0x0951,  # .. 0x0951 ; {'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Latn', 'Mlym', 'Orya', 'Shrd', 'Taml', 'Telu', 'Tirh'}
+    0x0952,  # .. 0x0952 ; {'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Latn', 'Mlym', 'Orya', 'Taml', 'Telu', 'Tirh'}
     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'}
+    0x0964,  # .. 0x0964 ; {'Beng', 'Deva', 'Dogr', 'Gong', 'Gonm', 'Gran', 'Gujr', 'Guru', 'Knda', 'Mahj', 'Mlym', 'Nand', 'Orya', 'Sind', 'Sinh', 'Sylo', 'Takr', 'Taml', 'Telu', 'Tirh'}
+    0x0965,  # .. 0x0965 ; {'Beng', 'Deva', 'Dogr', 'Gong', 'Gonm', 'Gran', 'Gujr', 'Guru', 'Knda', 'Limb', 'Mahj', 'Mlym', 'Nand', 'Orya', 'Sind', 'Sinh', 'Sylo', 'Takr', 'Taml', 'Telu', 'Tirh'}
+    0x0966,  # .. 0x096F ; {'Deva', 'Dogr', '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
+    0x0AF0,  # .. 0x0BE5 ; None
+    0x0BE6,  # .. 0x0BF3 ; {'Gran', 'Taml'}
+    0x0BF4,  # .. 0x0CE5 ; None
+    0x0CE6,  # .. 0x0CEF ; {'Knda', 'Nand'}
+    0x0CF0,  # .. 0x103F ; None
     0x1040,  # .. 0x1049 ; {'Cakm', 'Mymr', 'Tale'}
     0x104A,  # .. 0x10FA ; None
     0x10FB,  # .. 0x10FB ; {'Geor', 'Latn'}
@@ -98,27 +97,41 @@
     0x1804,  # .. 0x1804 ; None
     0x1805,  # .. 0x1805 ; {'Mong', 'Phag'}
     0x1806,  # .. 0x1CCF ; None
-    0x1CD0,  # .. 0x1CD0 ; {'Deva', 'Gran'}
+    0x1CD0,  # .. 0x1CD0 ; {'Beng', 'Deva', 'Gran', 'Knda'}
     0x1CD1,  # .. 0x1CD1 ; {'Deva'}
-    0x1CD2,  # .. 0x1CD3 ; {'Deva', 'Gran'}
-    0x1CD4,  # .. 0x1CD6 ; {'Deva'}
+    0x1CD2,  # .. 0x1CD2 ; {'Beng', 'Deva', 'Gran', 'Knda'}
+    0x1CD3,  # .. 0x1CD3 ; {'Deva', 'Gran'}
+    0x1CD4,  # .. 0x1CD4 ; {'Deva'}
+    0x1CD5,  # .. 0x1CD6 ; {'Beng', 'Deva'}
     0x1CD7,  # .. 0x1CD7 ; {'Deva', 'Shrd'}
-    0x1CD8,  # .. 0x1CD8 ; {'Deva'}
+    0x1CD8,  # .. 0x1CD8 ; {'Beng', 'Deva'}
     0x1CD9,  # .. 0x1CD9 ; {'Deva', 'Shrd'}
-    0x1CDA,  # .. 0x1CDA ; {'Deva', 'Knda', 'Mlym', 'Taml', 'Telu'}
+    0x1CDA,  # .. 0x1CDA ; {'Deva', 'Knda', 'Mlym', 'Orya', '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'}
+    0x1CE1,  # .. 0x1CE1 ; {'Beng', 'Deva'}
+    0x1CE2,  # .. 0x1CE8 ; {'Deva'}
+    0x1CE9,  # .. 0x1CE9 ; {'Deva', 'Nand'}
+    0x1CEA,  # .. 0x1CEA ; {'Beng', 'Deva'}
+    0x1CEB,  # .. 0x1CEC ; {'Deva'}
+    0x1CED,  # .. 0x1CED ; {'Beng', 'Deva'}
+    0x1CEE,  # .. 0x1CF1 ; {'Deva'}
+    0x1CF2,  # .. 0x1CF2 ; {'Beng', 'Deva', 'Gran', 'Knda', 'Nand', 'Orya', 'Telu', 'Tirh'}
+    0x1CF3,  # .. 0x1CF3 ; {'Deva', 'Gran'}
+    0x1CF4,  # .. 0x1CF4 ; {'Deva', 'Gran', 'Knda'}
+    0x1CF5,  # .. 0x1CF6 ; {'Beng', 'Deva'}
     0x1CF7,  # .. 0x1CF7 ; {'Beng'}
     0x1CF8,  # .. 0x1CF9 ; {'Deva', 'Gran'}
-    0x1CFA,  # .. 0x1DBF ; None
+    0x1CFA,  # .. 0x1CFA ; {'Nand'}
+    0x1CFB,  # .. 0x1DBF ; None
     0x1DC0,  # .. 0x1DC1 ; {'Grek'}
-    0x1DC2,  # .. 0x20EF ; None
+    0x1DC2,  # .. 0x1DF7 ; None
+    0x1DF8,  # .. 0x1DF8 ; {'Cyrl', 'Syrc'}
+    0x1DF9,  # .. 0x202E ; None
+    0x202F,  # .. 0x202F ; {'Latn', 'Mong'}
+    0x2030,  # .. 0x20EF ; None
     0x20F0,  # .. 0x20F0 ; {'Deva', 'Gran', 'Latn'}
     0x20F1,  # .. 0x2E42 ; None
     0x2E43,  # .. 0x2E43 ; {'Cyrl', 'Glag'}
@@ -160,7 +173,9 @@
     0x3280,  # .. 0x32B0 ; {'Hani'}
     0x32B1,  # .. 0x32BF ; None
     0x32C0,  # .. 0x32CB ; {'Hani'}
-    0x32CC,  # .. 0x3357 ; None
+    0x32CC,  # .. 0x32FE ; None
+    0x32FF,  # .. 0x32FF ; {'Hani'}
+    0x3300,  # .. 0x3357 ; None
     0x3358,  # .. 0x3370 ; {'Hani'}
     0x3371,  # .. 0x337A ; None
     0x337B,  # .. 0x337F ; {'Hani'}
@@ -168,9 +183,12 @@
     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'}
+    0xA670,  # .. 0xA6FF ; None
+    0xA700,  # .. 0xA707 ; {'Hani', 'Latn'}
+    0xA708,  # .. 0xA82F ; None
+    0xA830,  # .. 0xA832 ; {'Deva', 'Dogr', 'Gujr', 'Guru', 'Khoj', 'Knda', 'Kthi', 'Mahj', 'Mlym', 'Modi', 'Nand', 'Sind', 'Takr', 'Tirh'}
+    0xA833,  # .. 0xA835 ; {'Deva', 'Dogr', 'Gujr', 'Guru', 'Khoj', 'Knda', 'Kthi', 'Mahj', 'Modi', 'Nand', 'Sind', 'Takr', 'Tirh'}
+    0xA836,  # .. 0xA839 ; {'Deva', 'Dogr', 'Gujr', 'Guru', 'Khoj', 'Kthi', 'Mahj', 'Modi', 'Sind', 'Takr', 'Tirh'}
     0xA83A,  # .. 0xA8F0 ; None
     0xA8F1,  # .. 0xA8F1 ; {'Beng', 'Deva'}
     0xA8F2,  # .. 0xA8F2 ; None
@@ -203,9 +221,13 @@
     0x11301,  # .. 0x11301 ; {'Gran', 'Taml'}
     0x11302,  # .. 0x11302 ; None
     0x11303,  # .. 0x11303 ; {'Gran', 'Taml'}
-    0x11304,  # .. 0x1133B ; None
-    0x1133C,  # .. 0x1133C ; {'Gran', 'Taml'}
-    0x1133D,  # .. 0x1BC9F ; None
+    0x11304,  # .. 0x1133A ; None
+    0x1133B,  # .. 0x1133C ; {'Gran', 'Taml'}
+    0x1133D,  # .. 0x11FCF ; None
+    0x11FD0,  # .. 0x11FD1 ; {'Gran', 'Taml'}
+    0x11FD2,  # .. 0x11FD2 ; None
+    0x11FD3,  # .. 0x11FD3 ; {'Gran', 'Taml'}
+    0x11FD4,  # .. 0x1BC9F ; None
     0x1BCA0,  # .. 0x1BCA3 ; {'Dupl'}
     0x1BCA4,  # .. 0x1D35F ; None
     0x1D360,  # .. 0x1D371 ; {'Hani'}
@@ -226,42 +248,41 @@
     {'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,                                                      # 0488..060B
+    {'Arab', 'Rohg', 'Syrc', 'Thaa', 'Yezi'},                  # 060C..060C
     None,                                                      # 060D..061A
-    {'Arab', 'Syrc', 'Thaa'},                                  # 061B..061C
+    {'Arab', 'Rohg', 'Syrc', 'Thaa', 'Yezi'},                  # 061B..061B
+    {'Arab', 'Syrc', 'Thaa'},                                  # 061C..061C
     None,                                                      # 061D..061E
-    {'Arab', 'Syrc', 'Thaa'},                                  # 061F..061F
+    {'Arab', 'Rohg', 'Syrc', 'Thaa', 'Yezi'},                  # 061F..061F
     None,                                                      # 0620..063F
-    {'Adlm', 'Arab', 'Mand', 'Mani', 'Phlp', 'Syrc'},          # 0640..0640
+    {'Adlm', 'Arab', 'Mand', 'Mani', 'Phlp', 'Rohg', 'Sogd', 'Syrc'},  # 0640..0640
     None,                                                      # 0641..064A
     {'Arab', 'Syrc'},                                          # 064B..0655
     None,                                                      # 0656..065F
-    {'Arab', 'Thaa'},                                          # 0660..0669
+    {'Arab', 'Thaa', 'Yezi'},                                  # 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,                                                      # 0671..06D3
+    {'Arab', 'Rohg'},                                          # 06D4..06D4
+    None,                                                      # 06D5..0950
+    {'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Latn', 'Mlym', 'Orya', 'Shrd', 'Taml', 'Telu', 'Tirh'},  # 0951..0951
+    {'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Latn', 'Mlym', 'Orya', 'Taml', 'Telu', 'Tirh'},  # 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
+    {'Beng', 'Deva', 'Dogr', 'Gong', 'Gonm', 'Gran', 'Gujr', 'Guru', 'Knda', 'Mahj', 'Mlym', 'Nand', 'Orya', 'Sind', 'Sinh', 'Sylo', 'Takr', 'Taml', 'Telu', 'Tirh'},  # 0964..0964
+    {'Beng', 'Deva', 'Dogr', 'Gong', 'Gonm', 'Gran', 'Gujr', 'Guru', 'Knda', 'Limb', 'Mahj', 'Mlym', 'Nand', 'Orya', 'Sind', 'Sinh', 'Sylo', 'Takr', 'Taml', 'Telu', 'Tirh'},  # 0965..0965
+    {'Deva', 'Dogr', '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
+    None,                                                      # 0AF0..0BE5
+    {'Gran', 'Taml'},                                          # 0BE6..0BF3
+    None,                                                      # 0BF4..0CE5
+    {'Knda', 'Nand'},                                          # 0CE6..0CEF
+    None,                                                      # 0CF0..103F
     {'Cakm', 'Mymr', 'Tale'},                                  # 1040..1049
     None,                                                      # 104A..10FA
     {'Geor', 'Latn'},                                          # 10FB..10FB
@@ -272,27 +293,41 @@
     None,                                                      # 1804..1804
     {'Mong', 'Phag'},                                          # 1805..1805
     None,                                                      # 1806..1CCF
-    {'Deva', 'Gran'},                                          # 1CD0..1CD0
+    {'Beng', 'Deva', 'Gran', 'Knda'},                          # 1CD0..1CD0
     {'Deva'},                                                  # 1CD1..1CD1
-    {'Deva', 'Gran'},                                          # 1CD2..1CD3
-    {'Deva'},                                                  # 1CD4..1CD6
+    {'Beng', 'Deva', 'Gran', 'Knda'},                          # 1CD2..1CD2
+    {'Deva', 'Gran'},                                          # 1CD3..1CD3
+    {'Deva'},                                                  # 1CD4..1CD4
+    {'Beng', 'Deva'},                                          # 1CD5..1CD6
     {'Deva', 'Shrd'},                                          # 1CD7..1CD7
-    {'Deva'},                                                  # 1CD8..1CD8
+    {'Beng', 'Deva'},                                          # 1CD8..1CD8
     {'Deva', 'Shrd'},                                          # 1CD9..1CD9
-    {'Deva', 'Knda', 'Mlym', 'Taml', 'Telu'},                  # 1CDA..1CDA
+    {'Deva', 'Knda', 'Mlym', 'Orya', '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', 'Deva'},                                          # 1CE1..1CE1
+    {'Deva'},                                                  # 1CE2..1CE8
+    {'Deva', 'Nand'},                                          # 1CE9..1CE9
+    {'Beng', 'Deva'},                                          # 1CEA..1CEA
+    {'Deva'},                                                  # 1CEB..1CEC
+    {'Beng', 'Deva'},                                          # 1CED..1CED
+    {'Deva'},                                                  # 1CEE..1CF1
+    {'Beng', 'Deva', 'Gran', 'Knda', 'Nand', 'Orya', 'Telu', 'Tirh'},  # 1CF2..1CF2
+    {'Deva', 'Gran'},                                          # 1CF3..1CF3
+    {'Deva', 'Gran', 'Knda'},                                  # 1CF4..1CF4
+    {'Beng', 'Deva'},                                          # 1CF5..1CF6
     {'Beng'},                                                  # 1CF7..1CF7
     {'Deva', 'Gran'},                                          # 1CF8..1CF9
-    None,                                                      # 1CFA..1DBF
+    {'Nand'},                                                  # 1CFA..1CFA
+    None,                                                      # 1CFB..1DBF
     {'Grek'},                                                  # 1DC0..1DC1
-    None,                                                      # 1DC2..20EF
+    None,                                                      # 1DC2..1DF7
+    {'Cyrl', 'Syrc'},                                          # 1DF8..1DF8
+    None,                                                      # 1DF9..202E
+    {'Latn', 'Mong'},                                          # 202F..202F
+    None,                                                      # 2030..20EF
     {'Deva', 'Gran', 'Latn'},                                  # 20F0..20F0
     None,                                                      # 20F1..2E42
     {'Cyrl', 'Glag'},                                          # 2E43..2E43
@@ -334,7 +369,9 @@
     {'Hani'},                                                  # 3280..32B0
     None,                                                      # 32B1..32BF
     {'Hani'},                                                  # 32C0..32CB
-    None,                                                      # 32CC..3357
+    None,                                                      # 32CC..32FE
+    {'Hani'},                                                  # 32FF..32FF
+    None,                                                      # 3300..3357
     {'Hani'},                                                  # 3358..3370
     None,                                                      # 3371..337A
     {'Hani'},                                                  # 337B..337F
@@ -342,9 +379,12 @@
     {'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,                                                      # A670..A6FF
+    {'Hani', 'Latn'},                                          # A700..A707
+    None,                                                      # A708..A82F
+    {'Deva', 'Dogr', 'Gujr', 'Guru', 'Khoj', 'Knda', 'Kthi', 'Mahj', 'Mlym', 'Modi', 'Nand', 'Sind', 'Takr', 'Tirh'},  # A830..A832
+    {'Deva', 'Dogr', 'Gujr', 'Guru', 'Khoj', 'Knda', 'Kthi', 'Mahj', 'Modi', 'Nand', 'Sind', 'Takr', 'Tirh'},  # A833..A835
+    {'Deva', 'Dogr', 'Gujr', 'Guru', 'Khoj', 'Kthi', 'Mahj', 'Modi', 'Sind', 'Takr', 'Tirh'},  # A836..A839
     None,                                                      # A83A..A8F0
     {'Beng', 'Deva'},                                          # A8F1..A8F1
     None,                                                      # A8F2..A8F2
@@ -377,9 +417,13 @@
     {'Gran', 'Taml'},                                          # 11301..11301
     None,                                                      # 11302..11302
     {'Gran', 'Taml'},                                          # 11303..11303
-    None,                                                      # 11304..1133B
-    {'Gran', 'Taml'},                                          # 1133C..1133C
-    None,                                                      # 1133D..1BC9F
+    None,                                                      # 11304..1133A
+    {'Gran', 'Taml'},                                          # 1133B..1133C
+    None,                                                      # 1133D..11FCF
+    {'Gran', 'Taml'},                                          # 11FD0..11FD1
+    None,                                                      # 11FD2..11FD2
+    {'Gran', 'Taml'},                                          # 11FD3..11FD3
+    None,                                                      # 11FD4..1BC9F
     {'Dupl'},                                                  # 1BCA0..1BCA3
     None,                                                      # 1BCA4..1D35F
     {'Hani'},                                                  # 1D360..1D371
diff --git a/Lib/fontTools/unicodedata/Scripts.py b/Lib/fontTools/unicodedata/Scripts.py
index f39b430..12f9a0e 100644
--- a/Lib/fontTools/unicodedata/Scripts.py
+++ b/Lib/fontTools/unicodedata/Scripts.py
@@ -4,9 +4,9 @@
 # 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.
+# Scripts-13.0.0.txt
+# Date: 2020-01-22, 00:07:43 GMT
+# © 2020 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
 #
@@ -68,20 +68,15 @@
     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
+    0x0559,  # .. 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
+    0x05EB,  # .. 0x05EE ; Unknown
+    0x05EF,  # .. 0x05F4 ; Hebrew
     0x05F5,  # .. 0x05FF ; Unknown
     0x0600,  # .. 0x0604 ; Arabic
     0x0605,  # .. 0x0605 ; Common
@@ -111,7 +106,8 @@
     0x0780,  # .. 0x07B1 ; Thaana
     0x07B2,  # .. 0x07BF ; Unknown
     0x07C0,  # .. 0x07FA ; Nko
-    0x07FB,  # .. 0x07FF ; Unknown
+    0x07FB,  # .. 0x07FC ; Unknown
+    0x07FD,  # .. 0x07FF ; Nko
     0x0800,  # .. 0x082D ; Samaritan
     0x082E,  # .. 0x082F ; Unknown
     0x0830,  # .. 0x083E ; Samaritan
@@ -124,14 +120,14 @@
     0x086B,  # .. 0x089F ; Unknown
     0x08A0,  # .. 0x08B4 ; Arabic
     0x08B5,  # .. 0x08B5 ; Unknown
-    0x08B6,  # .. 0x08BD ; Arabic
-    0x08BE,  # .. 0x08D3 ; Unknown
-    0x08D4,  # .. 0x08E1 ; Arabic
+    0x08B6,  # .. 0x08C7 ; Arabic
+    0x08C8,  # .. 0x08D2 ; Unknown
+    0x08D3,  # .. 0x08E1 ; Arabic
     0x08E2,  # .. 0x08E2 ; Common
     0x08E3,  # .. 0x08FF ; Arabic
     0x0900,  # .. 0x0950 ; Devanagari
-    0x0951,  # .. 0x0952 ; Inherited
-    0x0953,  # .. 0x0963 ; Devanagari
+    0x0951,  # .. 0x0954 ; Inherited
+    0x0955,  # .. 0x0963 ; Devanagari
     0x0964,  # .. 0x0965 ; Common
     0x0966,  # .. 0x097F ; Devanagari
     0x0980,  # .. 0x0983 ; Bengali
@@ -160,8 +156,8 @@
     0x09DE,  # .. 0x09DE ; Unknown
     0x09DF,  # .. 0x09E3 ; Bengali
     0x09E4,  # .. 0x09E5 ; Unknown
-    0x09E6,  # .. 0x09FD ; Bengali
-    0x09FE,  # .. 0x0A00 ; Unknown
+    0x09E6,  # .. 0x09FE ; Bengali
+    0x09FF,  # .. 0x0A00 ; Unknown
     0x0A01,  # .. 0x0A03 ; Gurmukhi
     0x0A04,  # .. 0x0A04 ; Unknown
     0x0A05,  # .. 0x0A0A ; Gurmukhi
@@ -192,8 +188,8 @@
     0x0A5D,  # .. 0x0A5D ; Unknown
     0x0A5E,  # .. 0x0A5E ; Gurmukhi
     0x0A5F,  # .. 0x0A65 ; Unknown
-    0x0A66,  # .. 0x0A75 ; Gurmukhi
-    0x0A76,  # .. 0x0A80 ; Unknown
+    0x0A66,  # .. 0x0A76 ; Gurmukhi
+    0x0A77,  # .. 0x0A80 ; Unknown
     0x0A81,  # .. 0x0A83 ; Gujarati
     0x0A84,  # .. 0x0A84 ; Unknown
     0x0A85,  # .. 0x0A8D ; Gujarati
@@ -241,8 +237,8 @@
     0x0B47,  # .. 0x0B48 ; Oriya
     0x0B49,  # .. 0x0B4A ; Unknown
     0x0B4B,  # .. 0x0B4D ; Oriya
-    0x0B4E,  # .. 0x0B55 ; Unknown
-    0x0B56,  # .. 0x0B57 ; Oriya
+    0x0B4E,  # .. 0x0B54 ; Unknown
+    0x0B55,  # .. 0x0B57 ; Oriya
     0x0B58,  # .. 0x0B5B ; Unknown
     0x0B5C,  # .. 0x0B5D ; Oriya
     0x0B5E,  # .. 0x0B5E ; Unknown
@@ -282,9 +278,7 @@
     0x0BD8,  # .. 0x0BE5 ; Unknown
     0x0BE6,  # .. 0x0BFA ; Tamil
     0x0BFB,  # .. 0x0BFF ; Unknown
-    0x0C00,  # .. 0x0C03 ; Telugu
-    0x0C04,  # .. 0x0C04 ; Unknown
-    0x0C05,  # .. 0x0C0C ; Telugu
+    0x0C00,  # .. 0x0C0C ; Telugu
     0x0C0D,  # .. 0x0C0D ; Unknown
     0x0C0E,  # .. 0x0C10 ; Telugu
     0x0C11,  # .. 0x0C11 ; Unknown
@@ -305,11 +299,9 @@
     0x0C60,  # .. 0x0C63 ; Telugu
     0x0C64,  # .. 0x0C65 ; Unknown
     0x0C66,  # .. 0x0C6F ; Telugu
-    0x0C70,  # .. 0x0C77 ; Unknown
-    0x0C78,  # .. 0x0C7F ; Telugu
-    0x0C80,  # .. 0x0C83 ; Kannada
-    0x0C84,  # .. 0x0C84 ; Unknown
-    0x0C85,  # .. 0x0C8C ; Kannada
+    0x0C70,  # .. 0x0C76 ; Unknown
+    0x0C77,  # .. 0x0C7F ; Telugu
+    0x0C80,  # .. 0x0C8C ; Kannada
     0x0C8D,  # .. 0x0C8D ; Unknown
     0x0C8E,  # .. 0x0C90 ; Kannada
     0x0C91,  # .. 0x0C91 ; Unknown
@@ -335,9 +327,7 @@
     0x0CF0,  # .. 0x0CF0 ; Unknown
     0x0CF1,  # .. 0x0CF2 ; Kannada
     0x0CF3,  # .. 0x0CFF ; Unknown
-    0x0D00,  # .. 0x0D03 ; Malayalam
-    0x0D04,  # .. 0x0D04 ; Unknown
-    0x0D05,  # .. 0x0D0C ; Malayalam
+    0x0D00,  # .. 0x0D0C ; Malayalam
     0x0D0D,  # .. 0x0D0D ; Unknown
     0x0D0E,  # .. 0x0D10 ; Malayalam
     0x0D11,  # .. 0x0D11 ; Unknown
@@ -350,8 +340,8 @@
     0x0D54,  # .. 0x0D63 ; Malayalam
     0x0D64,  # .. 0x0D65 ; Unknown
     0x0D66,  # .. 0x0D7F ; Malayalam
-    0x0D80,  # .. 0x0D81 ; Unknown
-    0x0D82,  # .. 0x0D83 ; Sinhala
+    0x0D80,  # .. 0x0D80 ; Unknown
+    0x0D81,  # .. 0x0D83 ; Sinhala
     0x0D84,  # .. 0x0D84 ; Unknown
     0x0D85,  # .. 0x0D96 ; Sinhala
     0x0D97,  # .. 0x0D99 ; Unknown
@@ -383,28 +373,14 @@
     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
+    0x0E85,  # .. 0x0E85 ; Unknown
+    0x0E86,  # .. 0x0E8A ; Lao
+    0x0E8B,  # .. 0x0E8B ; Unknown
+    0x0E8C,  # .. 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
+    0x0EA7,  # .. 0x0EBD ; Lao
     0x0EBE,  # .. 0x0EBF ; Unknown
     0x0EC0,  # .. 0x0EC4 ; Lao
     0x0EC5,  # .. 0x0EC5 ; Unknown
@@ -517,8 +493,8 @@
     0x180F,  # .. 0x180F ; Unknown
     0x1810,  # .. 0x1819 ; Mongolian
     0x181A,  # .. 0x181F ; Unknown
-    0x1820,  # .. 0x1877 ; Mongolian
-    0x1878,  # .. 0x187F ; Unknown
+    0x1820,  # .. 0x1878 ; Mongolian
+    0x1879,  # .. 0x187F ; Unknown
     0x1880,  # .. 0x18AA ; Mongolian
     0x18AB,  # .. 0x18AF ; Unknown
     0x18B0,  # .. 0x18F5 ; Canadian_Aboriginal
@@ -557,8 +533,8 @@
     0x1A9A,  # .. 0x1A9F ; Unknown
     0x1AA0,  # .. 0x1AAD ; Tai_Tham
     0x1AAE,  # .. 0x1AAF ; Unknown
-    0x1AB0,  # .. 0x1ABE ; Inherited
-    0x1ABF,  # .. 0x1AFF ; Unknown
+    0x1AB0,  # .. 0x1AC0 ; Inherited
+    0x1AC1,  # .. 0x1AFF ; Unknown
     0x1B00,  # .. 0x1B4B ; Balinese
     0x1B4C,  # .. 0x1B4F ; Unknown
     0x1B50,  # .. 0x1B7C ; Balinese
@@ -574,7 +550,10 @@
     0x1C4D,  # .. 0x1C4F ; Lepcha
     0x1C50,  # .. 0x1C7F ; Ol_Chiki
     0x1C80,  # .. 0x1C88 ; Cyrillic
-    0x1C89,  # .. 0x1CBF ; Unknown
+    0x1C89,  # .. 0x1C8F ; Unknown
+    0x1C90,  # .. 0x1CBA ; Georgian
+    0x1CBB,  # .. 0x1CBC ; Unknown
+    0x1CBD,  # .. 0x1CBF ; Georgian
     0x1CC0,  # .. 0x1CC7 ; Sundanese
     0x1CC8,  # .. 0x1CCF ; Unknown
     0x1CD0,  # .. 0x1CD2 ; Inherited
@@ -588,7 +567,8 @@
     0x1CF4,  # .. 0x1CF4 ; Inherited
     0x1CF5,  # .. 0x1CF7 ; Common
     0x1CF8,  # .. 0x1CF9 ; Inherited
-    0x1CFA,  # .. 0x1CFF ; Unknown
+    0x1CFA,  # .. 0x1CFA ; Common
+    0x1CFB,  # .. 0x1CFF ; Unknown
     0x1D00,  # .. 0x1D25 ; Latin
     0x1D26,  # .. 0x1D2A ; Greek
     0x1D2B,  # .. 0x1D2B ; Cyrillic
@@ -674,15 +654,8 @@
     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
+    0x2B96,  # .. 0x2B96 ; Unknown
+    0x2B97,  # .. 0x2BFF ; Common
     0x2C00,  # .. 0x2C2E ; Glagolitic
     0x2C2F,  # .. 0x2C2F ; Unknown
     0x2C30,  # .. 0x2C5E ; Glagolitic
@@ -721,8 +694,8 @@
     0x2DD8,  # .. 0x2DDE ; Ethiopic
     0x2DDF,  # .. 0x2DDF ; Unknown
     0x2DE0,  # .. 0x2DFF ; Cyrillic
-    0x2E00,  # .. 0x2E49 ; Common
-    0x2E4A,  # .. 0x2E7F ; Unknown
+    0x2E00,  # .. 0x2E52 ; Common
+    0x2E53,  # .. 0x2E7F ; Unknown
     0x2E80,  # .. 0x2E99 ; Han
     0x2E9A,  # .. 0x2E9A ; Unknown
     0x2E9B,  # .. 0x2EF3 ; Han
@@ -753,13 +726,12 @@
     0x30FB,  # .. 0x30FC ; Common
     0x30FD,  # .. 0x30FF ; Katakana
     0x3100,  # .. 0x3104 ; Unknown
-    0x3105,  # .. 0x312E ; Bopomofo
-    0x312F,  # .. 0x3130 ; Unknown
+    0x3105,  # .. 0x312F ; Bopomofo
+    0x3130,  # .. 0x3130 ; Unknown
     0x3131,  # .. 0x318E ; Hangul
     0x318F,  # .. 0x318F ; Unknown
     0x3190,  # .. 0x319F ; Common
-    0x31A0,  # .. 0x31BA ; Bopomofo
-    0x31BB,  # .. 0x31BF ; Unknown
+    0x31A0,  # .. 0x31BF ; Bopomofo
     0x31C0,  # .. 0x31E3 ; Common
     0x31E4,  # .. 0x31EF ; Unknown
     0x31F0,  # .. 0x31FF ; Katakana
@@ -769,14 +741,13 @@
     0x3260,  # .. 0x327E ; Hangul
     0x327F,  # .. 0x32CF ; Common
     0x32D0,  # .. 0x32FE ; Katakana
-    0x32FF,  # .. 0x32FF ; Unknown
+    0x32FF,  # .. 0x32FF ; Common
     0x3300,  # .. 0x3357 ; Katakana
     0x3358,  # .. 0x33FF ; Common
-    0x3400,  # .. 0x4DB5 ; Han
-    0x4DB6,  # .. 0x4DBF ; Unknown
+    0x3400,  # .. 0x4DBF ; Han
     0x4DC0,  # .. 0x4DFF ; Common
-    0x4E00,  # .. 0x9FEA ; Han
-    0x9FEB,  # .. 0x9FFF ; Unknown
+    0x4E00,  # .. 0x9FFC ; Han
+    0x9FFD,  # .. 0x9FFF ; Unknown
     0xA000,  # .. 0xA48C ; Yi
     0xA48D,  # .. 0xA48F ; Unknown
     0xA490,  # .. 0xA4C6 ; Yi
@@ -790,13 +761,13 @@
     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
+    0xA78B,  # .. 0xA7BF ; Latin
+    0xA7C0,  # .. 0xA7C1 ; Unknown
+    0xA7C2,  # .. 0xA7CA ; Latin
+    0xA7CB,  # .. 0xA7F4 ; Unknown
+    0xA7F5,  # .. 0xA7FF ; Latin
+    0xA800,  # .. 0xA82C ; Syloti_Nagri
+    0xA82D,  # .. 0xA82F ; Unknown
     0xA830,  # .. 0xA839 ; Common
     0xA83A,  # .. 0xA83F ; Unknown
     0xA840,  # .. 0xA877 ; Phags_Pa
@@ -805,8 +776,7 @@
     0xA8C6,  # .. 0xA8CD ; Unknown
     0xA8CE,  # .. 0xA8D9 ; Saurashtra
     0xA8DA,  # .. 0xA8DF ; Unknown
-    0xA8E0,  # .. 0xA8FD ; Devanagari
-    0xA8FE,  # .. 0xA8FF ; Unknown
+    0xA8E0,  # .. 0xA8FF ; Devanagari
     0xA900,  # .. 0xA92D ; Kayah_Li
     0xA92E,  # .. 0xA92E ; Common
     0xA92F,  # .. 0xA92F ; Kayah_Li
@@ -850,7 +820,9 @@
     0xAB5B,  # .. 0xAB5B ; Common
     0xAB5C,  # .. 0xAB64 ; Latin
     0xAB65,  # .. 0xAB65 ; Greek
-    0xAB66,  # .. 0xAB6F ; Unknown
+    0xAB66,  # .. 0xAB69 ; Latin
+    0xAB6A,  # .. 0xAB6B ; Common
+    0xAB6C,  # .. 0xAB6F ; Unknown
     0xAB70,  # .. 0xABBF ; Cherokee
     0xABC0,  # .. 0xABED ; Meetei_Mayek
     0xABEE,  # .. 0xABEF ; Unknown
@@ -955,8 +927,8 @@
     0x10137,  # .. 0x1013F ; Common
     0x10140,  # .. 0x1018E ; Greek
     0x1018F,  # .. 0x1018F ; Unknown
-    0x10190,  # .. 0x1019B ; Common
-    0x1019C,  # .. 0x1019F ; Unknown
+    0x10190,  # .. 0x1019C ; Common
+    0x1019D,  # .. 0x1019F ; Unknown
     0x101A0,  # .. 0x101A0 ; Greek
     0x101A1,  # .. 0x101CF ; Unknown
     0x101D0,  # .. 0x101FC ; Common
@@ -1050,12 +1022,12 @@
     0x10A14,  # .. 0x10A14 ; Unknown
     0x10A15,  # .. 0x10A17 ; Kharoshthi
     0x10A18,  # .. 0x10A18 ; Unknown
-    0x10A19,  # .. 0x10A33 ; Kharoshthi
-    0x10A34,  # .. 0x10A37 ; Unknown
+    0x10A19,  # .. 0x10A35 ; Kharoshthi
+    0x10A36,  # .. 0x10A37 ; Unknown
     0x10A38,  # .. 0x10A3A ; Kharoshthi
     0x10A3B,  # .. 0x10A3E ; Unknown
-    0x10A3F,  # .. 0x10A47 ; Kharoshthi
-    0x10A48,  # .. 0x10A4F ; Unknown
+    0x10A3F,  # .. 0x10A48 ; Kharoshthi
+    0x10A49,  # .. 0x10A4F ; Unknown
     0x10A50,  # .. 0x10A58 ; Kharoshthi
     0x10A59,  # .. 0x10A5F ; Unknown
     0x10A60,  # .. 0x10A7F ; Old_South_Arabian
@@ -1087,29 +1059,46 @@
     0x10CC0,  # .. 0x10CF2 ; Old_Hungarian
     0x10CF3,  # .. 0x10CF9 ; Unknown
     0x10CFA,  # .. 0x10CFF ; Old_Hungarian
-    0x10D00,  # .. 0x10E5F ; Unknown
+    0x10D00,  # .. 0x10D27 ; Hanifi_Rohingya
+    0x10D28,  # .. 0x10D2F ; Unknown
+    0x10D30,  # .. 0x10D39 ; Hanifi_Rohingya
+    0x10D3A,  # .. 0x10E5F ; Unknown
     0x10E60,  # .. 0x10E7E ; Arabic
-    0x10E7F,  # .. 0x10FFF ; Unknown
+    0x10E7F,  # .. 0x10E7F ; Unknown
+    0x10E80,  # .. 0x10EA9 ; Yezidi
+    0x10EAA,  # .. 0x10EAA ; Unknown
+    0x10EAB,  # .. 0x10EAD ; Yezidi
+    0x10EAE,  # .. 0x10EAF ; Unknown
+    0x10EB0,  # .. 0x10EB1 ; Yezidi
+    0x10EB2,  # .. 0x10EFF ; Unknown
+    0x10F00,  # .. 0x10F27 ; Old_Sogdian
+    0x10F28,  # .. 0x10F2F ; Unknown
+    0x10F30,  # .. 0x10F59 ; Sogdian
+    0x10F5A,  # .. 0x10FAF ; Unknown
+    0x10FB0,  # .. 0x10FCB ; Chorasmian
+    0x10FCC,  # .. 0x10FDF ; Unknown
+    0x10FE0,  # .. 0x10FF6 ; Elymaic
+    0x10FF7,  # .. 0x10FFF ; Unknown
     0x11000,  # .. 0x1104D ; Brahmi
     0x1104E,  # .. 0x11051 ; Unknown
     0x11052,  # .. 0x1106F ; Brahmi
     0x11070,  # .. 0x1107E ; Unknown
     0x1107F,  # .. 0x1107F ; Brahmi
     0x11080,  # .. 0x110C1 ; Kaithi
-    0x110C2,  # .. 0x110CF ; Unknown
+    0x110C2,  # .. 0x110CC ; Unknown
+    0x110CD,  # .. 0x110CD ; Kaithi
+    0x110CE,  # .. 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
+    0x11136,  # .. 0x11147 ; Chakma
+    0x11148,  # .. 0x1114F ; Unknown
     0x11150,  # .. 0x11176 ; Mahajani
     0x11177,  # .. 0x1117F ; Unknown
-    0x11180,  # .. 0x111CD ; Sharada
-    0x111CE,  # .. 0x111CF ; Unknown
-    0x111D0,  # .. 0x111DF ; Sharada
+    0x11180,  # .. 0x111DF ; Sharada
     0x111E0,  # .. 0x111E0 ; Unknown
     0x111E1,  # .. 0x111F4 ; Sinhala
     0x111F5,  # .. 0x111FF ; Unknown
@@ -1144,7 +1133,8 @@
     0x11332,  # .. 0x11333 ; Grantha
     0x11334,  # .. 0x11334 ; Unknown
     0x11335,  # .. 0x11339 ; Grantha
-    0x1133A,  # .. 0x1133B ; Unknown
+    0x1133A,  # .. 0x1133A ; Unknown
+    0x1133B,  # .. 0x1133B ; Inherited
     0x1133C,  # .. 0x11344 ; Grantha
     0x11345,  # .. 0x11346 ; Unknown
     0x11347,  # .. 0x11348 ; Grantha
@@ -1161,12 +1151,10 @@
     0x1136D,  # .. 0x1136F ; Unknown
     0x11370,  # .. 0x11374 ; Grantha
     0x11375,  # .. 0x113FF ; Unknown
-    0x11400,  # .. 0x11459 ; Newa
-    0x1145A,  # .. 0x1145A ; Unknown
-    0x1145B,  # .. 0x1145B ; Newa
+    0x11400,  # .. 0x1145B ; Newa
     0x1145C,  # .. 0x1145C ; Unknown
-    0x1145D,  # .. 0x1145D ; Newa
-    0x1145E,  # .. 0x1147F ; Unknown
+    0x1145D,  # .. 0x11461 ; Newa
+    0x11462,  # .. 0x1147F ; Unknown
     0x11480,  # .. 0x114C7 ; Tirhuta
     0x114C8,  # .. 0x114CF ; Unknown
     0x114D0,  # .. 0x114D9 ; Tirhuta
@@ -1181,27 +1169,46 @@
     0x1165A,  # .. 0x1165F ; Unknown
     0x11660,  # .. 0x1166C ; Mongolian
     0x1166D,  # .. 0x1167F ; Unknown
-    0x11680,  # .. 0x116B7 ; Takri
-    0x116B8,  # .. 0x116BF ; Unknown
+    0x11680,  # .. 0x116B8 ; Takri
+    0x116B9,  # .. 0x116BF ; Unknown
     0x116C0,  # .. 0x116C9 ; Takri
     0x116CA,  # .. 0x116FF ; Unknown
-    0x11700,  # .. 0x11719 ; Ahom
-    0x1171A,  # .. 0x1171C ; Unknown
+    0x11700,  # .. 0x1171A ; Ahom
+    0x1171B,  # .. 0x1171C ; Unknown
     0x1171D,  # .. 0x1172B ; Ahom
     0x1172C,  # .. 0x1172F ; Unknown
     0x11730,  # .. 0x1173F ; Ahom
-    0x11740,  # .. 0x1189F ; Unknown
+    0x11740,  # .. 0x117FF ; Unknown
+    0x11800,  # .. 0x1183B ; Dogra
+    0x1183C,  # .. 0x1189F ; Unknown
     0x118A0,  # .. 0x118F2 ; Warang_Citi
     0x118F3,  # .. 0x118FE ; Unknown
     0x118FF,  # .. 0x118FF ; Warang_Citi
-    0x11900,  # .. 0x119FF ; Unknown
+    0x11900,  # .. 0x11906 ; Dives_Akuru
+    0x11907,  # .. 0x11908 ; Unknown
+    0x11909,  # .. 0x11909 ; Dives_Akuru
+    0x1190A,  # .. 0x1190B ; Unknown
+    0x1190C,  # .. 0x11913 ; Dives_Akuru
+    0x11914,  # .. 0x11914 ; Unknown
+    0x11915,  # .. 0x11916 ; Dives_Akuru
+    0x11917,  # .. 0x11917 ; Unknown
+    0x11918,  # .. 0x11935 ; Dives_Akuru
+    0x11936,  # .. 0x11936 ; Unknown
+    0x11937,  # .. 0x11938 ; Dives_Akuru
+    0x11939,  # .. 0x1193A ; Unknown
+    0x1193B,  # .. 0x11946 ; Dives_Akuru
+    0x11947,  # .. 0x1194F ; Unknown
+    0x11950,  # .. 0x11959 ; Dives_Akuru
+    0x1195A,  # .. 0x1199F ; Unknown
+    0x119A0,  # .. 0x119A7 ; Nandinagari
+    0x119A8,  # .. 0x119A9 ; Unknown
+    0x119AA,  # .. 0x119D7 ; Nandinagari
+    0x119D8,  # .. 0x119D9 ; Unknown
+    0x119DA,  # .. 0x119E4 ; Nandinagari
+    0x119E5,  # .. 0x119FF ; Unknown
     0x11A00,  # .. 0x11A47 ; Zanabazar_Square
     0x11A48,  # .. 0x11A4F ; Unknown
-    0x11A50,  # .. 0x11A83 ; Soyombo
-    0x11A84,  # .. 0x11A85 ; Unknown
-    0x11A86,  # .. 0x11A9C ; Soyombo
-    0x11A9D,  # .. 0x11A9D ; Unknown
-    0x11A9E,  # .. 0x11AA2 ; Soyombo
+    0x11A50,  # .. 0x11AA2 ; Soyombo
     0x11AA3,  # .. 0x11ABF ; Unknown
     0x11AC0,  # .. 0x11AF8 ; Pau_Cin_Hau
     0x11AF9,  # .. 0x11BFF ; Unknown
@@ -1232,7 +1239,26 @@
     0x11D3F,  # .. 0x11D47 ; Masaram_Gondi
     0x11D48,  # .. 0x11D4F ; Unknown
     0x11D50,  # .. 0x11D59 ; Masaram_Gondi
-    0x11D5A,  # .. 0x11FFF ; Unknown
+    0x11D5A,  # .. 0x11D5F ; Unknown
+    0x11D60,  # .. 0x11D65 ; Gunjala_Gondi
+    0x11D66,  # .. 0x11D66 ; Unknown
+    0x11D67,  # .. 0x11D68 ; Gunjala_Gondi
+    0x11D69,  # .. 0x11D69 ; Unknown
+    0x11D6A,  # .. 0x11D8E ; Gunjala_Gondi
+    0x11D8F,  # .. 0x11D8F ; Unknown
+    0x11D90,  # .. 0x11D91 ; Gunjala_Gondi
+    0x11D92,  # .. 0x11D92 ; Unknown
+    0x11D93,  # .. 0x11D98 ; Gunjala_Gondi
+    0x11D99,  # .. 0x11D9F ; Unknown
+    0x11DA0,  # .. 0x11DA9 ; Gunjala_Gondi
+    0x11DAA,  # .. 0x11EDF ; Unknown
+    0x11EE0,  # .. 0x11EF8 ; Makasar
+    0x11EF9,  # .. 0x11FAF ; Unknown
+    0x11FB0,  # .. 0x11FB0 ; Lisu
+    0x11FB1,  # .. 0x11FBF ; Unknown
+    0x11FC0,  # .. 0x11FF1 ; Tamil
+    0x11FF2,  # .. 0x11FFE ; Unknown
+    0x11FFF,  # .. 0x11FFF ; Tamil
     0x12000,  # .. 0x12399 ; Cuneiform
     0x1239A,  # .. 0x123FF ; Unknown
     0x12400,  # .. 0x1246E ; Cuneiform
@@ -1242,7 +1268,9 @@
     0x12480,  # .. 0x12543 ; Cuneiform
     0x12544,  # .. 0x12FFF ; Unknown
     0x13000,  # .. 0x1342E ; Egyptian_Hieroglyphs
-    0x1342F,  # .. 0x143FF ; Unknown
+    0x1342F,  # .. 0x1342F ; Unknown
+    0x13430,  # .. 0x13438 ; Egyptian_Hieroglyphs
+    0x13439,  # .. 0x143FF ; Unknown
     0x14400,  # .. 0x14646 ; Anatolian_Hieroglyphs
     0x14647,  # .. 0x167FF ; Unknown
     0x16800,  # .. 0x16A38 ; Bamum
@@ -1266,23 +1294,36 @@
     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
+    0x16B90,  # .. 0x16E3F ; Unknown
+    0x16E40,  # .. 0x16E9A ; Medefaidrin
+    0x16E9B,  # .. 0x16EFF ; Unknown
+    0x16F00,  # .. 0x16F4A ; Miao
+    0x16F4B,  # .. 0x16F4E ; Unknown
+    0x16F4F,  # .. 0x16F87 ; Miao
+    0x16F88,  # .. 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
+    0x16FE2,  # .. 0x16FE3 ; Common
+    0x16FE4,  # .. 0x16FE4 ; Khitan_Small_Script
+    0x16FE5,  # .. 0x16FEF ; Unknown
+    0x16FF0,  # .. 0x16FF1 ; Han
+    0x16FF2,  # .. 0x16FFF ; Unknown
+    0x17000,  # .. 0x187F7 ; Tangut
+    0x187F8,  # .. 0x187FF ; Unknown
+    0x18800,  # .. 0x18AFF ; Tangut
+    0x18B00,  # .. 0x18CD5 ; Khitan_Small_Script
+    0x18CD6,  # .. 0x18CFF ; Unknown
+    0x18D00,  # .. 0x18D08 ; Tangut
+    0x18D09,  # .. 0x1AFFF ; Unknown
     0x1B000,  # .. 0x1B000 ; Katakana
     0x1B001,  # .. 0x1B11E ; Hiragana
-    0x1B11F,  # .. 0x1B16F ; Unknown
+    0x1B11F,  # .. 0x1B14F ; Unknown
+    0x1B150,  # .. 0x1B152 ; Hiragana
+    0x1B153,  # .. 0x1B163 ; Unknown
+    0x1B164,  # .. 0x1B167 ; Katakana
+    0x1B168,  # .. 0x1B16F ; Unknown
     0x1B170,  # .. 0x1B2FB ; Nushu
     0x1B2FC,  # .. 0x1BBFF ; Unknown
     0x1BC00,  # .. 0x1BC6A ; Duployan
@@ -1311,11 +1352,13 @@
     0x1D1AE,  # .. 0x1D1E8 ; Common
     0x1D1E9,  # .. 0x1D1FF ; Unknown
     0x1D200,  # .. 0x1D245 ; Greek
-    0x1D246,  # .. 0x1D2FF ; Unknown
+    0x1D246,  # .. 0x1D2DF ; Unknown
+    0x1D2E0,  # .. 0x1D2F3 ; Common
+    0x1D2F4,  # .. 0x1D2FF ; Unknown
     0x1D300,  # .. 0x1D356 ; Common
     0x1D357,  # .. 0x1D35F ; Unknown
-    0x1D360,  # .. 0x1D371 ; Common
-    0x1D372,  # .. 0x1D3FF ; Unknown
+    0x1D360,  # .. 0x1D378 ; Common
+    0x1D379,  # .. 0x1D3FF ; Unknown
     0x1D400,  # .. 0x1D454 ; Common
     0x1D455,  # .. 0x1D455 ; Unknown
     0x1D456,  # .. 0x1D49C ; Common
@@ -1372,17 +1415,33 @@
     0x1E023,  # .. 0x1E024 ; Glagolitic
     0x1E025,  # .. 0x1E025 ; Unknown
     0x1E026,  # .. 0x1E02A ; Glagolitic
-    0x1E02B,  # .. 0x1E7FF ; Unknown
+    0x1E02B,  # .. 0x1E0FF ; Unknown
+    0x1E100,  # .. 0x1E12C ; Nyiakeng_Puachue_Hmong
+    0x1E12D,  # .. 0x1E12F ; Unknown
+    0x1E130,  # .. 0x1E13D ; Nyiakeng_Puachue_Hmong
+    0x1E13E,  # .. 0x1E13F ; Unknown
+    0x1E140,  # .. 0x1E149 ; Nyiakeng_Puachue_Hmong
+    0x1E14A,  # .. 0x1E14D ; Unknown
+    0x1E14E,  # .. 0x1E14F ; Nyiakeng_Puachue_Hmong
+    0x1E150,  # .. 0x1E2BF ; Unknown
+    0x1E2C0,  # .. 0x1E2F9 ; Wancho
+    0x1E2FA,  # .. 0x1E2FE ; Unknown
+    0x1E2FF,  # .. 0x1E2FF ; Wancho
+    0x1E300,  # .. 0x1E7FF ; Unknown
     0x1E800,  # .. 0x1E8C4 ; Mende_Kikakui
     0x1E8C5,  # .. 0x1E8C6 ; Unknown
     0x1E8C7,  # .. 0x1E8D6 ; Mende_Kikakui
     0x1E8D7,  # .. 0x1E8FF ; Unknown
-    0x1E900,  # .. 0x1E94A ; Adlam
-    0x1E94B,  # .. 0x1E94F ; Unknown
+    0x1E900,  # .. 0x1E94B ; Adlam
+    0x1E94C,  # .. 0x1E94F ; Unknown
     0x1E950,  # .. 0x1E959 ; Adlam
     0x1E95A,  # .. 0x1E95D ; Unknown
     0x1E95E,  # .. 0x1E95F ; Adlam
-    0x1E960,  # .. 0x1EDFF ; Unknown
+    0x1E960,  # .. 0x1EC70 ; Unknown
+    0x1EC71,  # .. 0x1ECB4 ; Common
+    0x1ECB5,  # .. 0x1ED00 ; Unknown
+    0x1ED01,  # .. 0x1ED3D ; Common
+    0x1ED3E,  # .. 0x1EDFF ; Unknown
     0x1EE00,  # .. 0x1EE03 ; Arabic
     0x1EE04,  # .. 0x1EE04 ; Unknown
     0x1EE05,  # .. 0x1EE1F ; Arabic
@@ -1463,14 +1522,8 @@
     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
+    0x1F100,  # .. 0x1F1AD ; Common
+    0x1F1AE,  # .. 0x1F1E5 ; Unknown
     0x1F1E6,  # .. 0x1F1FF ; Common
     0x1F200,  # .. 0x1F200 ; Hiragana
     0x1F201,  # .. 0x1F202 ; Common
@@ -1483,16 +1536,18 @@
     0x1F252,  # .. 0x1F25F ; Unknown
     0x1F260,  # .. 0x1F265 ; Common
     0x1F266,  # .. 0x1F2FF ; Unknown
-    0x1F300,  # .. 0x1F6D4 ; Common
-    0x1F6D5,  # .. 0x1F6DF ; Unknown
+    0x1F300,  # .. 0x1F6D7 ; Common
+    0x1F6D8,  # .. 0x1F6DF ; Unknown
     0x1F6E0,  # .. 0x1F6EC ; Common
     0x1F6ED,  # .. 0x1F6EF ; Unknown
-    0x1F6F0,  # .. 0x1F6F8 ; Common
-    0x1F6F9,  # .. 0x1F6FF ; Unknown
+    0x1F6F0,  # .. 0x1F6FC ; Common
+    0x1F6FD,  # .. 0x1F6FF ; Unknown
     0x1F700,  # .. 0x1F773 ; Common
     0x1F774,  # .. 0x1F77F ; Unknown
-    0x1F780,  # .. 0x1F7D4 ; Common
-    0x1F7D5,  # .. 0x1F7FF ; Unknown
+    0x1F780,  # .. 0x1F7D8 ; Common
+    0x1F7D9,  # .. 0x1F7DF ; Unknown
+    0x1F7E0,  # .. 0x1F7EB ; Common
+    0x1F7EC,  # .. 0x1F7FF ; Unknown
     0x1F800,  # .. 0x1F80B ; Common
     0x1F80C,  # .. 0x1F80F ; Unknown
     0x1F810,  # .. 0x1F847 ; Common
@@ -1502,23 +1557,39 @@
     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
+    0x1F8AE,  # .. 0x1F8AF ; Unknown
+    0x1F8B0,  # .. 0x1F8B1 ; Common
+    0x1F8B2,  # .. 0x1F8FF ; Unknown
+    0x1F900,  # .. 0x1F978 ; Common
+    0x1F979,  # .. 0x1F979 ; Unknown
+    0x1F97A,  # .. 0x1F9CB ; Common
+    0x1F9CC,  # .. 0x1F9CC ; Unknown
+    0x1F9CD,  # .. 0x1FA53 ; Common
+    0x1FA54,  # .. 0x1FA5F ; Unknown
+    0x1FA60,  # .. 0x1FA6D ; Common
+    0x1FA6E,  # .. 0x1FA6F ; Unknown
+    0x1FA70,  # .. 0x1FA74 ; Common
+    0x1FA75,  # .. 0x1FA77 ; Unknown
+    0x1FA78,  # .. 0x1FA7A ; Common
+    0x1FA7B,  # .. 0x1FA7F ; Unknown
+    0x1FA80,  # .. 0x1FA86 ; Common
+    0x1FA87,  # .. 0x1FA8F ; Unknown
+    0x1FA90,  # .. 0x1FAA8 ; Common
+    0x1FAA9,  # .. 0x1FAAF ; Unknown
+    0x1FAB0,  # .. 0x1FAB6 ; Common
+    0x1FAB7,  # .. 0x1FABF ; Unknown
+    0x1FAC0,  # .. 0x1FAC2 ; Common
+    0x1FAC3,  # .. 0x1FACF ; Unknown
+    0x1FAD0,  # .. 0x1FAD6 ; Common
+    0x1FAD7,  # .. 0x1FAFF ; Unknown
+    0x1FB00,  # .. 0x1FB92 ; Common
+    0x1FB93,  # .. 0x1FB93 ; Unknown
+    0x1FB94,  # .. 0x1FBCA ; Common
+    0x1FBCB,  # .. 0x1FBEF ; Unknown
+    0x1FBF0,  # .. 0x1FBF9 ; Common
+    0x1FBFA,  # .. 0x1FFFF ; Unknown
+    0x20000,  # .. 0x2A6DD ; Han
+    0x2A6DE,  # .. 0x2A6FF ; Unknown
     0x2A700,  # .. 0x2B734 ; Han
     0x2B735,  # .. 0x2B73F ; Unknown
     0x2B740,  # .. 0x2B81D ; Han
@@ -1528,7 +1599,9 @@
     0x2CEB0,  # .. 0x2EBE0 ; Han
     0x2EBE1,  # .. 0x2F7FF ; Unknown
     0x2F800,  # .. 0x2FA1D ; Han
-    0x2FA1E,  # .. 0xE0000 ; Unknown
+    0x2FA1E,  # .. 0x2FFFF ; Unknown
+    0x30000,  # .. 0x3134A ; Han
+    0x3134B,  # .. 0xE0000 ; Unknown
     0xE0001,  # .. 0xE0001 ; Common
     0xE0002,  # .. 0xE001F ; Unknown
     0xE0020,  # .. 0xE007F ; Common
@@ -1585,20 +1658,15 @@
     '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
+    'Armn',  # 0559..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',  # 05EB..05EE ; Unknown
+    'Hebr',  # 05EF..05F4 ; Hebrew
     'Zzzz',  # 05F5..05FF ; Unknown
     'Arab',  # 0600..0604 ; Arabic
     'Zyyy',  # 0605..0605 ; Common
@@ -1628,7 +1696,8 @@
     'Thaa',  # 0780..07B1 ; Thaana
     'Zzzz',  # 07B2..07BF ; Unknown
     'Nkoo',  # 07C0..07FA ; Nko
-    'Zzzz',  # 07FB..07FF ; Unknown
+    'Zzzz',  # 07FB..07FC ; Unknown
+    'Nkoo',  # 07FD..07FF ; Nko
     'Samr',  # 0800..082D ; Samaritan
     'Zzzz',  # 082E..082F ; Unknown
     'Samr',  # 0830..083E ; Samaritan
@@ -1641,14 +1710,14 @@
     'Zzzz',  # 086B..089F ; Unknown
     'Arab',  # 08A0..08B4 ; Arabic
     'Zzzz',  # 08B5..08B5 ; Unknown
-    'Arab',  # 08B6..08BD ; Arabic
-    'Zzzz',  # 08BE..08D3 ; Unknown
-    'Arab',  # 08D4..08E1 ; Arabic
+    'Arab',  # 08B6..08C7 ; Arabic
+    'Zzzz',  # 08C8..08D2 ; Unknown
+    'Arab',  # 08D3..08E1 ; Arabic
     'Zyyy',  # 08E2..08E2 ; Common
     'Arab',  # 08E3..08FF ; Arabic
     'Deva',  # 0900..0950 ; Devanagari
-    'Zinh',  # 0951..0952 ; Inherited
-    'Deva',  # 0953..0963 ; Devanagari
+    'Zinh',  # 0951..0954 ; Inherited
+    'Deva',  # 0955..0963 ; Devanagari
     'Zyyy',  # 0964..0965 ; Common
     'Deva',  # 0966..097F ; Devanagari
     'Beng',  # 0980..0983 ; Bengali
@@ -1677,8 +1746,8 @@
     'Zzzz',  # 09DE..09DE ; Unknown
     'Beng',  # 09DF..09E3 ; Bengali
     'Zzzz',  # 09E4..09E5 ; Unknown
-    'Beng',  # 09E6..09FD ; Bengali
-    'Zzzz',  # 09FE..0A00 ; Unknown
+    'Beng',  # 09E6..09FE ; Bengali
+    'Zzzz',  # 09FF..0A00 ; Unknown
     'Guru',  # 0A01..0A03 ; Gurmukhi
     'Zzzz',  # 0A04..0A04 ; Unknown
     'Guru',  # 0A05..0A0A ; Gurmukhi
@@ -1709,8 +1778,8 @@
     'Zzzz',  # 0A5D..0A5D ; Unknown
     'Guru',  # 0A5E..0A5E ; Gurmukhi
     'Zzzz',  # 0A5F..0A65 ; Unknown
-    'Guru',  # 0A66..0A75 ; Gurmukhi
-    'Zzzz',  # 0A76..0A80 ; Unknown
+    'Guru',  # 0A66..0A76 ; Gurmukhi
+    'Zzzz',  # 0A77..0A80 ; Unknown
     'Gujr',  # 0A81..0A83 ; Gujarati
     'Zzzz',  # 0A84..0A84 ; Unknown
     'Gujr',  # 0A85..0A8D ; Gujarati
@@ -1758,8 +1827,8 @@
     'Orya',  # 0B47..0B48 ; Oriya
     'Zzzz',  # 0B49..0B4A ; Unknown
     'Orya',  # 0B4B..0B4D ; Oriya
-    'Zzzz',  # 0B4E..0B55 ; Unknown
-    'Orya',  # 0B56..0B57 ; Oriya
+    'Zzzz',  # 0B4E..0B54 ; Unknown
+    'Orya',  # 0B55..0B57 ; Oriya
     'Zzzz',  # 0B58..0B5B ; Unknown
     'Orya',  # 0B5C..0B5D ; Oriya
     'Zzzz',  # 0B5E..0B5E ; Unknown
@@ -1799,9 +1868,7 @@
     'Zzzz',  # 0BD8..0BE5 ; Unknown
     'Taml',  # 0BE6..0BFA ; Tamil
     'Zzzz',  # 0BFB..0BFF ; Unknown
-    'Telu',  # 0C00..0C03 ; Telugu
-    'Zzzz',  # 0C04..0C04 ; Unknown
-    'Telu',  # 0C05..0C0C ; Telugu
+    'Telu',  # 0C00..0C0C ; Telugu
     'Zzzz',  # 0C0D..0C0D ; Unknown
     'Telu',  # 0C0E..0C10 ; Telugu
     'Zzzz',  # 0C11..0C11 ; Unknown
@@ -1822,11 +1889,9 @@
     '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',  # 0C70..0C76 ; Unknown
+    'Telu',  # 0C77..0C7F ; Telugu
+    'Knda',  # 0C80..0C8C ; Kannada
     'Zzzz',  # 0C8D..0C8D ; Unknown
     'Knda',  # 0C8E..0C90 ; Kannada
     'Zzzz',  # 0C91..0C91 ; Unknown
@@ -1852,9 +1917,7 @@
     'Zzzz',  # 0CF0..0CF0 ; Unknown
     'Knda',  # 0CF1..0CF2 ; Kannada
     'Zzzz',  # 0CF3..0CFF ; Unknown
-    'Mlym',  # 0D00..0D03 ; Malayalam
-    'Zzzz',  # 0D04..0D04 ; Unknown
-    'Mlym',  # 0D05..0D0C ; Malayalam
+    'Mlym',  # 0D00..0D0C ; Malayalam
     'Zzzz',  # 0D0D..0D0D ; Unknown
     'Mlym',  # 0D0E..0D10 ; Malayalam
     'Zzzz',  # 0D11..0D11 ; Unknown
@@ -1867,8 +1930,8 @@
     'Mlym',  # 0D54..0D63 ; Malayalam
     'Zzzz',  # 0D64..0D65 ; Unknown
     'Mlym',  # 0D66..0D7F ; Malayalam
-    'Zzzz',  # 0D80..0D81 ; Unknown
-    'Sinh',  # 0D82..0D83 ; Sinhala
+    'Zzzz',  # 0D80..0D80 ; Unknown
+    'Sinh',  # 0D81..0D83 ; Sinhala
     'Zzzz',  # 0D84..0D84 ; Unknown
     'Sinh',  # 0D85..0D96 ; Sinhala
     'Zzzz',  # 0D97..0D99 ; Unknown
@@ -1900,28 +1963,14 @@
     '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',  # 0E85..0E85 ; Unknown
+    'Laoo',  # 0E86..0E8A ; Lao
+    'Zzzz',  # 0E8B..0E8B ; Unknown
+    'Laoo',  # 0E8C..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
+    'Laoo',  # 0EA7..0EBD ; Lao
     'Zzzz',  # 0EBE..0EBF ; Unknown
     'Laoo',  # 0EC0..0EC4 ; Lao
     'Zzzz',  # 0EC5..0EC5 ; Unknown
@@ -2034,8 +2083,8 @@
     'Zzzz',  # 180F..180F ; Unknown
     'Mong',  # 1810..1819 ; Mongolian
     'Zzzz',  # 181A..181F ; Unknown
-    'Mong',  # 1820..1877 ; Mongolian
-    'Zzzz',  # 1878..187F ; Unknown
+    'Mong',  # 1820..1878 ; Mongolian
+    'Zzzz',  # 1879..187F ; Unknown
     'Mong',  # 1880..18AA ; Mongolian
     'Zzzz',  # 18AB..18AF ; Unknown
     'Cans',  # 18B0..18F5 ; Canadian_Aboriginal
@@ -2074,8 +2123,8 @@
     'Zzzz',  # 1A9A..1A9F ; Unknown
     'Lana',  # 1AA0..1AAD ; Tai_Tham
     'Zzzz',  # 1AAE..1AAF ; Unknown
-    'Zinh',  # 1AB0..1ABE ; Inherited
-    'Zzzz',  # 1ABF..1AFF ; Unknown
+    'Zinh',  # 1AB0..1AC0 ; Inherited
+    'Zzzz',  # 1AC1..1AFF ; Unknown
     'Bali',  # 1B00..1B4B ; Balinese
     'Zzzz',  # 1B4C..1B4F ; Unknown
     'Bali',  # 1B50..1B7C ; Balinese
@@ -2091,7 +2140,10 @@
     'Lepc',  # 1C4D..1C4F ; Lepcha
     'Olck',  # 1C50..1C7F ; Ol_Chiki
     'Cyrl',  # 1C80..1C88 ; Cyrillic
-    'Zzzz',  # 1C89..1CBF ; Unknown
+    'Zzzz',  # 1C89..1C8F ; Unknown
+    'Geor',  # 1C90..1CBA ; Georgian
+    'Zzzz',  # 1CBB..1CBC ; Unknown
+    'Geor',  # 1CBD..1CBF ; Georgian
     'Sund',  # 1CC0..1CC7 ; Sundanese
     'Zzzz',  # 1CC8..1CCF ; Unknown
     'Zinh',  # 1CD0..1CD2 ; Inherited
@@ -2105,7 +2157,8 @@
     'Zinh',  # 1CF4..1CF4 ; Inherited
     'Zyyy',  # 1CF5..1CF7 ; Common
     'Zinh',  # 1CF8..1CF9 ; Inherited
-    'Zzzz',  # 1CFA..1CFF ; Unknown
+    'Zyyy',  # 1CFA..1CFA ; Common
+    'Zzzz',  # 1CFB..1CFF ; Unknown
     'Latn',  # 1D00..1D25 ; Latin
     'Grek',  # 1D26..1D2A ; Greek
     'Cyrl',  # 1D2B..1D2B ; Cyrillic
@@ -2191,15 +2244,8 @@
     '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
+    'Zzzz',  # 2B96..2B96 ; Unknown
+    'Zyyy',  # 2B97..2BFF ; Common
     'Glag',  # 2C00..2C2E ; Glagolitic
     'Zzzz',  # 2C2F..2C2F ; Unknown
     'Glag',  # 2C30..2C5E ; Glagolitic
@@ -2238,8 +2284,8 @@
     'Ethi',  # 2DD8..2DDE ; Ethiopic
     'Zzzz',  # 2DDF..2DDF ; Unknown
     'Cyrl',  # 2DE0..2DFF ; Cyrillic
-    'Zyyy',  # 2E00..2E49 ; Common
-    'Zzzz',  # 2E4A..2E7F ; Unknown
+    'Zyyy',  # 2E00..2E52 ; Common
+    'Zzzz',  # 2E53..2E7F ; Unknown
     'Hani',  # 2E80..2E99 ; Han
     'Zzzz',  # 2E9A..2E9A ; Unknown
     'Hani',  # 2E9B..2EF3 ; Han
@@ -2270,13 +2316,12 @@
     'Zyyy',  # 30FB..30FC ; Common
     'Kana',  # 30FD..30FF ; Katakana
     'Zzzz',  # 3100..3104 ; Unknown
-    'Bopo',  # 3105..312E ; Bopomofo
-    'Zzzz',  # 312F..3130 ; Unknown
+    'Bopo',  # 3105..312F ; Bopomofo
+    'Zzzz',  # 3130..3130 ; Unknown
     'Hang',  # 3131..318E ; Hangul
     'Zzzz',  # 318F..318F ; Unknown
     'Zyyy',  # 3190..319F ; Common
-    'Bopo',  # 31A0..31BA ; Bopomofo
-    'Zzzz',  # 31BB..31BF ; Unknown
+    'Bopo',  # 31A0..31BF ; Bopomofo
     'Zyyy',  # 31C0..31E3 ; Common
     'Zzzz',  # 31E4..31EF ; Unknown
     'Kana',  # 31F0..31FF ; Katakana
@@ -2286,14 +2331,13 @@
     'Hang',  # 3260..327E ; Hangul
     'Zyyy',  # 327F..32CF ; Common
     'Kana',  # 32D0..32FE ; Katakana
-    'Zzzz',  # 32FF..32FF ; Unknown
+    'Zyyy',  # 32FF..32FF ; Common
     'Kana',  # 3300..3357 ; Katakana
     'Zyyy',  # 3358..33FF ; Common
-    'Hani',  # 3400..4DB5 ; Han
-    'Zzzz',  # 4DB6..4DBF ; Unknown
+    'Hani',  # 3400..4DBF ; Han
     'Zyyy',  # 4DC0..4DFF ; Common
-    'Hani',  # 4E00..9FEA ; Han
-    'Zzzz',  # 9FEB..9FFF ; Unknown
+    'Hani',  # 4E00..9FFC ; Han
+    'Zzzz',  # 9FFD..9FFF ; Unknown
     'Yiii',  # A000..A48C ; Yi
     'Zzzz',  # A48D..A48F ; Unknown
     'Yiii',  # A490..A4C6 ; Yi
@@ -2307,13 +2351,13 @@
     '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
+    'Latn',  # A78B..A7BF ; Latin
+    'Zzzz',  # A7C0..A7C1 ; Unknown
+    'Latn',  # A7C2..A7CA ; Latin
+    'Zzzz',  # A7CB..A7F4 ; Unknown
+    'Latn',  # A7F5..A7FF ; Latin
+    'Sylo',  # A800..A82C ; Syloti_Nagri
+    'Zzzz',  # A82D..A82F ; Unknown
     'Zyyy',  # A830..A839 ; Common
     'Zzzz',  # A83A..A83F ; Unknown
     'Phag',  # A840..A877 ; Phags_Pa
@@ -2322,8 +2366,7 @@
     'Zzzz',  # A8C6..A8CD ; Unknown
     'Saur',  # A8CE..A8D9 ; Saurashtra
     'Zzzz',  # A8DA..A8DF ; Unknown
-    'Deva',  # A8E0..A8FD ; Devanagari
-    'Zzzz',  # A8FE..A8FF ; Unknown
+    'Deva',  # A8E0..A8FF ; Devanagari
     'Kali',  # A900..A92D ; Kayah_Li
     'Zyyy',  # A92E..A92E ; Common
     'Kali',  # A92F..A92F ; Kayah_Li
@@ -2367,7 +2410,9 @@
     'Zyyy',  # AB5B..AB5B ; Common
     'Latn',  # AB5C..AB64 ; Latin
     'Grek',  # AB65..AB65 ; Greek
-    'Zzzz',  # AB66..AB6F ; Unknown
+    'Latn',  # AB66..AB69 ; Latin
+    'Zyyy',  # AB6A..AB6B ; Common
+    'Zzzz',  # AB6C..AB6F ; Unknown
     'Cher',  # AB70..ABBF ; Cherokee
     'Mtei',  # ABC0..ABED ; Meetei_Mayek
     'Zzzz',  # ABEE..ABEF ; Unknown
@@ -2472,8 +2517,8 @@
     'Zyyy',  # 10137..1013F ; Common
     'Grek',  # 10140..1018E ; Greek
     'Zzzz',  # 1018F..1018F ; Unknown
-    'Zyyy',  # 10190..1019B ; Common
-    'Zzzz',  # 1019C..1019F ; Unknown
+    'Zyyy',  # 10190..1019C ; Common
+    'Zzzz',  # 1019D..1019F ; Unknown
     'Grek',  # 101A0..101A0 ; Greek
     'Zzzz',  # 101A1..101CF ; Unknown
     'Zyyy',  # 101D0..101FC ; Common
@@ -2567,12 +2612,12 @@
     'Zzzz',  # 10A14..10A14 ; Unknown
     'Khar',  # 10A15..10A17 ; Kharoshthi
     'Zzzz',  # 10A18..10A18 ; Unknown
-    'Khar',  # 10A19..10A33 ; Kharoshthi
-    'Zzzz',  # 10A34..10A37 ; Unknown
+    'Khar',  # 10A19..10A35 ; Kharoshthi
+    'Zzzz',  # 10A36..10A37 ; Unknown
     'Khar',  # 10A38..10A3A ; Kharoshthi
     'Zzzz',  # 10A3B..10A3E ; Unknown
-    'Khar',  # 10A3F..10A47 ; Kharoshthi
-    'Zzzz',  # 10A48..10A4F ; Unknown
+    'Khar',  # 10A3F..10A48 ; Kharoshthi
+    'Zzzz',  # 10A49..10A4F ; Unknown
     'Khar',  # 10A50..10A58 ; Kharoshthi
     'Zzzz',  # 10A59..10A5F ; Unknown
     'Sarb',  # 10A60..10A7F ; Old_South_Arabian
@@ -2604,29 +2649,46 @@
     'Hung',  # 10CC0..10CF2 ; Old_Hungarian
     'Zzzz',  # 10CF3..10CF9 ; Unknown
     'Hung',  # 10CFA..10CFF ; Old_Hungarian
-    'Zzzz',  # 10D00..10E5F ; Unknown
+    'Rohg',  # 10D00..10D27 ; Hanifi_Rohingya
+    'Zzzz',  # 10D28..10D2F ; Unknown
+    'Rohg',  # 10D30..10D39 ; Hanifi_Rohingya
+    'Zzzz',  # 10D3A..10E5F ; Unknown
     'Arab',  # 10E60..10E7E ; Arabic
-    'Zzzz',  # 10E7F..10FFF ; Unknown
+    'Zzzz',  # 10E7F..10E7F ; Unknown
+    'Yezi',  # 10E80..10EA9 ; Yezidi
+    'Zzzz',  # 10EAA..10EAA ; Unknown
+    'Yezi',  # 10EAB..10EAD ; Yezidi
+    'Zzzz',  # 10EAE..10EAF ; Unknown
+    'Yezi',  # 10EB0..10EB1 ; Yezidi
+    'Zzzz',  # 10EB2..10EFF ; Unknown
+    'Sogo',  # 10F00..10F27 ; Old_Sogdian
+    'Zzzz',  # 10F28..10F2F ; Unknown
+    'Sogd',  # 10F30..10F59 ; Sogdian
+    'Zzzz',  # 10F5A..10FAF ; Unknown
+    'Chrs',  # 10FB0..10FCB ; Chorasmian
+    'Zzzz',  # 10FCC..10FDF ; Unknown
+    'Elym',  # 10FE0..10FF6 ; Elymaic
+    'Zzzz',  # 10FF7..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
+    'Zzzz',  # 110C2..110CC ; Unknown
+    'Kthi',  # 110CD..110CD ; Kaithi
+    'Zzzz',  # 110CE..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
+    'Cakm',  # 11136..11147 ; Chakma
+    'Zzzz',  # 11148..1114F ; Unknown
     'Mahj',  # 11150..11176 ; Mahajani
     'Zzzz',  # 11177..1117F ; Unknown
-    'Shrd',  # 11180..111CD ; Sharada
-    'Zzzz',  # 111CE..111CF ; Unknown
-    'Shrd',  # 111D0..111DF ; Sharada
+    'Shrd',  # 11180..111DF ; Sharada
     'Zzzz',  # 111E0..111E0 ; Unknown
     'Sinh',  # 111E1..111F4 ; Sinhala
     'Zzzz',  # 111F5..111FF ; Unknown
@@ -2661,7 +2723,8 @@
     'Gran',  # 11332..11333 ; Grantha
     'Zzzz',  # 11334..11334 ; Unknown
     'Gran',  # 11335..11339 ; Grantha
-    'Zzzz',  # 1133A..1133B ; Unknown
+    'Zzzz',  # 1133A..1133A ; Unknown
+    'Zinh',  # 1133B..1133B ; Inherited
     'Gran',  # 1133C..11344 ; Grantha
     'Zzzz',  # 11345..11346 ; Unknown
     'Gran',  # 11347..11348 ; Grantha
@@ -2678,12 +2741,10 @@
     'Zzzz',  # 1136D..1136F ; Unknown
     'Gran',  # 11370..11374 ; Grantha
     'Zzzz',  # 11375..113FF ; Unknown
-    'Newa',  # 11400..11459 ; Newa
-    'Zzzz',  # 1145A..1145A ; Unknown
-    'Newa',  # 1145B..1145B ; Newa
+    'Newa',  # 11400..1145B ; Newa
     'Zzzz',  # 1145C..1145C ; Unknown
-    'Newa',  # 1145D..1145D ; Newa
-    'Zzzz',  # 1145E..1147F ; Unknown
+    'Newa',  # 1145D..11461 ; Newa
+    'Zzzz',  # 11462..1147F ; Unknown
     'Tirh',  # 11480..114C7 ; Tirhuta
     'Zzzz',  # 114C8..114CF ; Unknown
     'Tirh',  # 114D0..114D9 ; Tirhuta
@@ -2698,27 +2759,46 @@
     'Zzzz',  # 1165A..1165F ; Unknown
     'Mong',  # 11660..1166C ; Mongolian
     'Zzzz',  # 1166D..1167F ; Unknown
-    'Takr',  # 11680..116B7 ; Takri
-    'Zzzz',  # 116B8..116BF ; Unknown
+    'Takr',  # 11680..116B8 ; Takri
+    'Zzzz',  # 116B9..116BF ; Unknown
     'Takr',  # 116C0..116C9 ; Takri
     'Zzzz',  # 116CA..116FF ; Unknown
-    'Ahom',  # 11700..11719 ; Ahom
-    'Zzzz',  # 1171A..1171C ; Unknown
+    'Ahom',  # 11700..1171A ; Ahom
+    'Zzzz',  # 1171B..1171C ; Unknown
     'Ahom',  # 1171D..1172B ; Ahom
     'Zzzz',  # 1172C..1172F ; Unknown
     'Ahom',  # 11730..1173F ; Ahom
-    'Zzzz',  # 11740..1189F ; Unknown
+    'Zzzz',  # 11740..117FF ; Unknown
+    'Dogr',  # 11800..1183B ; Dogra
+    'Zzzz',  # 1183C..1189F ; Unknown
     'Wara',  # 118A0..118F2 ; Warang_Citi
     'Zzzz',  # 118F3..118FE ; Unknown
     'Wara',  # 118FF..118FF ; Warang_Citi
-    'Zzzz',  # 11900..119FF ; Unknown
+    'Diak',  # 11900..11906 ; Dives_Akuru
+    'Zzzz',  # 11907..11908 ; Unknown
+    'Diak',  # 11909..11909 ; Dives_Akuru
+    'Zzzz',  # 1190A..1190B ; Unknown
+    'Diak',  # 1190C..11913 ; Dives_Akuru
+    'Zzzz',  # 11914..11914 ; Unknown
+    'Diak',  # 11915..11916 ; Dives_Akuru
+    'Zzzz',  # 11917..11917 ; Unknown
+    'Diak',  # 11918..11935 ; Dives_Akuru
+    'Zzzz',  # 11936..11936 ; Unknown
+    'Diak',  # 11937..11938 ; Dives_Akuru
+    'Zzzz',  # 11939..1193A ; Unknown
+    'Diak',  # 1193B..11946 ; Dives_Akuru
+    'Zzzz',  # 11947..1194F ; Unknown
+    'Diak',  # 11950..11959 ; Dives_Akuru
+    'Zzzz',  # 1195A..1199F ; Unknown
+    'Nand',  # 119A0..119A7 ; Nandinagari
+    'Zzzz',  # 119A8..119A9 ; Unknown
+    'Nand',  # 119AA..119D7 ; Nandinagari
+    'Zzzz',  # 119D8..119D9 ; Unknown
+    'Nand',  # 119DA..119E4 ; Nandinagari
+    'Zzzz',  # 119E5..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
+    'Soyo',  # 11A50..11AA2 ; Soyombo
     'Zzzz',  # 11AA3..11ABF ; Unknown
     'Pauc',  # 11AC0..11AF8 ; Pau_Cin_Hau
     'Zzzz',  # 11AF9..11BFF ; Unknown
@@ -2749,7 +2829,26 @@
     'Gonm',  # 11D3F..11D47 ; Masaram_Gondi
     'Zzzz',  # 11D48..11D4F ; Unknown
     'Gonm',  # 11D50..11D59 ; Masaram_Gondi
-    'Zzzz',  # 11D5A..11FFF ; Unknown
+    'Zzzz',  # 11D5A..11D5F ; Unknown
+    'Gong',  # 11D60..11D65 ; Gunjala_Gondi
+    'Zzzz',  # 11D66..11D66 ; Unknown
+    'Gong',  # 11D67..11D68 ; Gunjala_Gondi
+    'Zzzz',  # 11D69..11D69 ; Unknown
+    'Gong',  # 11D6A..11D8E ; Gunjala_Gondi
+    'Zzzz',  # 11D8F..11D8F ; Unknown
+    'Gong',  # 11D90..11D91 ; Gunjala_Gondi
+    'Zzzz',  # 11D92..11D92 ; Unknown
+    'Gong',  # 11D93..11D98 ; Gunjala_Gondi
+    'Zzzz',  # 11D99..11D9F ; Unknown
+    'Gong',  # 11DA0..11DA9 ; Gunjala_Gondi
+    'Zzzz',  # 11DAA..11EDF ; Unknown
+    'Maka',  # 11EE0..11EF8 ; Makasar
+    'Zzzz',  # 11EF9..11FAF ; Unknown
+    'Lisu',  # 11FB0..11FB0 ; Lisu
+    'Zzzz',  # 11FB1..11FBF ; Unknown
+    'Taml',  # 11FC0..11FF1 ; Tamil
+    'Zzzz',  # 11FF2..11FFE ; Unknown
+    'Taml',  # 11FFF..11FFF ; Tamil
     'Xsux',  # 12000..12399 ; Cuneiform
     'Zzzz',  # 1239A..123FF ; Unknown
     'Xsux',  # 12400..1246E ; Cuneiform
@@ -2759,7 +2858,9 @@
     'Xsux',  # 12480..12543 ; Cuneiform
     'Zzzz',  # 12544..12FFF ; Unknown
     'Egyp',  # 13000..1342E ; Egyptian_Hieroglyphs
-    'Zzzz',  # 1342F..143FF ; Unknown
+    'Zzzz',  # 1342F..1342F ; Unknown
+    'Egyp',  # 13430..13438 ; Egyptian_Hieroglyphs
+    'Zzzz',  # 13439..143FF ; Unknown
     'Hluw',  # 14400..14646 ; Anatolian_Hieroglyphs
     'Zzzz',  # 14647..167FF ; Unknown
     'Bamu',  # 16800..16A38 ; Bamum
@@ -2783,23 +2884,36 @@
     '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
+    'Zzzz',  # 16B90..16E3F ; Unknown
+    'Medf',  # 16E40..16E9A ; Medefaidrin
+    'Zzzz',  # 16E9B..16EFF ; Unknown
+    'Plrd',  # 16F00..16F4A ; Miao
+    'Zzzz',  # 16F4B..16F4E ; Unknown
+    'Plrd',  # 16F4F..16F87 ; Miao
+    'Zzzz',  # 16F88..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
+    'Zyyy',  # 16FE2..16FE3 ; Common
+    'Kits',  # 16FE4..16FE4 ; Khitan_Small_Script
+    'Zzzz',  # 16FE5..16FEF ; Unknown
+    'Hani',  # 16FF0..16FF1 ; Han
+    'Zzzz',  # 16FF2..16FFF ; Unknown
+    'Tang',  # 17000..187F7 ; Tangut
+    'Zzzz',  # 187F8..187FF ; Unknown
+    'Tang',  # 18800..18AFF ; Tangut
+    'Kits',  # 18B00..18CD5 ; Khitan_Small_Script
+    'Zzzz',  # 18CD6..18CFF ; Unknown
+    'Tang',  # 18D00..18D08 ; Tangut
+    'Zzzz',  # 18D09..1AFFF ; Unknown
     'Kana',  # 1B000..1B000 ; Katakana
     'Hira',  # 1B001..1B11E ; Hiragana
-    'Zzzz',  # 1B11F..1B16F ; Unknown
+    'Zzzz',  # 1B11F..1B14F ; Unknown
+    'Hira',  # 1B150..1B152 ; Hiragana
+    'Zzzz',  # 1B153..1B163 ; Unknown
+    'Kana',  # 1B164..1B167 ; Katakana
+    'Zzzz',  # 1B168..1B16F ; Unknown
     'Nshu',  # 1B170..1B2FB ; Nushu
     'Zzzz',  # 1B2FC..1BBFF ; Unknown
     'Dupl',  # 1BC00..1BC6A ; Duployan
@@ -2828,11 +2942,13 @@
     'Zyyy',  # 1D1AE..1D1E8 ; Common
     'Zzzz',  # 1D1E9..1D1FF ; Unknown
     'Grek',  # 1D200..1D245 ; Greek
-    'Zzzz',  # 1D246..1D2FF ; Unknown
+    'Zzzz',  # 1D246..1D2DF ; Unknown
+    'Zyyy',  # 1D2E0..1D2F3 ; Common
+    'Zzzz',  # 1D2F4..1D2FF ; Unknown
     'Zyyy',  # 1D300..1D356 ; Common
     'Zzzz',  # 1D357..1D35F ; Unknown
-    'Zyyy',  # 1D360..1D371 ; Common
-    'Zzzz',  # 1D372..1D3FF ; Unknown
+    'Zyyy',  # 1D360..1D378 ; Common
+    'Zzzz',  # 1D379..1D3FF ; Unknown
     'Zyyy',  # 1D400..1D454 ; Common
     'Zzzz',  # 1D455..1D455 ; Unknown
     'Zyyy',  # 1D456..1D49C ; Common
@@ -2889,17 +3005,33 @@
     'Glag',  # 1E023..1E024 ; Glagolitic
     'Zzzz',  # 1E025..1E025 ; Unknown
     'Glag',  # 1E026..1E02A ; Glagolitic
-    'Zzzz',  # 1E02B..1E7FF ; Unknown
+    'Zzzz',  # 1E02B..1E0FF ; Unknown
+    'Hmnp',  # 1E100..1E12C ; Nyiakeng_Puachue_Hmong
+    'Zzzz',  # 1E12D..1E12F ; Unknown
+    'Hmnp',  # 1E130..1E13D ; Nyiakeng_Puachue_Hmong
+    'Zzzz',  # 1E13E..1E13F ; Unknown
+    'Hmnp',  # 1E140..1E149 ; Nyiakeng_Puachue_Hmong
+    'Zzzz',  # 1E14A..1E14D ; Unknown
+    'Hmnp',  # 1E14E..1E14F ; Nyiakeng_Puachue_Hmong
+    'Zzzz',  # 1E150..1E2BF ; Unknown
+    'Wcho',  # 1E2C0..1E2F9 ; Wancho
+    'Zzzz',  # 1E2FA..1E2FE ; Unknown
+    'Wcho',  # 1E2FF..1E2FF ; Wancho
+    'Zzzz',  # 1E300..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',  # 1E900..1E94B ; Adlam
+    'Zzzz',  # 1E94C..1E94F ; Unknown
     'Adlm',  # 1E950..1E959 ; Adlam
     'Zzzz',  # 1E95A..1E95D ; Unknown
     'Adlm',  # 1E95E..1E95F ; Adlam
-    'Zzzz',  # 1E960..1EDFF ; Unknown
+    'Zzzz',  # 1E960..1EC70 ; Unknown
+    'Zyyy',  # 1EC71..1ECB4 ; Common
+    'Zzzz',  # 1ECB5..1ED00 ; Unknown
+    'Zyyy',  # 1ED01..1ED3D ; Common
+    'Zzzz',  # 1ED3E..1EDFF ; Unknown
     'Arab',  # 1EE00..1EE03 ; Arabic
     'Zzzz',  # 1EE04..1EE04 ; Unknown
     'Arab',  # 1EE05..1EE1F ; Arabic
@@ -2980,14 +3112,8 @@
     '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',  # 1F100..1F1AD ; Common
+    'Zzzz',  # 1F1AE..1F1E5 ; Unknown
     'Zyyy',  # 1F1E6..1F1FF ; Common
     'Hira',  # 1F200..1F200 ; Hiragana
     'Zyyy',  # 1F201..1F202 ; Common
@@ -3000,16 +3126,18 @@
     'Zzzz',  # 1F252..1F25F ; Unknown
     'Zyyy',  # 1F260..1F265 ; Common
     'Zzzz',  # 1F266..1F2FF ; Unknown
-    'Zyyy',  # 1F300..1F6D4 ; Common
-    'Zzzz',  # 1F6D5..1F6DF ; Unknown
+    'Zyyy',  # 1F300..1F6D7 ; Common
+    'Zzzz',  # 1F6D8..1F6DF ; Unknown
     'Zyyy',  # 1F6E0..1F6EC ; Common
     'Zzzz',  # 1F6ED..1F6EF ; Unknown
-    'Zyyy',  # 1F6F0..1F6F8 ; Common
-    'Zzzz',  # 1F6F9..1F6FF ; Unknown
+    'Zyyy',  # 1F6F0..1F6FC ; Common
+    'Zzzz',  # 1F6FD..1F6FF ; Unknown
     'Zyyy',  # 1F700..1F773 ; Common
     'Zzzz',  # 1F774..1F77F ; Unknown
-    'Zyyy',  # 1F780..1F7D4 ; Common
-    'Zzzz',  # 1F7D5..1F7FF ; Unknown
+    'Zyyy',  # 1F780..1F7D8 ; Common
+    'Zzzz',  # 1F7D9..1F7DF ; Unknown
+    'Zyyy',  # 1F7E0..1F7EB ; Common
+    'Zzzz',  # 1F7EC..1F7FF ; Unknown
     'Zyyy',  # 1F800..1F80B ; Common
     'Zzzz',  # 1F80C..1F80F ; Unknown
     'Zyyy',  # 1F810..1F847 ; Common
@@ -3019,23 +3147,39 @@
     '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
+    'Zzzz',  # 1F8AE..1F8AF ; Unknown
+    'Zyyy',  # 1F8B0..1F8B1 ; Common
+    'Zzzz',  # 1F8B2..1F8FF ; Unknown
+    'Zyyy',  # 1F900..1F978 ; Common
+    'Zzzz',  # 1F979..1F979 ; Unknown
+    'Zyyy',  # 1F97A..1F9CB ; Common
+    'Zzzz',  # 1F9CC..1F9CC ; Unknown
+    'Zyyy',  # 1F9CD..1FA53 ; Common
+    'Zzzz',  # 1FA54..1FA5F ; Unknown
+    'Zyyy',  # 1FA60..1FA6D ; Common
+    'Zzzz',  # 1FA6E..1FA6F ; Unknown
+    'Zyyy',  # 1FA70..1FA74 ; Common
+    'Zzzz',  # 1FA75..1FA77 ; Unknown
+    'Zyyy',  # 1FA78..1FA7A ; Common
+    'Zzzz',  # 1FA7B..1FA7F ; Unknown
+    'Zyyy',  # 1FA80..1FA86 ; Common
+    'Zzzz',  # 1FA87..1FA8F ; Unknown
+    'Zyyy',  # 1FA90..1FAA8 ; Common
+    'Zzzz',  # 1FAA9..1FAAF ; Unknown
+    'Zyyy',  # 1FAB0..1FAB6 ; Common
+    'Zzzz',  # 1FAB7..1FABF ; Unknown
+    'Zyyy',  # 1FAC0..1FAC2 ; Common
+    'Zzzz',  # 1FAC3..1FACF ; Unknown
+    'Zyyy',  # 1FAD0..1FAD6 ; Common
+    'Zzzz',  # 1FAD7..1FAFF ; Unknown
+    'Zyyy',  # 1FB00..1FB92 ; Common
+    'Zzzz',  # 1FB93..1FB93 ; Unknown
+    'Zyyy',  # 1FB94..1FBCA ; Common
+    'Zzzz',  # 1FBCB..1FBEF ; Unknown
+    'Zyyy',  # 1FBF0..1FBF9 ; Common
+    'Zzzz',  # 1FBFA..1FFFF ; Unknown
+    'Hani',  # 20000..2A6DD ; Han
+    'Zzzz',  # 2A6DE..2A6FF ; Unknown
     'Hani',  # 2A700..2B734 ; Han
     'Zzzz',  # 2B735..2B73F ; Unknown
     'Hani',  # 2B740..2B81D ; Han
@@ -3045,7 +3189,9 @@
     'Hani',  # 2CEB0..2EBE0 ; Han
     'Zzzz',  # 2EBE1..2F7FF ; Unknown
     'Hani',  # 2F800..2FA1D ; Han
-    'Zzzz',  # 2FA1E..E0000 ; Unknown
+    'Zzzz',  # 2FA1E..2FFFF ; Unknown
+    'Hani',  # 30000..3134A ; Han
+    'Zzzz',  # 3134B..E0000 ; Unknown
     'Zyyy',  # E0001..E0001 ; Common
     'Zzzz',  # E0002..E001F ; Unknown
     'Zyyy',  # E0020..E007F ; Common
@@ -3078,17 +3224,22 @@
     'Cari': 'Carian',
     'Cham': 'Cham',
     'Cher': 'Cherokee',
+    'Chrs': 'Chorasmian',
     'Copt': 'Coptic',
     'Cprt': 'Cypriot',
     'Cyrl': 'Cyrillic',
     'Deva': 'Devanagari',
+    'Diak': 'Dives_Akuru',
+    'Dogr': 'Dogra',
     'Dsrt': 'Deseret',
     'Dupl': 'Duployan',
     'Egyp': 'Egyptian_Hieroglyphs',
     'Elba': 'Elbasan',
+    'Elym': 'Elymaic',
     'Ethi': 'Ethiopic',
     'Geor': 'Georgian',
     'Glag': 'Glagolitic',
+    'Gong': 'Gunjala_Gondi',
     'Gonm': 'Masaram_Gondi',
     'Goth': 'Gothic',
     'Gran': 'Grantha',
@@ -3103,6 +3254,7 @@
     'Hira': 'Hiragana',
     'Hluw': 'Anatolian_Hieroglyphs',
     'Hmng': 'Pahawh_Hmong',
+    'Hmnp': 'Nyiakeng_Puachue_Hmong',
     'Hrkt': 'Katakana_Or_Hiragana',
     'Hung': 'Old_Hungarian',
     'Ital': 'Old_Italic',
@@ -3112,6 +3264,7 @@
     'Khar': 'Kharoshthi',
     'Khmr': 'Khmer',
     'Khoj': 'Khojki',
+    'Kits': 'Khitan_Small_Script',
     'Knda': 'Kannada',
     'Kthi': 'Kaithi',
     'Lana': 'Tai_Tham',
@@ -3125,9 +3278,11 @@
     'Lyci': 'Lycian',
     'Lydi': 'Lydian',
     'Mahj': 'Mahajani',
+    'Maka': 'Makasar',
     'Mand': 'Mandaic',
     'Mani': 'Manichaean',
     'Marc': 'Marchen',
+    'Medf': 'Medefaidrin',
     'Mend': 'Mende_Kikakui',
     'Merc': 'Meroitic_Cursive',
     'Mero': 'Meroitic_Hieroglyphs',
@@ -3138,6 +3293,7 @@
     'Mtei': 'Meetei_Mayek',
     'Mult': 'Multani',
     'Mymr': 'Myanmar',
+    'Nand': 'Nandinagari',
     'Narb': 'Old_North_Arabian',
     'Nbat': 'Nabataean',
     'Newa': 'Newa',
@@ -3159,6 +3315,7 @@
     'Plrd': 'Miao',
     'Prti': 'Inscriptional_Parthian',
     'Rjng': 'Rejang',
+    'Rohg': 'Hanifi_Rohingya',
     'Runr': 'Runic',
     'Samr': 'Samaritan',
     'Sarb': 'Old_South_Arabian',
@@ -3169,6 +3326,8 @@
     'Sidd': 'Siddham',
     'Sind': 'Khudawadi',
     'Sinh': 'Sinhala',
+    'Sogd': 'Sogdian',
+    'Sogo': 'Old_Sogdian',
     'Sora': 'Sora_Sompeng',
     'Soyo': 'Soyombo',
     'Sund': 'Sundanese',
@@ -3191,8 +3350,10 @@
     'Ugar': 'Ugaritic',
     'Vaii': 'Vai',
     'Wara': 'Warang_Citi',
+    'Wcho': 'Wancho',
     'Xpeo': 'Old_Persian',
     'Xsux': 'Cuneiform',
+    'Yezi': 'Yezidi',
     'Yiii': 'Yi',
     'Zanb': 'Zanabazar_Square',
     'Zinh': 'Inherited',
diff --git a/Lib/fontTools/unicodedata/__init__.py b/Lib/fontTools/unicodedata/__init__.py
index c2bc29e..8845b82 100644
--- a/Lib/fontTools/unicodedata/__init__.py
+++ b/Lib/fontTools/unicodedata/__init__.py
@@ -1,6 +1,4 @@
-from __future__ import (
-    print_function, division, absolute_import, unicode_literals)
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import byteord, tostr
 
 import re
 from bisect import bisect_right
@@ -13,7 +11,7 @@
     # fall back to built-in unicodedata (possibly outdated)
     from unicodedata import *
 
-from . import Blocks, Scripts, ScriptExtensions
+from . import Blocks, Scripts, ScriptExtensions, OTTags
 
 
 __all__ = [tostr(s) for s in (
@@ -38,6 +36,9 @@
     "script_extension",
     "script_name",
     "script_code",
+    "script_horizontal_direction",
+    "ot_tags_from_script",
+    "ot_tag_to_script",
 )]
 
 
@@ -49,7 +50,7 @@
     'Latn'
     >>> script(",")
     'Zyyy'
-    >>> script(unichr(0x10FFFF))
+    >>> script(chr(0x10FFFF))
     'Zzzz'
     """
     code = byteord(char)
@@ -72,9 +73,9 @@
 
     >>> script_extension("a") == {'Latn'}
     True
-    >>> script_extension(unichr(0x060C)) == {'Arab', 'Syrc', 'Thaa'}
+    >>> script_extension(chr(0x060C)) == {'Rohg', 'Syrc', 'Yezi', 'Arab', 'Thaa'}
     True
-    >>> script_extension(unichr(0x10FFFF)) == {'Zzzz'}
+    >>> script_extension(chr(0x10FFFF)) == {'Zzzz'}
     True
     """
     code = byteord(char)
@@ -133,17 +134,151 @@
         return default
 
 
+# The data on script direction is taken from CLDR 37:
+# https://github.com/unicode-org/cldr/blob/release-37/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
+
+    # Unicode-11.0 additions
+    'Rohg',  # Hanifi Rohingya
+    'Sogo',  # Old Sogdian
+    'Sogd',  # Sogdian
+
+    # Unicode-12.0 additions
+    'Elym',  # Elymaic
+
+    # Unicode-13.0 additions
+    'Chrs',  # Chorasmian
+    'Yezi',  # Yezidi
+}
+
+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))
+    >>> block(chr(0x060C))
     'Arabic'
-    >>> block(unichr(0xEFFFF))
+    >>> block(chr(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
index 589b96f..9d39747 100644
--- a/Lib/fontTools/varLib/__init__.py
+++ b/Lib/fontTools/varLib/__init__.py
@@ -10,39 +10,44 @@
 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
+	$ fontmake -o ttf-interpolatable -g NotoSansArabic-MM.glyphs
 
 Then you can make a variable-font this way:
 
-  $ fonttools varLib master_ufo/NotoSansArabic.designspace
+	$ 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.arrayTools import Vector
+from fontTools.misc.py23 import Tag, tostr
+from fontTools.misc.roundTools import noRound, otRound
+from fontTools.misc.vector 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.varLib import builder, designspace, models, varStore
-from fontTools.varLib.merger import VariationMerger, _all_equal
+from fontTools.ttLib.tables.otBase import OTTableWriter
+from fontTools.varLib import builder, models, varStore
+from fontTools.varLib.merger import VariationMerger
 from fontTools.varLib.mvar import MVAR_ENTRIES
 from fontTools.varLib.iup import iup_delta_optimize
-from collections import OrderedDict
+from fontTools.varLib.featureVars import addFeatureVariations
+from fontTools.designspaceLib import DesignSpaceDocument
+from functools import partial
+from collections import OrderedDict, namedtuple
 import os.path
 import logging
+from copy import deepcopy
 from pprint import pformat
+from .errors import VarLibError, VarLibValidationError
 
 log = logging.getLogger("fontTools.varLib")
 
-
-class VarLibError(Exception):
-	pass
+# This is a lib key for the designspace document. The value should be
+# an OpenType feature tag, to be used as the FeatureVariations feature.
+# If present, the DesignSpace <rules processing="..."> flag is ignored.
+FEAVAR_FEATURETAG_LIB_KEY = "com.github.fonttools.varLib.featureVarsFeatureTag"
 
 #
 # Creation routines
@@ -71,23 +76,31 @@
 		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)
+		axis.axisNameID = nameTable.addMultilingualName(a.labelNames, font, minNameID=256)
+		axis.flags = int(a.hidden)
 		fvar.axes.append(axis)
 
 	for instance in instances:
-		coordinates = instance['location']
-		name = tounicode(instance['stylename'])
-		psname = instance.get('postscriptfontname')
+		coordinates = instance.location
+
+		if "en" not in instance.localisedStyleName:
+			if not instance.styleName:
+				raise VarLibValidationError(
+					f"Instance at location '{coordinates}' must have a default English "
+					"style name ('stylename' attribute on the instance element or a "
+					"stylename element with an 'xml:lang=\"en\"' attribute)."
+				)
+			localisedStyleName = dict(instance.localisedStyleName)
+			localisedStyleName["en"] = tostr(instance.styleName)
+		else:
+			localisedStyleName = instance.localisedStyleName
+
+		psname = instance.postScriptFontName
 
 		inst = NamedInstance()
-		inst.subfamilyNameID = nameTable.addName(name)
+		inst.subfamilyNameID = nameTable.addMultilingualName(localisedStyleName)
 		if psname is not None:
-			psname = tounicode(psname)
+			psname = tostr(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()}
@@ -102,7 +115,7 @@
 	"""
 	Add 'avar' table to font.
 
-	axes is an ordered dictionary of DesignspaceAxis objects.
+	axes is an ordered dictionary of AxisDescriptor objects.
 	"""
 
 	assert axes
@@ -125,21 +138,39 @@
 		if not axis.map:
 			continue
 
-		items = sorted(axis.map.items())
+		items = sorted(axis.map)
 		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)
+		if axis.minimum != min(keys):
+			raise VarLibValidationError(
+				f"Axis '{axis.name}': there must be a mapping for the axis minimum "
+				f"value {axis.minimum} and it must be the lowest input mapping value."
+			)
+		if axis.maximum != max(keys):
+			raise VarLibValidationError(
+				f"Axis '{axis.name}': there must be a mapping for the axis maximum "
+				f"value {axis.maximum} and it must be the highest input mapping value."
+			)
+		if axis.default not in keys:
+			raise VarLibValidationError(
+				f"Axis '{axis.name}': there must be a mapping for the axis default "
+				f"value {axis.default}."
+			)
+		# No duplicate input values (output values can be >= their preceeding value).
+		if len(set(keys)) != len(keys):
+			raise VarLibValidationError(
+				f"Axis '{axis.name}': All axis mapping input='...' values must be "
+				"unique, but we found duplicates."
+			)
 		# Ascending values
-		assert sorted(vals) == vals
+		if sorted(vals) != vals:
+			raise VarLibValidationError(
+				f"Axis '{axis.name}': mapping output values must be in ascending order."
+			)
 
 		keys_triple = (axis.minimum, axis.default, axis.maximum)
 		vals_triple = tuple(axis.map_forward(v) for v in keys_triple)
@@ -168,148 +199,98 @@
 	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
 
-	nameTable = font['name']
+	if "STAT" in font:
+		return
 
-	assert "STAT" not in font
-	STAT = font["STAT"] = newTable('STAT')
-	stat = STAT.table = ot.STAT()
-	stat.Version = 0x00010000
+	from ..otlLib.builder import buildStatTable
+	fvarTable = font['fvar']
+	axes = [dict(tag=a.axisTag, name=a.axisNameID) for a in fvarTable.axes]
+	buildStatTable(font, axes)
 
-	axisRecords = []
-	for i,a in enumerate(axes.values()):
-		axis = ot.AxisRecord()
-		axis.AxisTag = Tag(a.tag)
-		# Meh. Reuse fvar nameID!
-		axis.AxisNameID = nameTable.addName(tounicode(a.labelname['en']))
-		axis.AxisOrdering = i
-		axisRecords.append(axis)
+_MasterData = namedtuple('_MasterData', ['glyf', 'hMetrics', 'vMetrics'])
 
-	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
-
-# 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 = round(rightSideX - leftSideX)
-	leftSideBearing = round(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
+def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
+	if tolerance < 0:
+		raise ValueError("`tolerance` must be a positive number.")
 
 	log.info("Generating gvar")
 	assert "gvar" not in font
 	gvar = font["gvar"] = newTable('gvar')
-	gvar.version = 1
-	gvar.reserved = 0
-	gvar.variations = {}
+	glyf = font['glyf']
+	defaultMasterIndex = masterModel.reverseMapping[0]
+
+	master_datas = [_MasterData(m['glyf'],
+				    m['hmtx'].metrics,
+				    getattr(m.get('vmtx'), 'metrics', None))
+			for m in master_ttfs]
 
 	for glyph in font.getGlyphOrder():
 
-		allData = [_GetCoordinates(m, glyph) for m in master_ttfs]
+		isComposite = glyf[glyph].isComposite()
+
+		allData = [
+			m.glyf._getCoordinatesAndControls(glyph, m.hMetrics, m.vMetrics)
+			for m in master_datas
+		]
+
+		if allData[defaultMasterIndex][1].numberOfContours != 0:
+			# If the default master is not empty, interpret empty non-default masters
+			# as missing glyphs from a sparse master
+			allData = [
+				d if d is not None and d[1].numberOfContours != 0 else None
+				for d in allData
+			]
+
+		model, allData = masterModel.getSubModel(allData)
+
 		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)):
+		if not models.allEqual(allControls):
 			log.warning("glyph %s has incompatible masters; skipping" % glyph)
 			continue
 		del allControls
 
 		# Update gvar
 		gvar.variations[glyph] = []
-		deltas = model.getDeltas(allCoords)
+		deltas = model.getDeltas(allCoords, round=partial(GlyphCoordinates.__round__, round=round))
 		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])))
+		endPts = control.endPts
 
 		for i,(delta,support) in enumerate(zip(deltas[1:], supports[1:])):
-			if all(abs(v) <= tolerance for v in delta.array):
+			if all(v == 0 for v in delta.array) and not isComposite:
 				continue
 			var = TupleVariation(support, delta)
 			if optimize:
 				delta_opt = iup_delta_optimize(delta, origCoords, endPts, tolerance=tolerance)
 
 				if None in delta_opt:
+					"""In composite glyphs, there should be one 0 entry
+					to make sure the gvar entry is written to the font.
+
+					This is to work around an issue with macOS 10.14 and can be
+					removed once the behaviour of macOS is changed.
+
+					https://github.com/fonttools/fonttools/issues/1381
+					"""
+					if all(d is None for d in delta_opt):
+						delta_opt = [(0, 0)] + [None] * (len(delta_opt) - 1)
 					# 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)
+					tupleData, auxData = var.compile(axis_tags)
 					unoptimized_len = len(tupleData) + len(auxData)
-					tupleData, auxData, _ = var_opt.compile(axis_tags, [], None)
+					tupleData, auxData = var_opt.compile(axis_tags)
 					optimized_len = len(tupleData) + len(auxData)
 
 					if optimized_len < unoptimized_len:
@@ -317,17 +298,19 @@
 
 			gvar.variations[glyph].append(var)
 
+
 def _remove_TTHinting(font):
 	for tag in ("cvar", "cvt ", "fpgm", "prep"):
 		if tag in font:
 			del font[tag]
+	maxp = font['maxp']
 	for attr in ("maxTwilightPoints", "maxStorage", "maxFunctionDefs", "maxInstructionDefs", "maxStackElements", "maxSizeOfInstructions"):
-		setattr(font["maxp"], attr, 0)
-	font["maxp"].maxZones = 1
+		setattr(maxp, attr, 0)
+	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):
+def _merge_TTHinting(font, masterModel, master_ttfs):
 
 	log.info("Merging TT hinting")
 	assert "cvar" not in font
@@ -338,12 +321,9 @@
 
 	for tag in ("fpgm", "prep"):
 		all_pgms = [m[tag].program for m in master_ttfs if tag in m]
-		if len(all_pgms) == 0:
+		if not all_pgms:
 			continue
-		if tag in font:
-			font_pgm = font[tag].program
-		else:
-			font_pgm = Program()
+		font_pgm = getattr(font.get(tag), 'program', None)
 		if any(pgm != font_pgm for pgm in all_pgms):
 			log.warning("Masters have incompatible %s tables, hinting is discarded." % tag)
 			_remove_TTHinting(font)
@@ -351,134 +331,238 @@
 
 	# glyf table
 
-	for name, glyph in font["glyf"].glyphs.items():
+	font_glyf = font['glyf']
+	master_glyfs = [m['glyf'] for m in master_ttfs]
+	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")
+			getattr(glyf.get(name), 'program', None)
+			for glyf in master_glyfs
 		]
 		if not any(all_pgms):
 			continue
-		glyph.expand(font["glyf"])
-		if hasattr(glyph, "program"):
-			font_pgm = glyph.program
-		else:
-			font_pgm = Program()
+		glyph.expand(font_glyf)
+		font_pgm = getattr(glyph, 'program', None)
 		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)
+			# TODO Only drop hinting from this glyph.
 			_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:
+	all_cvs = [Vector(m["cvt "].values) if 'cvt ' in m else None
+		   for m in master_ttfs]
+
+	nonNone_cvs = models.nonNone(all_cvs)
+	if not nonNone_cvs:
 		# 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)):
+	if not models.allEqual(len(c) for c in nonNone_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
+	variations = []
+	deltas, supports = masterModel.getDeltasAndSupports(all_cvs, round=round) # builtin round calls into Vector.__round__, which uses builtin round as we like
 	for i,(delta,support) in enumerate(zip(deltas[1:], supports[1:])):
-		delta = [round(d) for d in delta]
-		if all(abs(v) <= tolerance for v in delta):
+		if all(v == 0 for v in delta):
 			continue
 		var = TupleVariation(support, delta)
-		cvar.variations.append(var)
+		variations.append(var)
 
-def _add_HVAR(font, model, master_ttfs, axisTags):
+	# We can build the cvar table now.
+	if variations:
+		cvar = font["cvar"] = newTable('cvar')
+		cvar.version = 1
+		cvar.variations = variations
 
-	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(round(d) for d in model.getDeltas(hAdvances)[1:])
+_MetricsFields = namedtuple('_MetricsFields',
+	['tableTag', 'metricsTag', 'sb1', 'sb2', 'advMapping', 'vOrigMapping'])
 
-	# We only support the direct mapping right now.
+HVAR_FIELDS = _MetricsFields(tableTag='HVAR', metricsTag='hmtx', sb1='LsbMap',
+	sb2='RsbMap', advMapping='AdvWidthMap', vOrigMapping=None)
 
-	supports = model.supports[1:]
-	varTupleList = builder.buildVarRegionList(supports, axisTags)
-	varTupleIndexes = list(range(len(supports)))
-	n = len(supports)
-	items = []
-	zeroes = [0]*n
-	for glyphName in font.getGlyphOrder():
-		items.append(hAdvanceDeltas.get(glyphName, zeroes))
-	while items and items[-1] is zeroes:
-		del items[-1]
+VVAR_FIELDS = _MetricsFields(tableTag='VVAR', metricsTag='vmtx', sb1='TsbMap',
+	sb2='BsbMap', advMapping='AdvHeightMap', vOrigMapping='VOrgMap')
 
-	advanceMapping = None
-	# Add indirect mapping to save on duplicates
-	uniq = set(items)
-	# TODO Improve heuristic
-	if (len(items) - len(uniq)) * len(varTupleIndexes) > len(items):
-		newItems = sorted(uniq)
-		mapper = {v:i for i,v in enumerate(newItems)}
-		mapping = [mapper[item] for item in items]
-		while len(mapping) > 1 and mapping[-1] == mapping[-2]:
-			del mapping[-1]
-		advanceMapping = builder.buildVarIdxMap(mapping)
-		items = newItems
-		del mapper, mapping, newItems
-	del uniq
+def _add_HVAR(font, masterModel, master_ttfs, axisTags):
+	_add_VHVAR(font, masterModel, master_ttfs, axisTags, HVAR_FIELDS)
 
-	varData = builder.buildVarData(varTupleIndexes, items)
-	varstore = builder.buildVarStore(varTupleList, [varData])
+def _add_VVAR(font, masterModel, master_ttfs, axisTags):
+	_add_VHVAR(font, masterModel, master_ttfs, axisTags, VVAR_FIELDS)
 
-	assert "HVAR" not in font
-	HVAR = font["HVAR"] = newTable('HVAR')
-	hvar = HVAR.table = ot.HVAR()
-	hvar.Version = 0x00010000
-	hvar.VarStore = varstore
-	hvar.AdvWidthMap = advanceMapping
-	hvar.LsbMap = hvar.RsbMap = None
+def _add_VHVAR(font, masterModel, master_ttfs, axisTags, tableFields):
 
-def _add_MVAR(font, model, master_ttfs, axisTags):
+	tableTag = tableFields.tableTag
+	assert tableTag not in font
+	log.info("Generating " + tableTag)
+	VHVAR = newTable(tableTag)
+	tableClass = getattr(ot, tableTag)
+	vhvar = VHVAR.table = tableClass()
+	vhvar.Version = 0x00010000
+
+	glyphOrder = font.getGlyphOrder()
+
+	# Build list of source font advance widths for each glyph
+	metricsTag = tableFields.metricsTag
+	advMetricses = [m[metricsTag].metrics for m in master_ttfs]
+
+	# Build list of source font vertical origin coords for each glyph
+	if tableTag == 'VVAR' and 'VORG' in master_ttfs[0]:
+		vOrigMetricses = [m['VORG'].VOriginRecords for m in master_ttfs]
+		defaultYOrigs = [m['VORG'].defaultVertOriginY for m in master_ttfs]
+		vOrigMetricses = list(zip(vOrigMetricses, defaultYOrigs))
+	else:
+		vOrigMetricses = None
+
+	metricsStore, advanceMapping, vOrigMapping = _get_advance_metrics(font,
+		masterModel, master_ttfs, axisTags, glyphOrder, advMetricses,
+		vOrigMetricses)
+
+	vhvar.VarStore = metricsStore
+	if advanceMapping is None:
+		setattr(vhvar, tableFields.advMapping, None)
+	else:
+		setattr(vhvar, tableFields.advMapping, advanceMapping)
+	if vOrigMapping is not None:
+		setattr(vhvar, tableFields.vOrigMapping, vOrigMapping)
+	setattr(vhvar, tableFields.sb1, None)
+	setattr(vhvar, tableFields.sb2, None)
+
+	font[tableTag] = VHVAR
+	return
+
+def _get_advance_metrics(font, masterModel, master_ttfs,
+		axisTags, glyphOrder, advMetricses, vOrigMetricses=None):
+
+	vhAdvanceDeltasAndSupports = {}
+	vOrigDeltasAndSupports = {}
+	for glyph in glyphOrder:
+		vhAdvances = [metrics[glyph][0] if glyph in metrics else None for metrics in advMetricses]
+		vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vhAdvances, round=round)
+
+	singleModel = models.allEqual(id(v[1]) for v in vhAdvanceDeltasAndSupports.values())
+
+	if vOrigMetricses:
+		singleModel = False
+		for glyph in glyphOrder:
+			# We need to supply a vOrigs tuple with non-None default values
+			# for each glyph. vOrigMetricses contains values only for those
+			# glyphs which have a non-default vOrig.
+			vOrigs = [metrics[glyph] if glyph in metrics else defaultVOrig
+				for metrics, defaultVOrig in vOrigMetricses]
+			vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vOrigs, round=round)
+
+	directStore = None
+	if singleModel:
+		# Build direct mapping
+		supports = next(iter(vhAdvanceDeltasAndSupports.values()))[1][1:]
+		varTupleList = builder.buildVarRegionList(supports, axisTags)
+		varTupleIndexes = list(range(len(supports)))
+		varData = builder.buildVarData(varTupleIndexes, [], optimize=False)
+		for glyphName in glyphOrder:
+			varData.addItem(vhAdvanceDeltasAndSupports[glyphName][0], round=noRound)
+		varData.optimize()
+		directStore = builder.buildVarStore(varTupleList, [varData])
+
+	# Build optimized indirect mapping
+	storeBuilder = varStore.OnlineVarStoreBuilder(axisTags)
+	advMapping = {}
+	for glyphName in glyphOrder:
+		deltas, supports = vhAdvanceDeltasAndSupports[glyphName]
+		storeBuilder.setSupports(supports)
+		advMapping[glyphName] = storeBuilder.storeDeltas(deltas, round=noRound)
+
+	if vOrigMetricses:
+		vOrigMap = {}
+		for glyphName in glyphOrder:
+			deltas, supports = vOrigDeltasAndSupports[glyphName]
+			storeBuilder.setSupports(supports)
+			vOrigMap[glyphName] = storeBuilder.storeDeltas(deltas, round=noRound)
+
+	indirectStore = storeBuilder.finish()
+	mapping2 = indirectStore.optimize()
+	advMapping = [mapping2[advMapping[g]] for g in glyphOrder]
+	advanceMapping = builder.buildVarIdxMap(advMapping, glyphOrder)
+
+	if vOrigMetricses:
+		vOrigMap = [mapping2[vOrigMap[g]] for g in glyphOrder]
+
+	useDirect = False
+	vOrigMapping = None
+	if directStore:
+		# 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())
+
+		useDirect = directSize < indirectSize
+
+	if useDirect:
+		metricsStore = directStore
+		advanceMapping = None
+	else:
+		metricsStore = indirectStore
+		if vOrigMetricses:
+			vOrigMapping = builder.buildVarIdxMap(vOrigMap, glyphOrder)
+
+	return metricsStore, advanceMapping, vOrigMapping
+
+def _add_MVAR(font, masterModel, master_ttfs, axisTags):
 
 	log.info("Generating MVAR")
 
 	store_builder = varStore.OnlineVarStoreBuilder(axisTags)
-	store_builder.setModel(model)
 
 	records = []
 	lastTableTag = None
 	fontTable = None
 	tables = None
+	# HACK: we need to special-case post.underlineThickness and .underlinePosition
+	# and unilaterally/arbitrarily define a sentinel value to distinguish the case
+	# when a post table is present in a given master simply because that's where
+	# the glyph names in TrueType must be stored, but the underline values are not
+	# meant to be used for building MVAR's deltas. The value of -0x8000 (-36768)
+	# the minimum FWord (int16) value, was chosen for its unlikelyhood to appear
+	# in real-world underline position/thickness values.
+	specialTags = {"unds": -0x8000, "undo": -0x8000}
+
 	for tag, (tableTag, itemName) in sorted(MVAR_ENTRIES.items(), key=lambda kv: kv[1]):
+		# For each tag, fetch the associated table from all fonts (or not when we are
+		# still looking at a tag from the same tables) and set up the variation model
+		# for them.
 		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]
+				tables = []
+				for master in master_ttfs:
+					if tableTag not in master or (
+						tag in specialTags
+						and getattr(master[tableTag], itemName) == specialTags[tag]
+					):
+						tables.append(None)
+					else:
+						tables.append(master[tableTag])
+				model, tables = masterModel.getSubModel(tables)
+				store_builder.setModel(model)
 			lastTableTag = tableTag
-		if tables is None:
+
+		if tables is None:  # Tag not applicable to the master font.
 			continue
 
 		# TODO support gasp entries
 
 		master_values = [getattr(table, itemName) for table in tables]
-		if _all_equal(master_values):
+		if models.allEqual(master_values):
 			base, varIdx = master_values[0], None
 		else:
 			base, varIdx = store_builder.storeMasters(master_values)
@@ -494,167 +578,215 @@
 
 	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_builder.finish()
+		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 _add_BASE(font, masterModel, master_ttfs, axisTags):
+
+	log.info("Generating BASE")
+
+	merger = VariationMerger(masterModel, axisTags, font)
+	merger.mergeTables(font, master_ttfs, ['BASE'])
+	store = merger.store_builder.finish()
+
+	if not store.VarData:
+		return
+	base = font['BASE'].table
+	assert base.Version == 0x00010000
+	base.Version = 0x00010001
+	base.VarStore = store
+
+
 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'])
+	merger.mergeTables(font, master_fonts, ['GSUB', 'GDEF', 'GPOS'])
 	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')
+		font['GDEF'] = newTable('GDEF')
 		GDEFTable = font["GDEF"] = newTable('GDEF')
 		GDEF = GDEFTable.table = ot.GDEF()
+		GDEF.GlyphClassDef = None
+		GDEF.AttachList = None
+		GDEF.LigCaretList = None
+		GDEF.MarkAttachClassDef = None
+		GDEF.MarkGlyphSetsDef = None
+
 	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 _add_GSUB_feature_variations(font, axes, internal_axis_supports, rules, featureTag):
 
-	def __repr__(self):
-		return repr(self.__dict__)
+	def normalize(name, value):
+		return models.normalizeLocation(
+			{name: value}, internal_axis_supports
+		)[name]
 
-	@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)
+	log.info("Generating GSUB FeatureVariations")
 
-	def map_forward(self, v):
-		if self.map is None: return v
-		return self._map(v, self.map)
+	axis_tags = {name: axis.tag for name, axis in axes.items()}
 
-	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)
+	conditional_subs = []
+	for rule in rules:
+
+		region = []
+		for conditions in rule.conditionSets:
+			space = {}
+			for condition in conditions:
+				axis_name = condition["name"]
+				if condition["minimum"] is not None:
+					minimum = normalize(axis_name, condition["minimum"])
+				else:
+					minimum = -1.0
+				if condition["maximum"] is not None:
+					maximum = normalize(axis_name, condition["maximum"])
+				else:
+					maximum = 1.0
+				tag = axis_tags[axis_name]
+				space[tag] = (minimum, maximum)
+			region.append(space)
+
+		subs = {k: v for k, v in rule.subs}
+
+		conditional_subs.append((region, subs))
+
+	addFeatureVariations(font, conditional_subs, featureTag)
 
 
-def load_designspace(designspace_filename):
+_DesignSpaceData = namedtuple(
+	"_DesignSpaceData",
+	[
+		"axes",
+		"internal_axis_supports",
+		"base_idx",
+		"normalized_master_locs",
+		"masters",
+		"instances",
+		"rules",
+		"rulesProcessingLast",
+		"lib",
+	],
+)
 
-	ds = designspace.load(designspace_filename)
-	axes = ds.get('axes')
-	masters = ds.get('sources')
+
+def _add_CFF2(varFont, model, master_fonts):
+	from .cff import merge_region_fonts
+	glyphOrder = varFont.getGlyphOrder()
+	if "CFF2" not in varFont:
+		from .cff import convertCFFtoCFF2
+		convertCFFtoCFF2(varFont)
+	ordered_fonts_list = model.reorderMasters(master_fonts, model.reverseMapping)
+	# re-ordering the master list simplifies building the CFF2 data item lists.
+	merge_region_fonts(varFont, model, ordered_fonts_list, glyphOrder)
+
+
+def load_designspace(designspace):
+	# TODO: remove this and always assume 'designspace' is a DesignSpaceDocument,
+	# never a file path, as that's already handled by caller
+	if hasattr(designspace, "sources"):  # Assume a DesignspaceDocument
+		ds = designspace
+	else:  # Assume a file path
+		ds = DesignSpaceDocument.fromfile(designspace)
+
+	masters = ds.sources
 	if not masters:
-		raise VarLibError("no sources found in .designspace")
-	instances = ds.get('instances', [])
+		raise VarLibValidationError("Designspace must have at least one source.")
+	instances = ds.instances
 
+	# TODO: Use fontTools.designspaceLib.tagForAxisName instead.
 	standard_axis_map = OrderedDict([
-		('weight',  ('wght', {'en':'Weight'})),
-		('width',   ('wdth', {'en':'Width'})),
-		('slant',   ('slnt', {'en':'Slant'})),
-		('optical', ('opsz', {'en':'Optical Size'})),
+		('weight',  ('wght', {'en': u'Weight'})),
+		('width',   ('wdth', {'en': u'Width'})),
+		('slant',   ('slnt', {'en': u'Slant'})),
+		('optical', ('opsz', {'en': u'Optical Size'})),
+		('italic',  ('ital', {'en': u'Italic'})),
 		])
 
-
 	# 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 not ds.axes:
+		raise VarLibValidationError(f"Designspace must have at least one axis.")
 
-			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()
+	axes = OrderedDict()
+	for axis_index, axis in enumerate(ds.axes):
+		axis_name = axis.name
+		if not axis_name:
+			if not axis.tag:
+				raise VarLibValidationError(f"Axis at index {axis_index} needs a tag.")
+			axis_name = axis.name = axis.tag
 
-			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."
+		if axis_name in standard_axis_map:
+			if axis.tag is None:
+				axis.tag = standard_axis_map[axis_name][0]
+			if not axis.labelNames:
+				axis.labelNames.update(standard_axis_map[axis_name][1])
+		else:
+			if not axis.tag:
+				raise VarLibValidationError(f"Axis at index {axis_index} needs a tag.")
+			if not axis.labelNames:
+				axis.labelNames["en"] = tostr(axis_name)
 
-		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))
-
+		axes[axis_name] = axis
+	log.info("Axes:\n%s", pformat([axis.asdict() for axis in axes.values()]))
 
 	# 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']
+		obj_name = obj.name or obj.styleName or ''
+		loc = obj.location
+		if loc is None:
+			raise VarLibValidationError(
+				f"Source or instance '{obj_name}' has no location."
+			)
 		for axis_name in loc.keys():
-			assert axis_name in axes, "Location axis '%s' unknown for '%s'." % (axis_name, obj_name)
+			if axis_name not in axes:
+				raise VarLibValidationError(
+					f"Location axis '{axis_name}' unknown for '{obj_name}'."
+				)
 		for axis_name,axis in axes.items():
 			if axis_name not in loc:
-				loc[axis_name] = axis.default
+				# NOTE: `axis.default` is always user-space, but `obj.location` always design-space.
+				loc[axis_name] = axis.map_forward(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)
-
+				if not (axis.minimum <= v <= axis.maximum):
+					raise VarLibValidationError(
+						f"Source or instance '{obj_name}' has out-of-range location "
+						f"for axis '{axis_name}': is mapped to {v} but must be in "
+						f"mapped range [{axis.minimum}..{axis.maximum}] (NOTE: all "
+						"values are in user-space)."
+					)
 
 	# Normalize master locations
 
-	normalized_master_locs = [o['location'] for o in masters]
-	log.info("Internal master locations:\n%s", pformat(normalized_master_locs))
+	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 = {}
@@ -663,23 +795,77 @@
 		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 normalized_master_locs]
+	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
+			if base_idx is not None:
+				raise VarLibValidationError(
+					"More than one base master found in Designspace."
+				)
 			base_idx = i
-	assert base_idx is not None, "Base master not found; no master at default location?"
+	if base_idx is None:
+		raise VarLibValidationError(
+			"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
+	return _DesignSpaceData(
+		axes,
+		internal_axis_supports,
+		base_idx,
+		normalized_master_locs,
+		masters,
+		instances,
+		ds.rules,
+		ds.rulesProcessingLast,
+		ds.lib,
+	)
 
 
-def build(designspace_filename, master_finder=lambda s:s):
+# https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass
+WDTH_VALUE_TO_OS2_WIDTH_CLASS = {
+	50: 1,
+	62.5: 2,
+	75: 3,
+	87.5: 4,
+	100: 5,
+	112.5: 6,
+	125: 7,
+	150: 8,
+	200: 9,
+}
+
+
+def set_default_weight_width_slant(font, location):
+	if "OS/2" in font:
+		if "wght" in location:
+			weight_class = otRound(max(1, min(location["wght"], 1000)))
+			if font["OS/2"].usWeightClass != weight_class:
+				log.info("Setting OS/2.usWeightClass = %s", weight_class)
+				font["OS/2"].usWeightClass = weight_class
+
+		if "wdth" in location:
+			# map 'wdth' axis (50..200) to OS/2.usWidthClass (1..9), rounding to closest
+			widthValue = min(max(location["wdth"], 50), 200)
+			widthClass = otRound(
+				models.piecewiseLinearMap(widthValue, WDTH_VALUE_TO_OS2_WIDTH_CLASS)
+			)
+			if font["OS/2"].usWidthClass != widthClass:
+				log.info("Setting OS/2.usWidthClass = %s", widthClass)
+				font["OS/2"].usWidthClass = widthClass
+
+	if "slnt" in location and "post" in font:
+		italicAngle = max(-90, min(location["slnt"], 90))
+		if font["post"].italicAngle != italicAngle:
+			log.info("Setting post.italicAngle = %s", italicAngle)
+			font["post"].italicAngle = italicAngle
+
+
+def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True):
 	"""
 	Build variation font from a designspace file.
 
@@ -687,60 +873,228 @@
 	filename as found in designspace file and map it to master font
 	binary as to be opened (eg. .ttf or .otf).
 	"""
+	if hasattr(designspace, "sources"):  # Assume a DesignspaceDocument
+		pass
+	else:  # Assume a file path
+		designspace = DesignSpaceDocument.fromfile(designspace)
 
-	axes, internal_axis_supports, base_idx, normalized_master_locs, masters, instances = load_designspace(designspace_filename)
-
+	ds = load_designspace(designspace)
 	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])
+	master_fonts = load_masters(designspace, master_finder)
+
+	# TODO: 'master_ttfs' is unused except for return value, remove later
+	master_ttfs = []
+	for master in master_fonts:
+		try:
+			master_ttfs.append(master.reader.file.name)
+		except AttributeError:
+			master_ttfs.append(None)  # in-memory fonts have no path
+
+	# Copy the base master to work from it
+	vf = deepcopy(master_fonts[ds.base_idx])
 
 	# TODO append masters as named-instances as well; needs .designspace change.
-	fvar = _add_fvar(vf, axes, instances)
-	_add_stat(vf, axes)
-	_add_avar(vf, axes)
-	del instances
+	fvar = _add_fvar(vf, ds.axes, ds.instances)
+	if 'STAT' not in exclude:
+		_add_stat(vf, ds.axes)
+	if 'avar' not in exclude:
+		_add_avar(vf, ds.axes)
 
 	# 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
+	normalized_master_locs = [
+		{ds.axes[k].tag: v for k,v in loc.items()} for loc in ds.normalized_master_locs
+	]
 	# 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]
+	assert 0 == model.mapping[ds.base_idx]
 
 	log.info("Building variations tables")
-	_add_MVAR(vf, model, master_fonts, axisTags)
-	_add_HVAR(vf, model, master_fonts, axisTags)
-	_merge_OTL(vf, model, master_fonts, axisTags)
-	if 'glyf' in vf:
-		_add_gvar(vf, model, master_fonts)
+	if 'BASE' not in exclude and 'BASE' in vf:
+		_add_BASE(vf, model, master_fonts, axisTags)
+	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 'VVAR' not in exclude and 'vmtx' in vf:
+		_add_VVAR(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)
+	if 'GSUB' not in exclude and ds.rules:
+		featureTag = ds.lib.get(
+			FEAVAR_FEATURETAG_LIB_KEY,
+			"rclt" if ds.rulesProcessingLast else "rvrn"
+		)
+		_add_GSUB_feature_variations(vf, ds.axes, ds.internal_axis_supports, ds.rules, featureTag)
+	if 'CFF2' not in exclude and ('CFF ' in vf or 'CFF2' in vf):
+		_add_CFF2(vf, model, master_fonts)
+		if "post" in vf:
+			# set 'post' to format 2 to keep the glyph names dropped from CFF2
+			post = vf["post"]
+			if post.formatType != 2.0:
+				post.formatType = 2.0
+				post.extraNames = []
+				post.mapping = {}
 
+	set_default_weight_width_slant(
+		vf, location={axis.axisTag: axis.defaultValue for axis in vf["fvar"].axes}
+	)
+
+	for tag in exclude:
+		if tag in vf:
+			del vf[tag]
+
+	# TODO: Only return vf for 4.0+, the rest is unused.
 	return vf, model, master_ttfs
 
 
+def _open_font(path, master_finder=lambda s: s):
+	# load TTFont masters from given 'path': this can be either a .TTX or an
+	# OpenType binary font; or if neither of these, try use the 'master_finder'
+	# callable to resolve the path to a valid .TTX or OpenType font binary.
+	from fontTools.ttx import guessFileType
+
+	master_path = os.path.normpath(path)
+	tp = guessFileType(master_path)
+	if tp is None:
+		# not an OpenType binary/ttx, fall back to the master finder.
+		master_path = master_finder(master_path)
+		tp = guessFileType(master_path)
+	if tp in ("TTX", "OTX"):
+		font = TTFont()
+		font.importXML(master_path)
+	elif tp in ("TTF", "OTF", "WOFF", "WOFF2"):
+		font = TTFont(master_path)
+	else:
+		raise VarLibValidationError("Invalid master path: %r" % master_path)
+	return font
+
+
+def load_masters(designspace, master_finder=lambda s: s):
+	"""Ensure that all SourceDescriptor.font attributes have an appropriate TTFont
+	object loaded, or else open TTFont objects from the SourceDescriptor.path
+	attributes.
+
+	The paths can point to either an OpenType font, a TTX file, or a UFO. In the
+	latter case, use the provided master_finder callable to map from UFO paths to
+	the respective master font binaries (e.g. .ttf, .otf or .ttx).
+
+	Return list of master TTFont objects in the same order they are listed in the
+	DesignSpaceDocument.
+	"""
+	for master in designspace.sources:
+		# If a SourceDescriptor has a layer name, demand that the compiled TTFont
+		# be supplied by the caller. This spares us from modifying MasterFinder.
+		if master.layerName and master.font is None:
+			raise VarLibValidationError(
+				f"Designspace source '{master.name or '<Unknown>'}' specified a "
+				"layer name but lacks the required TTFont object in the 'font' "
+				"attribute."
+			)
+
+	return designspace.loadSourceFonts(_open_font, master_finder=master_finder)
+
+
+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):
+	"""Build a variable font from a designspace file and masters"""
 	from argparse import ArgumentParser
 	from fontTools import configLogger
 
-	parser = ArgumentParser(prog='varLib')
+	parser = ArgumentParser(prog='varLib', description = main.__doc__)
 	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".'
+		)
+	)
+	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)
 
-	# TODO: allow user to configure logging via command-line options
-	configLogger(level="INFO")
+	configLogger(level=(
+		"DEBUG" if options.verbose else
+		"ERROR" if options.quiet else
+		"INFO"))
 
 	designspace_filename = options.designspace
-	finder = lambda s: s.replace('master_ufo', 'master_ttf_interpolatable').replace('.ufo', '.ttf')
-	outfile = os.path.splitext(designspace_filename)[0] + '-VF.ttf'
+	finder = MasterFinder(options.master_finder)
 
-	vf, model, master_ttfs = build(designspace_filename, finder)
+	vf, _, _ = build(
+		designspace_filename,
+		finder,
+		exclude=options.exclude,
+		optimize=options.optimize
+	)
+
+	outfile = options.outfile
+	if outfile is None:
+		ext = "otf" if vf.sfntVersion == "OTTO" else "ttf"
+		outfile = os.path.splitext(designspace_filename)[0] + '-VF.' + ext
 
 	log.info("Saving variation font %s", outfile)
 	vf.save(outfile)
diff --git a/Lib/fontTools/varLib/__main__.py b/Lib/fontTools/varLib/__main__.py
index 5cf05e5..4b3a0f5 100644
--- a/Lib/fontTools/varLib/__main__.py
+++ b/Lib/fontTools/varLib/__main__.py
@@ -1,7 +1,6 @@
-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
index 388f247..60d7172 100644
--- a/Lib/fontTools/varLib/builder.py
+++ b/Lib/fontTools/varLib/builder.py
@@ -1,4 +1,3 @@
-from __future__ import print_function, division, absolute_import
 from fontTools import ttLib
 from fontTools.ttLib.tables import otTables as ot
 
@@ -15,7 +14,6 @@
 	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):
@@ -28,37 +26,71 @@
 	return self
 
 
-def _reorderItem(lst, narrows):
-	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:
-			out.append(lst[i])
-	return out
+def _reorderItem(lst, mapping):
+	return [lst[i] for i in mapping]
 
-def varDataCalculateNumShorts(self, optimize=True):
+def VarData_calculateNumShorts(self, optimize=False):
 	count = self.VarRegionCount
 	items = self.Item
-	narrows = set(range(count))
+	bit_lengths = [0] * count
 	for item in items:
-		wides = [i for i in narrows if not (-128 <= item[i] <= 127)]
-		narrows.difference_update(wides)
-		if not narrows:
-			break
+		# The "+ (i < -1)" magic is to handle two's-compliment.
+		# That is, we want to get back 7 for -128, whereas
+		# bit_length() returns 8. Similarly for -65536.
+		# The reason "i < -1" is used instead of "i < 0" is that
+		# the latter would make it return 0 for "-1" instead of 1.
+		bl = [(i + (i < -1)).bit_length() for i in item]
+		bit_lengths = [max(*pair) for pair in zip(bl, bit_lengths)]
+	# The addition of 8, instead of seven, is to account for the sign bit.
+	# This "((b + 8) >> 3) if b else 0" when combined with the above
+	# "(i + (i < -1)).bit_length()" is a faster way to compute byte-lengths
+	# conforming to:
+	#
+	# byte_length = (0 if i == 0 else
+	#		 1 if -128 <= i < 128 else
+	#		 2 if -65536 <= i < 65536 else
+	#		 ...)
+	byte_lengths = [((b + 8) >> 3) if b else 0 for b in bit_lengths]
+
+	# https://github.com/fonttools/fonttools/issues/2279
+	longWords = any(b > 2 for b in byte_lengths)
+
 	if optimize:
-		# Reorder columns such that all SHORT columns come before UINT8
-		self.VarRegionIndex = _reorderItem(self.VarRegionIndex, narrows)
-		for i in range(self.ItemCount):
-			items[i] = _reorderItem(items[i], narrows)
-		self.NumShorts = count - len(narrows)
+		# Reorder columns such that wider columns come before narrower columns
+		mapping = []
+		mapping.extend(i for i,b in enumerate(byte_lengths) if b > 2)
+		mapping.extend(i for i,b in enumerate(byte_lengths) if b == 2)
+		mapping.extend(i for i,b in enumerate(byte_lengths) if b == 1)
+
+		byte_lengths = _reorderItem(byte_lengths, mapping)
+		self.VarRegionIndex = _reorderItem(self.VarRegionIndex, mapping)
+		self.VarRegionCount = len(self.VarRegionIndex)
+		for i in range(len(items)):
+			items[i] = _reorderItem(items[i], mapping)
+
+	if longWords:
+		self.NumShorts = max((i for i,b in enumerate(byte_lengths) if b > 2), default=-1) + 1
+		self.NumShorts |= 0x8000
 	else:
-		wides = set(range(count)) - narrows
-		self.NumShorts = 1+max(wides) if wides else 0
+		self.NumShorts = max((i for i,b in enumerate(byte_lengths) if b > 1), default=-1) + 1
+
+	self.VarRegionCount = len(self.VarRegionIndex)
 	return self
 
+ot.VarData.calculateNumShorts = VarData_calculateNumShorts
+
+def VarData_CalculateNumShorts(self, optimize=True):
+	"""Deprecated name for VarData_calculateNumShorts() which
+	defaults to optimize=True.  Use varData.calculateNumShorts()
+	or varData.optimize()."""
+	return VarData_calculateNumShorts(self, optimize=optimize)
+
+def VarData_optimize(self):
+	return VarData_calculateNumShorts(self, optimize=True)
+
+ot.VarData.optimize = VarData_optimize
+
+
 def buildVarData(varRegionIndices, items, optimize=True):
 	self = ot.VarData()
 	self.VarRegionIndex = list(varRegionIndices)
@@ -69,7 +101,7 @@
 			assert len(item) == regionCount
 			records.append(list(item))
 	self.ItemCount = len(self.Item)
-	varDataCalculateNumShorts(self, optimize=optimize)
+	self.calculateNumShorts(optimize=optimize)
 	return self
 
 
@@ -84,12 +116,19 @@
 
 # Variation helpers
 
-def buildVarIdxMap(varIdxes):
-	# TODO Change VarIdxMap mapping to hold separate outer,inner indices
+def buildVarIdxMap(varIdxes, glyphOrder):
 	self = ot.VarIdxMap()
-	self.mapping = list(varIdxes)
+	self.mapping = {g:v for g,v in zip(glyphOrder, varIdxes)}
 	return self
 
+
+def buildDeltaSetIndexMap(varIdxes):
+	self = ot.DeltaSetIndexMap()
+	self.mapping = list(varIdxes)
+	self.Format = 1 if len(varIdxes) > 0xFFFF else 0
+	return self
+
+
 def buildVarDevTable(varIdx):
 	self = ot.Device()
 	self.DeltaFormat = 0x8000
diff --git a/Lib/fontTools/varLib/cff.py b/Lib/fontTools/varLib/cff.py
new file mode 100644
index 0000000..4eed8b3
--- /dev/null
+++ b/Lib/fontTools/varLib/cff.py
@@ -0,0 +1,653 @@
+from collections import namedtuple
+from fontTools.cffLib import (
+	maxStackLimit,
+	TopDictIndex,
+	buildOrder,
+	topDictOperators,
+	topDictOperators2,
+	privateDictOperators,
+	privateDictOperators2,
+	FDArrayIndex,
+	FontDict,
+	VarStoreData
+)
+from io import BytesIO
+from fontTools.cffLib.specializer import (
+	specializeCommands, commandsToProgram)
+from fontTools.ttLib import newTable
+from fontTools import varLib
+from fontTools.varLib.models import allEqual
+from fontTools.misc.roundTools import roundFunc
+from fontTools.misc.psCharStrings import T2CharString, T2OutlineExtractor
+from fontTools.pens.t2CharStringPen import T2CharStringPen
+from functools import partial
+
+from .errors import (
+	VarLibCFFDictMergeError, VarLibCFFPointTypeMergeError,
+	VarLibCFFHintTypeMergeError,VarLibMergeError)
+
+
+# Backwards compatibility
+MergeDictError = VarLibCFFDictMergeError
+MergeTypeError = VarLibCFFPointTypeMergeError
+
+
+def addCFFVarStore(varFont, varModel, varDataList, masterSupports):
+	fvarTable = varFont['fvar']
+	axisKeys = [axis.axisTag for axis in fvarTable.axes]
+	varTupleList = varLib.builder.buildVarRegionList(masterSupports, axisKeys)
+	varStoreCFFV = varLib.builder.buildVarStore(varTupleList, varDataList)
+
+	topDict = varFont['CFF2'].cff.topDictIndex[0]
+	topDict.VarStore = VarStoreData(otVarStore=varStoreCFFV)
+	if topDict.FDArray[0].vstore is None:
+		fdArray = topDict.FDArray
+		for fontDict in fdArray:
+			if hasattr(fontDict, "Private"):
+				fontDict.Private.vstore = topDict.VarStore
+
+
+def lib_convertCFFToCFF2(cff, otFont):
+	# This assumes a decompiled CFF table.
+	cff2GetGlyphOrder = cff.otFont.getGlyphOrder
+	topDictData = TopDictIndex(None, cff2GetGlyphOrder, None)
+	topDictData.items = cff.topDictIndex.items
+	cff.topDictIndex = topDictData
+	topDict = topDictData[0]
+	if hasattr(topDict, 'Private'):
+		privateDict = topDict.Private
+	else:
+		privateDict = None
+	opOrder = buildOrder(topDictOperators2)
+	topDict.order = opOrder
+	topDict.cff2GetGlyphOrder = cff2GetGlyphOrder
+	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)
+		if privateDict is not None:
+			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 list(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
+	# Now delete up the decrecated topDict operators from CFF 1.0
+	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)
+
+	# At this point, the Subrs and Charstrings are all still T2Charstring class
+	# easiest to fix this by compiling, then decompiling again
+	cff.major = 2
+	file = BytesIO()
+	cff.compile(file, otFont, isCFF2=True)
+	file.seek(0)
+	cff.decompile(file, otFont, isCFF2=True)
+
+
+def convertCFFtoCFF2(varFont):
+	# Convert base font to a single master CFF2 font.
+	cffTable = varFont['CFF ']
+	lib_convertCFFToCFF2(cffTable.cff, varFont)
+	newCFF2 = newTable("CFF2")
+	newCFF2.cff = cffTable.cff
+	varFont['CFF2'] = newCFF2
+	del varFont['CFF ']
+
+
+def conv_to_int(num):
+	if isinstance(num, float) and num.is_integer():
+		return int(num)
+	return num
+
+
+pd_blend_fields = ("BlueValues", "OtherBlues", "FamilyBlues",
+				   "FamilyOtherBlues", "BlueScale", "BlueShift",
+				   "BlueFuzz", "StdHW", "StdVW", "StemSnapH",
+				   "StemSnapV")
+
+
+def get_private(regionFDArrays, fd_index, ri, fd_map):
+	region_fdArray = regionFDArrays[ri]
+	region_fd_map = fd_map[fd_index]
+	if ri in region_fd_map:
+		region_fdIndex = region_fd_map[ri]
+		private = region_fdArray[region_fdIndex].Private
+	else:
+		private = None
+	return private
+
+
+def merge_PrivateDicts(top_dicts, vsindex_dict, var_model, fd_map):
+	"""
+	I step through the FontDicts in the FDArray of the varfont TopDict.
+	For each varfont FontDict:
+		step through each key in FontDict.Private.
+		For each key, step through each relevant source font Private dict, and
+		build a list of values to blend.
+	The 'relevant' source fonts are selected by first getting the right
+	submodel using vsindex_dict[vsindex]. The indices of the
+	subModel.locations are mapped to source font list indices by
+	assuming the latter order is the same as the order of the
+	var_model.locations. I can then get the index of each subModel
+	location in the list of var_model.locations.
+	"""
+
+	topDict = top_dicts[0]
+	region_top_dicts = top_dicts[1:]
+	if hasattr(region_top_dicts[0], 'FDArray'):
+		regionFDArrays = [fdTopDict.FDArray for fdTopDict in region_top_dicts]
+	else:
+		regionFDArrays = [[fdTopDict] for fdTopDict in region_top_dicts]
+	for fd_index, font_dict in enumerate(topDict.FDArray):
+		private_dict = font_dict.Private
+		vsindex = getattr(private_dict, 'vsindex', 0)
+		# At the moment, no PrivateDict has a vsindex key, but let's support
+		# how it should work. See comment at end of
+		# merge_charstrings() - still need to optimize use of vsindex.
+		sub_model, _ = vsindex_dict[vsindex]
+		master_indices = []
+		for loc in sub_model.locations[1:]:
+			i = var_model.locations.index(loc) - 1
+			master_indices.append(i)
+		pds = [private_dict]
+		last_pd = private_dict
+		for ri in master_indices:
+			pd = get_private(regionFDArrays, fd_index, ri, fd_map)
+			# If the region font doesn't have this FontDict, just reference
+			# the last one used.
+			if pd is None:
+				pd = last_pd
+			else:
+				last_pd = pd
+			pds.append(pd)
+		num_masters = len(pds)
+		for key, value in private_dict.rawDict.items():
+			dataList = []
+			if key not in pd_blend_fields:
+				continue
+			if isinstance(value, list):
+				try:
+					values = [pd.rawDict[key] for pd in pds]
+				except KeyError:
+					print(
+						"Warning: {key} in default font Private dict is "
+						"missing from another font, and was "
+						"discarded.".format(key=key))
+					continue
+				try:
+					values = zip(*values)
+				except IndexError:
+					raise VarLibCFFDictMergeError(key, value, values)
+				"""
+				Row 0 contains the first  value from each master.
+				Convert each row from absolute values to relative
+				values from the previous row.
+				e.g for three masters,	a list of values was:
+				master 0 OtherBlues = [-217,-205]
+				master 1 OtherBlues = [-234,-222]
+				master 1 OtherBlues = [-188,-176]
+				The call to zip() converts this to:
+				[(-217, -234, -188), (-205, -222, -176)]
+				and is converted finally to:
+				OtherBlues = [[-217, 17.0, 46.0], [-205, 0.0, 0.0]]
+				"""
+				prev_val_list = [0] * num_masters
+				any_points_differ = False
+				for val_list in values:
+					rel_list = [(val - prev_val_list[i]) for (
+							i, val) in enumerate(val_list)]
+					if (not any_points_differ) and not allEqual(rel_list):
+						any_points_differ = True
+					prev_val_list = val_list
+					deltas = sub_model.getDeltas(rel_list)
+					# For PrivateDict BlueValues, the default font
+					# values are absolute, not relative to the prior value.
+					deltas[0] = val_list[0]
+					dataList.append(deltas)
+				# If there are no blend values,then
+				# we can collapse the blend lists.
+				if not any_points_differ:
+					dataList = [data[0] for data in dataList]
+			else:
+				values = [pd.rawDict[key] for pd in pds]
+				if not allEqual(values):
+					dataList = sub_model.getDeltas(values)
+				else:
+					dataList = values[0]
+
+			# Convert numbers with no decimal part to an int
+			if isinstance(dataList, list):
+				for i, item in enumerate(dataList):
+					if isinstance(item, list):
+						for j, jtem in enumerate(item):
+							dataList[i][j] = conv_to_int(jtem)
+					else:
+						dataList[i] = conv_to_int(item)
+			else:
+				dataList = conv_to_int(dataList)
+
+			private_dict.rawDict[key] = dataList
+
+
+def _cff_or_cff2(font):
+	if "CFF " in font:
+		return font["CFF "]
+	return font["CFF2"]
+
+
+def getfd_map(varFont, fonts_list):
+	""" Since a subset source font may have fewer FontDicts in their
+	FDArray than the default font, we have to match up the FontDicts in
+	the different fonts . We do this with the FDSelect array, and by
+	assuming that the same glyph will reference  matching FontDicts in
+	each source font. We return a mapping from fdIndex in the default
+	font to a dictionary which maps each master list index of each
+	region font to the equivalent fdIndex in the region font."""
+	fd_map = {}
+	default_font = fonts_list[0]
+	region_fonts = fonts_list[1:]
+	num_regions = len(region_fonts)
+	topDict = _cff_or_cff2(default_font).cff.topDictIndex[0]
+	if not hasattr(topDict, 'FDSelect'):
+		# All glyphs reference only one FontDict.
+		# Map the FD index for regions to index 0.
+		fd_map[0] = {ri:0 for ri in range(num_regions)}
+		return fd_map
+
+	gname_mapping = {}
+	default_fdSelect = topDict.FDSelect
+	glyphOrder = default_font.getGlyphOrder()
+	for gid, fdIndex in enumerate(default_fdSelect):
+		gname_mapping[glyphOrder[gid]] = fdIndex
+		if fdIndex not in fd_map:
+			fd_map[fdIndex] = {}
+	for ri, region_font in enumerate(region_fonts):
+		region_glyphOrder = region_font.getGlyphOrder()
+		region_topDict = _cff_or_cff2(region_font).cff.topDictIndex[0]
+		if not hasattr(region_topDict, 'FDSelect'):
+			# All the glyphs share the same FontDict. Pick any glyph.
+			default_fdIndex = gname_mapping[region_glyphOrder[0]]
+			fd_map[default_fdIndex][ri] = 0
+		else:
+			region_fdSelect = region_topDict.FDSelect
+			for gid, fdIndex in enumerate(region_fdSelect):
+				default_fdIndex = gname_mapping[region_glyphOrder[gid]]
+				region_map = fd_map[default_fdIndex]
+				if ri not in region_map:
+					region_map[ri] = fdIndex
+	return fd_map
+
+
+CVarData = namedtuple('CVarData', 'varDataList masterSupports vsindex_dict')
+def merge_region_fonts(varFont, model, ordered_fonts_list, glyphOrder):
+	topDict = varFont['CFF2'].cff.topDictIndex[0]
+	top_dicts = [topDict] + [
+					_cff_or_cff2(ttFont).cff.topDictIndex[0]
+					for ttFont in ordered_fonts_list[1:]
+					]
+	num_masters = len(model.mapping)
+	cvData = merge_charstrings(glyphOrder, num_masters, top_dicts, model)
+	fd_map = getfd_map(varFont, ordered_fonts_list)
+	merge_PrivateDicts(top_dicts, cvData.vsindex_dict, model, fd_map)
+	addCFFVarStore(varFont, model, cvData.varDataList,
+		cvData.masterSupports)
+
+
+def _get_cs(charstrings, glyphName):
+	if glyphName not in charstrings:
+		return None
+	return charstrings[glyphName]
+
+def _add_new_vsindex(model, key, masterSupports, vsindex_dict,
+		vsindex_by_key, varDataList):
+	varTupleIndexes = []
+	for support in model.supports[1:]:
+		if support not in masterSupports:
+			masterSupports.append(support)
+		varTupleIndexes.append(masterSupports.index(support))
+	var_data = varLib.builder.buildVarData(varTupleIndexes, None, False)
+	vsindex = len(vsindex_dict)
+	vsindex_by_key[key] = vsindex
+	vsindex_dict[vsindex] = (model, [key])
+	varDataList.append(var_data)
+	return vsindex
+
+def merge_charstrings(glyphOrder, num_masters, top_dicts, masterModel):
+
+	vsindex_dict = {}
+	vsindex_by_key = {}
+	varDataList = []
+	masterSupports = []
+	default_charstrings = top_dicts[0].CharStrings
+	for gid, gname in enumerate(glyphOrder):
+		all_cs = [
+				_get_cs(td.CharStrings, gname)
+				for td in top_dicts]
+		if len([gs for gs in all_cs if gs is not None]) == 1:
+			continue
+		model, model_cs = masterModel.getSubModel(all_cs)
+		# create the first pass CFF2 charstring, from
+		# the default charstring.
+		default_charstring = model_cs[0]
+		var_pen = CFF2CharStringMergePen([], gname, num_masters, 0)
+		# We need to override outlineExtractor because these
+		# charstrings do have widths in the 'program'; we need to drop these
+		# values rather than post assertion error for them.
+		default_charstring.outlineExtractor = MergeOutlineExtractor
+		default_charstring.draw(var_pen)
+
+		# Add the coordinates from all the other regions to the
+		# blend lists in the CFF2 charstring.
+		region_cs = model_cs[1:]
+		for region_idx, region_charstring in enumerate(region_cs, start=1):
+			var_pen.restart(region_idx)
+			region_charstring.outlineExtractor = MergeOutlineExtractor
+			region_charstring.draw(var_pen)
+
+		# Collapse each coordinate list to a blend operator and its args.
+		new_cs = var_pen.getCharString(
+			private=default_charstring.private,
+			globalSubrs=default_charstring.globalSubrs,
+			var_model=model, optimize=True)
+		default_charstrings[gname] = new_cs
+
+		if (not var_pen.seen_moveto) or ('blend' not in new_cs.program):
+			# If this is not a marking glyph, or if there are no blend
+			# arguments, then we can use vsindex 0. No need to
+			# check if we need a new vsindex.
+			continue
+
+		# If the charstring required a new model, create
+		# a VarData table to go with, and set vsindex.
+		key = tuple(v is not None for v in all_cs)
+		try:
+			vsindex = vsindex_by_key[key]
+		except KeyError:
+			vsindex = _add_new_vsindex(model, key, masterSupports, vsindex_dict,
+				vsindex_by_key, varDataList)
+		# We do not need to check for an existing new_cs.private.vsindex,
+		# as we know it doesn't exist yet.
+		if vsindex != 0:
+			new_cs.program[:0] = [vsindex, 'vsindex']
+
+	# If there is no variation in any of the charstrings, then vsindex_dict
+	# never gets built. This could still be needed if there is variation
+	# in the PrivatDict, so we will build the default data for vsindex = 0.
+	if not vsindex_dict:
+		key = (True,) * num_masters
+		_add_new_vsindex(masterModel, key, masterSupports, vsindex_dict,
+			vsindex_by_key, varDataList)
+	cvData = CVarData(varDataList=varDataList, masterSupports=masterSupports,
+						vsindex_dict=vsindex_dict)
+	# XXX To do: optimize use of vsindex between the PrivateDicts and
+	# charstrings
+	return cvData
+
+
+class CFFToCFF2OutlineExtractor(T2OutlineExtractor):
+	""" This class is used to remove the initial width from the CFF
+	charstring without trying to add the width to self.nominalWidthX,
+	which is None. """
+	def popallWidth(self, evenOdd=0):
+		args = self.popall()
+		if not self.gotWidth:
+			if evenOdd ^ (len(args) % 2):
+				args = args[1:]
+			self.width = self.defaultWidthX
+			self.gotWidth = 1
+		return args
+
+
+class MergeOutlineExtractor(CFFToCFF2OutlineExtractor):
+	""" Used to extract the charstring commands - including hints - from a
+	CFF charstring in order to merge it as another set of region data
+	into a CFF2 variable font charstring."""
+
+	def __init__(self, pen, localSubrs, globalSubrs,
+			nominalWidthX, defaultWidthX, private=None):
+		super().__init__(pen, localSubrs,
+			globalSubrs, nominalWidthX, defaultWidthX, private)
+
+	def countHints(self):
+		args = self.popallWidth()
+		self.hintCount = self.hintCount + len(args) // 2
+		return args
+
+	def _hint_op(self, type, args):
+		self.pen.add_hint(type, args)
+
+	def op_hstem(self, index):
+		args = self.countHints()
+		self._hint_op('hstem', args)
+
+	def op_vstem(self, index):
+		args = self.countHints()
+		self._hint_op('vstem', args)
+
+	def op_hstemhm(self, index):
+		args = self.countHints()
+		self._hint_op('hstemhm', args)
+
+	def op_vstemhm(self, index):
+		args = self.countHints()
+		self._hint_op('vstemhm', args)
+
+	def _get_hintmask(self, index):
+		if not self.hintMaskBytes:
+			args = self.countHints()
+			if args:
+				self._hint_op('vstemhm', args)
+			self.hintMaskBytes = (self.hintCount + 7) // 8
+		hintMaskBytes, index = self.callingStack[-1].getBytes(index,
+			self.hintMaskBytes)
+		return index, hintMaskBytes
+
+	def op_hintmask(self, index):
+		index, hintMaskBytes = self._get_hintmask(index)
+		self.pen.add_hintmask('hintmask', [hintMaskBytes])
+		return hintMaskBytes, index
+
+	def op_cntrmask(self, index):
+		index, hintMaskBytes = self._get_hintmask(index)
+		self.pen.add_hintmask('cntrmask', [hintMaskBytes])
+		return hintMaskBytes, index
+
+
+class CFF2CharStringMergePen(T2CharStringPen):
+	"""Pen to merge Type 2 CharStrings.
+	"""
+	def __init__(
+				self, default_commands, glyphName, num_masters, master_idx,
+				roundTolerance=0.5):
+		super().__init__(
+							width=None,
+							glyphSet=None, CFF2=True,
+							roundTolerance=roundTolerance)
+		self.pt_index = 0
+		self._commands = default_commands
+		self.m_index = master_idx
+		self.num_masters = num_masters
+		self.prev_move_idx = 0
+		self.seen_moveto = False
+		self.glyphName = glyphName
+		self.round = roundFunc(roundTolerance, round=round)
+
+	def add_point(self, point_type, pt_coords):
+		if self.m_index == 0:
+			self._commands.append([point_type, [pt_coords]])
+		else:
+			cmd = self._commands[self.pt_index]
+			if cmd[0] != point_type:
+				raise VarLibCFFPointTypeMergeError(
+									point_type,
+									self.pt_index, len(cmd[1]),
+									cmd[0], self.glyphName)
+			cmd[1].append(pt_coords)
+		self.pt_index += 1
+
+	def add_hint(self, hint_type, args):
+		if self.m_index == 0:
+			self._commands.append([hint_type, [args]])
+		else:
+			cmd = self._commands[self.pt_index]
+			if cmd[0] != hint_type:
+				raise VarLibCFFHintTypeMergeError(hint_type, self.pt_index, len(cmd[1]),
+					cmd[0], self.glyphName)
+			cmd[1].append(args)
+		self.pt_index += 1
+
+	def add_hintmask(self, hint_type, abs_args):
+		# For hintmask, fonttools.cffLib.specializer.py expects
+		# each of these to be represented by two sequential commands:
+		# first holding only the operator name, with an empty arg list,
+		# second with an empty string as the op name, and the mask arg list.
+		if self.m_index == 0:
+			self._commands.append([hint_type, []])
+			self._commands.append(["", [abs_args]])
+		else:
+			cmd = self._commands[self.pt_index]
+			if cmd[0] != hint_type:
+				raise VarLibCFFHintTypeMergeError(hint_type, self.pt_index, len(cmd[1]),
+					cmd[0], self.glyphName)
+			self.pt_index += 1
+			cmd = self._commands[self.pt_index]
+			cmd[1].append(abs_args)
+		self.pt_index += 1
+
+	def _moveTo(self, pt):
+		if not self.seen_moveto:
+			self.seen_moveto = True
+		pt_coords = self._p(pt)
+		self.add_point('rmoveto', pt_coords)
+		# I set prev_move_idx here because add_point()
+		# can change self.pt_index.
+		self.prev_move_idx = self.pt_index - 1
+
+	def _lineTo(self, pt):
+		pt_coords = self._p(pt)
+		self.add_point('rlineto', pt_coords)
+
+	def _curveToOne(self, pt1, pt2, pt3):
+		_p = self._p
+		pt_coords = _p(pt1)+_p(pt2)+_p(pt3)
+		self.add_point('rrcurveto', pt_coords)
+
+	def _closePath(self):
+		pass
+
+	def _endPath(self):
+		pass
+
+	def restart(self, region_idx):
+		self.pt_index = 0
+		self.m_index = region_idx
+		self._p0 = (0, 0)
+
+	def getCommands(self):
+		return self._commands
+
+	def reorder_blend_args(self, commands, get_delta_func):
+		"""
+		We first re-order the master coordinate values.
+		For a moveto to lineto, the args are now arranged as:
+			[ [master_0 x,y], [master_1 x,y], [master_2 x,y] ]
+		We re-arrange this to
+		[	[master_0 x, master_1 x, master_2 x],
+			[master_0 y, master_1 y, master_2 y]
+		]
+		If the master values are all the same, we collapse the list to
+		as single value instead of a list.
+
+		We then convert this to:
+		[ [master_0 x] + [x delta tuple] + [numBlends=1]
+		  [master_0 y] + [y delta tuple] + [numBlends=1]
+		]
+		"""
+		for cmd in commands:
+			# arg[i] is the set of arguments for this operator from master i.
+			args = cmd[1]
+			m_args = zip(*args)
+			# m_args[n] is now all num_master args for the i'th argument
+			# for this operation.
+			cmd[1] = list(m_args)
+		lastOp = None
+		for cmd in commands:
+			op = cmd[0]
+			# masks are represented by two cmd's: first has only op names,
+			# second has only args.
+			if lastOp in ['hintmask', 'cntrmask']:
+				coord = list(cmd[1])
+				if not allEqual(coord):
+					raise VarLibMergeError("Hintmask values cannot differ between source fonts.")
+				cmd[1] = [coord[0][0]]
+			else:
+				coords = cmd[1]
+				new_coords = []
+				for coord in coords:
+					if allEqual(coord):
+						new_coords.append(coord[0])
+					else:
+						# convert to deltas
+						deltas = get_delta_func(coord)[1:]
+						coord = [coord[0]] + deltas
+						new_coords.append(coord)
+				cmd[1] = new_coords
+			lastOp = op
+		return commands
+
+	def getCharString(
+					self, private=None, globalSubrs=None,
+					var_model=None, optimize=True):
+		commands = self._commands
+		commands = self.reorder_blend_args(commands, partial (var_model.getDeltas, round=self.round))
+		if optimize:
+			commands = specializeCommands(
+						commands, generalizeFirst=False,
+						maxstack=maxStackLimit)
+		program = commandsToProgram(commands)
+		charString = T2CharString(
+						program=program, private=private,
+						globalSubrs=globalSubrs)
+		return charString
diff --git a/Lib/fontTools/varLib/designspace.py b/Lib/fontTools/varLib/designspace.py
deleted file mode 100644
index 7f235af..0000000
--- a/Lib/fontTools/varLib/designspace.py
+++ /dev/null
@@ -1,113 +0,0 @@
-"""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/errors.py b/Lib/fontTools/varLib/errors.py
new file mode 100644
index 0000000..f9cd9fb
--- /dev/null
+++ b/Lib/fontTools/varLib/errors.py
@@ -0,0 +1,190 @@
+import textwrap
+
+
+class VarLibError(Exception):
+    """Base exception for the varLib module."""
+
+
+class VarLibValidationError(VarLibError):
+    """Raised when input data is invalid from varLib's point of view."""
+
+
+class VarLibMergeError(VarLibError):
+    """Raised when input data cannot be merged into a variable font."""
+
+    def __init__(self, merger=None, **kwargs):
+        self.merger = merger
+        if not kwargs:
+            kwargs = {}
+        if "stack" in kwargs:
+            self.stack = kwargs["stack"]
+            del kwargs["stack"]
+        else:
+            self.stack = []
+        self.cause = kwargs
+
+    @property
+    def reason(self):
+        return self.__doc__
+
+    def _master_name(self, ix):
+        if self.merger is not None:
+            ttf = self.merger.ttfs[ix]
+            if (
+                "name" in ttf
+                and ttf["name"].getDebugName(1)
+                and ttf["name"].getDebugName(2)
+            ):
+                return ttf["name"].getDebugName(1) + " " + ttf["name"].getDebugName(2)
+            elif hasattr(ttf.reader, "file") and hasattr(ttf.reader.file, "name"):
+                return ttf.reader.file.name
+        return f"master number {ix}"
+
+    @property
+    def offender(self):
+        if "expected" in self.cause and "got" in self.cause:
+            index = [x == self.cause["expected"] for x in self.cause["got"]].index(
+                False
+            )
+            return index, self._master_name(index)
+        return None, None
+
+    @property
+    def details(self):
+        if "expected" in self.cause and "got" in self.cause:
+            offender_index, offender = self.offender
+            got = self.cause["got"][offender_index]
+            return f"Expected to see {self.stack[0]}=={self.cause['expected']}, instead saw {got}\n"
+        return ""
+
+    def __str__(self):
+        offender_index, offender = self.offender
+        location = ""
+        if offender:
+            location = f"\n\nThe problem is likely to be in {offender}:\n"
+        context = "".join(reversed(self.stack))
+        basic = textwrap.fill(
+            f"Couldn't merge the fonts, because {self.reason}. "
+            f"This happened while performing the following operation: {context}",
+            width=78,
+        )
+        return "\n\n" + basic + location + self.details
+
+
+class ShouldBeConstant(VarLibMergeError):
+    """some values were different, but should have been the same"""
+
+    @property
+    def details(self):
+        if self.stack[0] != ".FeatureCount" or self.merger is None:
+            return super().details
+        offender_index, offender = self.offender
+        bad_ttf = self.merger.ttfs[offender_index]
+        good_ttf = self.merger.ttfs[offender_index - 1]
+
+        good_features = [
+            x.FeatureTag
+            for x in good_ttf[self.stack[-1]].table.FeatureList.FeatureRecord
+        ]
+        bad_features = [
+            x.FeatureTag
+            for x in bad_ttf[self.stack[-1]].table.FeatureList.FeatureRecord
+        ]
+        return (
+            "\nIncompatible features between masters.\n"
+            f"Expected: {', '.join(good_features)}.\n"
+            f"Got: {', '.join(bad_features)}.\n"
+        )
+
+
+class FoundANone(VarLibMergeError):
+    """one of the values in a list was empty when it shouldn't have been"""
+
+    @property
+    def offender(self):
+        cause = self.argv[0]
+        index = [x is None for x in cause["got"]].index(True)
+        return index, self._master_name(index)
+
+    @property
+    def details(self):
+        cause, stack = self.args[0], self.args[1:]
+        return f"{stack[0]}=={cause['got']}\n"
+
+
+class MismatchedTypes(VarLibMergeError):
+    """data had inconsistent types"""
+
+
+class LengthsDiffer(VarLibMergeError):
+    """a list of objects had inconsistent lengths"""
+
+
+class KeysDiffer(VarLibMergeError):
+    """a list of objects had different keys"""
+
+
+class InconsistentGlyphOrder(VarLibMergeError):
+    """the glyph order was inconsistent between masters"""
+
+
+class InconsistentExtensions(VarLibMergeError):
+    """the masters use extension lookups in inconsistent ways"""
+
+
+class UnsupportedFormat(VarLibMergeError):
+    """an OpenType subtable (%s) had a format I didn't expect"""
+
+    @property
+    def reason(self):
+        cause, stack = self.args[0], self.args[1:]
+        return self.__doc__ % cause["subtable"]
+
+
+class UnsupportedFormat(UnsupportedFormat):
+    """an OpenType subtable (%s) had inconsistent formats between masters"""
+
+
+class VarLibCFFMergeError(VarLibError):
+    pass
+
+
+class VarLibCFFDictMergeError(VarLibCFFMergeError):
+    """Raised when a CFF PrivateDict cannot be merged."""
+
+    def __init__(self, key, value, values):
+        error_msg = (
+            f"For the Private Dict key '{key}', the default font value list:"
+            f"\n\t{value}\nhad a different number of values than a region font:"
+        )
+        for region_value in values:
+            error_msg += f"\n\t{region_value}"
+        self.args = (error_msg,)
+
+
+class VarLibCFFPointTypeMergeError(VarLibCFFMergeError):
+    """Raised when a CFF glyph cannot be merged because of point type differences."""
+
+    def __init__(self, point_type, pt_index, m_index, default_type, glyph_name):
+        error_msg = (
+            f"Glyph '{glyph_name}': '{point_type}' at point index {pt_index} in "
+            f"master index {m_index} differs from the default font point type "
+            f"'{default_type}'"
+        )
+        self.args = (error_msg,)
+
+
+class VarLibCFFHintTypeMergeError(VarLibCFFMergeError):
+    """Raised when a CFF glyph cannot be merged because of hint type differences."""
+
+    def __init__(self, hint_type, cmd_index, m_index, default_type, glyph_name):
+        error_msg = (
+            f"Glyph '{glyph_name}': '{hint_type}' at index {cmd_index} in "
+            f"master index {m_index} differs from the default font hint type "
+            f"'{default_type}'"
+        )
+        self.args = (error_msg,)
+
+
+class VariationModelError(VarLibError):
+    """Raised when a variation model is faulty."""
diff --git a/Lib/fontTools/varLib/featureVars.py b/Lib/fontTools/varLib/featureVars.py
new file mode 100644
index 0000000..45f3d83
--- /dev/null
+++ b/Lib/fontTools/varLib/featureVars.py
@@ -0,0 +1,497 @@
+"""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 fontTools.misc.dictTools import hashdict
+from fontTools.misc.intTools import popCount
+from fontTools.ttLib import newTable
+from fontTools.ttLib.tables import otTables as ot
+from fontTools.otlLib.builder import buildLookup, buildSingleSubstSubtable
+from collections import OrderedDict
+
+from .errors import VarLibError, VarLibValidationError
+
+
+def addFeatureVariations(font, conditionalSubstitutions, featureTag='rvrn'):
+    """Add conditional substitutions to a Variable Font.
+
+    The `conditionalSubstitutions` argument is a list of (Region, Substitutions)
+    tuples.
+
+    A Region is a list of Boxes. A Box is a dict mapping axisTags to
+    (minValue, maxValue) tuples. Irrelevant axes may be omitted and they are
+    interpretted as extending to end of axis in each direction.  A Box represents
+    an orthogonal '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 Boxes in the Region.
+    For efficiency, Boxes 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.
+    # ...     ([{"wdth": (0.5, 1.0)}], {"cent": "cent.rvrn"}),
+    # ...     ([{"wght": (0.5, 1.0)}], {"dollar": "dollar.rvrn"}),
+    # ... ]
+    # >>> addFeatureVariations(f, condSubst)
+    # >>> f.save(dstPath)
+    """
+
+    addFeatureVariationsRaw(font,
+                            overlayFeatureVariations(conditionalSubstitutions),
+                            featureTag)
+
+def overlayFeatureVariations(conditionalSubstitutions):
+    """Compute overlaps between all conditional substitutions.
+
+    The `conditionalSubstitutions` argument is a list of (Region, Substitutions)
+    tuples.
+
+    A Region is a list of Boxes. A Box is a dict mapping axisTags to
+    (minValue, maxValue) tuples. Irrelevant axes may be omitted and they are
+    interpretted as extending to end of axis in each direction.  A Box represents
+    an orthogonal '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 Boxes in the Region.
+    For efficiency, Boxes 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.
+
+    Returns data is in similar but different format.  Overlaps of distinct
+    substitution Boxes (*not* Regions) are explicitly listed as distinct rules,
+    and rules with the same Box merged.  The more specific rules appear earlier
+    in the resulting list.  Moreover, instead of just a dictionary of substitutions,
+    a list of dictionaries is returned for substitutions corresponding to each
+    unique space, with each dictionary being identical to one of the input
+    substitution dictionaries.  These dictionaries are not merged to allow data
+    sharing when they are converted into font tables.
+
+    Example:
+    >>> condSubst = [
+    ...     # A list of (Region, Substitution) tuples.
+    ...     ([{"wght": (0.5, 1.0)}], {"dollar": "dollar.rvrn"}),
+    ...     ([{"wght": (0.5, 1.0)}], {"dollar": "dollar.rvrn"}),
+    ...     ([{"wdth": (0.5, 1.0)}], {"cent": "cent.rvrn"}),
+    ...     ([{"wght": (0.5, 1.0), "wdth": (-1, 1.0)}], {"dollar": "dollar.rvrn"}),
+    ... ]
+    >>> from pprint import pprint
+    >>> pprint(overlayFeatureVariations(condSubst))
+    [({'wdth': (0.5, 1.0), 'wght': (0.5, 1.0)},
+      [{'dollar': 'dollar.rvrn'}, {'cent': 'cent.rvrn'}]),
+     ({'wdth': (0.5, 1.0)}, [{'cent': 'cent.rvrn'}]),
+     ({'wght': (0.5, 1.0)}, [{'dollar': 'dollar.rvrn'}])]
+    """
+
+    # Merge same-substitutions rules, as this creates fewer number oflookups.
+    merged = OrderedDict()
+    for value,key in conditionalSubstitutions:
+        key = hashdict(key)
+        if key in merged:
+            merged[key].extend(value)
+        else:
+            merged[key] = value
+    conditionalSubstitutions = [(v,dict(k)) for k,v in merged.items()]
+    del merged
+
+    # Merge same-region rules, as this is cheaper.
+    # Also convert boxes to hashdict()
+    #
+    # Reversing is such that earlier entries win in case of conflicting substitution
+    # rules for the same region.
+    merged = OrderedDict()
+    for key,value in reversed(conditionalSubstitutions):
+        key = tuple(sorted((hashdict(cleanupBox(k)) for k in key),
+                           key=lambda d: tuple(sorted(d.items()))))
+        if key in merged:
+            merged[key].update(value)
+        else:
+            merged[key] = dict(value)
+    conditionalSubstitutions = list(reversed(merged.items()))
+    del merged
+
+    # Overlay
+    #
+    # Rank is the bit-set of the index of all contributing layers.
+    initMapInit = ((hashdict(),0),) # Initializer representing the entire space
+    boxMap = OrderedDict(initMapInit) # Map from Box to Rank
+    for i,(currRegion,_) in enumerate(conditionalSubstitutions):
+        newMap = OrderedDict(initMapInit)
+        currRank = 1<<i
+        for box,rank in boxMap.items():
+            for currBox in currRegion:
+                intersection, remainder = overlayBox(currBox, box)
+                if intersection is not None:
+                    intersection = hashdict(intersection)
+                    newMap[intersection] = newMap.get(intersection, 0) | rank|currRank
+                if remainder is not None:
+                    remainder = hashdict(remainder)
+                    newMap[remainder] = newMap.get(remainder, 0) | rank
+        boxMap = newMap
+
+    # Generate output
+    items = []
+    for box,rank in sorted(boxMap.items(),
+                           key=(lambda BoxAndRank: -popCount(BoxAndRank[1]))):
+        # Skip any box that doesn't have any substitution.
+        if rank == 0:
+            continue
+        substsList = []
+        i = 0
+        while rank:
+          if rank & 1:
+              substsList.append(conditionalSubstitutions[i][1])
+          rank >>= 1
+          i += 1
+        items.append((dict(box),substsList))
+    return items
+
+
+#
+# Terminology:
+#
+# A 'Box' is a dict representing an orthogonal "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.
+#
+
+def overlayBox(top, bot):
+    """Overlays `top` box on top of `bot` box.
+
+    Returns two items:
+    - Box for intersection of `top` and `bot`, or None if they don't intersect.
+    - Box for remainder of `bot`.  Remainder box might not be exact (since the
+      remainder might not be a simple box), but is inclusive of the exact
+      remainder.
+    """
+
+    # Intersection
+    intersection = {}
+    intersection.update(top)
+    intersection.update(bot)
+    for axisTag in set(top) & set(bot):
+        min1, max1 = top[axisTag]
+        min2, max2 = bot[axisTag]
+        minimum = max(min1, min2)
+        maximum = min(max1, max2)
+        if not minimum < maximum:
+            return None, bot # Do not intersect
+        intersection[axisTag] = minimum,maximum
+
+    # Remainder
+    #
+    # Remainder is empty if bot's each axis range lies within that of intersection.
+    #
+    # Remainder is shrank if bot's each, except for exactly one, axis range lies
+    # within that of intersection, and that one axis, it spills out of the
+    # intersection only on one side.
+    #
+    # Bot is returned in full as remainder otherwise, as true remainder is not
+    # representable as a single box.
+
+    remainder = dict(bot)
+    exactlyOne = False
+    fullyInside = False
+    for axisTag in bot:
+        if axisTag not in intersection:
+            fullyInside = False
+            continue # Axis range lies fully within
+        min1, max1 = intersection[axisTag]
+        min2, max2 = bot[axisTag]
+        if min1 <= min2 and max2 <= max1:
+            continue # Axis range lies fully within
+
+        # Bot's range doesn't fully lie within that of top's for this axis.
+        # We know they intersect, so it cannot lie fully without either; so they
+        # overlap.
+
+        # If we have had an overlapping axis before, remainder is not
+        # representable as a box, so return full bottom and go home.
+        if exactlyOne:
+            return intersection, bot
+        exactlyOne = True
+        fullyInside = False
+
+        # Otherwise, cut remainder on this axis and continue.
+        if min1 <= min2:
+            # Right side survives.
+            minimum = max(max1, min2)
+            maximum = max2
+        elif max2 <= max1:
+            # Left side survives.
+            minimum = min2
+            maximum = min(min1, max2)
+        else:
+            # Remainder leaks out from both sides.  Can't cut either.
+            return intersection, bot
+
+        remainder[axisTag] = minimum,maximum
+
+    if fullyInside:
+        # bot is fully within intersection.  Remainder is empty.
+        return intersection, None
+
+    return intersection, remainder
+
+def cleanupBox(box):
+    """Return a sparse copy of `box`, without redundant (default) values.
+
+        >>> cleanupBox({})
+        {}
+        >>> cleanupBox({'wdth': (0.0, 1.0)})
+        {'wdth': (0.0, 1.0)}
+        >>> cleanupBox({'wdth': (-1.0, 1.0)})
+        {}
+
+    """
+    return {tag: limit for tag, limit in box.items() if limit != (-1.0, 1.0)}
+
+
+#
+# Low level implementation
+#
+
+def addFeatureVariationsRaw(font, conditionalSubstitutions, featureTag='rvrn'):
+    """Low level implementation of addFeatureVariations that directly
+    models the possibilities of the FeatureVariations table."""
+
+    #
+    # if there is no <featureTag> feature:
+    #     make empty <featureTag> feature
+    #     sort features, get <featureTag> feature index
+    #     add <featureTag> 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
+
+    varFeatureIndices = []
+    for index, feature in enumerate(gsub.FeatureList.FeatureRecord):
+        if feature.FeatureTag == featureTag:
+            varFeatureIndices.append(index)
+
+    if not varFeatureIndices:
+        varFeature = buildFeatureRecord(featureTag, [])
+        gsub.FeatureList.FeatureRecord.append(varFeature)
+        gsub.FeatureList.FeatureCount = len(gsub.FeatureList.FeatureRecord)
+
+        sortFeatureList(gsub)
+        varFeatureIndex = gsub.FeatureList.FeatureRecord.index(varFeature)
+
+        for scriptRecord in gsub.ScriptList.ScriptRecord:
+            if scriptRecord.Script.DefaultLangSys is None:
+                raise VarLibError(
+                    "Feature variations require that the script "
+                    f"'{scriptRecord.ScriptTag}' defines a default language system."
+                )
+            langSystems = [lsr.LangSys for lsr in scriptRecord.Script.LangSysRecord]
+            for langSys in [scriptRecord.Script.DefaultLangSys] + langSystems:
+                langSys.FeatureIndex.append(varFeatureIndex)
+
+        varFeatureIndices = [varFeatureIndex]
+
+    # 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()):
+            if minValue > maxValue:
+                raise VarLibValidationError(
+                    "A condition set has a minimum value above the maximum value."
+                )
+            ct = buildConditionTable(axisIndices[axisTag], minValue, maxValue)
+            conditionTable.append(ct)
+
+        lookupIndices = [lookupMap[subst] for subst in substitutions]
+        records = []
+        for varFeatureIndex in varFeatureIndices:
+            existingLookupIndices = gsub.FeatureList.FeatureRecord[varFeatureIndex].Feature.LookupListIndex
+            records.append(buildFeatureTableSubstitutionRecord(varFeatureIndex, existingLookupIndices + lookupIndices))
+        featureVariationRecords.append(buildFeatureVariationRecord(conditionTable, records))
+
+    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 = []
+    srec.Script.DefaultLangSys = langrec.LangSys
+
+    gsub.ScriptList.ScriptRecord.append(srec)
+    gsub.ScriptList.ScriptCount = 1
+    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
+    gsub.LookupList.LookupCount = len(gsub.LookupList.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
+    fr.Feature.populateDefaults()
+    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 = 0x00010000
+    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, sys
+    sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/varLib/instancer/__init__.py b/Lib/fontTools/varLib/instancer/__init__.py
new file mode 100644
index 0000000..1f6638a
--- /dev/null
+++ b/Lib/fontTools/varLib/instancer/__init__.py
@@ -0,0 +1,1444 @@
+""" Partially instantiate a variable font.
+
+The module exports an `instantiateVariableFont` function and CLI that allow to
+create full instances (i.e. static fonts) from variable fonts, as well as "partial"
+variable fonts that only contain a subset of the original variation space.
+
+For example, if you wish to pin the width axis to a given location while also
+restricting the weight axis to 400..700 range, you can do:
+
+$ fonttools varLib.instancer ./NotoSans-VF.ttf wdth=85 wght=400:700
+
+See `fonttools varLib.instancer --help` for more info on the CLI options.
+
+The module's entry point is the `instantiateVariableFont` function, which takes
+a TTFont object and a dict specifying either axis coodinates or (min, max) ranges,
+and returns a new TTFont representing either a partial VF, or full instance if all
+the VF axes were given an explicit coordinate.
+
+E.g. here's how to pin the wght axis at a given location in a wght+wdth variable
+font, keeping only the deltas associated with the wdth axis:
+
+| >>> from fontTools import ttLib
+| >>> from fontTools.varLib import instancer
+| >>> varfont = ttLib.TTFont("path/to/MyVariableFont.ttf")
+| >>> [a.axisTag for a in varfont["fvar"].axes]  # the varfont's current axes
+| ['wght', 'wdth']
+| >>> partial = instancer.instantiateVariableFont(varfont, {"wght": 300})
+| >>> [a.axisTag for a in partial["fvar"].axes]  # axes left after pinning 'wght'
+| ['wdth']
+
+If the input location specifies all the axes, the resulting instance is no longer
+'variable' (same as using fontools varLib.mutator):
+
+| >>> instance = instancer.instantiateVariableFont(
+| ...     varfont, {"wght": 700, "wdth": 67.5}
+| ... )
+| >>> "fvar" not in instance
+| True
+
+If one just want to drop an axis at the default location, without knowing in
+advance what the default value for that axis is, one can pass a `None` value:
+
+| >>> instance = instancer.instantiateVariableFont(varfont, {"wght": None})
+| >>> len(varfont["fvar"].axes)
+| 1
+
+From the console script, this is equivalent to passing `wght=drop` as input.
+
+This module is similar to fontTools.varLib.mutator, which it's intended to supersede.
+Note that, unlike varLib.mutator, when an axis is not mentioned in the input
+location, the varLib.instancer will keep the axis and the corresponding deltas,
+whereas mutator implicitly drops the axis at its default coordinate.
+
+The module currently supports only the first three "levels" of partial instancing,
+with the rest planned to be implemented in the future, namely:
+L1) dropping one or more axes while leaving the default tables unmodified;
+L2) dropping one or more axes while pinning them at non-default locations;
+L3) restricting the range of variation of one or more axes, by setting either
+    a new minimum or maximum, potentially -- though not necessarily -- dropping
+    entire regions of variations that fall completely outside this new range.
+L4) moving the default location of an axis.
+
+Currently only TrueType-flavored variable fonts (i.e. containing 'glyf' table)
+are supported, but support for CFF2 variable fonts will be added soon.
+
+The discussion and implementation of these features are tracked at
+https://github.com/fonttools/fonttools/issues/1537
+"""
+from fontTools.misc.fixedTools import (
+    floatToFixedToFloat,
+    strToFixedToFloat,
+    otRound,
+    MAX_F2DOT14,
+)
+from fontTools.varLib.models import supportScalar, normalizeValue, piecewiseLinearMap
+from fontTools.ttLib import TTFont
+from fontTools.ttLib.tables.TupleVariation import TupleVariation
+from fontTools.ttLib.tables import _g_l_y_f
+from fontTools import varLib
+
+# we import the `subset` module because we use the `prune_lookups` method on the GSUB
+# table class, and that method is only defined dynamically upon importing `subset`
+from fontTools import subset  # noqa: F401
+from fontTools.varLib import builder
+from fontTools.varLib.mvar import MVAR_ENTRIES
+from fontTools.varLib.merger import MutatorMerger
+from fontTools.varLib.instancer import names
+from contextlib import contextmanager
+import collections
+from copy import deepcopy
+from enum import IntEnum
+import logging
+from itertools import islice
+import os
+import re
+
+
+log = logging.getLogger("fontTools.varLib.instancer")
+
+
+class AxisRange(collections.namedtuple("AxisRange", "minimum maximum")):
+    def __new__(cls, *args, **kwargs):
+        self = super().__new__(cls, *args, **kwargs)
+        if self.minimum > self.maximum:
+            raise ValueError(
+                f"Range minimum ({self.minimum:g}) must be <= maximum ({self.maximum:g})"
+            )
+        return self
+
+    def __repr__(self):
+        return f"{type(self).__name__}({self.minimum:g}, {self.maximum:g})"
+
+
+class NormalizedAxisRange(AxisRange):
+    def __new__(cls, *args, **kwargs):
+        self = super().__new__(cls, *args, **kwargs)
+        if self.minimum < -1.0 or self.maximum > 1.0:
+            raise ValueError("Axis range values must be normalized to -1..+1 range")
+        if self.minimum > 0:
+            raise ValueError(f"Expected axis range minimum <= 0; got {self.minimum}")
+        if self.maximum < 0:
+            raise ValueError(f"Expected axis range maximum >= 0; got {self.maximum}")
+        return self
+
+
+class OverlapMode(IntEnum):
+    KEEP_AND_DONT_SET_FLAGS = 0
+    KEEP_AND_SET_FLAGS = 1
+    REMOVE = 2
+    REMOVE_AND_IGNORE_ERRORS = 3
+
+
+def instantiateTupleVariationStore(
+    variations, axisLimits, origCoords=None, endPts=None
+):
+    """Instantiate TupleVariation list at the given location, or limit axes' min/max.
+
+    The 'variations' list of TupleVariation objects is modified in-place.
+    The 'axisLimits' (dict) maps axis tags (str) to either a single coordinate along the
+    axis (float), or to minimum/maximum coordinates (NormalizedAxisRange).
+
+    A 'full' instance (i.e. static font) is produced when all the axes are pinned to
+    single coordinates; a 'partial' instance (i.e. a less variable font) is produced
+    when some of the axes are omitted, or restricted with a new range.
+
+    Tuples that do not participate are kept as they are. Those that have 0 influence
+    at the given location are removed from the variation store.
+    Those that are fully instantiated (i.e. all their axes are being pinned) are also
+    removed from the variation store, their scaled deltas accummulated and returned, so
+    that they can be added by the caller to the default instance's coordinates.
+    Tuples that are only partially instantiated (i.e. not all the axes that they
+    participate in are being pinned) are kept in the store, and their deltas multiplied
+    by the scalar support of the axes to be pinned at the desired location.
+
+    Args:
+        variations: List[TupleVariation] from either 'gvar' or 'cvar'.
+        axisLimits: Dict[str, Union[float, NormalizedAxisRange]]: axes' coordinates for
+            the full or partial instance, or ranges for restricting an axis' min/max.
+        origCoords: GlyphCoordinates: default instance's coordinates for computing 'gvar'
+            inferred points (cf. table__g_l_y_f._getCoordinatesAndControls).
+        endPts: List[int]: indices of contour end points, for inferring 'gvar' deltas.
+
+    Returns:
+        List[float]: the overall delta adjustment after applicable deltas were summed.
+    """
+    pinnedLocation, axisRanges = splitAxisLocationAndRanges(
+        axisLimits, rangeType=NormalizedAxisRange
+    )
+
+    newVariations = variations
+
+    if pinnedLocation:
+        newVariations = pinTupleVariationAxes(variations, pinnedLocation)
+
+    if axisRanges:
+        newVariations = limitTupleVariationAxisRanges(newVariations, axisRanges)
+
+    mergedVariations = collections.OrderedDict()
+    for var in newVariations:
+        # compute inferred deltas only for gvar ('origCoords' is None for cvar)
+        if origCoords is not None:
+            var.calcInferredDeltas(origCoords, endPts)
+
+        # merge TupleVariations with overlapping "tents"
+        axes = frozenset(var.axes.items())
+        if axes in mergedVariations:
+            mergedVariations[axes] += var
+        else:
+            mergedVariations[axes] = var
+
+    # drop TupleVariation if all axes have been pinned (var.axes.items() is empty);
+    # its deltas will be added to the default instance's coordinates
+    defaultVar = mergedVariations.pop(frozenset(), None)
+
+    for var in mergedVariations.values():
+        var.roundDeltas()
+    variations[:] = list(mergedVariations.values())
+
+    return defaultVar.coordinates if defaultVar is not None else []
+
+
+def pinTupleVariationAxes(variations, location):
+    newVariations = []
+    for var in variations:
+        # Compute the scalar support of the axes to be pinned at the desired location,
+        # excluding any axes that we are not pinning.
+        # If a TupleVariation doesn't mention an axis, it implies that the axis peak
+        # is 0 (i.e. the axis does not participate).
+        support = {axis: var.axes.pop(axis, (-1, 0, +1)) for axis in location}
+        scalar = supportScalar(location, support)
+        if scalar == 0.0:
+            # no influence, drop the TupleVariation
+            continue
+
+        var.scaleDeltas(scalar)
+        newVariations.append(var)
+    return newVariations
+
+
+def limitTupleVariationAxisRanges(variations, axisRanges):
+    for axisTag, axisRange in sorted(axisRanges.items()):
+        newVariations = []
+        for var in variations:
+            newVariations.extend(limitTupleVariationAxisRange(var, axisTag, axisRange))
+        variations = newVariations
+    return variations
+
+
+def _negate(*values):
+    yield from (-1 * v for v in values)
+
+
+def limitTupleVariationAxisRange(var, axisTag, axisRange):
+    if not isinstance(axisRange, NormalizedAxisRange):
+        axisRange = NormalizedAxisRange(*axisRange)
+
+    # skip when current axis is missing (i.e. doesn't participate), or when the
+    # 'tent' isn't fully on either the negative or positive side
+    lower, peak, upper = var.axes.get(axisTag, (-1, 0, 1))
+    if peak == 0 or lower > peak or peak > upper or (lower < 0 and upper > 0):
+        return [var]
+
+    negative = lower < 0
+    if negative:
+        if axisRange.minimum == -1.0:
+            return [var]
+        elif axisRange.minimum == 0.0:
+            return []
+    else:
+        if axisRange.maximum == 1.0:
+            return [var]
+        elif axisRange.maximum == 0.0:
+            return []
+
+    limit = axisRange.minimum if negative else axisRange.maximum
+
+    # Rebase axis bounds onto the new limit, which then becomes the new -1.0 or +1.0.
+    # The results are always positive, because both dividend and divisor are either
+    # all positive or all negative.
+    newLower = lower / limit
+    newPeak = peak / limit
+    newUpper = upper / limit
+    # for negative TupleVariation, swap lower and upper to simplify procedure
+    if negative:
+        newLower, newUpper = newUpper, newLower
+
+    # special case when innermost bound == peak == limit
+    if newLower == newPeak == 1.0:
+        var.axes[axisTag] = (-1.0, -1.0, -1.0) if negative else (1.0, 1.0, 1.0)
+        return [var]
+
+    # case 1: the whole deltaset falls outside the new limit; we can drop it
+    elif newLower >= 1.0:
+        return []
+
+    # case 2: only the peak and outermost bound fall outside the new limit;
+    # we keep the deltaset, update peak and outermost bound and and scale deltas
+    # by the scalar value for the restricted axis at the new limit.
+    elif newPeak >= 1.0:
+        scalar = supportScalar({axisTag: limit}, {axisTag: (lower, peak, upper)})
+        var.scaleDeltas(scalar)
+        newPeak = 1.0
+        newUpper = 1.0
+        if negative:
+            newLower, newPeak, newUpper = _negate(newUpper, newPeak, newLower)
+        var.axes[axisTag] = (newLower, newPeak, newUpper)
+        return [var]
+
+    # case 3: peak falls inside but outermost limit still fits within F2Dot14 bounds;
+    # we keep deltas as is and only scale the axes bounds. Deltas beyond -1.0
+    # or +1.0 will never be applied as implementations must clamp to that range.
+    elif newUpper <= 2.0:
+        if negative:
+            newLower, newPeak, newUpper = _negate(newUpper, newPeak, newLower)
+        elif MAX_F2DOT14 < newUpper <= 2.0:
+            # we clamp +2.0 to the max F2Dot14 (~1.99994) for convenience
+            newUpper = MAX_F2DOT14
+        var.axes[axisTag] = (newLower, newPeak, newUpper)
+        return [var]
+
+    # case 4: new limit doesn't fit; we need to chop the deltaset into two 'tents',
+    # because the shape of a triangle with part of one side cut off cannot be
+    # represented as a triangle itself. It can be represented as sum of two triangles.
+    # NOTE: This increases the file size!
+    else:
+        # duplicate the tent, then adjust lower/peak/upper so that the outermost limit
+        # of the original tent is +/-2.0, whereas the new tent's starts as the old
+        # one peaks and maxes out at +/-1.0.
+        newVar = TupleVariation(var.axes, var.coordinates)
+        if negative:
+            var.axes[axisTag] = (-2.0, -1 * newPeak, -1 * newLower)
+            newVar.axes[axisTag] = (-1.0, -1.0, -1 * newPeak)
+        else:
+            var.axes[axisTag] = (newLower, newPeak, MAX_F2DOT14)
+            newVar.axes[axisTag] = (newPeak, 1.0, 1.0)
+        # the new tent's deltas are scaled by the difference between the scalar value
+        # for the old tent at the desired limit...
+        scalar1 = supportScalar({axisTag: limit}, {axisTag: (lower, peak, upper)})
+        # ... and the scalar value for the clamped tent (with outer limit +/-2.0),
+        # which can be simplified like this:
+        scalar2 = 1 / (2 - newPeak)
+        newVar.scaleDeltas(scalar1 - scalar2)
+
+        return [var, newVar]
+
+
+def _instantiateGvarGlyph(glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=True):
+    coordinates, ctrl = glyf._getCoordinatesAndControls(glyphname, hMetrics, vMetrics)
+    endPts = ctrl.endPts
+
+    # Not every glyph may have variations
+    tupleVarStore = gvar.variations.get(glyphname)
+
+    if tupleVarStore:
+        defaultDeltas = instantiateTupleVariationStore(
+            tupleVarStore, axisLimits, coordinates, endPts
+        )
+
+        if defaultDeltas:
+            coordinates += _g_l_y_f.GlyphCoordinates(defaultDeltas)
+
+    # _setCoordinates also sets the hmtx/vmtx advance widths and sidebearings from
+    # the four phantom points and glyph bounding boxes.
+    # We call it unconditionally even if a glyph has no variations or no deltas are
+    # applied at this location, in case the glyph's xMin and in turn its sidebearing
+    # have changed. E.g. a composite glyph has no deltas for the component's (x, y)
+    # offset nor for the 4 phantom points (e.g. it's monospaced). Thus its entry in
+    # gvar table is empty; however, the composite's base glyph may have deltas
+    # applied, hence the composite's bbox and left/top sidebearings may need updating
+    # in the instanced font.
+    glyf._setCoordinates(glyphname, coordinates, hMetrics, vMetrics)
+
+    if not tupleVarStore:
+        if glyphname in gvar.variations:
+            del gvar.variations[glyphname]
+        return
+
+    if optimize:
+        isComposite = glyf[glyphname].isComposite()
+        for var in tupleVarStore:
+            var.optimize(coordinates, endPts, isComposite)
+
+def instantiateGvarGlyph(varfont, glyphname, axisLimits, optimize=True):
+    """Remove?
+    https://github.com/fonttools/fonttools/pull/2266"""
+    gvar = varfont["gvar"]
+    glyf = varfont["glyf"]
+    hMetrics = varfont['hmtx'].metrics
+    vMetrics = getattr(varfont.get('vmtx'), 'metrics', None)
+    _instantiateGvarGlyph(glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=optimize)
+
+def instantiateGvar(varfont, axisLimits, optimize=True):
+    log.info("Instantiating glyf/gvar tables")
+
+    gvar = varfont["gvar"]
+    glyf = varfont["glyf"]
+    hMetrics = varfont['hmtx'].metrics
+    vMetrics = getattr(varfont.get('vmtx'), 'metrics', None)
+    # Get list of glyph names sorted by component depth.
+    # If a composite glyph is processed before its base glyph, the bounds may
+    # be calculated incorrectly because deltas haven't been applied to the
+    # base glyph yet.
+    glyphnames = sorted(
+        glyf.glyphOrder,
+        key=lambda name: (
+            glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth
+            if glyf[name].isComposite()
+            else 0,
+            name,
+        ),
+    )
+    for glyphname in glyphnames:
+        _instantiateGvarGlyph(glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=optimize)
+
+    if not gvar.variations:
+        del varfont["gvar"]
+
+
+def setCvarDeltas(cvt, deltas):
+    for i, delta in enumerate(deltas):
+        if delta:
+            cvt[i] += otRound(delta)
+
+
+def instantiateCvar(varfont, axisLimits):
+    log.info("Instantiating cvt/cvar tables")
+
+    cvar = varfont["cvar"]
+
+    defaultDeltas = instantiateTupleVariationStore(cvar.variations, axisLimits)
+
+    if defaultDeltas:
+        setCvarDeltas(varfont["cvt "], defaultDeltas)
+
+    if not cvar.variations:
+        del varfont["cvar"]
+
+
+def setMvarDeltas(varfont, deltas):
+    mvar = varfont["MVAR"].table
+    records = mvar.ValueRecord
+    for rec in records:
+        mvarTag = rec.ValueTag
+        if mvarTag not in MVAR_ENTRIES:
+            continue
+        tableTag, itemName = MVAR_ENTRIES[mvarTag]
+        delta = deltas[rec.VarIdx]
+        if delta != 0:
+            setattr(
+                varfont[tableTag],
+                itemName,
+                getattr(varfont[tableTag], itemName) + otRound(delta),
+            )
+
+
+def instantiateMVAR(varfont, axisLimits):
+    log.info("Instantiating MVAR table")
+
+    mvar = varfont["MVAR"].table
+    fvarAxes = varfont["fvar"].axes
+    varStore = mvar.VarStore
+    defaultDeltas = instantiateItemVariationStore(varStore, fvarAxes, axisLimits)
+    setMvarDeltas(varfont, defaultDeltas)
+
+    if varStore.VarRegionList.Region:
+        varIndexMapping = varStore.optimize()
+        for rec in mvar.ValueRecord:
+            rec.VarIdx = varIndexMapping[rec.VarIdx]
+    else:
+        del varfont["MVAR"]
+
+
+def _remapVarIdxMap(table, attrName, varIndexMapping, glyphOrder):
+    oldMapping = getattr(table, attrName).mapping
+    newMapping = [varIndexMapping[oldMapping[glyphName]] for glyphName in glyphOrder]
+    setattr(table, attrName, builder.buildVarIdxMap(newMapping, glyphOrder))
+
+
+# TODO(anthrotype) Add support for HVAR/VVAR in CFF2
+def _instantiateVHVAR(varfont, axisLimits, tableFields):
+    tableTag = tableFields.tableTag
+    fvarAxes = varfont["fvar"].axes
+    # Deltas from gvar table have already been applied to the hmtx/vmtx. For full
+    # instances (i.e. all axes pinned), we can simply drop HVAR/VVAR and return
+    if set(
+        axisTag for axisTag, value in axisLimits.items() if not isinstance(value, tuple)
+    ).issuperset(axis.axisTag for axis in fvarAxes):
+        log.info("Dropping %s table", tableTag)
+        del varfont[tableTag]
+        return
+
+    log.info("Instantiating %s table", tableTag)
+    vhvar = varfont[tableTag].table
+    varStore = vhvar.VarStore
+    # since deltas were already applied, the return value here is ignored
+    instantiateItemVariationStore(varStore, fvarAxes, axisLimits)
+
+    if varStore.VarRegionList.Region:
+        # Only re-optimize VarStore if the HVAR/VVAR already uses indirect AdvWidthMap
+        # or AdvHeightMap. If a direct, implicit glyphID->VariationIndex mapping is
+        # used for advances, skip re-optimizing and maintain original VariationIndex.
+        if getattr(vhvar, tableFields.advMapping):
+            varIndexMapping = varStore.optimize()
+            glyphOrder = varfont.getGlyphOrder()
+            _remapVarIdxMap(vhvar, tableFields.advMapping, varIndexMapping, glyphOrder)
+            if getattr(vhvar, tableFields.sb1):  # left or top sidebearings
+                _remapVarIdxMap(vhvar, tableFields.sb1, varIndexMapping, glyphOrder)
+            if getattr(vhvar, tableFields.sb2):  # right or bottom sidebearings
+                _remapVarIdxMap(vhvar, tableFields.sb2, varIndexMapping, glyphOrder)
+            if tableTag == "VVAR" and getattr(vhvar, tableFields.vOrigMapping):
+                _remapVarIdxMap(
+                    vhvar, tableFields.vOrigMapping, varIndexMapping, glyphOrder
+                )
+
+
+def instantiateHVAR(varfont, axisLimits):
+    return _instantiateVHVAR(varfont, axisLimits, varLib.HVAR_FIELDS)
+
+
+def instantiateVVAR(varfont, axisLimits):
+    return _instantiateVHVAR(varfont, axisLimits, varLib.VVAR_FIELDS)
+
+
+class _TupleVarStoreAdapter(object):
+    def __init__(self, regions, axisOrder, tupleVarData, itemCounts):
+        self.regions = regions
+        self.axisOrder = axisOrder
+        self.tupleVarData = tupleVarData
+        self.itemCounts = itemCounts
+
+    @classmethod
+    def fromItemVarStore(cls, itemVarStore, fvarAxes):
+        axisOrder = [axis.axisTag for axis in fvarAxes]
+        regions = [
+            region.get_support(fvarAxes) for region in itemVarStore.VarRegionList.Region
+        ]
+        tupleVarData = []
+        itemCounts = []
+        for varData in itemVarStore.VarData:
+            variations = []
+            varDataRegions = (regions[i] for i in varData.VarRegionIndex)
+            for axes, coordinates in zip(varDataRegions, zip(*varData.Item)):
+                variations.append(TupleVariation(axes, list(coordinates)))
+            tupleVarData.append(variations)
+            itemCounts.append(varData.ItemCount)
+        return cls(regions, axisOrder, tupleVarData, itemCounts)
+
+    def rebuildRegions(self):
+        # Collect the set of all unique region axes from the current TupleVariations.
+        # We use an OrderedDict to de-duplicate regions while keeping the order.
+        uniqueRegions = collections.OrderedDict.fromkeys(
+            (
+                frozenset(var.axes.items())
+                for variations in self.tupleVarData
+                for var in variations
+            )
+        )
+        # Maintain the original order for the regions that pre-existed, appending
+        # the new regions at the end of the region list.
+        newRegions = []
+        for region in self.regions:
+            regionAxes = frozenset(region.items())
+            if regionAxes in uniqueRegions:
+                newRegions.append(region)
+                del uniqueRegions[regionAxes]
+        if uniqueRegions:
+            newRegions.extend(dict(region) for region in uniqueRegions)
+        self.regions = newRegions
+
+    def instantiate(self, axisLimits):
+        defaultDeltaArray = []
+        for variations, itemCount in zip(self.tupleVarData, self.itemCounts):
+            defaultDeltas = instantiateTupleVariationStore(variations, axisLimits)
+            if not defaultDeltas:
+                defaultDeltas = [0] * itemCount
+            defaultDeltaArray.append(defaultDeltas)
+
+        # rebuild regions whose axes were dropped or limited
+        self.rebuildRegions()
+
+        pinnedAxes = {
+            axisTag
+            for axisTag, value in axisLimits.items()
+            if not isinstance(value, tuple)
+        }
+        self.axisOrder = [
+            axisTag for axisTag in self.axisOrder if axisTag not in pinnedAxes
+        ]
+
+        return defaultDeltaArray
+
+    def asItemVarStore(self):
+        regionOrder = [frozenset(axes.items()) for axes in self.regions]
+        varDatas = []
+        for variations, itemCount in zip(self.tupleVarData, self.itemCounts):
+            if variations:
+                assert len(variations[0].coordinates) == itemCount
+                varRegionIndices = [
+                    regionOrder.index(frozenset(var.axes.items())) for var in variations
+                ]
+                varDataItems = list(zip(*(var.coordinates for var in variations)))
+                varDatas.append(
+                    builder.buildVarData(varRegionIndices, varDataItems, optimize=False)
+                )
+            else:
+                varDatas.append(
+                    builder.buildVarData([], [[] for _ in range(itemCount)])
+                )
+        regionList = builder.buildVarRegionList(self.regions, self.axisOrder)
+        itemVarStore = builder.buildVarStore(regionList, varDatas)
+        # remove unused regions from VarRegionList
+        itemVarStore.prune_regions()
+        return itemVarStore
+
+
+def instantiateItemVariationStore(itemVarStore, fvarAxes, axisLimits):
+    """Compute deltas at partial location, and update varStore in-place.
+
+    Remove regions in which all axes were instanced, or fall outside the new axis
+    limits. Scale the deltas of the remaining regions where only some of the axes
+    were instanced.
+
+    The number of VarData subtables, and the number of items within each, are
+    not modified, in order to keep the existing VariationIndex valid.
+    One may call VarStore.optimize() method after this to further optimize those.
+
+    Args:
+        varStore: An otTables.VarStore object (Item Variation Store)
+        fvarAxes: list of fvar's Axis objects
+        axisLimits: Dict[str, float] mapping axis tags to normalized axis coordinates
+            (float) or ranges for restricting an axis' min/max (NormalizedAxisRange).
+            May not specify coordinates/ranges for all the fvar axes.
+
+    Returns:
+        defaultDeltas: to be added to the default instance, of type dict of floats
+            keyed by VariationIndex compound values: i.e. (outer << 16) + inner.
+    """
+    tupleVarStore = _TupleVarStoreAdapter.fromItemVarStore(itemVarStore, fvarAxes)
+    defaultDeltaArray = tupleVarStore.instantiate(axisLimits)
+    newItemVarStore = tupleVarStore.asItemVarStore()
+
+    itemVarStore.VarRegionList = newItemVarStore.VarRegionList
+    assert itemVarStore.VarDataCount == newItemVarStore.VarDataCount
+    itemVarStore.VarData = newItemVarStore.VarData
+
+    defaultDeltas = {
+        ((major << 16) + minor): delta
+        for major, deltas in enumerate(defaultDeltaArray)
+        for minor, delta in enumerate(deltas)
+    }
+    return defaultDeltas
+
+
+def instantiateOTL(varfont, axisLimits):
+    # TODO(anthrotype) Support partial instancing of JSTF and BASE tables
+
+    if (
+        "GDEF" not in varfont
+        or varfont["GDEF"].table.Version < 0x00010003
+        or not varfont["GDEF"].table.VarStore
+    ):
+        return
+
+    if "GPOS" in varfont:
+        msg = "Instantiating GDEF and GPOS tables"
+    else:
+        msg = "Instantiating GDEF table"
+    log.info(msg)
+
+    gdef = varfont["GDEF"].table
+    varStore = gdef.VarStore
+    fvarAxes = varfont["fvar"].axes
+
+    defaultDeltas = instantiateItemVariationStore(varStore, fvarAxes, axisLimits)
+
+    # When VF are built, big lookups may overflow and be broken into multiple
+    # subtables. MutatorMerger (which inherits from AligningMerger) reattaches
+    # them upon instancing, in case they can now fit a single subtable (if not,
+    # they will be split again upon compilation).
+    # This 'merger' also works as a 'visitor' that traverses the OTL tables and
+    # calls specific methods when instances of a given type are found.
+    # Specifically, it adds default deltas to GPOS Anchors/ValueRecords and GDEF
+    # LigatureCarets, and optionally deletes all VariationIndex tables if the
+    # VarStore is fully instanced.
+    merger = MutatorMerger(
+        varfont, defaultDeltas, deleteVariations=(not varStore.VarRegionList.Region)
+    )
+    merger.mergeTables(varfont, [varfont], ["GDEF", "GPOS"])
+
+    if varStore.VarRegionList.Region:
+        varIndexMapping = varStore.optimize()
+        gdef.remap_device_varidxes(varIndexMapping)
+        if "GPOS" in varfont:
+            varfont["GPOS"].table.remap_device_varidxes(varIndexMapping)
+    else:
+        # Downgrade GDEF.
+        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 varfont["GDEF"]
+
+
+def instantiateFeatureVariations(varfont, axisLimits):
+    for tableTag in ("GPOS", "GSUB"):
+        if tableTag not in varfont or not getattr(
+            varfont[tableTag].table, "FeatureVariations", None
+        ):
+            continue
+        log.info("Instantiating FeatureVariations of %s table", tableTag)
+        _instantiateFeatureVariations(
+            varfont[tableTag].table, varfont["fvar"].axes, axisLimits
+        )
+        # remove unreferenced lookups
+        varfont[tableTag].prune_lookups()
+
+
+def _featureVariationRecordIsUnique(rec, seen):
+    conditionSet = []
+    for cond in rec.ConditionSet.ConditionTable:
+        if cond.Format != 1:
+            # can't tell whether this is duplicate, assume is unique
+            return True
+        conditionSet.append(
+            (cond.AxisIndex, cond.FilterRangeMinValue, cond.FilterRangeMaxValue)
+        )
+    # besides the set of conditions, we also include the FeatureTableSubstitution
+    # version to identify unique FeatureVariationRecords, even though only one
+    # version is currently defined. It's theoretically possible that multiple
+    # records with same conditions but different substitution table version be
+    # present in the same font for backward compatibility.
+    recordKey = frozenset([rec.FeatureTableSubstitution.Version] + conditionSet)
+    if recordKey in seen:
+        return False
+    else:
+        seen.add(recordKey)  # side effect
+        return True
+
+
+def _limitFeatureVariationConditionRange(condition, axisRange):
+    minValue = condition.FilterRangeMinValue
+    maxValue = condition.FilterRangeMaxValue
+
+    if (
+        minValue > maxValue
+        or minValue > axisRange.maximum
+        or maxValue < axisRange.minimum
+    ):
+        # condition invalid or out of range
+        return
+
+    values = [minValue, maxValue]
+    for i, value in enumerate(values):
+        if value < 0:
+            if axisRange.minimum == 0:
+                newValue = 0
+            else:
+                newValue = value / abs(axisRange.minimum)
+                if newValue <= -1.0:
+                    newValue = -1.0
+        elif value > 0:
+            if axisRange.maximum == 0:
+                newValue = 0
+            else:
+                newValue = value / axisRange.maximum
+                if newValue >= 1.0:
+                    newValue = 1.0
+        else:
+            newValue = 0
+        values[i] = newValue
+
+    return AxisRange(*values)
+
+
+def _instantiateFeatureVariationRecord(
+    record, recIdx, location, fvarAxes, axisIndexMap
+):
+    applies = True
+    newConditions = []
+    for i, condition in enumerate(record.ConditionSet.ConditionTable):
+        if condition.Format == 1:
+            axisIdx = condition.AxisIndex
+            axisTag = fvarAxes[axisIdx].axisTag
+            if axisTag in location:
+                minValue = condition.FilterRangeMinValue
+                maxValue = condition.FilterRangeMaxValue
+                v = location[axisTag]
+                if not (minValue <= v <= maxValue):
+                    # condition not met so remove entire record
+                    applies = False
+                    newConditions = None
+                    break
+            else:
+                # axis not pinned, keep condition with remapped axis index
+                applies = False
+                condition.AxisIndex = axisIndexMap[axisTag]
+                newConditions.append(condition)
+        else:
+            log.warning(
+                "Condition table {0} of FeatureVariationRecord {1} has "
+                "unsupported format ({2}); ignored".format(i, recIdx, condition.Format)
+            )
+            applies = False
+            newConditions.append(condition)
+
+    if newConditions:
+        record.ConditionSet.ConditionTable = newConditions
+        shouldKeep = True
+    else:
+        shouldKeep = False
+
+    return applies, shouldKeep
+
+
+def _limitFeatureVariationRecord(record, axisRanges, fvarAxes):
+    newConditions = []
+    for i, condition in enumerate(record.ConditionSet.ConditionTable):
+        if condition.Format == 1:
+            axisIdx = condition.AxisIndex
+            axisTag = fvarAxes[axisIdx].axisTag
+            if axisTag in axisRanges:
+                axisRange = axisRanges[axisTag]
+                newRange = _limitFeatureVariationConditionRange(condition, axisRange)
+                if newRange:
+                    # keep condition with updated limits and remapped axis index
+                    condition.FilterRangeMinValue = newRange.minimum
+                    condition.FilterRangeMaxValue = newRange.maximum
+                    newConditions.append(condition)
+                else:
+                    # condition out of range, remove entire record
+                    newConditions = None
+                    break
+            else:
+                newConditions.append(condition)
+        else:
+            newConditions.append(condition)
+
+    if newConditions:
+        record.ConditionSet.ConditionTable = newConditions
+        shouldKeep = True
+    else:
+        shouldKeep = False
+
+    return shouldKeep
+
+
+def _instantiateFeatureVariations(table, fvarAxes, axisLimits):
+    location, axisRanges = splitAxisLocationAndRanges(
+        axisLimits, rangeType=NormalizedAxisRange
+    )
+    pinnedAxes = set(location.keys())
+    axisOrder = [axis.axisTag for axis in fvarAxes if axis.axisTag not in pinnedAxes]
+    axisIndexMap = {axisTag: axisOrder.index(axisTag) for axisTag in axisOrder}
+
+    featureVariationApplied = False
+    uniqueRecords = set()
+    newRecords = []
+
+    for i, record in enumerate(table.FeatureVariations.FeatureVariationRecord):
+        applies, shouldKeep = _instantiateFeatureVariationRecord(
+            record, i, location, fvarAxes, axisIndexMap
+        )
+        if shouldKeep:
+            shouldKeep = _limitFeatureVariationRecord(record, axisRanges, fvarAxes)
+
+        if shouldKeep and _featureVariationRecordIsUnique(record, uniqueRecords):
+            newRecords.append(record)
+
+        if applies and not featureVariationApplied:
+            assert record.FeatureTableSubstitution.Version == 0x00010000
+            for rec in record.FeatureTableSubstitution.SubstitutionRecord:
+                table.FeatureList.FeatureRecord[rec.FeatureIndex].Feature = rec.Feature
+            # Set variations only once
+            featureVariationApplied = True
+
+    if newRecords:
+        table.FeatureVariations.FeatureVariationRecord = newRecords
+        table.FeatureVariations.FeatureVariationCount = len(newRecords)
+    else:
+        del table.FeatureVariations
+
+
+def _isValidAvarSegmentMap(axisTag, segmentMap):
+    if not segmentMap:
+        return True
+    if not {(-1.0, -1.0), (0, 0), (1.0, 1.0)}.issubset(segmentMap.items()):
+        log.warning(
+            f"Invalid avar SegmentMap record for axis '{axisTag}': does not "
+            "include all required value maps {-1.0: -1.0, 0: 0, 1.0: 1.0}"
+        )
+        return False
+    previousValue = None
+    for fromCoord, toCoord in sorted(segmentMap.items()):
+        if previousValue is not None and previousValue > toCoord:
+            log.warning(
+                f"Invalid avar AxisValueMap({fromCoord}, {toCoord}) record "
+                f"for axis '{axisTag}': the toCoordinate value must be >= to "
+                f"the toCoordinate value of the preceding record ({previousValue})."
+            )
+            return False
+        previousValue = toCoord
+    return True
+
+
+def instantiateAvar(varfont, axisLimits):
+    # 'axisLimits' dict must contain user-space (non-normalized) coordinates.
+
+    location, axisRanges = splitAxisLocationAndRanges(axisLimits)
+
+    segments = varfont["avar"].segments
+
+    # drop table if we instantiate all the axes
+    pinnedAxes = set(location.keys())
+    if pinnedAxes.issuperset(segments):
+        log.info("Dropping avar table")
+        del varfont["avar"]
+        return
+
+    log.info("Instantiating avar table")
+    for axis in pinnedAxes:
+        if axis in segments:
+            del segments[axis]
+
+    # First compute the default normalization for axisRanges coordinates: i.e.
+    # min = -1.0, default = 0, max = +1.0, and in between values interpolated linearly,
+    # without using the avar table's mappings.
+    # Then, for each SegmentMap, if we are restricting its axis, compute the new
+    # mappings by dividing the key/value pairs by the desired new min/max values,
+    # dropping any mappings that fall outside the restricted range.
+    # The keys ('fromCoord') are specified in default normalized coordinate space,
+    # whereas the values ('toCoord') are "mapped forward" using the SegmentMap.
+    normalizedRanges = normalizeAxisLimits(varfont, axisRanges, usingAvar=False)
+    newSegments = {}
+    for axisTag, mapping in segments.items():
+        if not _isValidAvarSegmentMap(axisTag, mapping):
+            continue
+        if mapping and axisTag in normalizedRanges:
+            axisRange = normalizedRanges[axisTag]
+            mappedMin = floatToFixedToFloat(
+                piecewiseLinearMap(axisRange.minimum, mapping), 14
+            )
+            mappedMax = floatToFixedToFloat(
+                piecewiseLinearMap(axisRange.maximum, mapping), 14
+            )
+            newMapping = {}
+            for fromCoord, toCoord in mapping.items():
+                if fromCoord < 0:
+                    if axisRange.minimum == 0 or fromCoord < axisRange.minimum:
+                        continue
+                    else:
+                        fromCoord /= abs(axisRange.minimum)
+                elif fromCoord > 0:
+                    if axisRange.maximum == 0 or fromCoord > axisRange.maximum:
+                        continue
+                    else:
+                        fromCoord /= axisRange.maximum
+                if toCoord < 0:
+                    assert mappedMin != 0
+                    assert toCoord >= mappedMin
+                    toCoord /= abs(mappedMin)
+                elif toCoord > 0:
+                    assert mappedMax != 0
+                    assert toCoord <= mappedMax
+                    toCoord /= mappedMax
+                fromCoord = floatToFixedToFloat(fromCoord, 14)
+                toCoord = floatToFixedToFloat(toCoord, 14)
+                newMapping[fromCoord] = toCoord
+            newMapping.update({-1.0: -1.0, 1.0: 1.0})
+            newSegments[axisTag] = newMapping
+        else:
+            newSegments[axisTag] = mapping
+    varfont["avar"].segments = newSegments
+
+
+def isInstanceWithinAxisRanges(location, axisRanges):
+    for axisTag, coord in location.items():
+        if axisTag in axisRanges:
+            axisRange = axisRanges[axisTag]
+            if coord < axisRange.minimum or coord > axisRange.maximum:
+                return False
+    return True
+
+
+def instantiateFvar(varfont, axisLimits):
+    # 'axisLimits' dict must contain user-space (non-normalized) coordinates
+
+    location, axisRanges = splitAxisLocationAndRanges(axisLimits, rangeType=AxisRange)
+
+    fvar = varfont["fvar"]
+
+    # drop table if we instantiate all the axes
+    if set(location).issuperset(axis.axisTag for axis in fvar.axes):
+        log.info("Dropping fvar table")
+        del varfont["fvar"]
+        return
+
+    log.info("Instantiating fvar table")
+
+    axes = []
+    for axis in fvar.axes:
+        axisTag = axis.axisTag
+        if axisTag in location:
+            continue
+        if axisTag in axisRanges:
+            axis.minValue, axis.maxValue = axisRanges[axisTag]
+        axes.append(axis)
+    fvar.axes = axes
+
+    # only keep NamedInstances whose coordinates == pinned axis location
+    instances = []
+    for instance in fvar.instances:
+        if any(instance.coordinates[axis] != value for axis, value in location.items()):
+            continue
+        for axisTag in location:
+            del instance.coordinates[axisTag]
+        if not isInstanceWithinAxisRanges(instance.coordinates, axisRanges):
+            continue
+        instances.append(instance)
+    fvar.instances = instances
+
+
+def instantiateSTAT(varfont, axisLimits):
+    # 'axisLimits' dict must contain user-space (non-normalized) coordinates
+
+    stat = varfont["STAT"].table
+    if not stat.DesignAxisRecord or not (
+        stat.AxisValueArray and stat.AxisValueArray.AxisValue
+    ):
+        return  # STAT table empty, nothing to do
+
+    log.info("Instantiating STAT table")
+    newAxisValueTables = axisValuesFromAxisLimits(stat, axisLimits)
+    stat.AxisValueArray.AxisValue = newAxisValueTables
+    stat.AxisValueCount = len(stat.AxisValueArray.AxisValue)
+
+
+def axisValuesFromAxisLimits(stat, axisLimits):
+    location, axisRanges = splitAxisLocationAndRanges(axisLimits, rangeType=AxisRange)
+
+    def isAxisValueOutsideLimits(axisTag, axisValue):
+        if axisTag in location and axisValue != location[axisTag]:
+            return True
+        elif axisTag in axisRanges:
+            axisRange = axisRanges[axisTag]
+            if axisValue < axisRange.minimum or axisValue > axisRange.maximum:
+                return True
+        return False
+
+    # only keep AxisValues whose axis is not pinned nor restricted, or is pinned at the
+    # exact (nominal) value, or is restricted but the value is within the new range
+    designAxes = stat.DesignAxisRecord.Axis
+    newAxisValueTables = []
+    for axisValueTable in stat.AxisValueArray.AxisValue:
+        axisValueFormat = axisValueTable.Format
+        if axisValueFormat in (1, 2, 3):
+            axisTag = designAxes[axisValueTable.AxisIndex].AxisTag
+            if axisValueFormat == 2:
+                axisValue = axisValueTable.NominalValue
+            else:
+                axisValue = axisValueTable.Value
+            if isAxisValueOutsideLimits(axisTag, axisValue):
+                continue
+        elif axisValueFormat == 4:
+            # drop 'non-analytic' AxisValue if _any_ AxisValueRecord doesn't match
+            # the pinned location or is outside range
+            dropAxisValueTable = False
+            for rec in axisValueTable.AxisValueRecord:
+                axisTag = designAxes[rec.AxisIndex].AxisTag
+                axisValue = rec.Value
+                if isAxisValueOutsideLimits(axisTag, axisValue):
+                    dropAxisValueTable = True
+                    break
+            if dropAxisValueTable:
+                continue
+        else:
+            log.warning("Unknown AxisValue table format (%s); ignored", axisValueFormat)
+        newAxisValueTables.append(axisValueTable)
+    return newAxisValueTables
+
+
+def setMacOverlapFlags(glyfTable):
+    flagOverlapCompound = _g_l_y_f.OVERLAP_COMPOUND
+    flagOverlapSimple = _g_l_y_f.flagOverlapSimple
+    for glyphName in glyfTable.keys():
+        glyph = glyfTable[glyphName]
+        # Set OVERLAP_COMPOUND bit for compound glyphs
+        if glyph.isComposite():
+            glyph.components[0].flags |= flagOverlapCompound
+        # Set OVERLAP_SIMPLE bit for simple glyphs
+        elif glyph.numberOfContours > 0:
+            glyph.flags[0] |= flagOverlapSimple
+
+
+def normalize(value, triple, avarMapping):
+    value = normalizeValue(value, triple)
+    if avarMapping:
+        value = piecewiseLinearMap(value, avarMapping)
+    # Quantize to F2Dot14, to avoid surprise interpolations.
+    return floatToFixedToFloat(value, 14)
+
+
+def normalizeAxisLimits(varfont, axisLimits, usingAvar=True):
+    fvar = varfont["fvar"]
+    badLimits = set(axisLimits.keys()).difference(a.axisTag for a in fvar.axes)
+    if badLimits:
+        raise ValueError("Cannot limit: {} not present in fvar".format(badLimits))
+
+    axes = {
+        a.axisTag: (a.minValue, a.defaultValue, a.maxValue)
+        for a in fvar.axes
+        if a.axisTag in axisLimits
+    }
+
+    avarSegments = {}
+    if usingAvar and "avar" in varfont:
+        avarSegments = varfont["avar"].segments
+
+    for axis_tag, (_, default, _) in axes.items():
+        value = axisLimits[axis_tag]
+        if isinstance(value, tuple):
+            minV, maxV = value
+            if minV > default or maxV < default:
+                raise NotImplementedError(
+                    f"Unsupported range {axis_tag}={minV:g}:{maxV:g}; "
+                    f"can't change default position ({axis_tag}={default:g})"
+                )
+
+    normalizedLimits = {}
+    for axis_tag, triple in axes.items():
+        avarMapping = avarSegments.get(axis_tag, None)
+        value = axisLimits[axis_tag]
+        if isinstance(value, tuple):
+            normalizedLimits[axis_tag] = NormalizedAxisRange(
+                *(normalize(v, triple, avarMapping) for v in value)
+            )
+        else:
+            normalizedLimits[axis_tag] = normalize(value, triple, avarMapping)
+    return normalizedLimits
+
+
+def sanityCheckVariableTables(varfont):
+    if "fvar" not in varfont:
+        raise ValueError("Missing required table fvar")
+    if "gvar" in varfont:
+        if "glyf" not in varfont:
+            raise ValueError("Can't have gvar without glyf")
+    # TODO(anthrotype) Remove once we do support partial instancing CFF2
+    if "CFF2" in varfont:
+        raise NotImplementedError("Instancing CFF2 variable fonts is not supported yet")
+
+
+def populateAxisDefaults(varfont, axisLimits):
+    if any(value is None for value in axisLimits.values()):
+        fvar = varfont["fvar"]
+        defaultValues = {a.axisTag: a.defaultValue for a in fvar.axes}
+        return {
+            axisTag: defaultValues[axisTag] if value is None else value
+            for axisTag, value in axisLimits.items()
+        }
+    return axisLimits
+
+
+def instantiateVariableFont(
+    varfont,
+    axisLimits,
+    inplace=False,
+    optimize=True,
+    overlap=OverlapMode.KEEP_AND_SET_FLAGS,
+    updateFontNames=False,
+):
+    """Instantiate variable font, either fully or partially.
+
+    Depending on whether the `axisLimits` dictionary references all or some of the
+    input varfont's axes, the output font will either be a full instance (static
+    font) or a variable font with possibly less variation data.
+
+    Args:
+        varfont: a TTFont instance, which must contain at least an 'fvar' table.
+            Note that variable fonts with 'CFF2' table are not supported yet.
+        axisLimits: a dict keyed by axis tags (str) containing the coordinates (float)
+            along one or more axes where the desired instance will be located.
+            If the value is `None`, the default coordinate as per 'fvar' table for
+            that axis is used.
+            The limit values can also be (min, max) tuples for restricting an
+            axis's variation range. The default axis value must be included in
+            the new range.
+        inplace (bool): whether to modify input TTFont object in-place instead of
+            returning a distinct object.
+        optimize (bool): if False, do not perform IUP-delta optimization on the
+            remaining 'gvar' table's deltas. Possibly faster, and might work around
+            rendering issues in some buggy environments, at the cost of a slightly
+            larger file size.
+        overlap (OverlapMode): variable fonts usually contain overlapping contours, and
+            some font rendering engines on Apple platforms require that the
+            `OVERLAP_SIMPLE` and `OVERLAP_COMPOUND` flags in the 'glyf' table be set to
+            force rendering using a non-zero fill rule. Thus we always set these flags
+            on all glyphs to maximise cross-compatibility of the generated instance.
+            You can disable this by passing OverlapMode.KEEP_AND_DONT_SET_FLAGS.
+            If you want to remove the overlaps altogether and merge overlapping
+            contours and components, you can pass OverlapMode.REMOVE (or
+            REMOVE_AND_IGNORE_ERRORS to not hard-fail on tricky glyphs). Note that this
+            requires the skia-pathops package (available to pip install).
+            The overlap parameter only has effect when generating full static instances.
+        updateFontNames (bool): if True, update the instantiated font's name table using
+            the Axis Value Tables from the STAT table. The name table will be updated so
+            it conforms to the R/I/B/BI model. If the STAT table is missing or
+            an Axis Value table is missing for a given axis coordinate, a ValueError will
+            be raised.
+    """
+    # 'overlap' used to be bool and is now enum; for backward compat keep accepting bool
+    overlap = OverlapMode(int(overlap))
+
+    sanityCheckVariableTables(varfont)
+
+    axisLimits = populateAxisDefaults(varfont, axisLimits)
+
+    normalizedLimits = normalizeAxisLimits(varfont, axisLimits)
+
+    log.info("Normalized limits: %s", normalizedLimits)
+
+    if not inplace:
+        varfont = deepcopy(varfont)
+
+    if updateFontNames:
+        log.info("Updating name table")
+        names.updateNameTable(varfont, axisLimits)
+
+    if "gvar" in varfont:
+        instantiateGvar(varfont, normalizedLimits, optimize=optimize)
+
+    if "cvar" in varfont:
+        instantiateCvar(varfont, normalizedLimits)
+
+    if "MVAR" in varfont:
+        instantiateMVAR(varfont, normalizedLimits)
+
+    if "HVAR" in varfont:
+        instantiateHVAR(varfont, normalizedLimits)
+
+    if "VVAR" in varfont:
+        instantiateVVAR(varfont, normalizedLimits)
+
+    instantiateOTL(varfont, normalizedLimits)
+
+    instantiateFeatureVariations(varfont, normalizedLimits)
+
+    if "avar" in varfont:
+        instantiateAvar(varfont, axisLimits)
+
+    with names.pruningUnusedNames(varfont):
+        if "STAT" in varfont:
+            instantiateSTAT(varfont, axisLimits)
+
+        instantiateFvar(varfont, axisLimits)
+
+    if "fvar" not in varfont:
+        if "glyf" in varfont:
+            if overlap == OverlapMode.KEEP_AND_SET_FLAGS:
+                setMacOverlapFlags(varfont["glyf"])
+            elif overlap in (OverlapMode.REMOVE, OverlapMode.REMOVE_AND_IGNORE_ERRORS):
+                from fontTools.ttLib.removeOverlaps import removeOverlaps
+
+                log.info("Removing overlaps from glyf table")
+                removeOverlaps(
+                    varfont,
+                    ignoreErrors=(overlap == OverlapMode.REMOVE_AND_IGNORE_ERRORS),
+                )
+
+    varLib.set_default_weight_width_slant(
+        varfont,
+        location={
+            axisTag: limit
+            for axisTag, limit in axisLimits.items()
+            if not isinstance(limit, tuple)
+        },
+    )
+
+    return varfont
+
+
+def splitAxisLocationAndRanges(axisLimits, rangeType=AxisRange):
+    location, axisRanges = {}, {}
+    for axisTag, value in axisLimits.items():
+        if isinstance(value, rangeType):
+            axisRanges[axisTag] = value
+        elif isinstance(value, (int, float)):
+            location[axisTag] = value
+        elif isinstance(value, tuple):
+            axisRanges[axisTag] = rangeType(*value)
+        else:
+            raise TypeError(
+                f"Expected number or {rangeType.__name__}, "
+                f"got {type(value).__name__}: {value!r}"
+            )
+    return location, axisRanges
+
+
+def parseLimits(limits):
+    result = {}
+    for limitString in limits:
+        match = re.match(r"^(\w{1,4})=(?:(drop)|(?:([^:]+)(?:[:](.+))?))$", limitString)
+        if not match:
+            raise ValueError("invalid location format: %r" % limitString)
+        tag = match.group(1).ljust(4)
+        if match.group(2):  # 'drop'
+            lbound = None
+        else:
+            lbound = strToFixedToFloat(match.group(3), precisionBits=16)
+        ubound = lbound
+        if match.group(4):
+            ubound = strToFixedToFloat(match.group(4), precisionBits=16)
+        if lbound != ubound:
+            result[tag] = AxisRange(lbound, ubound)
+        else:
+            result[tag] = lbound
+    return result
+
+
+def parseArgs(args):
+    """Parse argv.
+
+    Returns:
+        3-tuple (infile, axisLimits, options)
+        axisLimits is either a Dict[str, Optional[float]], for pinning variation axes
+        to specific coordinates along those axes (with `None` as a placeholder for an
+        axis' default value); or a Dict[str, Tuple(float, float)], meaning limit this
+        axis to min/max range.
+        Axes locations are in user-space coordinates, as defined in the "fvar" table.
+    """
+    from fontTools import configLogger
+    import argparse
+
+    parser = argparse.ArgumentParser(
+        "fonttools varLib.instancer",
+        description="Partially 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 consists of "
+        "the tag of a variation axis, followed by '=' and one of number, "
+        "number:number or the literal string 'drop'. "
+        "E.g.: wdth=100 or wght=75.0:125.0 or wght=drop",
+    )
+    parser.add_argument(
+        "-o",
+        "--output",
+        metavar="OUTPUT.ttf",
+        default=None,
+        help="Output instance TTF file (default: INPUT-instance.ttf).",
+    )
+    parser.add_argument(
+        "--no-optimize",
+        dest="optimize",
+        action="store_false",
+        help="Don't perform IUP optimization on the remaining gvar TupleVariations",
+    )
+    parser.add_argument(
+        "--no-overlap-flag",
+        dest="overlap",
+        action="store_false",
+        help="Don't set OVERLAP_SIMPLE/OVERLAP_COMPOUND glyf flags (only applicable "
+        "when generating a full instance)",
+    )
+    parser.add_argument(
+        "--remove-overlaps",
+        dest="remove_overlaps",
+        action="store_true",
+        help="Merge overlapping contours and components (only applicable "
+        "when generating a full instance). Requires skia-pathops",
+    )
+    parser.add_argument(
+        "--ignore-overlap-errors",
+        dest="ignore_overlap_errors",
+        action="store_true",
+        help="Don't crash if the remove-overlaps operation fails for some glyphs.",
+    )
+    parser.add_argument(
+        "--update-name-table",
+        action="store_true",
+        help="Update the instantiated font's `name` table. Input font must have "
+        "a STAT table with Axis Value Tables",
+    )
+    loggingGroup = parser.add_mutually_exclusive_group(required=False)
+    loggingGroup.add_argument(
+        "-v", "--verbose", action="store_true", help="Run more verbosely."
+    )
+    loggingGroup.add_argument(
+        "-q", "--quiet", action="store_true", help="Turn verbosity off."
+    )
+    options = parser.parse_args(args)
+
+    if options.remove_overlaps:
+        if options.ignore_overlap_errors:
+            options.overlap = OverlapMode.REMOVE_AND_IGNORE_ERRORS
+        else:
+            options.overlap = OverlapMode.REMOVE
+    else:
+        options.overlap = OverlapMode(int(options.overlap))
+
+    infile = options.input
+    if not os.path.isfile(infile):
+        parser.error("No such file '{}'".format(infile))
+
+    configLogger(
+        level=("DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO")
+    )
+
+    try:
+        axisLimits = parseLimits(options.locargs)
+    except ValueError as e:
+        parser.error(str(e))
+
+    if len(axisLimits) != len(options.locargs):
+        parser.error("Specified multiple limits for the same axis")
+
+    return (infile, axisLimits, options)
+
+
+def main(args=None):
+    """Partially instantiate a variable font."""
+    infile, axisLimits, options = parseArgs(args)
+    log.info("Restricting axes: %s", axisLimits)
+
+    log.info("Loading variable font")
+    varfont = TTFont(infile)
+
+    isFullInstance = {
+        axisTag for axisTag, limit in axisLimits.items() if not isinstance(limit, tuple)
+    }.issuperset(axis.axisTag for axis in varfont["fvar"].axes)
+
+    instantiateVariableFont(
+        varfont,
+        axisLimits,
+        inplace=True,
+        optimize=options.optimize,
+        overlap=options.overlap,
+        updateFontNames=options.update_name_table,
+    )
+
+    outfile = (
+        os.path.splitext(infile)[0]
+        + "-{}.ttf".format("instance" if isFullInstance else "partial")
+        if not options.output
+        else options.output
+    )
+
+    log.info(
+        "Saving %s font %s",
+        "instance" if isFullInstance else "partial variable",
+        outfile,
+    )
+    varfont.save(outfile)
diff --git a/Lib/fontTools/varLib/instancer/__main__.py b/Lib/fontTools/varLib/instancer/__main__.py
new file mode 100644
index 0000000..64ffff2
--- /dev/null
+++ b/Lib/fontTools/varLib/instancer/__main__.py
@@ -0,0 +1,5 @@
+import sys
+from fontTools.varLib.instancer import main
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Lib/fontTools/varLib/instancer/names.py b/Lib/fontTools/varLib/instancer/names.py
new file mode 100644
index 0000000..cfe12a9
--- /dev/null
+++ b/Lib/fontTools/varLib/instancer/names.py
@@ -0,0 +1,379 @@
+"""Helpers for instantiating name table records."""
+
+from contextlib import contextmanager
+from copy import deepcopy
+from enum import IntEnum
+import re
+
+
+class NameID(IntEnum):
+    FAMILY_NAME = 1
+    SUBFAMILY_NAME = 2
+    UNIQUE_FONT_IDENTIFIER = 3
+    FULL_FONT_NAME = 4
+    VERSION_STRING = 5
+    POSTSCRIPT_NAME = 6
+    TYPOGRAPHIC_FAMILY_NAME = 16
+    TYPOGRAPHIC_SUBFAMILY_NAME = 17
+    VARIATIONS_POSTSCRIPT_NAME_PREFIX = 25
+
+
+ELIDABLE_AXIS_VALUE_NAME = 2
+
+
+def getVariationNameIDs(varfont):
+    used = []
+    if "fvar" in varfont:
+        fvar = varfont["fvar"]
+        for axis in fvar.axes:
+            used.append(axis.axisNameID)
+        for instance in fvar.instances:
+            used.append(instance.subfamilyNameID)
+            if instance.postscriptNameID != 0xFFFF:
+                used.append(instance.postscriptNameID)
+    if "STAT" in varfont:
+        stat = varfont["STAT"].table
+        for axis in stat.DesignAxisRecord.Axis if stat.DesignAxisRecord else ():
+            used.append(axis.AxisNameID)
+        for value in stat.AxisValueArray.AxisValue if stat.AxisValueArray else ():
+            used.append(value.ValueNameID)
+    # nameIDs <= 255 are reserved by OT spec so we don't touch them
+    return {nameID for nameID in used if nameID > 255}
+
+
+@contextmanager
+def pruningUnusedNames(varfont):
+    from . import log
+
+    origNameIDs = getVariationNameIDs(varfont)
+
+    yield
+
+    log.info("Pruning name table")
+    exclude = origNameIDs - getVariationNameIDs(varfont)
+    varfont["name"].names[:] = [
+        record for record in varfont["name"].names if record.nameID not in exclude
+    ]
+    if "ltag" in varfont:
+        # Drop the whole 'ltag' table if all the language-dependent Unicode name
+        # records that reference it have been dropped.
+        # TODO: Only prune unused ltag tags, renumerating langIDs accordingly.
+        # Note ltag can also be used by feat or morx tables, so check those too.
+        if not any(
+            record
+            for record in varfont["name"].names
+            if record.platformID == 0 and record.langID != 0xFFFF
+        ):
+            del varfont["ltag"]
+
+
+def updateNameTable(varfont, axisLimits):
+    """Update instatiated variable font's name table using STAT AxisValues.
+
+    Raises ValueError if the STAT table is missing or an Axis Value table is
+    missing for requested axis locations.
+
+    First, collect all STAT AxisValues that match the new default axis locations
+    (excluding "elided" ones); concatenate the strings in design axis order,
+    while giving priority to "synthetic" values (Format 4), to form the
+    typographic subfamily name associated with the new default instance.
+    Finally, update all related records in the name table, making sure that
+    legacy family/sub-family names conform to the the R/I/B/BI (Regular, Italic,
+    Bold, Bold Italic) naming model.
+
+    Example: Updating a partial variable font:
+    | >>> ttFont = TTFont("OpenSans[wdth,wght].ttf")
+    | >>> updateNameTable(ttFont, {"wght": AxisRange(400, 900), "wdth": 75})
+
+    The name table records will be updated in the following manner:
+    NameID 1 familyName: "Open Sans" --> "Open Sans Condensed"
+    NameID 2 subFamilyName: "Regular" --> "Regular"
+    NameID 3 Unique font identifier: "3.000;GOOG;OpenSans-Regular" --> \
+        "3.000;GOOG;OpenSans-Condensed"
+    NameID 4 Full font name: "Open Sans Regular" --> "Open Sans Condensed"
+    NameID 6 PostScript name: "OpenSans-Regular" --> "OpenSans-Condensed"
+    NameID 16 Typographic Family name: None --> "Open Sans"
+    NameID 17 Typographic Subfamily name: None --> "Condensed"
+
+    References:
+    https://docs.microsoft.com/en-us/typography/opentype/spec/stat
+    https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids
+    """
+    from . import AxisRange, axisValuesFromAxisLimits
+
+    if "STAT" not in varfont:
+        raise ValueError("Cannot update name table since there is no STAT table.")
+    stat = varfont["STAT"].table
+    if not stat.AxisValueArray:
+        raise ValueError("Cannot update name table since there are no STAT Axis Values")
+    fvar = varfont["fvar"]
+
+    # The updated name table will reflect the new 'zero origin' of the font.
+    # If we're instantiating a partial font, we will populate the unpinned
+    # axes with their default axis values.
+    fvarDefaults = {a.axisTag: a.defaultValue for a in fvar.axes}
+    defaultAxisCoords = deepcopy(axisLimits)
+    for axisTag, val in fvarDefaults.items():
+        if axisTag not in defaultAxisCoords or isinstance(
+            defaultAxisCoords[axisTag], AxisRange
+        ):
+            defaultAxisCoords[axisTag] = val
+
+    axisValueTables = axisValuesFromAxisLimits(stat, defaultAxisCoords)
+    checkAxisValuesExist(stat, axisValueTables, defaultAxisCoords)
+
+    # ignore "elidable" axis values, should be omitted in application font menus.
+    axisValueTables = [
+        v for v in axisValueTables if not v.Flags & ELIDABLE_AXIS_VALUE_NAME
+    ]
+    axisValueTables = _sortAxisValues(axisValueTables)
+    _updateNameRecords(varfont, axisValueTables)
+
+
+def checkAxisValuesExist(stat, axisValues, axisCoords):
+    seen = set()
+    designAxes = stat.DesignAxisRecord.Axis
+    for axisValueTable in axisValues:
+        axisValueFormat = axisValueTable.Format
+        if axisValueTable.Format in (1, 2, 3):
+            axisTag = designAxes[axisValueTable.AxisIndex].AxisTag
+            if axisValueFormat == 2:
+                axisValue = axisValueTable.NominalValue
+            else:
+                axisValue = axisValueTable.Value
+            if axisTag in axisCoords and axisValue == axisCoords[axisTag]:
+                seen.add(axisTag)
+        elif axisValueTable.Format == 4:
+            for rec in axisValueTable.AxisValueRecord:
+                axisTag = designAxes[rec.AxisIndex].AxisTag
+                if axisTag in axisCoords and rec.Value == axisCoords[axisTag]:
+                    seen.add(axisTag)
+
+    missingAxes = set(axisCoords) - seen
+    if missingAxes:
+        missing = ", ".join(f"'{i}={axisCoords[i]}'" for i in missingAxes)
+        raise ValueError(f"Cannot find Axis Values [{missing}]")
+
+
+def _sortAxisValues(axisValues):
+    # Sort by axis index, remove duplicates and ensure that format 4 AxisValues
+    # are dominant.
+    # The MS Spec states: "if a format 1, format 2 or format 3 table has a
+    # (nominal) value used in a format 4 table that also has values for
+    # other axes, the format 4 table, being the more specific match, is used",
+    # https://docs.microsoft.com/en-us/typography/opentype/spec/stat#axis-value-table-format-4
+    results = []
+    seenAxes = set()
+    # Sort format 4 axes so the tables with the most AxisValueRecords are first
+    format4 = sorted(
+        [v for v in axisValues if v.Format == 4],
+        key=lambda v: len(v.AxisValueRecord),
+        reverse=True,
+    )
+
+    for val in format4:
+        axisIndexes = set(r.AxisIndex for r in val.AxisValueRecord)
+        minIndex = min(axisIndexes)
+        if not seenAxes & axisIndexes:
+            seenAxes |= axisIndexes
+            results.append((minIndex, val))
+
+    for val in axisValues:
+        if val in format4:
+            continue
+        axisIndex = val.AxisIndex
+        if axisIndex not in seenAxes:
+            seenAxes.add(axisIndex)
+            results.append((axisIndex, val))
+
+    return [axisValue for _, axisValue in sorted(results)]
+
+
+def _updateNameRecords(varfont, axisValues):
+    # Update nametable based on the axisValues using the R/I/B/BI model.
+    nametable = varfont["name"]
+    stat = varfont["STAT"].table
+
+    axisValueNameIDs = [a.ValueNameID for a in axisValues]
+    ribbiNameIDs = [n for n in axisValueNameIDs if _isRibbi(nametable, n)]
+    nonRibbiNameIDs = [n for n in axisValueNameIDs if n not in ribbiNameIDs]
+    elidedNameID = stat.ElidedFallbackNameID
+    elidedNameIsRibbi = _isRibbi(nametable, elidedNameID)
+
+    getName = nametable.getName
+    platforms = set((r.platformID, r.platEncID, r.langID) for r in nametable.names)
+    for platform in platforms:
+        if not all(getName(i, *platform) for i in (1, 2, elidedNameID)):
+            # Since no family name and subfamily name records were found,
+            # we cannot update this set of name Records.
+            continue
+
+        subFamilyName = " ".join(
+            getName(n, *platform).toUnicode() for n in ribbiNameIDs
+        )
+        if nonRibbiNameIDs:
+            typoSubFamilyName = " ".join(
+                getName(n, *platform).toUnicode() for n in axisValueNameIDs
+            )
+        else:
+            typoSubFamilyName = None
+
+        # If neither subFamilyName and typographic SubFamilyName exist,
+        # we will use the STAT's elidedFallbackName
+        if not typoSubFamilyName and not subFamilyName:
+            if elidedNameIsRibbi:
+                subFamilyName = getName(elidedNameID, *platform).toUnicode()
+            else:
+                typoSubFamilyName = getName(elidedNameID, *platform).toUnicode()
+
+        familyNameSuffix = " ".join(
+            getName(n, *platform).toUnicode() for n in nonRibbiNameIDs
+        )
+
+        _updateNameTableStyleRecords(
+            varfont,
+            familyNameSuffix,
+            subFamilyName,
+            typoSubFamilyName,
+            *platform,
+        )
+
+
+def _isRibbi(nametable, nameID):
+    englishRecord = nametable.getName(nameID, 3, 1, 0x409)
+    return (
+        True
+        if englishRecord is not None
+        and englishRecord.toUnicode() in ("Regular", "Italic", "Bold", "Bold Italic")
+        else False
+    )
+
+
+def _updateNameTableStyleRecords(
+    varfont,
+    familyNameSuffix,
+    subFamilyName,
+    typoSubFamilyName,
+    platformID=3,
+    platEncID=1,
+    langID=0x409,
+):
+    # TODO (Marc F) It may be nice to make this part a standalone
+    # font renamer in the future.
+    nametable = varfont["name"]
+    platform = (platformID, platEncID, langID)
+
+    currentFamilyName = nametable.getName(
+        NameID.TYPOGRAPHIC_FAMILY_NAME, *platform
+    ) or nametable.getName(NameID.FAMILY_NAME, *platform)
+
+    currentStyleName = nametable.getName(
+        NameID.TYPOGRAPHIC_SUBFAMILY_NAME, *platform
+    ) or nametable.getName(NameID.SUBFAMILY_NAME, *platform)
+
+    if not all([currentFamilyName, currentStyleName]):
+        raise ValueError(f"Missing required NameIDs 1 and 2 for platform {platform}")
+
+    currentFamilyName = currentFamilyName.toUnicode()
+    currentStyleName = currentStyleName.toUnicode()
+
+    nameIDs = {
+        NameID.FAMILY_NAME: currentFamilyName,
+        NameID.SUBFAMILY_NAME: subFamilyName or "Regular",
+    }
+    if typoSubFamilyName:
+        nameIDs[NameID.FAMILY_NAME] = f"{currentFamilyName} {familyNameSuffix}".strip()
+        nameIDs[NameID.TYPOGRAPHIC_FAMILY_NAME] = currentFamilyName
+        nameIDs[NameID.TYPOGRAPHIC_SUBFAMILY_NAME] = typoSubFamilyName
+    else:
+        # Remove previous Typographic Family and SubFamily names since they're
+        # no longer required
+        for nameID in (
+            NameID.TYPOGRAPHIC_FAMILY_NAME,
+            NameID.TYPOGRAPHIC_SUBFAMILY_NAME,
+        ):
+            nametable.removeNames(nameID=nameID)
+
+    newFamilyName = (
+        nameIDs.get(NameID.TYPOGRAPHIC_FAMILY_NAME) or nameIDs[NameID.FAMILY_NAME]
+    )
+    newStyleName = (
+        nameIDs.get(NameID.TYPOGRAPHIC_SUBFAMILY_NAME) or nameIDs[NameID.SUBFAMILY_NAME]
+    )
+
+    nameIDs[NameID.FULL_FONT_NAME] = f"{newFamilyName} {newStyleName}"
+    nameIDs[NameID.POSTSCRIPT_NAME] = _updatePSNameRecord(
+        varfont, newFamilyName, newStyleName, platform
+    )
+
+    uniqueID = _updateUniqueIdNameRecord(varfont, nameIDs, platform)
+    if uniqueID:
+        nameIDs[NameID.UNIQUE_FONT_IDENTIFIER] = uniqueID
+
+    for nameID, string in nameIDs.items():
+        assert string, nameID
+        nametable.setName(string, nameID, *platform)
+
+    if "fvar" not in varfont:
+        nametable.removeNames(NameID.VARIATIONS_POSTSCRIPT_NAME_PREFIX)
+
+
+def _updatePSNameRecord(varfont, familyName, styleName, platform):
+    # Implementation based on Adobe Technical Note #5902 :
+    # https://wwwimages2.adobe.com/content/dam/acom/en/devnet/font/pdfs/5902.AdobePSNameGeneration.pdf
+    nametable = varfont["name"]
+
+    family_prefix = nametable.getName(
+        NameID.VARIATIONS_POSTSCRIPT_NAME_PREFIX, *platform
+    )
+    if family_prefix:
+        family_prefix = family_prefix.toUnicode()
+    else:
+        family_prefix = familyName
+
+    psName = f"{family_prefix}-{styleName}"
+    # Remove any characters other than uppercase Latin letters, lowercase
+    # Latin letters, digits and hyphens.
+    psName = re.sub(r"[^A-Za-z0-9-]", r"", psName)
+
+    if len(psName) > 127:
+        # Abbreviating the stylename so it fits within 127 characters whilst
+        # conforming to every vendor's specification is too complex. Instead
+        # we simply truncate the psname and add the required "..."
+        return f"{psName[:124]}..."
+    return psName
+
+
+def _updateUniqueIdNameRecord(varfont, nameIDs, platform):
+    nametable = varfont["name"]
+    currentRecord = nametable.getName(NameID.UNIQUE_FONT_IDENTIFIER, *platform)
+    if not currentRecord:
+        return None
+
+    # Check if full name and postscript name are a substring of currentRecord
+    for nameID in (NameID.FULL_FONT_NAME, NameID.POSTSCRIPT_NAME):
+        nameRecord = nametable.getName(nameID, *platform)
+        if not nameRecord:
+            continue
+        if nameRecord.toUnicode() in currentRecord.toUnicode():
+            return currentRecord.toUnicode().replace(
+                nameRecord.toUnicode(), nameIDs[nameRecord.nameID]
+            )
+
+    # Create a new string since we couldn't find any substrings.
+    fontVersion = _fontVersion(varfont, platform)
+    achVendID = varfont["OS/2"].achVendID
+    # Remove non-ASCII characers and trailing spaces
+    vendor = re.sub(r"[^\x00-\x7F]", "", achVendID).strip()
+    psName = nameIDs[NameID.POSTSCRIPT_NAME]
+    return f"{fontVersion};{vendor};{psName}"
+
+
+def _fontVersion(font, platform=(3, 1, 0x409)):
+    nameRecord = font["name"].getName(NameID.VERSION_STRING, *platform)
+    if nameRecord is None:
+        return f'{font["head"].fontRevision:.3f}'
+    # "Version 1.101; ttfautohint (v1.8.1.43-b0c9)" --> "1.101"
+    # Also works fine with inputs "Version 1.101" or "1.101" etc
+    versionNumber = nameRecord.toUnicode().split(";")[0]
+    return versionNumber.lstrip("Version ").strip()
diff --git a/Lib/fontTools/varLib/interpolatable.py b/Lib/fontTools/varLib/interpolatable.py
index d4feed2..cff76ec 100644
--- a/Lib/fontTools/varLib/interpolatable.py
+++ b/Lib/fontTools/varLib/interpolatable.py
@@ -6,176 +6,367 @@
 $ 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
+from fontTools.pens.momentsPen import OpenContourError
+from collections import OrderedDict
 import itertools
+import sys
 
 
 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 __init__(self, Pen, glyphset=None):
+        BasePen.__init__(self, glyphset)
+        self._glyphset = glyphset
+        self._Pen = Pen
+        self._pen = None
+        self.value = []
 
-	def _newItem(self):
-		self._pen = pen = self._Pen()
-		self.value.append(pen)
+    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 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))
+    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
+    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))
+    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
+    n = len(G)
+    try:
+        from scipy.optimize import linear_sum_assignment
 
-	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
+        rows, cols = linear_sum_assignment(G)
+        assert (rows == list(range(n))).all()
+        return list(cols), _matching_cost(G, cols)
+    except ImportError:
+        pass
 
-	if n > 6:
-		raise Exception("Install Python module 'munkres' or 'scipy >= 0.17.0'")
+    try:
+        from munkres import Munkres
 
-	# 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
+        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()
+    if names is None:
+        names = glyphsets
+    if glyphs is None:
+        glyphs = glyphsets[0].keys()
 
-	hist = []
-	for glyph_name in glyphs:
-		#print()
-		#print(glyph_name)
+    hist = []
+    problems = OrderedDict()
 
-		try:
-			allVectors = []
-			for glyphset,name in zip(glyphsets, names):
-				#print('.', end='')
-				glyph = glyphset[glyph_name]
+    def add_problem(glyphname, problem):
+        problems.setdefault(glyphname, []).append(problem)
 
-				perContourPen = PerContourOrComponentPen(RecordingPen, glyphset=glyphset)
-				glyph.draw(perContourPen)
-				contourPens = perContourPen.value
-				del perContourPen
+    for glyph_name in glyphs:
+        # print()
+        # print(glyph_name)
 
-				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)
+        try:
+            allVectors = []
+            allNodeTypes = []
+            for glyphset, name in zip(glyphsets, names):
+                # print('.', end='')
+                if glyph_name not in glyphset:
+                    add_problem(glyph_name, {"type": "missing", "master": name})
+                    continue
+                glyph = glyphset[glyph_name]
 
-			# 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))
+                perContourPen = PerContourOrComponentPen(
+                    RecordingPen, glyphset=glyphset
+                )
+                glyph.draw(perContourPen)
+                contourPens = perContourPen.value
+                del perContourPen
+
+                contourVectors = []
+                nodeTypes = []
+                allNodeTypes.append(nodeTypes)
+                allVectors.append(contourVectors)
+                for ix, contour in enumerate(contourPens):
+                    nodeTypes.append(
+                        tuple(instruction[0] for instruction in contour.value)
+                    )
+                    stats = StatisticsPen(glyphset=glyphset)
+                    try:
+                        contour.replay(stats)
+                    except OpenContourError as e:
+                        add_problem(
+                            glyph_name,
+                            {"master": name, "contour": ix, "type": "open_path"},
+                        )
+                        continue
+                    size = abs(stats.area) ** 0.5 * 0.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(allNodeTypes[:-1], allNodeTypes[1:])):
+                if len(m0) != len(m1):
+                    add_problem(
+                        glyph_name,
+                        {
+                            "type": "path_count",
+                            "master_1": names[i],
+                            "master_2": names[i + 1],
+                            "value_1": len(m0),
+                            "value_2": len(m1),
+                        },
+                    )
+                if m0 == m1:
+                    continue
+                for pathIx, (nodes1, nodes2) in enumerate(zip(m0, m1)):
+                    if nodes1 == nodes2:
+                        continue
+                    if len(nodes1) != len(nodes2):
+                        add_problem(
+                            glyph_name,
+                            {
+                                "type": "node_count",
+                                "path": pathIx,
+                                "master_1": names[i],
+                                "master_2": names[i + 1],
+                                "value_1": len(nodes1),
+                                "value_2": len(nodes2),
+                            },
+                        )
+                        continue
+                    for nodeIx, (n1, n2) in enumerate(zip(nodes1, nodes2)):
+                        if n1 != n2:
+                            add_problem(
+                                glyph_name,
+                                {
+                                    "type": "node_incompatibility",
+                                    "path": pathIx,
+                                    "node": nodeIx,
+                                    "master_1": names[i],
+                                    "master_2": names[i + 1],
+                                    "value_1": n1,
+                                    "value_2": n2,
+                                },
+                            )
+                            continue
+
+            for i, (m0, m1) in enumerate(zip(allVectors[:-1], allVectors[1:])):
+                if len(m0) != len(m1):
+                    # We already reported this
+                    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))):
+                    add_problem(
+                        glyph_name,
+                        {
+                            "type": "contour_order",
+                            "master_1": names[i],
+                            "master_2": names[i + 1],
+                            "value_1": list(range(len(m0))),
+                            "value_2": matching,
+                        },
+                    )
+                    break
+                upem = 2048
+                item_cost = round(
+                    (matching_cost / len(m0) / len(m0[0])) ** 0.5 / upem * 100
+                )
+                hist.append(item_cost)
+                threshold = 7
+                if item_cost >= threshold:
+                    add_problem(
+                        glyph_name,
+                        {
+                            "type": "high_cost",
+                            "master_1": names[i],
+                            "master_2": names[i + 1],
+                            "value_1": item_cost,
+                            "value_2": threshold,
+                        },
+                    )
+
+        except ValueError as e:
+            add_problem(
+                glyph_name,
+                {"type": "math_error", "master": name, "error": e},
+            )
+    return problems
 
 
-		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=None):
+    """Test for interpolatability issues between fonts"""
+    import argparse
 
-def main(args):
-	filenames = args
-	glyphs = None
-	#glyphs = ['uni08DB', 'uniFD76']
-	#glyphs = ['uni08DE', 'uni0034']
-	#glyphs = ['uni08DE', 'uni0034', 'uni0751', 'uni0753', 'uni0754', 'uni08A4', 'uni08A4.fina', 'uni08A5.fina']
+    parser = argparse.ArgumentParser(
+        "fonttools varLib.interpolatable",
+        description=main.__doc__,
+    )
+    parser.add_argument(
+        "--json",
+        action="store_true",
+        help="Output report in JSON format",
+    )
+    parser.add_argument(
+        "inputs", metavar="FILE", type=str, nargs="+", help="Input TTF/UFO files"
+    )
 
-	from os.path import basename
-	names = [basename(filename).rsplit('.', 1)[0] for filename in filenames]
+    args = parser.parse_args(args)
+    glyphs = None
+    # glyphs = ['uni08DB', 'uniFD76']
+    # glyphs = ['uni08DE', 'uni0034']
+    # glyphs = ['uni08DE', 'uni0034', 'uni0751', 'uni0753', 'uni0754', 'uni08A4', 'uni08A4.fina', 'uni08A5.fina']
 
-	from fontTools.ttLib import TTFont
-	fonts = [TTFont(filename) for filename in filenames]
+    from os.path import basename
 
-	glyphsets = [font.getGlyphSet() for font in fonts]
-	test(glyphsets, glyphs=glyphs, names=names)
+    names = [basename(filename).rsplit(".", 1)[0] for filename in args.inputs]
 
-if __name__ == '__main__':
-	import sys
-	main(sys.argv[1:])
+    fonts = []
+    for filename in args.inputs:
+        if filename.endswith(".ufo"):
+            from fontTools.ufoLib import UFOReader
+
+            fonts.append(UFOReader(filename))
+        else:
+            from fontTools.ttLib import TTFont
+
+            fonts.append(TTFont(filename))
+
+    glyphsets = [font.getGlyphSet() for font in fonts]
+    problems = test(glyphsets, glyphs=glyphs, names=names)
+    if args.json:
+        import json
+
+        print(json.dumps(problems))
+    else:
+        for glyph, glyph_problems in problems.items():
+            print(f"Glyph {glyph} was not compatible: ")
+            for p in glyph_problems:
+                if p["type"] == "missing":
+                    print("    Glyph was missing in master %s" % p["master"])
+                if p["type"] == "open_path":
+                    print("    Glyph has an open path in master %s" % p["master"])
+                if p["type"] == "path_count":
+                    print(
+                        "    Path count differs: %i in %s, %i in %s"
+                        % (p["value_1"], p["master_1"], p["value_2"], p["master_2"])
+                    )
+                if p["type"] == "node_count":
+                    print(
+                        "    Node count differs in path %i: %i in %s, %i in %s"
+                        % (
+                            p["path"],
+                            p["value_1"],
+                            p["master_1"],
+                            p["value_2"],
+                            p["master_2"],
+                        )
+                    )
+                if p["type"] == "node_incompatibility":
+                    print(
+                        "    Node %o incompatible in path %i: %s in %s, %s in %s"
+                        % (
+                            p["node"],
+                            p["path"],
+                            p["value_1"],
+                            p["master_1"],
+                            p["value_2"],
+                            p["master_2"],
+                        )
+                    )
+                if p["type"] == "contour_order":
+                    print(
+                        "    Contour order differs: %s in %s, %s in %s"
+                        % (
+                            p["value_1"],
+                            p["master_1"],
+                            p["value_2"],
+                            p["master_2"],
+                        )
+                    )
+                if p["type"] == "high_cost":
+                    print(
+                        "    Interpolation has high cost: cost of %s to %s = %i, threshold %i"
+                        % (
+                            p["master_1"],
+                            p["master_2"],
+                            p["value_1"],
+                            p["value_2"],
+                        )
+                    )
+    if problems:
+        return problems
+
+
+if __name__ == "__main__":
+    import sys
+
+    problems = main()
+    sys.exit(int(bool(problems)))
diff --git a/Lib/fontTools/varLib/interpolate_layout.py b/Lib/fontTools/varLib/interpolate_layout.py
index ca9ccfe..6d0385d 100644
--- a/Lib/fontTools/varLib/interpolate_layout.py
+++ b/Lib/fontTools/varLib/interpolate_layout.py
@@ -1,19 +1,18 @@
 """
 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 import models, VarLibError, load_designspace, load_masters
 from fontTools.varLib.merger import InstancerMerger
 import os.path
 import logging
+from copy import deepcopy
 from pprint import pformat
 
 log = logging.getLogger("fontTools.varLib.interpolate_layout")
 
 
-def interpolate_layout(designspace_filename, loc, master_finder=lambda s:s, mapped=False):
+def interpolate_layout(designspace, loc, master_finder=lambda s:s, mapped=False):
 	"""
 	Interpolate GPOS from a designspace file and location.
 
@@ -26,61 +25,75 @@
 	it is assumed that location is in designspace's internal space and
 	no mapping is performed.
 	"""
+	if hasattr(designspace, "sources"):  # Assume a DesignspaceDocument
+		pass
+	else:  # Assume a file path
+		from fontTools.designspaceLib import DesignSpaceDocument
+		designspace = DesignSpaceDocument.fromfile(designspace)
 
-	axes, internal_axis_supports, base_idx, normalized_master_locs, masters, instances = load_designspace(designspace_filename)
-
-
+	ds = load_designspace(designspace)
 	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("Loading master fonts")
+	master_fonts = load_masters(designspace, master_finder)
+	font = deepcopy(master_fonts[ds.base_idx])
 
 	log.info("Location: %s", pformat(loc))
 	if not mapped:
-		loc = {name:axes[name].map_forward(v) for name,v in loc.items()}
+		loc = {name: ds.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)
+	loc = models.normalizeLocation(loc, ds.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]
+	model = models.VariationModel(ds.normalized_master_locs)
+	assert 0 == model.mapping[ds.base_idx]
 
 	merger = InstancerMerger(font, model, loc)
 
 	log.info("Building interpolated tables")
+	# TODO GSUB/GDEF
 	merger.mergeTables(font, master_fonts, ['GPOS'])
 	return font
 
 
 def main(args=None):
+	"""Interpolate GDEF/GPOS/GSUB tables for a point on a designspace"""
 	from fontTools import configLogger
-
+	import argparse
 	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'
+	parser = argparse.ArgumentParser(
+		"fonttools varLib.interpolate_layout",
+		description=main.__doc__,
+	)
+	parser.add_argument('designspace_filename', metavar='DESIGNSPACE',
+		help="Input TTF files")
+	parser.add_argument('locations', metavar='LOCATION', type=str, nargs='+',
+		help="Axis locations (e.g. wdth=120")
+	parser.add_argument('-o', '--output', metavar='OUTPUT',
+		help="Output font file (defaults to <designspacename>-instance.ttf)")
+	parser.add_argument('-l', '--loglevel', metavar='LEVEL', default="INFO",
+		help="Logging level (defaults to INFO)")
 
-	# TODO: allow user to configure logging via command-line options
-	configLogger(level="INFO")
+
+	args = parser.parse_args(args)
+
+	if not args.output:
+		args.output = os.path.splitext(args.designspace_filename)[0] + '-instance.ttf'
+
+	configLogger(level=args.loglevel)
 
 	finder = lambda s: s.replace('master_ufo', 'master_ttf_interpolatable').replace('.ufo', '.ttf')
 
 	loc = {}
-	for arg in locargs:
+	for arg in args.locations:
 		tag,val = arg.split('=')
 		loc[tag] = float(val)
 
-	font = interpolate_layout(designspace_filename, loc, finder)
-	log.info("Saving font %s", outfile)
-	font.save(outfile)
+	font = interpolate_layout(args.designspace_filename, loc, finder)
+	log.info("Saving font %s", args.output)
+	font.save(args.output)
 
 
 if __name__ == "__main__":
diff --git a/Lib/fontTools/varLib/iup.py b/Lib/fontTools/varLib/iup.py
index 912ff0a..45a7a5e 100644
--- a/Lib/fontTools/varLib/iup.py
+++ b/Lib/fontTools/varLib/iup.py
@@ -1,8 +1,3 @@
-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
@@ -170,8 +165,8 @@
 
 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.
+	points 0 to i where i is explicitly encoded.  We find this by considering all previous
+	explicit points j and check whether interpolation can fill points between j and i.
 
 	Note that solution always encodes last point explicitly.  Higher-level is responsible
 	for removing that restriction.
diff --git a/Lib/fontTools/varLib/merger.py b/Lib/fontTools/varLib/merger.py
index 4ada4ba..aaf2a51 100644
--- a/Lib/fontTools/varLib/merger.py
+++ b/Lib/fontTools/varLib/merger.py
@@ -1,16 +1,40 @@
 """
 Merge OpenType Layout tables (GDEF / GPOS / GSUB).
 """
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+import os
+import copy
+from operator import ior
+import logging
 from fontTools.misc import classifyTools
+from fontTools.misc.roundTools import otRound
 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 import builder, models, varStore
+from fontTools.varLib.models import nonNone, allNone, allEqual, allEqualTo
 from fontTools.varLib.varStore import VarStoreInstancer
 from functools import reduce
+from fontTools.otlLib.builder import buildSinglePos
+from fontTools.otlLib.optimize.gpos import (
+    compact_pair_pos,
+    GPOS_COMPACT_MODE_DEFAULT,
+    GPOS_COMPACT_MODE_ENV_KEY,
+)
 
+log = logging.getLogger("fontTools.varLib.merger")
+
+from .errors import (
+    ShouldBeConstant,
+    FoundANone,
+    MismatchedTypes,
+    LengthsDiffer,
+    KeysDiffer,
+    InconsistentGlyphOrder,
+    InconsistentExtensions,
+    UnsupportedFormat,
+    UnsupportedFormat,
+    VarLibMergeError,
+)
 
 class Merger(object):
 
@@ -57,9 +81,16 @@
 		return _default
 
 	def mergeObjects(self, out, lst, exclude=()):
+		if hasattr(out, "ensureDecompiled"):
+			out.ensureDecompiled()
+		for item in lst:
+			if hasattr(item, "ensureDecompiled"):
+				item.ensureDecompiled()
 		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])
+		if not all(keys == sorted(vars(v).keys()) for v in lst):
+			raise KeysDiffer(self, expected=keys,
+				got=[sorted(vars(v).keys()) for v in lst]
+			)
 		mergers = self.mergersFor(out)
 		defaultMerger = mergers.get('*', self.__class__.mergeThings)
 		try:
@@ -69,42 +100,47 @@
 				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,)
+		except VarLibMergeError as e:
+			e.stack.append('.'+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])
+		if not allEqualTo(out, lst, len):
+			raise LengthsDiffer(self, expected=len(out), got=[len(x) for x 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,)
+			except VarLibMergeError as e:
+				e.stack.append('[%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
+		if not allEqualTo(out, lst, type):
+			raise MismatchedTypes(self,
+					expected=type(out).__name__,
+					got=[type(x).__name__ for x in 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:
+			if not allEqualTo(out, lst):
+				raise ShouldBeConstant(self, expected=out, got=lst)
 
-	def mergeTables(self, font, master_ttfs, tables):
-
-		for tag in tables:
+	def mergeTables(self, font, master_ttfs, tableTags):
+		for tag in tableTags:
 			if tag not in font: continue
-			self.mergeThings(font[tag], [m[tag] for m in master_ttfs])
+			try:
+				self.ttfs = [m for m in master_ttfs if tag in m]
+				self.mergeThings(font[tag], [m[tag] if tag in m else None
+							     for m in master_ttfs])
+			except VarLibMergeError as e:
+				e.stack.append(tag)
+				raise
 
 #
 # Aligning merger
@@ -112,6 +148,29 @@
 class AligningMerger(Merger):
 	pass
 
+@AligningMerger.merger(ot.GDEF, "GlyphClassDef")
+def merge(merger, self, lst):
+	if self is None:
+		if not allNone(lst):
+			raise NotANone(merger, expected=None, got=lst)
+		return
+
+	lst = [l.classDefs for l in lst]
+	self.classDefs = {}
+	# We only care about the .classDefs
+	self = self.classDefs
+
+	allKeys = set()
+	allKeys.update(*[l.keys() for l in lst])
+	for k in allKeys:
+		allValues = nonNone(l.get(k) for l in lst)
+		if not allEqual(allValues):
+			raise ShouldBeConstant(merger, expected=allValues[0], got=lst, stack=["." + k])
+		if not allValues:
+			self[k] = None
+		else:
+			self[k] = allValues[0]
+
 def _SinglePosUpgradeToFormat2(self):
 	if self.Format == 2: return self
 
@@ -119,7 +178,7 @@
 	ret.Format = 2
 	ret.Coverage = self.Coverage
 	ret.ValueFormat = self.ValueFormat
-	ret.Value = [self.Value for g in ret.Coverage.glyphs]
+	ret.Value = [self.Value for _ in ret.Coverage.glyphs]
 	ret.ValueCount = len(ret.Value)
 
 	return ret
@@ -142,7 +201,8 @@
 	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)
+	if not all(sorted(vs, key=sortKey) == vs for vs in lst):
+		raise InconsistentGlyphOrder()
 	del combined
 
 	paddedValues = None
@@ -157,7 +217,7 @@
 			  for dict_set in dict_sets]
 	return order, padded
 
-def _Lookup_SinglePos_get_effective_value(subtables, glyph):
+def _Lookup_SinglePos_get_effective_value(merger, subtables, glyph):
 	for self in subtables:
 		if self is None or \
 		   type(self) != ot.SinglePos or \
@@ -169,10 +229,10 @@
 		elif self.Format == 2:
 			return self.Value[self.Coverage.glyphs.index(glyph)]
 		else:
-			assert 0
+			raise UnsupportedFormat(merger, subtable="single positioning lookup")
 	return None
 
-def _Lookup_PairPos_get_effective_value_pair(subtables, firstGlyph, secondGlyph):
+def _Lookup_PairPos_get_effective_value_pair(merger, subtables, firstGlyph, secondGlyph):
 	for self in subtables:
 		if self is None or \
 		   type(self) != ot.PairPos or \
@@ -191,16 +251,18 @@
 			klass2 = self.ClassDef2.classDefs.get(secondGlyph, 0)
 			return self.Class1Record[klass1].Class2Record[klass2]
 		else:
-			assert 0
+			raise UnsupportedFormat(merger, subtable="pair positioning lookup")
 	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 not (len(lst) == 1 or (valueFormat & ~0xF == 0)):
+		raise UnsupportedFormat(merger, subtable="single positioning lookup")
 
 	# 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):
+	coverageGlyphs = self.Coverage.glyphs
+	if all(v.Format == 1 for v in lst) and all(coverageGlyphs == 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()
@@ -216,7 +278,7 @@
 					    [v.Value for v in lst])
 
 	self.Coverage.glyphs = glyphs
-	self.Value = [otBase.ValueRecord(valueFormat) for g in glyphs]
+	self.Value = [otBase.ValueRecord(valueFormat) for _ in glyphs]
 	self.ValueCount = len(self.Value)
 
 	for i,values in enumerate(padded):
@@ -226,7 +288,7 @@
 			# 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)
+			v = _Lookup_SinglePos_get_effective_value(merger, merger.lookup_subtables[i], glyph)
 			if v is None:
 				v = otBase.ValueRecord(valueFormat)
 			values[j] = v
@@ -236,7 +298,7 @@
 	# 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)
+	self.ValueFormat = reduce(int.__or__, [v.getEffectiveFormat() for v in self.Value], 0)
 
 @AligningMerger.merger(ot.PairSet)
 def merge(merger, self, lst):
@@ -262,11 +324,14 @@
 			if values[j] is not None:
 				vpair = values[j]
 			else:
-				vpair = _Lookup_PairPos_get_effective_value_pair(merger.lookup_subtables[i], self._firstGlyph, glyph)
+				vpair = _Lookup_PairPos_get_effective_value_pair(
+					merger, merger.lookup_subtables[i], self._firstGlyph, glyph
+				)
 			if vpair is None:
 				v1, v2 = None, None
 			else:
-				v1, v2 = vpair.Value1, vpair.Value2
+				v1 = getattr(vpair, "Value1", None)
+				v2 = getattr(vpair, "Value2", None)
 			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
@@ -275,12 +340,13 @@
 	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."
+	assert allEqual([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'))
+				     'PairSet', 'PairSetCount',
+				     'ValueFormat1', 'ValueFormat2'))
 
 	empty = ot.PairSet()
 	empty.PairValueRecord = []
@@ -293,7 +359,7 @@
 					    default=empty)
 
 	self.Coverage.glyphs = glyphs
-	self.PairSet = [ot.PairSet() for g in glyphs]
+	self.PairSet = [ot.PairSet() for _ in glyphs]
 	self.PairSetCount = len(self.PairSet)
 	for glyph, ps in zip(glyphs, self.PairSet):
 		ps._firstGlyph = glyph
@@ -328,19 +394,22 @@
 
 	return ret
 
-def _ClassDef_merge_classify(lst, allGlyphs=None):
+def _ClassDef_merge_classify(lst, allGlyphses=None):
 	self = ot.ClassDef()
 	self.classDefs = classDefs = {}
+	allGlyphsesWasNone = allGlyphses is None
+	if allGlyphsesWasNone:
+		allGlyphses = [None] * len(lst)
 
 	classifier = classifyTools.Classifier()
-	for l in lst:
-		sets = _ClassDef_invert(l, allGlyphs=allGlyphs)
+	for classDef,allGlyphs in zip(lst, allGlyphses):
+		sets = _ClassDef_invert(classDef, allGlyphs)
 		if allGlyphs is None:
 			sets = sets[1:]
 		classifier.update(sets)
 	classes = classifier.getClasses()
 
-	if allGlyphs is None:
+	if allGlyphsesWasNone:
 		classes.insert(0, set())
 
 	for i,classSet in enumerate(classes):
@@ -351,25 +420,12 @@
 
 	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.ClassDef1, classes = _ClassDef_merge_classify([l.ClassDef1 for l in lst], [l.Coverage.glyphs for l in lst])
 	self.Class1Count = len(classes)
 	new_matrices = []
 	for l,matrix in zip(lst, matrices):
@@ -380,6 +436,11 @@
 		for classSet in classes:
 			exemplarGlyph = next(iter(classSet))
 			if exemplarGlyph not in coverage:
+				# Follow-up to e6125b353e1f54a0280ded5434b8e40d042de69f,
+				# Fixes https://github.com/googlei18n/fontmake/issues/470
+				# Again, revert 8d441779e5afc664960d848f62c7acdbfc71d7b9
+				# when merger becomes selfless.
+				nullRow = None
 				if nullRow is None:
 					nullRow = ot.Class1Record()
 					class2records = nullRow.Class2Record = []
@@ -389,8 +450,8 @@
 							rec2 = None
 						else:
 							rec2 = ot.Class2Record()
-							rec2.Value1 = otBase.ValueRecord(l.ValueFormat1) if l.ValueFormat1 else None
-							rec2.Value2 = otBase.ValueRecord(l.ValueFormat2) if l.ValueFormat2 else None
+							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:
@@ -403,7 +464,6 @@
 
 	# 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):
@@ -420,7 +480,7 @@
 					exemplarGlyph = next(iter(classSet))
 					klass = classDef2.get(exemplarGlyph, 0)
 					rec2 = oldClass2Records[klass]
-				class2Records.append(rec2)
+				class2Records.append(copy.deepcopy(rec2))
 			class1Records.append(rec1new)
 		new_matrices.append(class1Records)
 	matrices = new_matrices
@@ -429,13 +489,14 @@
 	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."
+	assert allEqual([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'))
+				     'Class1Record',
+				     'ValueFormat1', 'ValueFormat2'))
 
 	# Align coverages
 	glyphs, _ = _merge_GlyphOrders(merger.font,
@@ -460,16 +521,15 @@
 
 @AligningMerger.merger(ot.PairPos)
 def merge(merger, self, lst):
-	# TODO Support differing ValueFormats.
-	merger.valueFormat1 = self.ValueFormat1
-	merger.valueFormat2 = self.ValueFormat2
+	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
+		raise UnsupportedFormat(merger, subtable="pair positioning lookup")
 
 	del merger.valueFormat1, merger.valueFormat2
 
@@ -480,29 +540,137 @@
 	if self.Format == 1:
 		for pairSet in self.PairSet:
 			for pairValueRecord in pairSet.PairValueRecord:
-				pv1 = pairValueRecord.Value1
+				pv1 = getattr(pairValueRecord, "Value1", None)
 				if pv1 is not None:
 					vf1 |= pv1.getFormat()
-				pv2 = pairValueRecord.Value2
+				pv2 = getattr(pairValueRecord, "Value2", None)
 				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
+				pv1 = getattr(class2Record, "Value1", None)
 				if pv1 is not None:
 					vf1 |= pv1.getFormat()
-				pv2 = class2Record.Value2
+				pv2 = getattr(class2Record, "Value2", None)
 				if pv2 is not None:
 					vf2 |= pv2.getFormat()
 	self.ValueFormat1 = vf1
 	self.ValueFormat2 = vf2
 
+def _MarkBasePosFormat1_merge(self, lst, merger, Mark='Mark', Base='Base'):
+	self.ClassCount = max(l.ClassCount for l in lst)
+
+	MarkCoverageGlyphs, MarkRecords = \
+		_merge_GlyphOrders(merger.font,
+				   [getattr(l, Mark+'Coverage').glyphs for l in lst],
+				   [getattr(l, Mark+'Array').MarkRecord for l in lst])
+	getattr(self, Mark+'Coverage').glyphs = MarkCoverageGlyphs
+
+	BaseCoverageGlyphs, BaseRecords = \
+		_merge_GlyphOrders(merger.font,
+				   [getattr(l, Base+'Coverage').glyphs for l in lst],
+				   [getattr(getattr(l, Base+'Array'), Base+'Record') for l in lst])
+	getattr(self, Base+'Coverage').glyphs = BaseCoverageGlyphs
+
+	# MarkArray
+	records = []
+	for g,glyphRecords in zip(MarkCoverageGlyphs, zip(*MarkRecords)):
+		allClasses = [r.Class for r in glyphRecords if r is not None]
+
+		# TODO Right now we require that all marks have same class in
+		# all masters that cover them.  This is not required.
+		#
+		# We can relax that by just requiring that all marks that have
+		# the same class in a master, have the same class in every other
+		# master.  Indeed, if, say, a sparse master only covers one mark,
+		# that mark probably will get class 0, which would possibly be
+		# different from its class in other masters.
+		#
+		# We can even go further and reclassify marks to support any
+		# input.  But, since, it's unlikely that two marks being both,
+		# say, "top" in one master, and one being "top" and other being
+		# "top-right" in another master, we shouldn't do that, as any
+		# failures in that case will probably signify mistakes in the
+		# input masters.
+
+		if not allEqual(allClasses):
+			raise ShouldBeConstant(merger, expected=allClasses[0], got=allClasses)
+		else:
+			rec = ot.MarkRecord()
+			rec.Class = allClasses[0]
+			allAnchors = [None if r is None else r.MarkAnchor for r in glyphRecords]
+			if allNone(allAnchors):
+				anchor = None
+			else:
+				anchor = ot.Anchor()
+				anchor.Format = 1
+				merger.mergeThings(anchor, allAnchors)
+			rec.MarkAnchor = anchor
+		records.append(rec)
+	array = ot.MarkArray()
+	array.MarkRecord = records
+	array.MarkCount = len(records)
+	setattr(self, Mark+"Array", array)
+
+	# BaseArray
+	records = []
+	for g,glyphRecords in zip(BaseCoverageGlyphs, zip(*BaseRecords)):
+		if allNone(glyphRecords):
+			rec = None
+		else:
+			rec = getattr(ot, Base+'Record')()
+			anchors = []
+			setattr(rec, Base+'Anchor', anchors)
+			glyphAnchors = [[] if r is None else getattr(r, Base+'Anchor')
+					for r in glyphRecords]
+			for l in glyphAnchors:
+				l.extend([None] * (self.ClassCount - len(l)))
+			for allAnchors in zip(*glyphAnchors):
+				if allNone(allAnchors):
+					anchor = None
+				else:
+					anchor = ot.Anchor()
+					anchor.Format = 1
+					merger.mergeThings(anchor, allAnchors)
+				anchors.append(anchor)
+		records.append(rec)
+	array = getattr(ot, Base+'Array')()
+	setattr(array, Base+'Record', records)
+	setattr(array, Base+'Count', len(records))
+	setattr(self, Base+'Array', array)
+
+@AligningMerger.merger(ot.MarkBasePos)
+def merge(merger, self, lst):
+	if not allEqualTo(self.Format, (l.Format for l in lst)):
+		raise InconsistentFormats(
+			merger,
+			subtable="mark-to-base positioning lookup",
+			expected=self.Format,
+			got=[l.Format for l in lst]
+		)
+	if self.Format == 1:
+		_MarkBasePosFormat1_merge(self, lst, merger)
+	else:
+		raise UnsupportedFormat(merger, subtable="mark-to-base positioning lookup")
+
+@AligningMerger.merger(ot.MarkMarkPos)
+def merge(merger, self, lst):
+	if not allEqualTo(self.Format, (l.Format for l in lst)):
+		raise InconsistentFormats(
+			merger,
+			subtable="mark-to-mark positioning lookup",
+			expected=self.Format,
+			got=[l.Format for l in lst]
+		)
+	if self.Format == 1:
+		_MarkBasePosFormat1_merge(self, lst, merger, 'Mark1', 'Mark2')
+	else:
+		raise UnsupportedFormat(merger, subtable="mark-to-mark positioning lookup")
 
 def _PairSet_flatten(lst, font):
 	self = ot.PairSet()
 	self.Coverage = ot.Coverage()
-	self.Coverage.Format = 1
 
 	# Align them
 	glyphs, padded = _merge_GlyphOrders(font,
@@ -523,12 +691,11 @@
 	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."
+	assert allEqual([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)
 
@@ -544,12 +711,11 @@
 	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."
+	assert allEqual([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)
 
@@ -592,6 +758,30 @@
 
 	return lst
 
+def _Lookup_SinglePos_subtables_flatten(lst, font, min_inclusive_rec_format):
+	glyphs, _ = _merge_GlyphOrders(font,
+		[v.Coverage.glyphs for v in lst], None)
+	num_glyphs = len(glyphs)
+	new = ot.SinglePos()
+	new.Format = 2
+	new.ValueFormat = min_inclusive_rec_format
+	new.Coverage = ot.Coverage()
+	new.Coverage.glyphs = glyphs
+	new.ValueCount = num_glyphs
+	new.Value = [None] * num_glyphs
+	for singlePos in lst:
+		if singlePos.Format == 1:
+			val_rec = singlePos.Value
+			for gname in singlePos.Coverage.glyphs:
+				i = glyphs.index(gname)
+				new.Value[i] = copy.deepcopy(val_rec)
+		elif singlePos.Format == 2:
+			for j, gname in enumerate(singlePos.Coverage.glyphs):
+				val_rec = singlePos.Value[j]
+				i = glyphs.index(gname)
+				new.Value[i] = copy.deepcopy(val_rec)
+	return [new]
+
 @AligningMerger.merger(ot.Lookup)
 def merge(merger, self, lst):
 	subtables = merger.lookup_subtables = [l.SubTable for l in lst]
@@ -601,8 +791,14 @@
 		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])
+			if not allEqual([st.__class__ for st in sts]):
+				raise InconsistentExtensions(
+					merger,
+					expected="Extension",
+					got=[st.__class__.__name__ for st in sts]
+				)
+			if not allEqual([st.ExtensionLookupType for st in sts]):
+				raise InconsistentExtensions(merger)
 			l.LookupType = sts[0].ExtensionLookupType
 			new_sts = [st.ExtSubTable for st in sts]
 			del sts[:]
@@ -611,12 +807,28 @@
 	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]
+	else:
+		isSinglePos = self.SubTable and isinstance(self.SubTable[0], ot.SinglePos)
+		if isSinglePos:
+			numSubtables = [len(st) for st in subtables]
+			if not all([nums == numSubtables[0] for nums in numSubtables]):
+				# Flatten list of SinglePos subtables to single Format 2 subtable,
+				# with all value records set to the rec format type.
+				# We use buildSinglePos() to optimize the lookup after merging.
+				valueFormatList = [t.ValueFormat for st in subtables for t in st]
+				# Find the minimum value record that can accomodate all the singlePos subtables.
+				mirf = reduce(ior, valueFormatList)
+				self.SubTable = _Lookup_SinglePos_subtables_flatten(self.SubTable, merger.font, mirf)
+				subtables = merger.lookup_subtables = [
+					_Lookup_SinglePos_subtables_flatten(st, merger.font, mirf) for st in subtables]
+				flattened = True
+			else:
+				flattened = False
 
 	merger.mergeLists(self.SubTable, subtables)
 	self.SubTableCount = len(self.SubTable)
@@ -634,11 +846,29 @@
 			self.SubTable.pop(-1)
 			self.SubTableCount -= 1
 
+		# Compact the merged subtables
+		# This is a good moment to do it because the compaction should create
+		# smaller subtables, which may prevent overflows from happening.
+		mode = os.environ.get(GPOS_COMPACT_MODE_ENV_KEY, GPOS_COMPACT_MODE_DEFAULT)
+		if mode and mode != "0":
+			log.info("Compacting GPOS...")
+			self.SubTable = compact_pair_pos(merger.font, mode, self.SubTable)
+			self.SubTableCount = len(self.SubTable)
+
+	elif isSinglePos and flattened:
+		singlePosTable = self.SubTable[0]
+		glyphs = singlePosTable.Coverage.glyphs
+		# We know that singlePosTable is Format 2, as this is set
+		# in _Lookup_SinglePos_subtables_flatten.
+		singlePosMapping = {
+			gname: valRecord
+			for gname, valRecord in zip(glyphs, singlePosTable.Value)
+		}
+		self.SubTable = buildSinglePos(singlePosMapping, merger.font.getReverseGlyphMap())
 	merger.mergeObjects(self, lst, exclude=['SubTable', 'SubTableCount'])
 
 	del merger.lookup_subtables
 
-
 #
 # InstancerMerger
 #
@@ -653,14 +883,23 @@
 		self.location = location
 		self.scalars = model.getScalars(location)
 
+@InstancerMerger.merger(ot.CaretValue)
+def merge(merger, self, lst):
+	assert self.Format == 1
+	Coords = [a.Coordinate for a in lst]
+	model = merger.model
+	scalars = merger.scalars
+	self.Coordinate = otRound(model.interpolateFromMastersAndScalars(Coords, scalars))
+
 @InstancerMerger.merger(ot.Anchor)
 def merge(merger, self, lst):
+	assert self.Format == 1
 	XCoords = [a.XCoordinate for a in lst]
 	YCoords = [a.YCoordinate for a in lst]
 	model = merger.model
 	scalars = merger.scalars
-	self.XCoordinate = round(model.interpolateFromMastersAndScalars(XCoords, scalars))
-	self.YCoordinate = round(model.interpolateFromMastersAndScalars(YCoords, scalars))
+	self.XCoordinate = otRound(model.interpolateFromMastersAndScalars(XCoords, scalars))
+	self.YCoordinate = otRound(model.interpolateFromMastersAndScalars(YCoords, scalars))
 
 @InstancerMerger.merger(otBase.ValueRecord)
 def merge(merger, self, lst):
@@ -676,7 +915,7 @@
 
 		if hasattr(self, name):
 			values = [getattr(a, name, 0) for a in lst]
-			value = round(model.interpolateFromMastersAndScalars(values, scalars))
+			value = otRound(model.interpolateFromMastersAndScalars(values, scalars))
 			setattr(self, name, value)
 
 
@@ -686,42 +925,43 @@
 
 class MutatorMerger(AligningMerger):
 	"""A merger that takes a variable font, and instantiates
-	an instance."""
+	an instance.  While there's no "merging" to be done per se,
+	the operation can benefit from many operations that the
+	aligning merger does."""
 
-	def __init__(self, font, location):
+	def __init__(self, font, instancer, deleteVariations=True):
 		Merger.__init__(self, font)
-		self.location = location
+		self.instancer = instancer
+		self.deleteVariations = deleteVariations
 
-		store = None
-		if 'GDEF' in font:
-			gdef = font['GDEF'].table
-			if gdef.Version >= 0x00010003:
-				store = gdef.VarStore
+@MutatorMerger.merger(ot.CaretValue)
+def merge(merger, self, lst):
 
-		self.instancer = VarStoreInstancer(store, font['fvar'].axes, location)
+	# Hack till we become selfless.
+	self.__dict__ = lst[0].__dict__.copy()
 
-	def instantiate(self):
-		font = self.font
+	if self.Format != 3:
+		return
 
-		self.mergeTables(font, [font], ['GPOS'])
+	instancer = merger.instancer
+	dev = self.DeviceTable
+	if merger.deleteVariations:
+		del self.DeviceTable
+	if dev:
+		assert dev.DeltaFormat == 0x8000
+		varidx = (dev.StartSize << 16) + dev.EndSize
+		delta = otRound(instancer[varidx])
+		self.Coordinate += delta
 
-		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']
+	if merger.deleteVariations:
+		self.Format = 1
 
 @MutatorMerger.merger(ot.Anchor)
 def merge(merger, self, lst):
+
+	# Hack till we become selfless.
+	self.__dict__ = lst[0].__dict__.copy()
+
 	if self.Format != 3:
 		return
 
@@ -731,29 +971,28 @@
 		if not hasattr(self, tableName):
 			continue
 		dev = getattr(self, tableName)
-		delattr(self, tableName)
+		if merger.deleteVariations:
+			delattr(self, tableName)
 		if dev is None:
 			continue
 
 		assert dev.DeltaFormat == 0x8000
 		varidx = (dev.StartSize << 16) + dev.EndSize
-		delta = round(instancer[varidx])
+		delta = otRound(instancer[varidx])
 
 		attr = v+'Coordinate'
 		setattr(self, attr, getattr(self, attr) + delta)
 
-	self.Format = 1
+	if merger.deleteVariations:
+		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.
+	# Hack till we become selfless.
 	self.__dict__ = lst[0].__dict__.copy()
 
 	instancer = merger.instancer
-	# TODO Handle differing valueformats
 	for name, tableName in [('XAdvance','XAdvDevice'),
 				('YAdvance','YAdvDevice'),
 				('XPlacement','XPlaDevice'),
@@ -762,15 +1001,16 @@
 		if not hasattr(self, tableName):
 			continue
 		dev = getattr(self, tableName)
-		delattr(self, tableName)
+		if merger.deleteVariations:
+			delattr(self, tableName)
 		if dev is None:
 			continue
 
 		assert dev.DeltaFormat == 0x8000
 		varidx = (dev.StartSize << 16) + dev.EndSize
-		delta = round(instancer[varidx])
+		delta = otRound(instancer[varidx])
 
-		setattr(self, name, getattr(self, name) + delta)
+		setattr(self, name, getattr(self, name, 0) + delta)
 
 
 #
@@ -783,29 +1023,58 @@
 
 	def __init__(self, model, axisTags, font):
 		Merger.__init__(self, font)
-		self.model = model
 		self.store_builder = varStore.OnlineVarStoreBuilder(axisTags)
+		self.setModel(model)
+
+	def setModel(self, model):
+		self.model = model
 		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 mergeThings(self, out, lst):
+		masterModel = None
+		if None in lst:
+			if allNone(lst):
+				if out is not None:
+					raise FoundANone(self, got=lst)
+				return
+			masterModel = self.model
+			model, lst = masterModel.getSubModel(lst)
+			self.setModel(model)
+
+		super(VariationMerger, self).mergeThings(out, lst)
+
+		if masterModel:
+			self.setModel(masterModel)
+
 
 def buildVarDevTable(store_builder, master_values):
-	if _all_equal(master_values):
+	if allEqual(master_values):
 		return master_values[0], None
 	base, varIdx = store_builder.storeMasters(master_values)
 	return base, builder.buildVarDevTable(varIdx)
 
+@VariationMerger.merger(ot.BaseCoord)
+def merge(merger, self, lst):
+	if self.Format != 1:
+		raise UnsupportedFormat(merger, subtable="a baseline coordinate")
+	self.Coordinate, DeviceTable = buildVarDevTable(merger.store_builder, [a.Coordinate for a in lst])
+	if DeviceTable:
+		self.Format = 3
+		self.DeviceTable = DeviceTable
+
+@VariationMerger.merger(ot.CaretValue)
+def merge(merger, self, lst):
+	if self.Format != 1:
+		raise UnsupportedFormat(merger, subtable="a caret")
+	self.Coordinate, DeviceTable = buildVarDevTable(merger.store_builder, [a.Coordinate for a in lst])
+	if DeviceTable:
+		self.Format = 3
+		self.DeviceTable = DeviceTable
+
 @VariationMerger.merger(ot.Anchor)
 def merge(merger, self, lst):
-	assert self.Format == 1
+	if self.Format != 1:
+		raise UnsupportedFormat(merger, subtable="an anchor")
 	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:
diff --git a/Lib/fontTools/varLib/models.py b/Lib/fontTools/varLib/models.py
index 10825ac..54063a9 100644
--- a/Lib/fontTools/varLib/models.py
+++ b/Lib/fontTools/varLib/models.py
@@ -1,124 +1,174 @@
 """Variation fonts interpolation models."""
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 
-__all__ = ['normalizeValue', 'normalizeLocation', 'supportScalar', 'VariationModel']
+__all__ = [
+    "nonNone",
+    "allNone",
+    "allEqual",
+    "allEqualTo",
+    "subList",
+    "normalizeValue",
+    "normalizeLocation",
+    "supportScalar",
+    "VariationModel",
+]
+
+from fontTools.misc.roundTools import noRound
+from .errors import VariationModelError
+
+
+def nonNone(lst):
+    return [l for l in lst if l is not None]
+
+
+def allNone(lst):
+    return all(l is None for l in lst)
+
+
+def allEqualTo(ref, lst, mapper=None):
+    if mapper is None:
+        return all(ref == item for item in lst)
+
+    mapped = mapper(ref)
+    return all(mapped == mapper(item) for item in lst)
+
+
+def allEqual(lst, mapper=None):
+    if not lst:
+        return True
+    it = iter(lst)
+    try:
+        first = next(it)
+    except StopIteration:
+        return True
+    return allEqualTo(first, it, mapper=mapper)
+
+
+def subList(truth, lst):
+    assert len(truth) == len(lst)
+    return [l for l, t in zip(lst, truth) if t]
+
 
 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
+    """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
+    if not (lower <= default <= upper):
+        raise ValueError(
+            f"Invalid axis values, must be minimum, default, maximum: "
+            f"{lower:3.3f}, {default:3.3f}, {upper:3.3f}"
+        )
+    v = max(min(v, upper), lower)
+    if v == default:
+        v = 0.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
+    """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
+    """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.0
+    for axis, (lower, peak, upper) in support.items():
+        if ot:
+            # OpenType-specific case handling
+            if peak == 0.0:
+                continue
+            if lower > peak or peak > upper:
+                continue
+            if lower < 0.0 and upper > 0.0:
+                continue
+            v = location.get(axis, 0.0)
+        else:
+            assert axis in location
+            v = location[axis]
+        if v == peak:
+            continue
+        if v <= lower or upper <= v:
+            scalar = 0.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
@@ -152,165 +202,328 @@
 	 {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.25},
+	 {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.16666666666666669,
+	  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
+    def __init__(self, locations, axisOrder=None):
+        if len(set(tuple(sorted(l.items())) for l in locations)) != len(locations):
+            raise VariationModelError("Locations must be unique.")
 
-		self._computeMasterSupports(axisPoints)
+        self.origLocations = locations
+        self.axisOrder = axisOrder if axisOrder is not None else []
 
-	@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]
-			axisPoints[axis].add(value)
+        locations = [{k: v for k, v in loc.items() if v != 0.0} for loc in locations]
+        keyFunc = self.getMasterLocationsSortKeyFunc(
+            locations, axisOrder=self.axisOrder
+        )
+        self.locations = sorted(locations, key=keyFunc)
 
-		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
+        # Mapping from user's master order to our master order
+        self.mapping = [self.locations.index(l) for l in locations]
+        self.reverseMapping = [locations.index(l) for l in self.locations]
 
-		ret = getKey(axisPoints, axisOrder)
-		ret.axisPoints = axisPoints
-		return ret
+        self._computeMasterSupports()
+        self._subModels = {}
 
-	@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 getSubModel(self, items):
+        if None not in items:
+            return self, items
+        key = tuple(v is not None for v in items)
+        subModel = self._subModels.get(key)
+        if subModel is None:
+            subModel = VariationModel(subList(key, self.origLocations), self.axisOrder)
+            self._subModels[key] = subModel
+        return subModel, subList(key, items)
 
-	def _computeMasterSupports(self, axisPoints):
-		supports = []
-		deltaWeights = []
-		locations = self.locations
-		for i,loc in enumerate(locations):
-			box = {}
+    @staticmethod
+    def getMasterLocationsSortKeyFunc(locations, axisOrder=[]):
+        if {} not in locations:
+            raise VariationModelError("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.0}
+            assert (
+                value not in axisPoints[axis]
+            ), 'Value "%s" in axisPoints["%s"] -->  %s' % (value, axis, axisPoints)
+            axisPoints[axis].add(value)
 
-			# Account for axisPoints first
-			for axis,values in axisPoints.items():
-				if not axis in loc:
-					continue
-				locV = loc[axis]
-				box[axis] = (self.lowerBound(locV, values), locV, self.upperBound(locV, values))
+        def getKey(axisPoints, axisOrder):
+            def sign(v):
+                return -1 if v < 0 else +1 if v > 0 else 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,_,upper) in box.items():
-					if axis in m and not (lower < m[axis] < upper):
-						relevant = False
-						break
-				if not relevant:
-					continue
-				# Split the box for new master
-				for axis,val in m.items():
-					assert axis in box
-					lower,locV,upper = box[axis]
-					if val < locV:
-						lower = val
-					elif locV < val:
-						upper = val
-					box[axis] = (lower,locV,upper)
-			supports.append(box)
+            def key(loc):
+                rank = len(loc)
+                onPointAxes = [
+                    axis
+                    for axis, value in loc.items()
+                    if axis in axisPoints and 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
+                )
 
-			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)
+            return key
 
-		self.supports = supports
-		self.deltaWeights = deltaWeights
+        ret = getKey(axisPoints, axisOrder)
+        return ret
 
-	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 reorderMasters(self, master_list, mapping):
+        # For changing the master data order without
+        # recomputing supports and deltaWeights.
+        new_list = [master_list[idx] for idx in mapping]
+        self.origLocations = [self.origLocations[idx] for idx in mapping]
+        locations = [
+            {k: v for k, v in loc.items() if v != 0.0} for loc in self.origLocations
+        ]
+        self.mapping = [self.locations.index(l) for l in locations]
+        self.reverseMapping = [locations.index(l) for l in self.locations]
+        self._subModels = {}
+        return new_list
 
-	def getScalars(self, loc):
-		return [supportScalar(loc, support) for support in self.supports]
+    def _computeMasterSupports(self):
+        self.supports = []
+        regions = self._locationsToRegions()
+        for i, region in enumerate(regions):
+            locAxes = set(region.keys())
+            # Walk over previous masters now
+            for prev_region in regions[:i]:
+                # Master with extra axes do not participte
+                if not set(prev_region.keys()).issubset(locAxes):
+                    continue
+                # If it's NOT in the current box, it does not participate
+                relevant = True
+                for axis, (lower, peak, upper) in region.items():
+                    if axis not in prev_region or not (
+                        prev_region[axis][1] == peak
+                        or lower < prev_region[axis][1] < upper
+                    ):
+                        relevant = False
+                        break
+                if not relevant:
+                    continue
 
-	@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
+                # Split the box for new master; split in whatever direction
+                # that has largest range ratio.
+                #
+                # For symmetry, we actually cut across multiple axes
+                # if they have the largest, equal, ratio.
+                # https://github.com/fonttools/fonttools/commit/7ee81c8821671157968b097f3e55309a1faa511e#commitcomment-31054804
 
-	def interpolateFromDeltas(self, loc, deltas):
-		scalars = self.getScalars(loc)
-		return self.interpolateFromDeltasAndScalars(deltas, scalars)
+                bestAxes = {}
+                bestRatio = -1
+                for axis in prev_region.keys():
+                    val = prev_region[axis][1]
+                    assert axis in region
+                    lower, locV, upper = region[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:
+                        bestAxes = {}
+                        bestRatio = ratio
+                    if ratio == bestRatio:
+                        bestAxes[axis] = (newLower, locV, newUpper)
 
-	def interpolateFromMasters(self, loc, masterValues):
-		deltas = self.getDeltas(masterValues)
-		return self.interpolateFromDeltas(loc, deltas)
+                for axis, triple in bestAxes.items():
+                    region[axis] = triple
+            self.supports.append(region)
+        self._computeDeltaWeights()
 
-	def interpolateFromMastersAndScalars(self, masterValues, scalars):
-		deltas = self.getDeltas(masterValues)
-		return self.interpolateFromDeltasAndScalars(deltas, scalars)
+    def _locationsToRegions(self):
+        locations = self.locations
+        # Compute min/max across each axis, use it as total range.
+        # TODO Take this as input from outside?
+        minV = {}
+        maxV = {}
+        for l in locations:
+            for k, v in l.items():
+                minV[k] = min(v, minV.get(k, v))
+                maxV[k] = max(v, maxV.get(k, v))
+
+        regions = []
+        for loc in locations:
+            region = {}
+            for axis, locV in loc.items():
+                if locV > 0:
+                    region[axis] = (0, locV, maxV[axis])
+                else:
+                    region[axis] = (minV[axis], locV, 0)
+            regions.append(region)
+        return regions
+
+    def _computeDeltaWeights(self):
+        self.deltaWeights = []
+        for i, loc in enumerate(self.locations):
+            deltaWeight = {}
+            # Walk over previous masters now, populate deltaWeight
+            for j, support in enumerate(self.supports[:i]):
+                scalar = supportScalar(loc, support)
+                if scalar:
+                    deltaWeight[j] = scalar
+            self.deltaWeights.append(deltaWeight)
+
+    def getDeltas(self, masterValues, *, round=noRound):
+        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():
+                if weight == 1:
+                    delta -= out[j]
+                else:
+                    delta -= out[j] * weight
+            out.append(round(delta))
+        return out
+
+    def getDeltasAndSupports(self, items, *, round=noRound):
+        model, items = self.getSubModel(items)
+        return model.getDeltas(items, round=round), model.supports
+
+    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 delta, scalar in 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, *, round=noRound):
+        deltas = self.getDeltas(masterValues, round=round)
+        return self.interpolateFromDeltas(loc, deltas)
+
+    def interpolateFromMastersAndScalars(self, masterValues, scalars, *, round=noRound):
+        deltas = self.getDeltas(masterValues, round=round)
+        return self.interpolateFromDeltasAndScalars(deltas, scalars)
+
+
+def piecewiseLinearMap(v, mapping):
+    keys = mapping.keys()
+    if not keys:
+        return v
+    if v in keys:
+        return mapping[v]
+    k = min(keys)
+    if v < k:
+        return v + mapping[k] - k
+    k = max(keys)
+    if v > k:
+        return v + mapping[k] - k
+    # Interpolate
+    a = max(k for k in keys if k < v)
+    b = min(k for k in keys if k > v)
+    va = mapping[a]
+    vb = mapping[b]
+    return va + (vb - va) * (v - a) / (b - a)
+
+
+def main(args=None):
+    """Normalize locations on a given designspace"""
+    from fontTools import configLogger
+    import argparse
+
+    parser = argparse.ArgumentParser(
+        "fonttools varLib.models",
+        description=main.__doc__,
+    )
+    parser.add_argument(
+        "--loglevel",
+        metavar="LEVEL",
+        default="INFO",
+        help="Logging level (defaults to INFO)",
+    )
+
+    group = parser.add_mutually_exclusive_group(required=True)
+    group.add_argument("-d", "--designspace", metavar="DESIGNSPACE", type=str)
+    group.add_argument(
+        "-l",
+        "--locations",
+        metavar="LOCATION",
+        nargs="+",
+        help="Master locations as comma-separate coordinates. One must be all zeros.",
+    )
+
+    args = parser.parse_args(args)
+
+    configLogger(level=args.loglevel)
+    from pprint import pprint
+
+    if args.designspace:
+        from fontTools.designspaceLib import DesignSpaceDocument
+
+        doc = DesignSpaceDocument()
+        doc.read(args.designspace)
+        locs = [s.location for s in doc.sources]
+        print("Original locations:")
+        pprint(locs)
+        doc.normalize()
+        print("Normalized locations:")
+        locs = [s.location for s in doc.sources]
+        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.locations
+        ]
+
+    model = VariationModel(locs)
+    print("Sorted locations:")
+    pprint(model.locations)
+    print("Supports:")
+    pprint(model.supports)
 
 
 if __name__ == "__main__":
-	import doctest, sys
-	sys.exit(doctest.testmod().failed)
+    import doctest, sys
+
+    if len(sys.argv) > 1:
+        sys.exit(main())
+
+    sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/varLib/mutator.py b/Lib/fontTools/varLib/mutator.py
index 0d1e5b1..263c4e6 100644
--- a/Lib/fontTools/varLib/mutator.py
+++ b/Lib/fontTools/varLib/mutator.py
@@ -1,27 +1,155 @@
 """
 Instantiate a variation font.  Run, eg:
 
-$ python mutator.py ./NotoSansArabic-VF.ttf wght=140 wdth=85
+$ 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
-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.misc.fixedTools import floatToFixedToFloat, floatToFixed
+from fontTools.misc.roundTools import otRound
+from fontTools.pens.boundsPen import BoundsPen
+from fontTools.ttLib import TTFont, newTable
+from fontTools.ttLib.tables import ttProgram
+from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates, flagOverlapSimple, OVERLAP_COMPOUND
+from fontTools.varLib.models import (
+	supportScalar,
+	normalizeLocation,
+	piecewiseLinearMap,
+)
 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 fontTools.subset.cff
 import os.path
 import logging
+from io import BytesIO
 
 
 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):
+
+def interpolate_cff2_PrivateDict(topDict, interpolateFromDeltas):
+	pd_blend_lists = ("BlueValues", "OtherBlues", "FamilyBlues",
+						"FamilyOtherBlues", "StemSnapH",
+						"StemSnapV")
+	pd_blend_values = ("BlueScale", "BlueShift",
+						"BlueFuzz", "StdHW", "StdVW")
+	for fontDict in topDict.FDArray:
+		pd = fontDict.Private
+		vsindex = pd.vsindex if (hasattr(pd, 'vsindex')) else 0
+		for key, value in pd.rawDict.items():
+			if (key in pd_blend_values) and isinstance(value, list):
+					delta = interpolateFromDeltas(vsindex, value[1:])
+					pd.rawDict[key] = otRound(value[0] + delta)
+			elif (key in pd_blend_lists) and isinstance(value[0], list):
+				"""If any argument in a BlueValues list is a blend list,
+				then they all are. The first value of each list is an
+				absolute value. The delta tuples are calculated from
+				relative master values, hence we need to append all the
+				deltas to date to each successive absolute value."""
+				delta = 0
+				for i, val_list in enumerate(value):
+					delta += otRound(interpolateFromDeltas(vsindex,
+										val_list[1:]))
+					value[i] = val_list[0] + delta
+
+
+def interpolate_cff2_charstrings(topDict, interpolateFromDeltas, glyphOrder):
+	charstrings = topDict.CharStrings
+	for gname in glyphOrder:
+		# Interpolate charstring
+		# e.g replace blend op args with regular args,
+		# and use and discard vsindex op.
+		charstring = charstrings[gname]
+		new_program = []
+		vsindex = 0
+		last_i = 0
+		for i, token in enumerate(charstring.program):
+			if token == 'vsindex':
+				vsindex = charstring.program[i - 1]
+				if last_i != 0:
+					new_program.extend(charstring.program[last_i:i - 1])
+				last_i = i + 1
+			elif token == 'blend':
+				num_regions = charstring.getNumRegions(vsindex)
+				numMasters = 1 + num_regions
+				num_args = charstring.program[i - 1]
+				# The program list starting at program[i] is now:
+				# ..args for following operations
+				# num_args values  from the default font
+				# num_args tuples, each with numMasters-1 delta values
+				# num_blend_args
+				# 'blend'
+				argi = i - (num_args * numMasters + 1)
+				end_args = tuplei = argi + num_args
+				while argi < end_args:
+					next_ti = tuplei + num_regions
+					deltas = charstring.program[tuplei:next_ti]
+					delta = interpolateFromDeltas(vsindex, deltas)
+					charstring.program[argi] += otRound(delta)
+					tuplei = next_ti
+					argi += 1
+				new_program.extend(charstring.program[last_i:end_args])
+				last_i = i + 1
+		if last_i != 0:
+			new_program.extend(charstring.program[last_i:])
+			charstring.program = new_program
+
+
+def interpolate_cff2_metrics(varfont, topDict, glyphOrder, loc):
+	"""Unlike TrueType glyphs, neither advance width nor bounding box
+	info is stored in a CFF2 charstring. The width data exists only in
+	the hmtx and HVAR tables. Since LSB data cannot be interpolated
+	reliably from the master LSB values in the hmtx table, we traverse
+	the charstring to determine the actual bound box. """
+
+	charstrings = topDict.CharStrings
+	boundsPen = BoundsPen(glyphOrder)
+	hmtx = varfont['hmtx']
+	hvar_table = None
+	if 'HVAR' in varfont:
+		hvar_table = varfont['HVAR'].table
+		fvar = varfont['fvar']
+		varStoreInstancer = VarStoreInstancer(hvar_table.VarStore, fvar.axes, loc)
+
+	for gid, gname in enumerate(glyphOrder):
+		entry = list(hmtx[gname])
+		# get width delta.
+		if hvar_table:
+			if hvar_table.AdvWidthMap:
+				width_idx = hvar_table.AdvWidthMap.mapping[gname]
+			else:
+				width_idx = gid
+			width_delta = otRound(varStoreInstancer[width_idx])
+		else:
+			width_delta = 0
+
+		# get LSB.
+		boundsPen.init()
+		charstring = charstrings[gname]
+		charstring.draw(boundsPen)
+		if boundsPen.bounds is None:
+			# Happens with non-marking glyphs
+			lsb_delta = 0
+		else:
+			lsb = otRound(boundsPen.bounds[0])
+			lsb_delta = entry[1] - lsb
+
+		if lsb_delta or width_delta:
+			if width_delta:
+				entry[0] += width_delta
+			if lsb_delta:
+				entry[1] = lsb
+			hmtx[gname] = tuple(entry)
+
+
+def instantiateVariableFont(varfont, location, inplace=False, overlap=True):
 	""" 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.:
@@ -30,6 +158,10 @@
 
 	By default, a new TTFont object is returned. If ``inplace`` is True, the
 	input varfont is modified and reduced to a static font.
+
+	When the overlap parameter is defined as True,
+	OVERLAP_SIMPLE and OVERLAP_COMPOUND bits are set to 1.  See
+	https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
 	"""
 	if not inplace:
 		# make a copy to leave input varfont unmodified
@@ -43,37 +175,41 @@
 	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()}
+		loc = {k: piecewiseLinearMap(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 'gvar' in varfont:
+		log.info("Mutating glyf/gvar tables")
+		gvar = varfont['gvar']
+		glyf = varfont['glyf']
+		hMetrics = varfont['hmtx'].metrics
+		vMetrics = getattr(varfont.get('vmtx'), 'metrics', None)
+		# 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, _ = glyf._getCoordinatesAndControls(glyphname, hMetrics, vMetrics)
+			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, g = glyf._getCoordinatesAndControls(glyphname, hMetrics, vMetrics)
+					delta = iup_delta(delta, origCoords, g.endPts)
+				coordinates += GlyphCoordinates(delta) * scalar
+			glyf._setCoordinates(glyphname, coordinates, hMetrics, vMetrics)
+	else:
+		glyf = None
 
 	if 'cvar' in varfont:
 		log.info("Mutating cvt/cvar tables")
@@ -87,7 +223,21 @@
 				if c is not None:
 					deltas[i] = deltas.get(i, 0) + scalar * c
 		for i, delta in deltas.items():
-			cvt[i] += round(delta)
+			cvt[i] += otRound(delta)
+
+	if 'CFF2' in varfont:
+		log.info("Mutating CFF2 table")
+		glyphOrder = varfont.getGlyphOrder()
+		CFF2 = varfont['CFF2']
+		topDict = CFF2.cff.topDictIndex[0]
+		vsInstancer = VarStoreInstancer(topDict.VarStore.otVarStore, fvar.axes, loc)
+		interpolateFromDeltas = vsInstancer.interpolateFromDeltas
+		interpolate_cff2_PrivateDict(topDict, interpolateFromDeltas)
+		CFF2.desubroutinize()
+		interpolate_cff2_charstrings(topDict, interpolateFromDeltas, glyphOrder)
+		interpolate_cff2_metrics(varfont, topDict, glyphOrder, loc)
+		del topDict.rawDict['VarStore']
+		del topDict.VarStore
 
 	if 'MVAR' in varfont:
 		log.info("Mutating MVAR table")
@@ -99,18 +249,143 @@
 			if mvarTag not in MVAR_ENTRIES:
 				continue
 			tableTag, itemName = MVAR_ENTRIES[mvarTag]
-			delta = round(varStoreInstancer[rec.VarIdx])
+			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("Mutating FeatureVariations")
+	for tableTag in 'GSUB','GPOS':
+		if not tableTag in varfont:
+			continue
+		table = varfont[tableTag].table
+		if not getattr(table, 'FeatureVariations', None):
+			continue
+		variations = table.FeatureVariations
+		for record in variations.FeatureVariationRecord:
+			applies = True
+			for condition in record.ConditionSet.ConditionTable:
+				if condition.Format == 1:
+					axisIdx = condition.AxisIndex
+					axisTag = fvar.axes[axisIdx].axisTag
+					Min = condition.FilterRangeMinValue
+					Max = condition.FilterRangeMaxValue
+					v = loc[axisTag]
+					if not (Min <= v <= Max):
+						applies = False
+				else:
+					applies = False
+				if not applies:
+					break
 
-		log.info("Building interpolated tables")
-		merger.instantiate()
+			if applies:
+				assert record.FeatureTableSubstitution.Version == 0x00010000
+				for rec in record.FeatureTableSubstitution.SubstitutionRecord:
+					table.FeatureList.FeatureRecord[rec.FeatureIndex].Feature = rec.Feature
+				break
+		del table.FeatureVariations
+
+	if 'GDEF' in varfont and varfont['GDEF'].table.Version >= 0x00010003:
+		log.info("Mutating GDEF/GPOS/GSUB tables")
+		gdef = varfont['GDEF'].table
+		instancer = VarStoreInstancer(gdef.VarStore, fvar.axes, loc)
+
+		merger = MutatorMerger(varfont, instancer)
+		merger.mergeTables(varfont, [varfont], ['GDEF', 'GPOS'])
+
+		# Downgrade GDEF.
+		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 varfont['GDEF']
+
+	addidef = False
+	if glyf:
+		for glyph in glyf.glyphs.values():
+			if hasattr(glyph, "program"):
+				instructions = glyph.program.getAssembly()
+				# If GETVARIATION opcode is used in bytecode of any glyph add IDEF
+				addidef = any(op.startswith("GETVARIATION") for op in instructions)
+				if addidef:
+					break
+		if overlap:
+			for glyph_name in glyf.keys():
+				glyph = glyf[glyph_name]
+				# Set OVERLAP_COMPOUND bit for compound glyphs
+				if glyph.isComposite():
+					glyph.components[0].flags |= OVERLAP_COMPOUND
+				# Set OVERLAP_SIMPLE bit for simple glyphs
+				elif glyph.numberOfContours > 0:
+					glyph.flags[0] |= flagOverlapSimple
+	if addidef:
+		log.info("Adding IDEF to fpgm table for GETVARIATION opcode")
+		asm = []
+		if 'fpgm' in varfont:
+			fpgm = varfont['fpgm']
+			asm = fpgm.program.getAssembly()
+		else:
+			fpgm = newTable('fpgm')
+			fpgm.program = ttProgram.Program()
+			varfont['fpgm'] = fpgm
+		asm.append("PUSHB[000] 145")
+		asm.append("IDEF[ ]")
+		args = [str(len(loc))]
+		for a in fvar.axes:
+			args.append(str(floatToFixed(loc[a.axisTag], 14)))
+		asm.append("NPUSHW[ ] " + ' '.join(args))
+		asm.append("ENDF[ ]")
+		fpgm.program.fromAssembly(asm)
+
+		# Change maxp attributes as IDEF is added
+		if 'maxp' in varfont:
+			maxp = varfont['maxp']
+			setattr(maxp, "maxInstructionDefs", 1 + getattr(maxp, "maxInstructionDefs", 0))
+			setattr(maxp, "maxStackElements", max(len(loc), getattr(maxp, "maxStackElements", 0)))
+
+	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)
+		if 'ltag' in varfont:
+			# Drop the whole 'ltag' table if all its language tags are referenced by
+			# name records to be pruned.
+			# TODO: prune unused ltag tags and re-enumerate langIDs accordingly
+			excludedUnicodeLangIDs = [
+				n.langID for n in varfont['name'].names
+				if n.nameID in exclude and n.platformID == 0 and n.langID != 0xFFFF
+			]
+			if set(excludedUnicodeLangIDs) == set(range(len((varfont['ltag'].tags)))):
+				del varfont['ltag']
+		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'):
@@ -121,30 +396,58 @@
 
 
 def main(args=None):
+	"""Instantiate a variation font"""
 	from fontTools import configLogger
+	import argparse
 
-	if args is None:
-		import sys
-		args = sys.argv[1:]
+	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.")
+	parser.add_argument(
+		"--no-overlap",
+		dest="overlap",
+		action="store_false",
+		help="Don't set OVERLAP_SIMPLE/OVERLAP_COMPOUND glyf flags."
+	)
+	options = parser.parse_args(args)
 
-	varfilename = args[0]
-	locargs = args[1:]
-	outfile = os.path.splitext(varfilename)[0] + '-instance.ttf'
-
-	# TODO Allow to specify logging verbosity as command line option
-	configLogger(level=logging.INFO)
+	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 locargs:
-		tag,val = arg.split('=')
-		assert len(tag) <= 4
-		loc[tag.ljust(4)] = float(val)
+	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)
+	instantiateVariableFont(varfont, loc, inplace=True, overlap=options.overlap)
 
 	log.info("Saving instance font %s", outfile)
 	varfont.save(outfile)
diff --git a/Lib/fontTools/varLib/mvar.py b/Lib/fontTools/varLib/mvar.py
index 92083dd..8b1355b 100644
--- a/Lib/fontTools/varLib/mvar.py
+++ b/Lib/fontTools/varLib/mvar.py
@@ -1,7 +1,3 @@
-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
diff --git a/Lib/fontTools/varLib/plot.py b/Lib/fontTools/varLib/plot.py
new file mode 100644
index 0000000..811559f
--- /dev/null
+++ b/Lib/fontTools/varLib/plot.py
@@ -0,0 +1,167 @@
+"""Visualize DesignSpaceDocument and resulting VariationModel."""
+
+from fontTools.varLib.models import VariationModel, supportScalar
+from fontTools.designspaceLib import DesignSpaceDocument
+from matplotlib import pyplot
+from mpl_toolkits.mplot3d import axes3d
+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 _plotLocationsDots(locations, axes, subplot, **kwargs):
+	for loc, color in zip(locations, cycle(pyplot.cm.Set1.colors)):
+		if len(axes) == 1:
+			subplot.plot(
+				[loc.get(axes[0], 0)],
+				[1.],
+				'o',
+				color=color,
+				**kwargs
+			)
+		elif len(axes) == 2:
+			subplot.plot(
+				[loc.get(axes[0], 0)],
+				[loc.get(axes[1], 0)],
+				[1.],
+				'o',
+				color=color,
+				**kwargs
+			)
+		else:
+			raise AssertionError(len(axes))
+
+
+def plotLocations(locations, fig, names=None, **kwargs):
+	n = len(locations)
+	cols = math.ceil(n**.5)
+	rows = math.ceil(n / cols)
+
+	if names is None:
+		names = [None] * len(locations)
+
+	model = VariationModel(locations)
+	names = [names[model.reverseMapping[i]] for i in range(len(names))]
+
+	axes = sorted(locations[0].keys())
+	if len(axes) == 1:
+		_plotLocations2D(
+			model, axes[0], fig, cols, rows, names=names, **kwargs
+		)
+	elif len(axes) == 2:
+		_plotLocations3D(
+			model, axes, fig, cols, rows, names=names, **kwargs
+		)
+	else:
+		raise ValueError("Only 1 or 2 axes are supported")
+
+
+def _plotLocations2D(model, axis, fig, cols, rows, names, **kwargs):
+	subplot = fig.add_subplot(111)
+	for i, (support, color, name) in enumerate(
+		zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names))
+	):
+		if name is not None:
+			subplot.set_title(name)
+		subplot.set_xlabel(axis)
+		pyplot.xlim(-1.,+1.)
+
+		Xs = support.get(axis, (-1.,0.,+1.))
+		X, Y = [], []
+		for x in stops(Xs):
+			y = supportScalar({axis:x}, support)
+			X.append(x)
+			Y.append(y)
+		subplot.plot(X, Y, color=color, **kwargs)
+
+		_plotLocationsDots(model.locations, [axis], subplot)
+
+
+def _plotLocations3D(model, axes, fig, rows, cols, names, **kwargs):
+	ax1, ax2 = axes
+
+	axis3D = fig.add_subplot(111, projection='3d')
+	for i, (support, color, name) in enumerate(
+		zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names))
+	):
+		if name is not None:
+			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)
+
+		_plotLocationsDots(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]
+	plotLocations(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()
+	fig.set_tight_layout(True)
+
+	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]
+		plotLocations(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
index 985aa25..bcf81b3 100644
--- a/Lib/fontTools/varLib/varStore.py
+++ b/Lib/fontTools/varLib/varStore.py
@@ -1,10 +1,10 @@
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
-from fontTools.misc.py23 import *
+from fontTools.misc.roundTools import noRound, otRound
+from fontTools.ttLib.tables import otTables as ot
 from fontTools.varLib.models import supportScalar
 from fontTools.varLib.builder import (buildVarRegionList, buildVarStore,
-				      buildVarRegion, buildVarData,
-				      varDataCalculateNumShorts)
+				      buildVarRegion, buildVarData)
+from functools import partial
+from collections import defaultdict
 
 
 def _getLocationKey(loc):
@@ -18,14 +18,38 @@
 		self._regionMap = {}
 		self._regionList = buildVarRegionList([], axisTags)
 		self._store = buildVarStore(self._regionList, [])
+		self._data = None
+		self._model = None
+		self._supports = None
+		self._varDataIndices = {}
+		self._varDataCaches = {}
+		self._cache = {}
 
 	def setModel(self, model):
+		self.setSupports(model.supports)
 		self._model = model
 
+	def setSupports(self, supports):
+		self._model = None
+		self._supports = list(supports)
+		if not self._supports[0]:
+			del self._supports[0] # Drop base master support
+		self._cache = {}
+		self._data = None
+
+	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)
+			data.calculateNumShorts(optimize=optimize)
+		return self._store
+
+	def _add_VarData(self):
 		regionMap = self._regionMap
 		regionList = self._regionList
 
-		regions = model.supports[1:]
+		regions = self._supports
 		regionIndices = []
 		for region in regions:
 			key = _getLocationKey(region)
@@ -36,30 +60,80 @@
 				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)
+		# Check if we have one already...
+		key = tuple(regionIndices)
+		varDataIdx = self._varDataIndices.get(key)
+		if varDataIdx is not None:
+			self._outer = varDataIdx
+			self._data = self._store.VarData[varDataIdx]
+			self._cache = self._varDataCaches[key]
+			if len(self._data.Item) == 0xFFFF:
+				# This is full.  Need new one.
+				varDataIdx = None
 
-	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)
-			varDataCalculateNumShorts(data, optimize)
-		return self._store
+		if varDataIdx is None:
+			self._data = buildVarData(regionIndices, [], optimize=False)
+			self._outer = len(self._store.VarData)
+			self._store.VarData.append(self._data)
+			self._varDataIndices[key] = self._outer
+			if key not in self._varDataCaches:
+				self._varDataCaches[key] = {}
+			self._cache = self._varDataCaches[key]
+
 
 	def storeMasters(self, master_values):
-		deltas = [round(d) for d in self._model.getDeltas(master_values)]
+		deltas = self._model.getDeltas(master_values, round=round)
 		base = deltas.pop(0)
-		inner = len(self._data.Item)
-		self._data.Item.append(deltas)
-		# TODO Check for full data array?
-		return base, (self._outer << 16) + inner
+		return base, self.storeDeltas(deltas, round=noRound)
 
+	def storeDeltas(self, deltas, *, round=round):
+		deltas = [round(d) for d in deltas]
+		if len(deltas) == len(self._supports) + 1:
+			deltas = tuple(deltas[1:])
+		else:
+			assert len(deltas) == len(self._supports)
+			deltas = tuple(deltas)
+
+		varIdx = self._cache.get(deltas)
+		if varIdx is not None:
+			return 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.storeDeltas(deltas)
+		self._data.addItem(deltas, round=noRound)
+
+		varIdx = (self._outer << 16) + inner
+		self._cache[deltas] = varIdx
+		return varIdx
+
+def VarData_addItem(self, deltas, *, round=round):
+	deltas = [round(d) for d in deltas]
+
+	countUs = self.VarRegionCount
+	countThem = len(deltas)
+	if countUs + 1 == countThem:
+		deltas = tuple(deltas[1:])
+	else:
+		assert countUs == countThem, (countUs, countThem)
+		deltas = tuple(deltas)
+	self.Item.append(list(deltas))
+	self.ItemCount = len(self.Item)
+
+ot.VarData.addItem = VarData_addItem
 
 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)}
+	return {
+		fvar_axes[i].axisTag: (reg.StartCoord,reg.PeakCoord,reg.EndCoord)
+		for i, reg in enumerate(self.VarRegionAxis)
+		if reg.PeakCoord != 0
+	}
+
+ot.VarRegion.get_support = VarRegion_get_support
 
 class VarStoreInstancer(object):
 
@@ -80,21 +154,453 @@
 	def _getScalar(self, regionIdx):
 		scalar = self._scalars.get(regionIdx)
 		if scalar is None:
-			support = VarRegion_get_support(self._regions[regionIdx], self.fvar_axes)
+			support = self._regions[regionIdx].get_support(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]
+	@staticmethod
+	def interpolateFromDeltasAndScalars(deltas, scalars):
 		delta = 0.
 		for d,s in zip(deltas, scalars):
+			if not s: continue
 			delta += d * s
 		return delta
 
+	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]
+		return self.interpolateFromDeltasAndScalars(deltas, scalars)
+
+	def interpolateFromDeltas(self, varDataIndex, deltas):
+		varData = self._varData
+		scalars = [self._getScalar(ri) for ri in
+					varData[varDataIndex].VarRegionIndex]
+		return self.interpolateFromDeltasAndScalars(deltas, scalars)
+
+
+#
+# Optimizations
+#
+# retainFirstMap - If true, major 0 mappings are retained. Deltas for unused indices are zeroed
+# advIdxes - Set of major 0 indices for advance deltas to be listed first. Other major 0 indices follow.
+
+def VarStore_subset_varidxes(self, varIdxes, optimize=True, retainFirstMap=False, advIdxes=set()):
+
+	# 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 = len(newVarData)
+		newVarData.append(data)
+
+		items = data.Item
+		newItems = []
+		if major == 0 and retainFirstMap:
+			for minor in range(len(items)):
+				newItems.append(items[minor] if minor in usedMinors else [0] * len(items[minor]))
+				varDataMap[minor] = minor
+		else:
+			if major == 0:
+				minors = sorted(advIdxes) + sorted(usedMinors - advIdxes)
+			else:
+				minors = sorted(usedMinors)
+			for minor in minors:
+				newMinor = len(newItems)
+				newItems.append(items[minor])
+				varDataMap[(major<<16)+minor] = (newMajor<<16)+newMinor
+
+		data.Item = newItems
+		data.ItemCount = len(data.Item)
+
+		data.calculateNumShorts(optimize=optimize)
+
+	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, func):
+	"""Recurse down from self, if type of an object is ot.Device,
+	call func() on it.  Works on otData-style classes."""
+
+	if type(self) == ot.Device:
+		func(self)
+
+	elif isinstance(self, list):
+		for that in self:
+			_visit(that, func)
+
+	elif hasattr(self, 'getConverters') and not hasattr(self, 'postRead'):
+		for conv in self.getConverters():
+			that = getattr(self, conv.name, None)
+			if that is not None:
+				_visit(that, func)
+
+	elif isinstance(self, ot.ValueRecord):
+		for that in self.__dict__.values():
+			_visit(that, 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, 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):
+	"""Map VarIdx in this Device table (if any) through mapping."""
+	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, 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 & 0b1111:
+				c += 2
+			chars >>= 4
+		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."""
+		longWords = False
+
+		chars = 0
+		i = 1
+		for v in row:
+			if v:
+				chars += i
+			if not (-128 <= v <= 127):
+				chars += i * 0b0010
+			if not (-32768 <= v <= 32767):
+				longWords = True
+				break
+			i <<= 4
+
+		if longWords:
+			# Redo; only allow 2byte/4byte encoding
+			chars = 0
+			i = 1
+			for v in row:
+				if v:
+					chars += i * 0b0011
+				if not (-32768 <= v <= 32767):
+					chars += i * 0b1100
+				i <<= 4
+
+		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 = [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 = list(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)
+		data.optimize()
+
+	return varidx_map
+
+ot.VarStore.optimize = VarStore_optimize
+
+
+def main(args=None):
+	"""Optimize a font's GDEF variation store"""
+	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', description= main.__doc__)
+	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/ast.py b/Lib/fontTools/voltLib/ast.py
index 93c3415..3a1f4a0 100644
--- a/Lib/fontTools/voltLib/ast.py
+++ b/Lib/fontTools/voltLib/ast.py
@@ -1,52 +1,62 @@
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
 from fontTools.voltLib.error import VoltLibError
+from typing import NamedTuple
 
 
-class Statement(object):
-    def __init__(self, location):
+class Pos(NamedTuple):
+    adv: int
+    dx: int
+    dy: int
+    adv_adjust_by: dict
+    dx_adjust_by: dict
+    dy_adjust_by: dict
+
+    def __str__(self):
+        res = ' POS'
+        for attr in ('adv', 'dx', 'dy'):
+            value = getattr(self, attr)
+            if value is not None:
+                res += f' {attr.upper()} {value}'
+                adjust_by = getattr(self, f'{attr}_adjust_by', {})
+                for size, adjustment in adjust_by.items():
+                    res += f' ADJUST_BY {adjustment} AT {size}'
+        res += ' END_POS'
+        return res
+
+
+class Element(object):
+    def __init__(self, location=None):
         self.location = location
 
     def build(self, builder):
         pass
 
-
-class Expression(object):
-    def __init__(self, location):
-        self.location = location
-
-    def build(self, builder):
-        pass
+    def __str__(self):
+        raise NotImplementedError
 
 
-class Block(Statement):
-    def __init__(self, location):
-        Statement.__init__(self, location)
+class Statement(Element):
+    pass
+
+
+class Expression(Element):
+    pass
+
+
+class VoltFile(Statement):
+    def __init__(self):
+        Statement.__init__(self, location=None)
         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, location, name):
-        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()
+    def __str__(self):
+        return '\n' + '\n'.join(str(s) for s in self.statements) + ' END\n'
 
 
 class GlyphDefinition(Statement):
-    def __init__(self, location, name, gid, gunicode, gtype, components):
+    def __init__(self, name, gid, gunicode, gtype, components, location=None):
         Statement.__init__(self, location)
         self.name = name
         self.id = gid
@@ -54,9 +64,24 @@
         self.type = gtype
         self.components = components
 
+    def __str__(self):
+        res = f'DEF_GLYPH "{self.name}" ID {self.id}'
+        if self.unicode is not None:
+            if len(self.unicode) > 1:
+                unicodes = ','.join(f'U+{u:04X}' for u in self.unicode)
+                res += f' UNICODEVALUES "{unicodes}"'
+            else:
+                res += f' UNICODE {self.unicode[0]}'
+        if self.type is not None:
+            res += f' TYPE {self.type}'
+        if self.components is not None:
+            res += f' COMPONENTS {self.components}'
+        res += ' END_GLYPH'
+        return res
+
 
 class GroupDefinition(Statement):
-    def __init__(self, location, name, enum):
+    def __init__(self, name, enum, location=None):
         Statement.__init__(self, location)
         self.name = name
         self.enum = enum
@@ -75,20 +100,27 @@
             self.glyphs_ = self.enum.glyphSet(groups)
         return self.glyphs_
 
+    def __str__(self):
+        enum = self.enum and str(self.enum) or ''
+        return f'DEF_GROUP "{self.name}"\n{enum}\nEND_GROUP'
+
 
 class GlyphName(Expression):
     """A single glyph name, such as cedilla."""
-    def __init__(self, location, glyph):
+    def __init__(self, glyph, location=None):
         Expression.__init__(self, location)
         self.glyph = glyph
 
     def glyphSet(self):
-        return frozenset((self.glyph,))
+        return (self.glyph,)
+
+    def __str__(self):
+        return f' GLYPH "{self.glyph}"'
 
 
 class Enum(Expression):
     """An enum"""
-    def __init__(self, location, enum):
+    def __init__(self, enum, location=None):
         Expression.__init__(self, location)
         self.enum = enum
 
@@ -97,18 +129,22 @@
             yield e
 
     def glyphSet(self, groups=None):
-        glyphs = set()
+        glyphs = []
         for element in self.enum:
             if isinstance(element, (GroupName, Enum)):
-                glyphs = glyphs.union(element.glyphSet(groups))
+                glyphs.extend(element.glyphSet(groups))
             else:
-                glyphs = glyphs.union(element.glyphSet())
-        return frozenset(glyphs)
+                glyphs.extend(element.glyphSet())
+        return tuple(glyphs)
+
+    def __str__(self):
+        enum = ''.join(str(e) for e in self.enum)
+        return f' ENUM{enum} END_ENUM'
 
 
 class GroupName(Expression):
     """A glyph group"""
-    def __init__(self, location, group, parser):
+    def __init__(self, group, parser, location=None):
         Expression.__init__(self, location)
         self.group = group
         self.parser_ = parser
@@ -123,51 +159,84 @@
                 'Group "%s" is used but undefined.' % (self.group),
                 self.location)
 
+    def __str__(self):
+        return f' GROUP "{self.group}"'
+
 
 class Range(Expression):
     """A glyph range"""
-    def __init__(self, location, start, end, parser):
+    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)
+        return tuple(self.parser.glyph_range(self.start, self.end))
+
+    def __str__(self):
+        return f' RANGE "{self.start}" TO "{self.end}"'
 
 
 class ScriptDefinition(Statement):
-    def __init__(self, location, name, tag, langs):
+    def __init__(self, name, tag, langs, location=None):
         Statement.__init__(self, location)
         self.name = name
         self.tag = tag
         self.langs = langs
 
+    def __str__(self):
+        res = 'DEF_SCRIPT'
+        if self.name is not None:
+            res += f' NAME "{self.name}"'
+        res += f' TAG "{self.tag}"\n\n'
+        for lang in self.langs:
+            res += f'{lang}'
+        res += 'END_SCRIPT'
+        return res
+
 
 class LangSysDefinition(Statement):
-    def __init__(self, location, name, tag, features):
+    def __init__(self, name, tag, features, location=None):
         Statement.__init__(self, location)
         self.name = name
         self.tag = tag
         self.features = features
 
+    def __str__(self):
+        res = 'DEF_LANGSYS'
+        if self.name is not None:
+            res += f' NAME "{self.name}"'
+        res += f' TAG "{self.tag}"\n\n'
+        for feature in self.features:
+            res += f'{feature}'
+        res += 'END_LANGSYS\n'
+        return res
+
 
 class FeatureDefinition(Statement):
-    def __init__(self, location, name, tag, lookups):
+    def __init__(self, name, tag, lookups, location=None):
         Statement.__init__(self, location)
         self.name = name
         self.tag = tag
         self.lookups = lookups
 
+    def __str__(self):
+        res = f'DEF_FEATURE NAME "{self.name}" TAG "{self.tag}"\n'
+        res += ' ' + ' '.join(f'LOOKUP "{l}"' for l in self.lookups) + '\n'
+        res += 'END_FEATURE\n'
+        return res
+
 
 class LookupDefinition(Statement):
-    def __init__(self, location, name, process_base, process_marks, direction,
-                 reversal, comments, context, sub, pos):
+    def __init__(self, name, process_base, process_marks, mark_glyph_set,
+                 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.mark_glyph_set = mark_glyph_set
         self.direction = direction
         self.reversal = reversal
         self.comments = comments
@@ -175,72 +244,162 @@
         self.sub = sub
         self.pos = pos
 
+    def __str__(self):
+        res = f'DEF_LOOKUP "{self.name}"'
+        res += f' {self.process_base and "PROCESS_BASE" or "SKIP_BASE"}'
+        if self.process_marks:
+            res += ' PROCESS_MARKS '
+            if self.mark_glyph_set:
+                res += f'MARK_GLYPH_SET "{self.mark_glyph_set}"'
+            elif isinstance(self.process_marks, str):
+                res += f'"{self.process_marks}"'
+            else:
+                res += 'ALL'
+        else:
+            res += ' SKIP_MARKS'
+        if self.direction is not None:
+            res += f' DIRECTION {self.direction}'
+        if self.reversal:
+            res += ' REVERSAL'
+        if self.comments is not None:
+            comments = self.comments.replace('\n', r'\n')
+            res += f'\nCOMMENTS "{comments}"'
+        if self.context:
+            res += '\n' + '\n'.join(str(c) for c in self.context)
+        else:
+            res += '\nIN_CONTEXT\nEND_CONTEXT'
+        if self.sub:
+            res += f'\n{self.sub}'
+        if self.pos:
+            res += f'\n{self.pos}'
+        return res
+
 
 class SubstitutionDefinition(Statement):
-    def __init__(self, location, mapping):
+    def __init__(self, mapping, location=None):
         Statement.__init__(self, location)
         self.mapping = mapping
 
+    def __str__(self):
+        res = 'AS_SUBSTITUTION\n'
+        for src, dst in self.mapping.items():
+            src = ''.join(str(s) for s in src)
+            dst = ''.join(str(d) for d in dst)
+            res += f'SUB{src}\nWITH{dst}\nEND_SUB\n'
+        res += 'END_SUBSTITUTION'
+        return res
+
 
 class SubstitutionSingleDefinition(SubstitutionDefinition):
-    def __init__(self, location, mapping):
-        SubstitutionDefinition.__init__(self, location, mapping)
+    pass
 
 
 class SubstitutionMultipleDefinition(SubstitutionDefinition):
-    def __init__(self, location, mapping):
-        SubstitutionDefinition.__init__(self, location, mapping)
+    pass
 
 
 class SubstitutionLigatureDefinition(SubstitutionDefinition):
-    def __init__(self, location, mapping):
-        SubstitutionDefinition.__init__(self, location, mapping)
+    pass
 
 
 class SubstitutionReverseChainingSingleDefinition(SubstitutionDefinition):
-    def __init__(self, location, mapping):
-        SubstitutionDefinition.__init__(self, location, mapping)
+    pass
 
 
 class PositionAttachDefinition(Statement):
-    def __init__(self, location, coverage, coverage_to):
+    def __init__(self, coverage, coverage_to, location=None):
         Statement.__init__(self, location)
         self.coverage = coverage
         self.coverage_to = coverage_to
 
+    def __str__(self):
+        coverage = ''.join(str(c) for c in self.coverage)
+        res = f'AS_POSITION\nATTACH{coverage}\nTO'
+        for coverage, anchor in self.coverage_to:
+            coverage = ''.join(str(c) for c in coverage)
+            res += f'{coverage} AT ANCHOR "{anchor}"'
+        res += '\nEND_ATTACH\nEND_POSITION'
+        return res
+
 
 class PositionAttachCursiveDefinition(Statement):
-    def __init__(self, location, coverages_exit, coverages_enter):
+    def __init__(self, coverages_exit, coverages_enter, location=None):
         Statement.__init__(self, location)
         self.coverages_exit = coverages_exit
         self.coverages_enter = coverages_enter
 
+    def __str__(self):
+        res = 'AS_POSITION\nATTACH_CURSIVE'
+        for coverage in self.coverages_exit:
+            coverage = ''.join(str(c) for c in coverage)
+            res += f'\nEXIT {coverage}'
+        for coverage in self.coverages_enter:
+            coverage = ''.join(str(c) for c in coverage)
+            res += f'\nENTER {coverage}'
+        res += '\nEND_ATTACH\nEND_POSITION'
+        return res
+
 
 class PositionAdjustPairDefinition(Statement):
-    def __init__(self, location, coverages_1, coverages_2, adjust_pair):
+    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
 
+    def __str__(self):
+        res = 'AS_POSITION\nADJUST_PAIR\n'
+        for coverage in self.coverages_1:
+            coverage = ' '.join(str(c) for c in coverage)
+            res += f' FIRST {coverage}'
+        res += '\n'
+        for coverage in self.coverages_2:
+            coverage = ' '.join(str(c) for c in coverage)
+            res += f' SECOND {coverage}'
+        res += '\n'
+        for (id_1, id_2), (pos_1, pos_2) in self.adjust_pair.items():
+            res += f' {id_1} {id_2} BY{pos_1}{pos_2}\n'
+        res += '\nEND_ADJUST\nEND_POSITION'
+        return res
+
 
 class PositionAdjustSingleDefinition(Statement):
-    def __init__(self, location, adjust_single):
+    def __init__(self, adjust_single, location=None):
         Statement.__init__(self, location)
         self.adjust_single = adjust_single
 
+    def __str__(self):
+        res = 'AS_POSITION\nADJUST_SINGLE'
+        for coverage, pos in self.adjust_single:
+            coverage = ''.join(str(c) for c in coverage)
+            res += f'{coverage} BY{pos}'
+        res += '\nEND_ADJUST\nEND_POSITION'
+        return res
+
+
 
 class ContextDefinition(Statement):
-    def __init__(self, location, ex_or_in, left=[], right=[]):
+    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
-        self.right = right
+        self.left = left if left is not None else []
+        self.right = right if right is not None else []
+
+    def __str__(self):
+        res = self.ex_or_in + '\n'
+        for coverage in self.left:
+            coverage = ''.join(str(c) for c in coverage)
+            res += f' LEFT{coverage}\n'
+        for coverage in self.right:
+            coverage = ''.join(str(c) for c in coverage)
+            res += f' RIGHT{coverage}\n'
+        res += 'END_CONTEXT'
+        return res
 
 
 class AnchorDefinition(Statement):
-    def __init__(self, location, name, gid, glyph_name, component, locked,
-                 pos):
+    def __init__(self, name, gid, glyph_name, component, locked,
+                 pos, location=None):
         Statement.__init__(self, location)
         self.name = name
         self.gid = gid
@@ -249,9 +408,26 @@
         self.locked = locked
         self.pos = pos
 
+    def __str__(self):
+        locked = self.locked and ' LOCKED' or ''
+        return (f'DEF_ANCHOR "{self.name}"'
+                f' ON {self.gid}'
+                f' GLYPH {self.glyph_name}'
+                f' COMPONENT {self.component}'
+                f'{locked}'
+                f' AT {self.pos} END_ANCHOR')
+
 
 class SettingDefinition(Statement):
-    def __init__(self, location, name, value):
+    def __init__(self, name, value, location=None):
         Statement.__init__(self, location)
         self.name = name
         self.value = value
+
+    def __str__(self):
+        if self.value is True:
+            return f'{self.name}'
+        if isinstance(self.value, (tuple, list)):
+            value = " ".join(str(v) for v in self.value)
+            return f'{self.name} {value}'
+        return f'{self.name} {self.value}'
diff --git a/Lib/fontTools/voltLib/error.py b/Lib/fontTools/voltLib/error.py
index f9900ad..a905de1 100644
--- a/Lib/fontTools/voltLib/error.py
+++ b/Lib/fontTools/voltLib/error.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
 
 
 class VoltLibError(Exception):
diff --git a/Lib/fontTools/voltLib/lexer.py b/Lib/fontTools/voltLib/lexer.py
index 271fe3b..bc982a7 100644
--- a/Lib/fontTools/voltLib/lexer.py
+++ b/Lib/fontTools/voltLib/lexer.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
 from fontTools.voltLib.error import VoltLibError
 
 class Lexer(object):
@@ -39,10 +37,13 @@
             if token_type not in {Lexer.NEWLINE}:
                 return (token_type, token, location)
 
+    def location_(self):
+        column = self.pos_ - self.line_start_ + 1
+        return (self.filename_ or "<volt>", self.line_, column)
+
     def next_(self):
         self.scan_over_(Lexer.CHAR_WHITESPACE_)
-        column = self.pos_ - self.line_start_ + 1
-        location = (self.filename_, self.line_, column)
+        location = self.location_()
         start = self.pos_
         text = self.text_
         limit = len(text)
diff --git a/Lib/fontTools/voltLib/parser.py b/Lib/fontTools/voltLib/parser.py
index e491ca0..0e68d53 100644
--- a/Lib/fontTools/voltLib/parser.py
+++ b/Lib/fontTools/voltLib/parser.py
@@ -1,6 +1,3 @@
-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
@@ -15,9 +12,10 @@
     "GRID_PPEM": "parse_ppem_",
     "PRESENTATION_PPEM": "parse_ppem_",
     "PPOSITIONING_PPEM": "parse_ppem_",
-    "COMPILER_USEEXTENSIONLOOKUPS": "parse_compiler_flag_",
-    "COMPILER_USEPAIRPOSFORMAT2": "parse_compiler_flag_",
+    "COMPILER_USEEXTENSIONLOOKUPS": "parse_noarg_option_",
+    "COMPILER_USEPAIRPOSFORMAT2": "parse_noarg_option_",
     "CMAP_FORMAT": "parse_cmap_format",
+    "DO_NOT_TOUCH_CMAP": "parse_noarg_option_",
 }
 
 
@@ -32,10 +30,19 @@
         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.make_lexer_(path)
         self.advance_lexer_()
 
+    def make_lexer_(self, file_or_path):
+        if hasattr(file_or_path, "read"):
+            filename = getattr(file_or_path, "name", None)
+            data = file_or_path.read()
+        else:
+            filename = file_or_path
+            with open(file_or_path, "r") as f:
+                data = f.read()
+        self.lexer_ = Lexer(data, filename)
+
     def parse(self):
         statements = self.doc_.statements
         while self.next_token_type_ is not None:
@@ -44,10 +51,7 @@
                 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_
+                break
             else:
                 raise VoltLibError(
                     "Expected " + ", ".join(sorted(PARSE_FUNCS.keys())),
@@ -76,7 +80,7 @@
         if self.next_token_ == "TYPE":
             self.expect_keyword_("TYPE")
             gtype = self.expect_name_()
-            assert gtype in ("BASE", "LIGATURE", "MARK")
+            assert gtype in ("BASE", "LIGATURE", "MARK", "COMPONENT")
         components = None
         if self.next_token_ == "COMPONENTS":
             self.expect_keyword_("COMPONENTS")
@@ -87,8 +91,9 @@
                 'Glyph "%s" (gid %i) already defined' % (name, gid),
                 location
             )
-        def_glyph = ast.GlyphDefinition(location, name, gid,
-                                        gunicode, gtype, components)
+        def_glyph = ast.GlyphDefinition(name, gid,
+                                        gunicode, gtype, components,
+                                        location=location)
         self.glyphs_.define(name, def_glyph)
         return def_glyph
 
@@ -98,7 +103,6 @@
         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:
@@ -107,7 +111,8 @@
                 'group names are case insensitive' % name,
                 location
             )
-        def_group = ast.GroupDefinition(location, name, enum)
+        def_group = ast.GroupDefinition(name, enum,
+                                        location=location)
         self.groups_.define(name, def_group)
         return def_group
 
@@ -142,7 +147,7 @@
             langs.append(lang)
         self.expect_keyword_("END_SCRIPT")
         self.langs_.exit_scope()
-        def_script = ast.ScriptDefinition(location, name, tag, langs)
+        def_script = ast.ScriptDefinition(name, tag, langs, location=location)
         self.scripts_.define(tag, def_script)
         return def_script
 
@@ -161,7 +166,8 @@
             feature = self.parse_feature_()
             self.expect_keyword_("END_FEATURE")
             features.append(feature)
-        def_langsys = ast.LangSysDefinition(location, name, tag, features)
+        def_langsys = ast.LangSysDefinition(name, tag, features,
+                                            location=location)
         return def_langsys
 
     def parse_feature_(self):
@@ -177,7 +183,8 @@
             self.expect_keyword_("LOOKUP")
             lookup = self.expect_string_()
             lookups.append(lookup)
-        feature = ast.FeatureDefinition(location, name, tag, lookups)
+        feature = ast.FeatureDefinition(name, tag, lookups,
+                                        location=location)
         return feature
 
     def parse_def_lookup_(self):
@@ -202,18 +209,22 @@
             self.advance_lexer_()
             process_base = False
         process_marks = True
+        mark_glyph_set = None
         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_()
+                mark_glyph_set = self.expect_string_()
             elif self.next_token_ == "ALL":
                 self.advance_lexer_()
+            elif self.next_token_ == "NONE":
+                self.advance_lexer_()
+                process_marks = False
+            elif self.next_token_type_ == Lexer.STRING:
+                process_marks = self.expect_string_()
             else:
                 raise VoltLibError(
-                    "Expected ALL, MARK_GLYPH_SET or an ID. "
+                    "Expected ALL, NONE, MARK_GLYPH_SET or an ID. "
                     "Got %s" % (self.next_token_type_),
                     location)
         elif self.next_token_ == "SKIP_MARKS":
@@ -231,7 +242,7 @@
         comments = None
         if self.next_token_ == "COMMENTS":
             self.expect_keyword_("COMMENTS")
-            comments = self.expect_string_()
+            comments = self.expect_string_().replace(r'\n', '\n')
         context = []
         while self.next_token_ in ("EXCEPT_CONTEXT", "IN_CONTEXT"):
             context = self.parse_context_()
@@ -248,8 +259,8 @@
                 "Got %s" % (as_pos_or_sub),
                 location)
         def_lookup = ast.LookupDefinition(
-            location, name, process_base, process_marks, direction, reversal,
-            comments, context, sub, pos)
+            name, process_base, process_marks, mark_glyph_set, direction,
+            reversal, comments, context, sub, pos, location=location)
         self.lookups_.define(name, def_lookup)
         return def_lookup
 
@@ -272,8 +283,8 @@
                     else:
                         right.append(coverage)
                 self.expect_keyword_("END_CONTEXT")
-                context = ast.ContextDefinition(location, ex_or_in, left,
-                                                right)
+                context = ast.ContextDefinition(ex_or_in, left,
+                                                right, location=location)
                 contexts.append(context)
             else:
                 self.expect_keyword_("END_CONTEXT")
@@ -301,17 +312,20 @@
             raise VoltLibError(
                 "Invalid substitution type",
                 location)
-        mapping = OrderedDict(zip(tuple(src), tuple(dest)))
+        mapping = dict(zip(tuple(src), tuple(dest)))
         if max_src == 1 and max_dest == 1:
             if reversal:
                 sub = ast.SubstitutionReverseChainingSingleDefinition(
-                    location, mapping)
+                    mapping, location=location)
             else:
-                sub = ast.SubstitutionSingleDefinition(location, mapping)
+                sub = ast.SubstitutionSingleDefinition(mapping,
+                                                       location=location)
         elif max_src == 1 and max_dest > 1:
-            sub = ast.SubstitutionMultipleDefinition(location, mapping)
+            sub = ast.SubstitutionMultipleDefinition(mapping,
+                                                     location=location)
         elif max_src > 1 and max_dest == 1:
-            sub = ast.SubstitutionLigatureDefinition(location, mapping)
+            sub = ast.SubstitutionLigatureDefinition(mapping,
+                                                     location=location)
         return sub
 
     def parse_position_(self):
@@ -348,7 +362,7 @@
             coverage_to.append((cov, anchor_name))
         self.expect_keyword_("END_ATTACH")
         position = ast.PositionAttachDefinition(
-            location, coverage, coverage_to)
+            coverage, coverage_to, location=location)
         return position
 
     def parse_attach_cursive_(self):
@@ -364,7 +378,7 @@
             coverages_enter.append(self.parse_coverage_())
         self.expect_keyword_("END_ATTACH")
         position = ast.PositionAttachCursiveDefinition(
-            location, coverages_exit, coverages_enter)
+            coverages_exit, coverages_enter, location=location)
         return position
 
     def parse_adjust_pair_(self):
@@ -390,7 +404,7 @@
             adjust_pair[(id_1, id_2)] = (pos_1, pos_2)
         self.expect_keyword_("END_ADJUST")
         position = ast.PositionAdjustPairDefinition(
-            location, coverages_1, coverages_2, adjust_pair)
+            coverages_1, coverages_2, adjust_pair, location=location)
         return position
 
     def parse_adjust_single_(self):
@@ -404,7 +418,7 @@
             adjust_single.append((coverages, pos))
         self.expect_keyword_("END_ADJUST")
         position = ast.PositionAdjustSingleDefinition(
-            location, adjust_single)
+            adjust_single, location=location)
         return position
 
     def parse_def_anchor_(self):
@@ -415,16 +429,17 @@
         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_()
+        # check for duplicate anchor names on this glyph
+        if glyph_name in self.anchors_:
+            anchor = self.anchors_[glyph_name].resolve(name)
+            if anchor is not None and anchor.component == component:
+                raise VoltLibError(
+                    'Anchor "%s" already defined, '
+                    'anchor names are case insensitive' % name,
+                    location
+                )
         if self.next_token_ == "LOCKED":
             locked = True
             self.advance_lexer_()
@@ -433,8 +448,9 @@
         self.expect_keyword_("AT")
         pos = self.parse_pos_()
         self.expect_keyword_("END_ANCHOR")
-        anchor = ast.AnchorDefinition(location, name, gid, glyph_name,
-                                      component, locked, pos)
+        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)
@@ -478,7 +494,7 @@
                 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)
+        return ast.Pos(adv, dx, dy, adv_adjust_by, dx_adjust_by, dy_adjust_by)
 
     def parse_unicode_values_(self):
         location = self.cur_token_location_
@@ -492,9 +508,9 @@
         return unicode_values if unicode_values != [] else None
 
     def parse_enum_(self):
-        assert self.is_cur_keyword_("ENUM")
+        self.expect_keyword_("ENUM")
         location = self.cur_token_location_
-        enum = self.parse_coverage_()
+        enum = ast.Enum(self.parse_coverage_(), location=location)
         self.expect_keyword_("END_ENUM")
         return enum
 
@@ -503,55 +519,42 @@
         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)
+                coverage.append(ast.GlyphName(name, location=location))
             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)
+                coverage.append(ast.GroupName(name, self, location=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))
+                coverage.append(ast.Range(start, end, self, location=location))
         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)
+        return self.glyphs_.range(start, end)
 
     def parse_ppem_(self):
         location = self.cur_token_location_
         ppem_name = self.cur_token_
         value = self.expect_number_()
-        setting = ast.SettingDefinition(location, ppem_name, value)
+        setting = ast.SettingDefinition(ppem_name, value, location=location)
         return setting
 
-    def parse_compiler_flag_(self):
+    def parse_noarg_option_(self):
         location = self.cur_token_location_
-        flag_name = self.cur_token_
+        name = self.cur_token_
         value = True
-        setting = ast.SettingDefinition(location, flag_name, value)
+        setting = ast.SettingDefinition(name, value, location=location)
         return setting
 
     def parse_cmap_format(self):
@@ -559,7 +562,7 @@
         name = self.cur_token_
         value = (self.expect_number_(), self.expect_number_(),
                  self.expect_number_())
-        setting = ast.SettingDefinition(location, name, value)
+        setting = ast.SettingDefinition(name, value, location=location)
         return setting
 
     def is_cur_keyword_(self, k):
@@ -594,6 +597,8 @@
         self.cur_token_type_, self.cur_token_, self.cur_token_location_ = (
             self.next_token_type_, self.next_token_, self.next_token_location_)
         try:
+            if self.is_cur_keyword_("END"):
+                raise StopIteration
             (self.next_token_type_, self.next_token_,
              self.next_token_location_) = self.lexer_.next()
         except StopIteration:
@@ -627,10 +632,10 @@
 
 class OrderedSymbolTable(SymbolTable):
     def __init__(self):
-        self.scopes_ = [OrderedDict()]
+        self.scopes_ = [{}]
 
     def enter_scope(self):
-        self.scopes_.append(OrderedDict())
+        self.scopes_.append({})
 
     def resolve(self, name, case_insensitive=False):
         SymbolTable.resolve(self, name, case_insensitive=case_insensitive)
diff --git a/MANIFEST.in b/MANIFEST.in
index fa84292..8e2bcd1 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -11,7 +11,9 @@
 
 include *requirements.txt
 include tox.ini
-include run-tests.sh
+include mypy.ini
+
+recursive-include Lib/fontTools py.typed
 
 include .appveyor.yml
 include .codecov.yml
@@ -19,8 +21,13 @@
 include .travis.yml
 recursive-include .travis *.sh
 
+recursive-include Icons *.png *.pdf
+
 include Doc/Makefile
 include Doc/make.bat
+include Doc/docs-requirements.txt
+include Doc/README.md
+include Doc/source/assets/img/favicon.ico
 recursive-include Doc/man/man1 *.1
 recursive-include Doc/source *.py *.rst
 
@@ -31,3 +38,6 @@
 recursive-include Tests *.txt README
 recursive-include Tests *.lwfn *.pfa *.pfb
 recursive-include Tests *.xml *.designspace *.bin
+recursive-include Tests *.afm
+recursive-include Tests *.json
+recursive-include Tests *.ufoz
diff --git a/Makefile b/Makefile
index bd91d7f..21cad6c 100644
--- a/Makefile
+++ b/Makefile
@@ -14,9 +14,12 @@
 	pip uninstall --yes fonttools
 
 check: all
-	./run-tests.sh
+	pytest
 
 clean:
 	./setup.py clean --all
 
-.PHONY: all dist install install-user uninstall check clean
+docs:
+	cd Doc && $(MAKE) html
+
+.PHONY: all dist install install-user uninstall check clean docs
diff --git a/MetaTools/buildTableList.py b/MetaTools/buildTableList.py
index de8a039..c3766b9 100755
--- a/MetaTools/buildTableList.py
+++ b/MetaTools/buildTableList.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 
 import sys
 import os
@@ -11,7 +11,7 @@
 fontToolsDir= os.path.normpath(fontToolsDir)
 tablesDir = os.path.join(fontToolsDir,
 		"Lib", "fontTools", "ttLib", "tables")
-docFile = os.path.join(fontToolsDir, "README.rst")
+docFile = os.path.join(fontToolsDir, "Doc/source/ttx.rst")
 
 names = glob.glob1(tablesDir, "*.py")
 
@@ -30,12 +30,9 @@
 tables.sort()
 
 
-file = open(os.path.join(tablesDir, "__init__.py"), "w")
+with open(os.path.join(tablesDir, "__init__.py"), "w") as file:
 
-file.write('''
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-
+	file.write('''
 # DON'T EDIT! This file is generated by MetaTools/buildTableList.py.
 def _moduleFinderHint():
 	"""Dummy function to let modulefinder know what tables may be
@@ -45,29 +42,30 @@
 	"""
 ''')
 
-for module in modules:
-	file.write("\tfrom . import %s\n" % module)
+	for module in modules:
+		file.write("\tfrom . import %s\n" % module)
 
-file.write('''
+	file.write('''
 if __name__ == "__main__":
 	import doctest, sys
 	sys.exit(doctest.testmod().failed)
 ''')
 
-file.close()
 
-
-begin = ".. begin table list\n.. code::\n"
+begin = ".. begin table list\n"
 end = ".. end table list"
-doc = open(docFile).read()
+with open(docFile) as f:
+	doc = f.read()
 beginPos = doc.find(begin)
 assert beginPos > 0
 beginPos = beginPos + len(begin) + 1
 endPos = doc.find(end)
 
 lines = textwrap.wrap(", ".join(tables[:-1]) + " and " + tables[-1], 66)
+intro = "The following tables are currently supported::\n\n"
 blockquote = "\n".join(" "*4 + line for line in lines) + "\n"
 
-doc = doc[:beginPos] + blockquote + doc[endPos:]
+doc = doc[:beginPos] + intro + blockquote + "\n" + doc[endPos:]
 
-open(docFile, "w").write(doc)
+with open(docFile, "w") as f:
+	f.write(doc)
diff --git a/MetaTools/buildUCD.py b/MetaTools/buildUCD.py
index 12bd58f..16ae150 100755
--- a/MetaTools/buildUCD.py
+++ b/MetaTools/buildUCD.py
@@ -1,10 +1,8 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 """
 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
diff --git a/MetaTools/roundTrip.py b/MetaTools/roundTrip.py
index 122b39b..f9094ab 100755
--- a/MetaTools/roundTrip.py
+++ b/MetaTools/roundTrip.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 
 """usage: ttroundtrip [options] font1 ... fontN
 
@@ -31,9 +31,9 @@
 
 def roundTrip(ttFile1, options, report):
 	fn = os.path.basename(ttFile1)
-	xmlFile1 = tempfile.mktemp(".%s.ttx1" % fn)
-	ttFile2 = tempfile.mktemp(".%s" % fn)
-	xmlFile2 = tempfile.mktemp(".%s.ttx2" % fn)
+	xmlFile1 = tempfile.mkstemp(".%s.ttx1" % fn)
+	ttFile2 = tempfile.mkstemp(".%s" % fn)
+	xmlFile2 = tempfile.mkstemp(".%s.ttx2" % fn)
 	
 	try:
 		ttx.ttDump(ttFile1, xmlFile1, options)
@@ -74,23 +74,22 @@
 	if not files:
 		usage()
 	
-	report = open("report.txt", "a+")
-	options = ttx.Options(rawOptions, len(files))
-	for ttFile in files:
-		try:
-			roundTrip(ttFile, options, report)
-		except KeyboardInterrupt:
-			print("(Cancelled)")
-			break
-		except:
-			print("*** round tripping aborted ***")
-			traceback.print_exc()
-			report.write("=============================================================\n")
-			report.write("  An exception occurred while round tripping")
-			report.write("  \"%s\"\n" % ttFile)
-			traceback.print_exc(file=report)
-			report.write("-------------------------------------------------------------\n")
-	report.close()
+	with open("report.txt", "a+") as report:
+		options = ttx.Options(rawOptions, len(files))
+		for ttFile in files:
+			try:
+				roundTrip(ttFile, options, report)
+			except KeyboardInterrupt:
+				print("(Cancelled)")
+				break
+			except:
+				print("*** round tripping aborted ***")
+				traceback.print_exc()
+				report.write("=============================================================\n")
+				report.write("  An exception occurred while round tripping")
+				report.write("  \"%s\"\n" % ttFile)
+				traceback.print_exc(file=report)
+				report.write("-------------------------------------------------------------\n")
 
 	
 main(sys.argv[1:])
diff --git a/NEWS.rst b/NEWS.rst
index 7552b96..f220df9 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,3 +1,1332 @@
+4.26.2 (released 2021-08-09)
+----------------------------
+
+- [otTables] Added missing ``CompositeMode.PLUS`` operator (#2390).
+
+4.26.1 (released 2021-08-03)
+----------------------------
+
+- [transform] Added ``transformVector`` and ``transformVectors`` methods to the
+  ``Transform`` class. Similar to ``transformPoint`` but ignore the translation
+  part (#2386).
+
+4.26.0 (released 2021-08-03)
+----------------------------
+
+- [xmlWriter] Default to ``"\n"`` for ``newlinestr`` instead of platform-specific
+  ``os.linesep`` (#2384).
+- [otData] Define COLRv1 ClipList and ClipBox (#2379).
+- [removeOverlaps/instancer] Added --ignore-overlap-errors option to work around
+  Skia PathOps.Simplify bug (#2382, #2363, google/fonts#3365).
+- NOTE: This will be the last version to support Python 3.6. FontTools will require
+  Python 3.7 or above from the next release (#2350)
+
+4.25.2 (released 2021-07-26)
+----------------------------
+
+- [COLRv1] Various changes to sync with the latest CORLv1 draft spec. In particular:  
+  define COLR.VarIndexMap, remove/inline ColorIndex struct, add VarIndexBase to ``PaintVar*`` tables (#2372);  
+  add reduced-precicion specialized transform Paints;  
+  define Angle as fraction of half circle encoded as F2Dot14;  
+  use FWORD (int16) for all Paint center coordinates;  
+  change PaintTransform to have an offset to Affine2x3;  
+- [ttLib] when importing XML, only set sfntVersion if the font has no reader and is empty (#2376)
+
+4.25.1 (released 2021-07-16)
+----------------------------
+
+- [ttGlyphPen] Fixed bug in ``TTGlyphPointPen``, whereby open contours (i.e. starting
+  with segmentType "move") would throw ``NotImplementedError``. They are now treated
+  as if they are closed, like with the ``TTGlyphPen`` (#2364, #2366).
+
+4.25.0 (released 2021-07-05)
+----------------------------
+
+- [tfmLib] Added new library for parsing TeX Font Metric (TFM) files (#2354).
+- [TupleVariation] Make shared tuples order deterministic on python < 3.7 where
+  Counter (subclass of dict) doesn't remember insertion order (#2351, #2353).
+- [otData] Renamed COLRv1 structs to remove 'v1' suffix and match the updated draft
+  spec: 'LayerV1List' -> 'LayerList', 'BaseGlyphV1List' -> 'BaseGlyphList',
+  'BaseGlyphV1Record' -> 'BaseGlyphPaintRecord' (#2346).
+  Added 8 new ``PaintScale*`` tables: with/without centers, uniform vs non-uniform.
+  Added ``*AroundCenter`` variants to ``PaintRotate`` and ``PaintSkew``: the default
+  versions no longer have centerX/Y, but default to origin.
+  ``PaintRotate``, ``PaintSkew`` and ``PaintComposite`` formats were re-numbered.
+  NOTE: these are breaking changes; clients using the experimental COLRv1 API will
+  have to be updated (#2348).
+- [pointPens] Allow ``GuessSmoothPointPen`` to accept a tolerance. Fixed call to
+  ``math.atan2`` with x/y parameters inverted. Sync the code with fontPens (#2344).
+- [post] Fixed parsing ``post`` table format 2.0 when it contains extra garbage
+  at the end of the stringData array (#2314).
+- [subset] drop empty features unless 'size' with FeatureParams table (#2324).
+- [otlLib] Added ``otlLib.optimize`` module; added GPOS compaction algorithm.
+  The compaction can be run on existing fonts with ``fonttools otlLib.optimize``
+  or using the snippet ``compact_gpos.py``. There's experimental support for
+  compacting fonts at compilation time using an environment variable, but that
+  might be removed later (#2326).
+
+4.24.4 (released 2021-05-25)
+----------------------------
+
+- [subset/instancer] Fixed ``AttributeError`` when instantiating a VF that
+  contains GPOS ValueRecords with ``Device`` tables but without the respective
+  non-Device values (e.g. ``XAdvDevice`` without ``XAdvance``). When not
+  explicitly set, the latter are assumed to be 0 (#2323).
+
+4.24.3 (released 2021-05-20)
+----------------------------
+
+- [otTables] Fixed ``AttributeError`` in methods that split LigatureSubst,
+  MultipleSubst and AlternateSubst subtables when an offset overflow occurs.
+  The ``Format`` attribute was removed in v4.22.0 (#2319).
+
+4.24.2 (released 2021-05-20)
+----------------------------
+
+- [ttGlyphPen] Fixed typing annotation of TTGlyphPen glyphSet parameter (#2315).
+- Fixed two instances of DeprecationWarning: invalid escape sequence (#2311).
+
+4.24.1 (released 2021-05-20)
+----------------------------
+
+- [subset] Fixed AttributeError when SinglePos subtable has None Value (ValueFormat 0)
+  (#2312, #2313).
+
+4.24.0 (released 2021-05-17)
+----------------------------
+
+- [pens] Add ``ttGlyphPen.TTGlyphPointPen`` similar to ``TTGlyphPen`` (#2205).
+
+4.23.1 (released 2021-05-14)
+----------------------------
+
+- [subset] Fix ``KeyError`` after subsetting ``COLR`` table that initially contains
+  both v0 and v1 color glyphs when the subset only requested v1 glyphs; we were
+  not pruning the v0 portion of the table (#2308).
+- [colorLib] Set ``LayerV1List`` attribute to ``None`` when empty, it's optional
+  in CORLv1 (#2308).
+
+4.23.0 (released 2021-05-13)
+----------------------------
+
+- [designspaceLib] Allow to use ``\\UNC`` absolute paths on Windows (#2299, #2306).
+- [varLib.merger] Fixed bug where ``VarLibMergeError`` was raised with incorrect
+  parameters (#2300).
+- [feaLib] Allow substituting a glyph class with ``NULL`` to delete multiple glyphs
+  (#2303).
+- [glyf] Fixed ``NameError`` exception in ``getPhantomPoints`` (#2295, #2305).
+- [removeOverlaps] Retry pathops.simplify after rounding path coordinates to integers
+  if it fails the first time using floats, to work around a rare and hard to debug
+  Skia bug (#2288).
+- [varLib] Added support for building, reading, writing and optimizing 32-bit
+  ``ItemVariationStore`` as used in COLRv1 table (#2285).
+- [otBase/otConverters] Add array readers/writers for int types (#2285).
+- [feaLib] Allow more than one lookahead glyph/class in contextual positioning with
+  "value at end" (#2293, #2294).
+- [COLRv1] Default varIdx should be 0xFFFFFFFF (#2297, #2298).
+- [pens] Make RecordingPointPen actually pass on identifiers; replace asserts with
+  explicit ``PenError`` exception (#2284).
+- [mutator] Round lsb for CF2 fonts as well (#2286).
+
+4.22.1 (released 2021-04-26)
+----------------------------
+
+- [feaLib] Skip references to named lookups if the lookup block definition
+  is empty, similarly to makeotf. This also fixes an ``AttributeError`` while
+  generating ``aalt`` feature (#2276, #2277).
+- [subset] Fixed bug with ``--no-hinting`` implementation for Device tables (#2272,
+  #2275). The previous code was alwyas dropping Device tables if no-hinting was
+  requested, but some Device tables (DeltaFormat=0x8000) are also used to encode
+  variation indices and need to be retained.
+- [otBase] Fixed bug in getting the ValueRecordSize when decompiling ``MVAR``
+  table with ``lazy=True`` (#2273, #2274).
+- [varLib/glyf/gvar] Optimized and simplified ``GlyphCoordinates`` and
+  ``TupleVariation`` classes, use ``bytearray`` where possible, refactored
+  phantom-points calculations. We measured about 30% speedup in total time
+  of loading master ttfs, building gvar, and saving (#2261, #2266).
+- [subset] Fixed ``AssertionError`` while pruning unused CPAL palettes when
+  ``0xFFFF`` is present (#2257, #2259).
+
+4.22.0 (released 2021-04-01)
+----------------------------
+
+- [ttLib] Remove .Format from Coverage, ClassDef, SingleSubst, LigatureSubst,
+  AlternateSubst, MultipleSubst (#2238).
+  ATTENTION: This will change your TTX dumps!
+- [misc.arrayTools] move Vector to its own submodule, and rewrite as a tuple
+  subclass (#2201).
+- [docs] Added a terminology section for varLib (#2209).
+- [varLib] Move rounding to VariationModel, to avoid error accumulation from
+  multiple deltas (#2214)
+- [varLib] Explain merge errors in more human-friendly terms (#2223, #2226)
+- [otlLib] Correct some documentation (#2225)
+- [varLib/otlLib] Allow merging into VariationFont without first saving GPOS
+  PairPos2 (#2229)
+- [subset] Improve PairPosFormat2 subsetting (#2221)
+- [ttLib] TTFont.save: create file on disk as late as possible (#2253)
+- [cffLib] Add missing CFF2 dict operators LanguageGroup and ExpansionFactor
+  (#2249)
+  ATTENTION: This will change your TTX dumps!
+
+4.21.1 (released 2021-02-26)
+----------------------------
+
+- [pens] Reverted breaking change that turned ``AbstractPen`` and ``AbstractPointPen``
+  into abstract base classes (#2164, #2198).
+
+4.21.0 (released 2021-02-26)
+----------------------------
+
+- [feaLib] Indent anchor statements in ``asFea()`` to make them more legible and
+  diff-able (#2193).
+- [pens] Turn ``AbstractPen`` and ``AbstractPointPen`` into abstract base classes
+  (#2164).
+- [feaLib] Added support for parsing and building ``STAT`` table from AFDKO feature
+  files (#2039).
+- [instancer] Added option to update name table of generated instance using ``STAT``
+  table's axis values (#2189).
+- [bezierTools] Added functions to compute bezier point-at-time, as well as line-line,
+  curve-line and curve-curve intersections (#2192).
+
+4.20.0 (released 2021-02-15)
+----------------------------
+
+- [COLRv1] Added ``unbuildColrV1`` to deconstruct COLRv1 otTables to raw json-able
+  data structure; it does the reverse of ``buildColrV1`` (#2171).
+- [feaLib] Allow ``sub X by NULL`` sequence to delete a glyph (#2170).
+- [arrayTools] Fixed ``Vector`` division (#2173).
+- [COLRv1] Define new ``PaintSweepGradient`` (#2172).
+- [otTables] Moved ``Paint.Format`` enum class outside of ``Paint`` class definition,
+  now named ``PaintFormat``. It was clashing with paint instance ``Format`` attribute
+  and thus was breaking lazy load of COLR table which relies on magic ``__getattr__``
+  (#2175).
+- [COLRv1] Replace hand-coded builder functions with otData-driven dynamic
+  implementation (#2181).
+- [COLRv1] Define additional static (non-variable) Paint formats (#2181).
+- [subset] Added support for subsetting COLR v1 and CPAL tables (#2174, #2177).
+- [fontBuilder] Allow ``setupFvar`` to optionally take ``designspaceLib.AxisDescriptor``
+  objects. Added new ``setupAvar`` method. Support localised names for axes and
+  named instances (#2185).
+
+4.19.1 (released 2021-01-28)
+----------------------------
+
+- [woff2] An initial off-curve point with an overlap flag now stays an off-curve
+  point after compression.
+
+4.19.0 (released 2021-01-25)
+----------------------------
+
+- [codecs] Handle ``errors`` parameter different from 'strict' for the custom
+  extended mac encodings (#2137, #2132).
+- [featureVars] Raise better error message when a script is missing the required
+  default language system (#2154).
+- [COLRv1] Avoid abrupt change caused by rounding ``PaintRadialGradient.c0`` when
+  the start circle almost touches the end circle's perimeter (#2148).
+- [COLRv1] Support building unlimited lists of paints as 255-ary trees of
+  ``PaintColrLayers`` tables (#2153).
+- [subset] Prune redundant format-12 cmap subtables when all non-BMP characters
+  are dropped (#2146).
+- [basePen] Raise ``MissingComponentError`` instead of bare ``KeyError`` when a
+  referenced component is missing (#2145).
+
+4.18.2 (released 2020-12-16)
+----------------------------
+
+- [COLRv1] Implemented ``PaintTranslate`` paint format (#2129).
+- [varLib.cff] Fixed unbound local variable error (#1787).
+- [otlLib] Don't crash when creating OpenType class definitions if some glyphs
+  occur more than once (#2125).
+
+4.18.1 (released 2020-12-09)
+----------------------------
+
+- [colorLib] Speed optimization for ``LayerV1ListBuilder`` (#2119).
+- [mutator] Fixed missing tab in ``interpolate_cff2_metrics`` (0957dc7a).
+
+4.18.0 (released 2020-12-04)
+----------------------------
+
+- [COLRv1] Update to latest draft: added ``PaintRotate`` and ``PaintSkew`` (#2118).
+- [woff2] Support new ``brotlicffi`` bindings for PyPy (#2117).
+- [glifLib] Added ``expectContentsFile`` parameter to ``GlyphSet``, for use when
+  reading existing UFOs, to comply with the specification stating that a
+  ``contents.plist`` file must exist in a glyph set (#2114).
+- [subset] Allow ``LangSys`` tags in ``--layout-scripts`` option (#2112). For example:
+  ``--layout-scripts=arab.dflt,arab.URD,latn``; this will keep ``DefaultLangSys``
+  and ``URD`` language for ``arab`` script, and all languages for ``latn`` script.
+- [varLib.interpolatable] Allow UFOs to be checked; report open paths, non existant
+  glyphs; add a ``--json`` option to produce a machine-readable list of
+  incompatibilities
+- [pens] Added ``QuartzPen`` to create ``CGPath`` from glyph outlines on macOS.
+  Requires pyobjc (#2107).
+- [feaLib] You can export ``FONTTOOLS_LOOKUP_DEBUGGING=1`` to enable feature file
+  debugging info stored in ``Debg`` table (#2106).
+- [otlLib] Build more efficient format 1 and format 2 contextual lookups whenever
+  possible (#2101).
+
+4.17.1 (released 2020-11-16)
+----------------------------
+
+- [colorLib] Fixed regression in 4.17.0 when building COLR v0 table; when color
+  layers are stored in UFO lib plist, we can't distinguish tuples from lists so
+  we need to accept either types (e5439eb9, googlefonts/ufo2ft/issues#426).
+
+4.17.0 (released 2020-11-12)
+----------------------------
+
+- [colorLib/otData] Updated to latest draft ``COLR`` v1 spec (#2092).
+- [svgLib] Fixed parsing error when arc commands' boolean flags are not separated
+  by space or comma (#2094).
+- [varLib] Interpret empty non-default glyphs as 'missing', if the default glyph is
+  not empty (#2082).
+- [feaLib.builder] Only stash lookup location for ``Debg`` if ``Builder.buildLookups_``
+  has cooperated (#2065, #2067).
+- [varLib] Fixed bug in VarStore optimizer (#2073, #2083).
+- [varLib] Add designspace lib key for custom feavar feature tag (#2080).
+- Add HashPointPen adapted from psautohint. With this pen, a hash value of a glyph
+  can be computed, which can later be used to detect glyph changes (#2005).
+
+4.16.1 (released 2020-10-05)
+----------------------------
+
+- [varLib.instancer] Fixed ``TypeError`` exception when instantiating a VF with
+  a GSUB table 1.1 in which ``FeatureVariations`` attribute is present but set to
+  ``None`` -- indicating that optional ``FeatureVariations`` is missing (#2077).
+- [glifLib] Make ``x`` and ``y`` attributes of the ``point`` element required
+  even when validation is turned off, and raise a meaningful ``GlifLibError``
+  message when that happens (#2075).
+
+4.16.0 (released 2020-09-30)
+----------------------------
+
+- [removeOverlaps] Added new module and ``removeOverlaps`` function that merges
+  overlapping contours and components in TrueType glyphs. It requires the
+  `skia-pathops <https://github.com/fonttools/skia-pathops>`__ module.
+  Note that removing overlaps invalidates the TrueType hinting (#2068).
+- [varLib.instancer] Added ``--remove-overlaps`` command-line option.
+  The ``overlap`` option in ``instantiateVariableFont`` now takes an ``OverlapMode``
+  enum: 0: KEEP_AND_DONT_SET_FLAGS, 1: KEEP_AND_SET_FLAGS (default), and 2: REMOVE.
+  The latter is equivalent to calling ``removeOverlaps`` on the generated static
+  instance. The option continues to accept ``bool`` value for backward compatibility.
+
+
+4.15.0 (released 2020-09-21)
+----------------------------
+
+- [plistlib] Added typing annotations to plistlib module. Set up mypy static
+  typechecker to run automatically on CI (#2061).
+- [ttLib] Implement private ``Debg`` table, a reverse-DNS namespaced JSON dict.
+- [feaLib] Optionally add an entry into the ``Debg`` table with the original
+  lookup name (if any), feature name / script / language combination (if any),
+  and original source filename and line location. Annotate the ttx output for
+  a lookup with the information from the Debg table (#2052).
+- [sfnt] Disabled checksum checking by default in ``SFNTReader`` (#2058).
+- [Docs] Document ``mtiLib`` module (#2027).
+- [varLib.interpolatable] Added checks for contour node count and operation type
+  of each node (#2054).
+- [ttLib] Added API to register custom table packer/unpacker classes (#2055).
+
+4.14.0 (released 2020-08-19)
+----------------------------
+
+- [feaLib] Allow anonymous classes in LookupFlags definitions (#2037).
+- [Docs] Better document DesignSpace rules processing order (#2041).
+- [ttLib] Fixed 21-year old bug in ``maxp.maxComponentDepth`` calculation (#2044,
+  #2045).
+- [varLib.models] Fixed misspelled argument name in CLI entry point (81d0042a).
+- [subset] When subsetting GSUB v1.1, fixed TypeError by checking whether the
+  optional FeatureVariations table is present (e63ecc5b).
+- [Snippets] Added snippet to show how to decompose glyphs in a TTF (#2030).
+- [otlLib] Generate GSUB type 5 and GPOS type 7 contextual lookups where appropriate
+  (#2016).
+
+4.13.0 (released 2020-07-10)
+----------------------------
+
+- [feaLib/otlLib] Moved lookup subtable builders from feaLib to otlLib; refactored
+  some common code (#2004, #2007).
+- [docs] Document otlLib module (#2009).
+- [glifLib] Fixed bug with some UFO .glif filenames clashing on case-insensitive
+  filesystems (#2001, #2002).
+- [colorLib] Updated COLRv1 implementation following changes in the draft spec:
+  (#2008, googlefonts/colr-gradients-spec#24).
+
+4.12.1 (released 2020-06-16)
+----------------------------
+
+- [_n_a_m_e] Fixed error in ``addMultilingualName`` with one-character names.
+  Only attempt to recovered malformed UTF-16 data from a ``bytes`` string,
+  not from unicode ``str`` (#1997, #1998).
+
+4.12.0 (released 2020-06-09)
+----------------------------
+
+- [otlLib/varLib] Ensure that the ``AxisNameID`` in the ``STAT`` and ``fvar``
+  tables is grater than 255 as per OpenType spec (#1985, #1986).
+- [docs] Document more modules in ``fontTools.misc`` package: ``filenames``,
+  ``fixedTools``, ``intTools``, ``loggingTools``, ``macCreatorType``, ``macRes``,
+  ``plistlib`` (#1981).
+- [OS/2] Don't calculate whole sets of unicode codepoints, use faster and more memory
+  efficient ranges and bisect lookups (#1984).
+- [voltLib] Support writing back abstract syntax tree as VOLT data (#1983).
+- [voltLib] Accept DO_NOT_TOUCH_CMAP keyword (#1987).
+- [subset/merge] Fixed a namespace clash involving a private helper class (#1955).
+
+4.11.0 (released 2020-05-28)
+----------------------------
+
+- [feaLib] Introduced ``includeDir`` parameter on Parser and IncludingLexer to
+  explicitly specify the directory to search when ``include()`` statements are
+  encountered (#1973).
+- [ufoLib] Silently delete duplicate glyphs within the same kerning group when reading
+  groups (#1970).
+- [ttLib] Set version of COLR table when decompiling COLRv1 (commit 9d8a7e2).
+
+4.10.2 (released 2020-05-20)
+----------------------------
+
+- [sfnt] Fixed ``NameError: SimpleNamespace`` while reading TTC header. The regression
+  was introduced with 4.10.1 after removing ``py23`` star import.
+
+4.10.1 (released 2020-05-19)
+----------------------------
+
+- [sfnt] Make ``SFNTReader`` pickleable even when TTFont is loaded with lazy=True
+  option and thus keeps a reference to an external file (#1962, #1967).
+- [feaLib.ast] Restore backward compatibility (broken in 4.10 with #1905) for
+  ``ChainContextPosStatement`` and ``ChainContextSubstStatement`` classes.
+  Make them accept either list of lookups or list of lists of lookups (#1961).
+- [docs] Document some modules in ``fontTools.misc`` package: ``arrayTools``,
+  ``bezierTools`` ``cliTools`` and ``eexec`` (#1956).
+- [ttLib._n_a_m_e] Fixed ``findMultilingualName()`` when name record's ``string`` is
+  encoded as bytes sequence (#1963).
+
+4.10.0 (released 2020-05-15)
+----------------------------
+
+- [varLib] Allow feature variations to be active across the entire space (#1957).
+- [ufoLib] Added support for ``formatVersionMinor`` in UFO's ``fontinfo.plist`` and for
+  ``formatMinor`` attribute in GLIF file as discussed in unified-font-object/ufo-spec#78.
+  No changes in reading or writing UFOs until an upcoming (non-0) minor update of the
+  UFO specification is published (#1786).
+- [merge] Fixed merging fonts with different versions of ``OS/2`` table (#1865, #1952).
+- [subset] Fixed ``AttributeError`` while subsetting ``ContextSubst`` and ``ContextPos``
+  Format 3 subtable (#1879, #1944).
+- [ttLib.table._m_e_t_a] if data happens to be ascii, emit comment in TTX (#1938).
+- [feaLib] Support multiple lookups per glyph position (#1905).
+- [psCharStrings] Use inheritance to avoid repeated code in initializer (#1932).
+- [Doc] Improved documentation for the following modules: ``afmLib`` (#1933), ``agl``
+  (#1934), ``cffLib`` (#1935), ``cu2qu`` (#1937), ``encodings`` (#1940), ``feaLib``
+  (#1941), ``merge`` (#1949).
+- [Doc] Split off developer-centric info to new page, making front page of docs more
+  user-focused. List all utilities and sub-modules with brief descriptions.
+  Make README more concise and focused (#1914).
+- [otlLib] Add function to build STAT table from high-level description (#1926).
+- [ttLib._n_a_m_e] Add ``findMultilingualName()`` method (#1921).
+- [unicodedata] Update ``RTL_SCRIPTS`` for Unicode 13.0 (#1925).
+- [gvar] Sort ``gvar`` XML output by glyph name, not glyph order (#1907, #1908).
+- [Doc] Added help options to ``fonttools`` command line tool (#1913, #1920).
+  Ensure all fonttools CLI tools have help documentation (#1948).
+- [ufoLib] Only write fontinfo.plist when there actually is content (#1911).
+
+4.9.0 (released 2020-04-29)
+---------------------------
+
+- [subset] Fixed subsetting of FeatureVariations table. The subsetter no longer drops
+  FeatureVariationRecords that have empty substitutions as that will keep the search
+  going and thus change the logic. It will only drop empty records that occur at the
+  end of the FeatureVariationRecords array (#1881).
+- [subset] Remove FeatureVariations table and downgrade GSUB/GPOS to version 0x10000
+  when FeatureVariations contain no FeatureVariationRecords after subsetting (#1903).
+- [agl] Add support for legacy Adobe Glyph List of glyph names in ``fontTools.agl``
+  (#1895).
+- [feaLib] Ignore superfluous script statements (#1883).
+- [feaLib] Hide traceback by default on ``fonttools feaLib`` command line.
+  Use ``--traceback`` option to show (#1898).
+- [feaLib] Check lookup index in chaining sub/pos lookups and print better error
+  message (#1896, #1897).
+- [feaLib] Fix building chained alt substitutions (#1902).
+- [Doc] Included all fontTools modules in the sphinx-generated documentation, and
+  published it to ReadTheDocs for continuous documentation of the fontTools project
+  (#1333). Check it out at https://fonttools.readthedocs.io/. Thanks to Chris Simpkins!
+- [transform] The ``Transform`` class is now subclass of ``typing.NamedTuple``. No
+  change in functionality (#1904).
+
+
+4.8.1 (released 2020-04-17)
+---------------------------
+
+- [feaLib] Fixed ``AttributeError: 'NoneType' has no attribute 'getAlternateGlyphs'``
+  when ``aalt`` feature references a chain contextual substitution lookup
+  (googlefonts/fontmake#648, #1878).
+
+4.8.0 (released 2020-04-16)
+---------------------------
+
+- [feaLib] If Parser is initialized without a ``glyphNames`` parameter, it cannot
+  distinguish between a glyph name containing an hyphen, or a range of glyph names;
+  instead of raising an error, it now interprets them as literal glyph names, while
+  also outputting a logging warning to alert user about the ambiguity (#1768, #1870).
+- [feaLib] When serializing AST to string, emit spaces around hyphens that denote
+  ranges. Also, fixed an issue with CID ranges when round-tripping AST->string->AST
+  (#1872).
+- [Snippets/otf2ttf] In otf2ttf.py script update LSB in hmtx to match xMin (#1873).
+- [colorLib] Added experimental support for building ``COLR`` v1 tables as per
+  the `colr-gradients-spec <https://github.com/googlefonts/colr-gradients-spec/blob/master/colr-gradients-spec.md>`__
+  draft proposal. **NOTE**: both the API and the XML dump of ``COLR`` v1 are
+  susceptible to change while the proposal is being discussed and formalized (#1822).
+
+4.7.0 (released 2020-04-03)
+---------------------------
+
+- [cu2qu] Added ``fontTools.cu2qu`` package, imported from the original
+  `cu2qu <https://github.com/googlefonts/cu2qu>`__ project. The ``cu2qu.pens`` module
+  was moved to ``fontTools.pens.cu2quPen``. The optional cu2qu extension module
+  can be compiled by installing `Cython <https://cython.org/>`__ before installing
+  fonttools from source (i.e. git repo or sdist tarball). The wheel package that
+  is published on PyPI (i.e. the one ``pip`` downloads, unless ``--no-binary``
+  option is used), will continue to be pure-Python for now (#1868).
+
+4.6.0 (released 2020-03-24)
+---------------------------
+
+- [varLib] Added support for building variable ``BASE`` table version 1.1 (#1858).
+- [CPAL] Added ``fromRGBA`` method to ``Color`` class (#1861).
+
+
+4.5.0 (released 2020-03-20)
+---------------------------
+
+- [designspaceLib] Added ``add{Axis,Source,Instance,Rule}Descriptor`` methods to
+  ``DesignSpaceDocument`` class, to initialize new descriptor objects using keyword
+  arguments, and at the same time append them to the current document (#1860).
+- [unicodedata] Update to Unicode 13.0 (#1859).
+
+4.4.3 (released 2020-03-13)
+---------------------------
+
+- [varLib] Always build ``gvar`` table for TrueType-flavored Variable Fonts,
+  even if it contains no variation data. The table is required according to
+  the OpenType spec (#1855, #1857).
+
+4.4.2 (released 2020-03-12)
+---------------------------
+
+- [ttx] Annotate ``LookupFlag`` in XML dump with comment explaining what bits
+  are set and what they mean (#1850).
+- [feaLib] Added more descriptive message to ``IncludedFeaNotFound`` error (#1842).
+
+4.4.1 (released 2020-02-26)
+---------------------------
+
+- [woff2] Skip normalizing ``glyf`` and ``loca`` tables if these are missing from
+  a font (e.g. in NotoColorEmoji using ``CBDT/CBLC`` tables).
+- [timeTools] Use non-localized date parsing in ``timestampFromString``, to fix
+  error when non-English ``LC_TIME`` locale is set (#1838, #1839).
+- [fontBuilder] Make sure the CFF table generated by fontBuilder can be used by varLib
+  without having to compile and decompile the table first. This was breaking in
+  converting the CFF table to CFF2 due to some unset attributes (#1836).
+
+4.4.0 (released 2020-02-18)
+---------------------------
+
+- [colorLib] Added ``fontTools.colorLib.builder`` module, initially with ``buildCOLR``
+  and ``buildCPAL`` public functions. More color font formats will follow (#1827).
+- [fontBuilder] Added ``setupCOLR`` and ``setupCPAL`` methods (#1826).
+- [ttGlyphPen] Quantize ``GlyphComponent.transform`` floats to ``F2Dot14`` to fix
+  round-trip issue when computing bounding boxes of transformed components (#1830).
+- [glyf] If a component uses reference points (``firstPt`` and ``secondPt``) for
+  alignment (instead of X and Y offsets), compute the effective translation offset
+  *after* having applied any transform (#1831).
+- [glyf] When all glyphs have zero contours, compile ``glyf`` table data as a single
+  null byte in order to pass validation by OTS and Windows (#1829).
+- [feaLib] Parsing feature code now ensures that referenced glyph names are part of
+  the known glyph set, unless a glyph set was not provided.
+- [varLib] When filling in the default axis value for a missing location of a source or
+  instance, correctly map the value forward.
+- [varLib] The avar table can now contain mapping output values that are greater than
+  OR EQUAL to the preceeding value, as the avar specification allows this.
+- [varLib] The errors of the module are now ordered hierarchically below VarLibError.
+  See #1821.
+
+4.3.0 (released 2020-02-03)
+---------------------------
+
+- [EBLC/CBLC] Fixed incorrect padding length calculation for Format 3 IndexSubTable
+  (#1817, #1818).
+- [varLib] Fixed error when merging OTL tables and TTFonts were loaded as ``lazy=True``
+  (#1808, #1809).
+- [varLib] Allow to use master fonts containing ``CFF2`` table when building VF (#1816).
+- [ttLib] Make ``recalcBBoxes`` option work also with ``CFF2`` table (#1816).
+- [feaLib] Don't reset ``lookupflag`` in lookups defined inside feature blocks.
+  They will now inherit the current ``lookupflag`` of the feature. This is what
+  Adobe ``makeotf`` also does in this case (#1815).
+- [feaLib] Fixed bug with mixed single/multiple substitutions. If a single substitution
+  involved a glyph class, we were incorrectly using only the first glyph in the class
+  (#1814).
+
+4.2.5 (released 2020-01-29)
+---------------------------
+
+- [feaLib] Do not fail on duplicate multiple substitutions, only warn (#1811).
+- [subset] Optimize SinglePos subtables to Format 1 if all ValueRecords are the same
+  (#1802).
+
+4.2.4 (released 2020-01-09)
+---------------------------
+
+- [unicodedata] Update RTL_SCRIPTS for Unicode 11 and 12.
+
+4.2.3 (released 2020-01-07)
+---------------------------
+
+- [otTables] Fixed bug when splitting `MarkBasePos` subtables as offsets overflow.
+  The mark class values in the split subtable were not being updated, leading to
+  invalid mark-base attachments (#1797, googlefonts/noto-source#145).
+- [feaLib] Only log a warning instead of error when features contain duplicate
+  substitutions (#1767).
+- [glifLib] Strip XML comments when parsing with lxml (#1784, #1785).
+
+4.2.2 (released 2019-12-12)
+---------------------------
+
+- [subset] Fixed issue with subsetting FeatureVariations table when the index
+  of features changes as features get dropped. The feature index need to be
+  remapped to point to index of the remaining features (#1777, #1782).
+- [fontBuilder] Added `addFeatureVariations` method to `FontBuilder` class. This
+  is a shorthand for calling `featureVars.addFeatureVariations` on the builder's
+  TTFont object (#1781).
+- [glyf] Fixed the flags bug in glyph.drawPoints() like we did for glyph.draw()
+  (#1771, #1774).
+
+4.2.1 (released 2019-12-06)
+---------------------------
+
+- [glyf] Use the ``flagOnCurve`` bit mask in ``glyph.draw()``, so that we ignore
+  the ``overlap`` flag that may be set when instantiating variable fonts (#1771).
+
+4.2.0 (released 2019-11-28)
+---------------------------
+
+- [pens] Added the following pens:
+
+  * ``roundingPen.RoundingPen``: filter pen that rounds coordinates and components'
+    offsets to integer;
+  * ``roundingPen.RoundingPointPen``: like the above, but using PointPen protocol.
+  * ``filterPen.FilterPointPen``: base class for filter point pens;
+  * ``transformPen.TransformPointPen``: filter point pen to apply affine transform;
+  * ``recordingPen.RecordingPointPen``: records and replays point-pen commands.
+
+- [ttGlyphPen] Always round float coordinates and component offsets to integers
+  (#1763).
+- [ufoLib] When converting kerning groups from UFO2 to UFO3, avoid confusing
+  groups with the same name as one of the glyphs (#1761, #1762,
+  unified-font-object/ufo-spec#98).
+
+4.1.0 (released 2019-11-18)
+---------------------------
+
+- [instancer] Implemented restricting axis ranges (level 3 partial instancing).
+  You can now pass ``{axis_tag: (min, max)}`` tuples as input to the
+  ``instantiateVariableFont`` function. Note that changing the default axis
+  position is not supported yet. The command-line script also accepts axis ranges
+  in the form of colon-separated float values, e.g. ``wght=400:700`` (#1753, #1537).
+- [instancer] Never drop STAT ``DesignAxis`` records, but only prune out-of-range
+  ``AxisValue`` records.
+- [otBase/otTables] Enforce that VarStore.RegionAxisCount == fvar.axisCount, even
+  when regions list is empty to appease OTS < v8.0 (#1752).
+- [designspaceLib] Defined new ``processing`` attribute for ``<rules>`` element,
+  with values "first" or "last", plus other editorial changes to DesignSpace
+  specification. Bumped format version to 4.1 (#1750).
+- [varLib] Improved error message when masters' glyph orders do not match (#1758,
+  #1759).
+- [featureVars] Allow to specify custom feature tag in ``addFeatureVariations``;
+  allow said feature to already exist, in which case we append new lookup indices
+  to existing features. Implemented ``<rules>`` attribute ``processing`` according to
+  DesignSpace specification update in #1750. Depending on this flag, we generate
+  either an 'rvrn' (always processed first) or a 'rclt' feature (follows lookup order,
+  therefore last) (#1747, #1625, #1371).
+- [ttCollection] Added support for context manager auto-closing via ``with`` statement
+  like with ``TTFont`` (#1751).
+- [unicodedata] Require unicodedata2 >= 12.1.0.
+- [py2.py3] Removed yet more PY2 vestiges (#1743).
+- [_n_a_m_e] Fixed issue when comparing NameRecords with different string types (#1742).
+- [fixedTools] Changed ``fixedToFloat`` to not do any rounding but simply return
+  ``value / (1 << precisionBits)``. Added ``floatToFixedToStr`` and
+  ``strToFixedToFloat`` functions to be used when loading from or dumping to XML.
+  Fixed values (e.g. fvar axes and instance coordinates, avar mappings, etc.) are
+  are now stored as un-rounded decimal floats upon decompiling (#1740, #737).
+- [feaLib] Fixed handling of multiple ``LigatureCaret`` statements for the same glyph.
+  Only the first rule per glyph is used, additional ones are ignored (#1733).
+
+4.0.2 (released 2019-09-26)
+---------------------------
+
+- [voltLib] Added support for ``ALL`` and ``NONE`` in ``PROCESS_MARKS`` (#1732).
+- [Silf] Fixed issue in ``Silf`` table compilation and decompilation regarding str vs
+  bytes in python3 (#1728).
+- [merge] Handle duplicate glyph names better: instead of appending font index to
+  all glyph names, use similar code like we use in ``post`` and ``CFF`` tables (#1729).
+
+4.0.1 (released 2019-09-11)
+---------------------------
+
+- [otTables] Support fixing offset overflows in ``MultipleSubst`` lookup subtables
+  (#1706).
+- [subset] Prune empty strikes in ``EBDT`` and ``CBDT`` table data (#1698, #1633).
+- [pens] Fixed issue in ``PointToSegmentPen`` when last point of closed contour has
+  same coordinates as the starting point and was incorrectly dropped (#1720).
+- [Graphite] Fixed ``Sill`` table output to pass OTS (#1705).
+- [name] Added ``removeNames`` method to ``table__n_a_m_e`` class (#1719).
+- [ttLib] Added aliases for renamed entries ``ascender`` and ``descender`` in
+  ``hhea`` table (#1715).
+
+4.0.0 (released 2019-08-22)
+---------------------------
+
+- NOTE: The v4.x version series only supports Python 3.6 or greater. You can keep
+  using fonttools 3.x if you need support for Python 2.
+- [py23] Removed all the python2-only code since it is no longer reachable, thus
+  unused; only the Python3 symbols were kept, but these are no-op. The module is now
+  DEPRECATED and will removed in the future.
+- [ttLib] Fixed UnboundLocalError for empty loca/glyph tables (#1680). Also, allow
+  the glyf table to be incomplete when dumping to XML (#1681).
+- [varLib.models] Fixed KeyError while sorting masters and there are no on-axis for
+  a given axis (38a8eb0e).
+- [cffLib] Make sure glyph names are unique (#1699).
+- [feaLib] Fix feature parser to correctly handle octal numbers (#1700).
+
+3.44.0 (released 2019-08-02)
+----------------------------
+
+- NOTE: This is the last scheduled release to support Python 2.7. The upcoming fonttools
+  v4.x series is going to require Python 3.6 or greater.
+- [varLib] Added new ``varLib.instancer`` module for partially instantiating variable
+  fonts. This extends (and will eventually replace) ``varLib.mutator`` module, as
+  it allows to create not just full static instances from a variable font, but also
+  "partial" or "less variable" fonts where some of the axes are dropped or
+  instantiated at a particular value.
+  Also available from the command-line as `fonttools varLib.instancer --help`
+  (#1537, #1628).
+- [cffLib] Added support for ``FDSelect`` format 4 (#1677).
+- [subset] Added support for subsetting ``sbix`` (Apple bitmap color font) table.
+- [t1Lib] Fixed issue parsing ``eexec`` section in Type1 fonts when whitespace
+  characters are interspersed among the trailing zeros (#1676).
+- [cffLib.specializer] Fixed bug in ``programToCommands`` with CFF2 charstrings (#1669).
+
+3.43.2 (released 2019-07-10)
+----------------------------
+
+- [featureVars] Fixed region-merging code on python3 (#1659).
+- [varLib.cff] Fixed merging of sparse PrivateDict items (#1653).
+
+3.43.1 (released 2019-06-19)
+----------------------------
+
+- [subset] Fixed regression when passing ``--flavor=woff2`` option with an input font
+  that was already compressed as WOFF 1.0 (#1650).
+
+3.43.0 (released 2019-06-18)
+----------------------------
+
+- [woff2] Added support for compressing/decompressing WOFF2 fonts with non-transformed
+  ``glyf`` and ``loca`` tables, as well as with transformed ``hmtx`` table.
+  Removed ``Snippets/woff2_compress.py`` and ``Snippets/woff2_decompress.py`` scripts,
+  and replaced them with a new console entry point ``fonttools ttLib.woff2``
+  that provides two sub-commands ``compress`` and ``decompress``.
+- [varLib.cff] Fixed bug when merging CFF2 ``PrivateDicts``. The ``PrivateDict``
+  data from the first region font was incorrecty used for all subsequent fonts.
+  The bug would only affect variable CFF2 fonts with hinting (#1643, #1644).
+  Also, fixed a merging bug when VF masters have no blends or marking glyphs (#1632,
+  #1642).
+- [loggingTools] Removed unused backport of ``LastResortLogger`` class.
+- [subset] Gracefully handle partial MATH table (#1635).
+- [featureVars] Avoid duplicate references to ``rvrn`` feature record in
+  ``DefaultLangSys`` tables when calling ``addFeatureVariations`` on a font that
+  does not already have a ``GSUB`` table (aa8a5bc6).
+- [varLib] Fixed merging of class-based kerning. Before, the process could introduce
+  rogue kerning values and variations for random classes against class zero (everything
+  not otherwise classed).
+- [varLib] Fixed merging GPOS tables from master fonts with different number of
+  ``SinglePos`` subtables (#1621, #1641).
+- [unicodedata] Updated Blocks, Scripts and ScriptExtensions to Unicode 12.1.
+
+3.42.0 (released 2019-05-28)
+----------------------------
+
+- [OS/2] Fixed sign of ``fsType``: it should be ``uint16``, not ``int16`` (#1619).
+- [subset] Skip out-of-range class values in mark attachment (#1478).
+- [fontBuilder] Add an empty ``DSIG`` table with ``setupDummyDSIG`` method (#1621).
+- [varLib.merger] Fixed bug whereby ``GDEF.GlyphClassDef`` were being dropped
+  when generating instance via ``varLib.mutator`` (#1614).
+- [varLib] Added command-line options ``-v`` and ``-q`` to configure logging (#1613).
+- [subset] Update font extents in head table (#1612).
+- [subset] Make --retain-gids truncate empty glyphs after the last non-empty glyph
+  (#1611).
+- [requirements] Updated ``unicodedata2`` backport for Unicode 12.0.
+
+3.41.2 (released 2019-05-13)
+----------------------------
+
+- [cffLib] Fixed issue when importing a ``CFF2`` variable font from XML, whereby
+  the VarStore state was not propagated to PrivateDict (#1598).
+- [varLib] Don't drop ``post`` glyph names when building CFF2 variable font (#1609).
+
+
+3.41.1 (released 2019-05-13)
+----------------------------
+
+- [designspaceLib] Added ``loadSourceFonts`` method to load source fonts using
+  custom opener function (#1606).
+- [head] Round font bounding box coordinates to integers to fix compile error
+  if CFF font has float coordinates (#1604, #1605).
+- [feaLib] Don't write ``None`` in ``ast.ValueRecord.asFea()`` (#1599).
+- [subset] Fixed issue ``AssertionError`` when using ``--desubroutinize`` option
+  (#1590, #1594).
+- [graphite] Fixed bug in ``Silf`` table's ``decompile`` method unmasked by
+  previous typo fix (#1597). Decode languange code as UTF-8 in ``Sill`` table's
+  ``decompile`` method (#1600).
+
+3.41.0 (released 2019-04-29)
+----------------------------
+
+- [varLib/cffLib] Added support for building ``CFF2`` variable font from sparse
+  masters, or masters with more than one model (multiple ``VarStore.VarData``).
+  In ``cffLib.specializer``, added support for ``CFF2`` CharStrings with
+  ``blend`` operators (#1547, #1591).
+- [subset] Fixed subsetting ``HVAR`` and ``VVAR`` with ``--retain-gids`` option,
+  and when advances mapping is null while sidebearings mappings are non-null
+  (#1587, #1588).
+- Added ``otlLib.maxContextCalc`` module to compute ``OS/2.usMaxContext`` value.
+  Calculate it automatically when compiling features with feaLib. Added option
+  ``--recalc-max-context`` to ``subset`` module (#1582).
+- [otBase/otTables] Fixed ``AttributeError`` on missing OT table fields after
+  importing font from TTX (#1584).
+- [graphite] Fixed typo ``Silf`` table's ``decompile`` method (#1586).
+- [otlLib] Better compress ``GPOS`` SinglePos (LookupType 1) subtables (#1539).
+
+3.40.0 (released 2019-04-08)
+----------------------------
+
+- [subset] Fixed error while subsetting ``VVAR`` with ``--retain-gids``
+  option (#1552).
+- [designspaceLib] Use up-to-date default location in ``findDefault`` method
+  (#1554).
+- [voltLib] Allow passing file-like object to Parser.
+- [arrayTools/glyf] ``calcIntBounds`` (used to compute bounding boxes of glyf
+  table's glyphs) now uses ``otRound`` instead of ``round3`` (#1566).
+- [svgLib] Added support for converting more SVG shapes to path ``d`` strings
+  (ellipse, line, polyline), as well as support for ``transform`` attributes.
+  Only ``matrix`` transformations are currently supported (#1564, #1564).
+- [varLib] Added support for building ``VVAR`` table from ``vmtx`` and ``VORG``
+  tables (#1551).
+- [fontBuilder] Enable making CFF2 fonts with ``post`` table format 2 (#1557).
+- Fixed ``DeprecationWarning`` on invalid escape sequences (#1562).
+
+3.39.0 (released 2019-03-19)
+----------------------------
+
+- [ttLib/glyf] Raise more specific error when encountering recursive
+  component references (#1545, #1546).
+- [Doc/designspaceLib] Defined new ``public.skipExportGlyphs`` lib key (#1534,
+  unified-font-object/ufo-spec#84).
+- [varLib] Use ``vmtx`` to compute vertical phantom points; or ``hhea.ascent``
+  and ``head.unitsPerEM`` if ``vmtx`` is missing (#1528).
+- [gvar/cvar] Sort XML element's min/value/max attributes in TupleVariation
+  toXML to improve readability of TTX dump (#1527).
+- [varLib.plot] Added support for 2D plots with only 1 variation axis (#1522).
+- [designspaceLib] Use axes maps when normalizing locations in
+  DesignSpaceDocument (#1226, #1521), and when finding default source (#1535).
+- [mutator] Set ``OVERLAP_SIMPLE`` and ``OVERLAP_COMPOUND`` glyf flags by
+  default in ``instantiateVariableFont``. Added ``--no-overlap`` cli option
+  to disable this (#1518).
+- [subset] Fixed subsetting ``VVAR`` table (#1516, #1517).
+  Fixed subsetting an ``HVAR`` table that has an ``AdvanceWidthMap`` when the
+  option ``--retain-gids`` is used.
+- [feaLib] Added ``forceChained`` in MultipleSubstStatement (#1511).
+  Fixed double indentation of ``subtable`` statement (#1512).
+  Added support for ``subtable`` statement in more places than just PairPos
+  lookups (#1520).
+  Handle lookupflag 0 and lookupflag without a value (#1540).
+- [varLib] In ``load_designspace``, provide a default English name for the
+  ``ital`` axis tag.
+- Remove pyftinspect because it is unmaintained and bitrotted.
+
+3.38.0 (released 2019-02-18)
+----------------------------
+
+- [cffLib] Fixed RecursionError when unpickling or deepcopying TTFont with
+  CFF table (#1488, 649dc49).
+- [subset] Fixed AttributeError when using --desubroutinize option (#1490).
+  Also, fixed desubroutinizing bug when subrs contain hints (#1499).
+- [CPAL] Make Color a subclass of namedtuple (173a0f5).
+- [feaLib] Allow hyphen in glyph class names.
+- [feaLib] Added 'tables' option to __main__.py (#1497).
+- [feaLib] Add support for special-case contextual positioning formatting
+  (#1501).
+- [svgLib] Support converting SVG basic shapes (rect, circle, etc.) into
+  equivalent SVG paths (#1500, #1508).
+- [Snippets] Added name-viewer.ipynb Jupyter notebook.
+
+
+3.37.3 (released 2019-02-05)
+----------------------------
+
+- The previous release accidentally changed several files from Unix to DOS
+  line-endings. Fix that.
+
+3.37.2 (released 2019-02-05)
+----------------------------
+
+- [varLib] Temporarily revert the fix to ``load_masters()``, which caused a
+  crash in ``interpolate_layout()`` when ``deepcopy``-ing OTFs.
+
+3.37.1 (released 2019-02-05)
+----------------------------
+
+- [varLib] ``load_masters()`` now actually assigns the fonts it loads to the
+  source.font attributes.
+- [varLib] Fixed an MVAR table generation crash when sparse masters were
+  involved.
+- [voltLib] ``parse_coverage_()`` returns a tuple instead of an ast.Enum.
+- [feaLib] A MarkClassDefinition inside a block is no longer doubly indented
+  compared to the rest of the block.
+
+3.37.0 (released 2019-01-28)
+----------------------------
+
+- [svgLib] Added support for converting elliptical arcs to cubic bezier curves
+  (#1464).
+- [py23] Added backport for ``math.isfinite``.
+- [varLib] Apply HIDDEN flag to fvar axis if designspace axis has attribute
+  ``hidden=1``.
+- Fixed "DeprecationWarning: invalid escape sequence" in Python 3.7.
+- [voltLib] Fixed parsing glyph groups. Distinguish different PROCESS_MARKS.
+  Accept COMPONENT glyph type.
+- [feaLib] Distinguish missing value and explicit ``<NULL>`` for PairPos2
+  format A (#1459). Round-trip ``useExtension`` keyword. Implemented
+  ``ValueRecord.asFea`` method.
+- [subset] Insert empty widths into hdmx when retaining gids (#1458).
+
+3.36.0 (released 2019-01-17)
+----------------------------
+
+- [ttx] Added ``--no-recalc-timestamp`` option to keep the original font's
+  ``head.modified`` timestamp (#1455, #46).
+- [ttx/psCharStrings] Fixed issues while dumping and round-tripping CFF2 table
+  with ttx (#1451, #1452, #1456).
+- [voltLib] Fixed check for duplicate anchors (#1450). Don't try to read past
+  the ``END`` operator in .vtp file (#1453).
+- [varLib] Use sentinel value -0x8000 (-32768) to ignore post.underlineThickness
+  and post.underlinePosition when generating MVAR deltas (#1449,
+  googlei18n/ufo2ft#308).
+- [subset] Added ``--retain-gids`` option to subset font without modifying the
+  current glyph indices (#1443, #1447).
+- [ufoLib] Replace deprecated calls to ``getbytes`` and ``setbytes`` with new
+  equivalent ``readbytes`` and ``writebytes`` calls. ``fs`` >= 2.2 no required.
+- [varLib] Allow loading masters from TTX files as well (#1441).
+
+3.35.2 (released 2019-01-14)
+----------------------------
+
+- [hmtx/vmtx]: Allow to compile/decompile ``hmtx`` and ``vmtx`` tables even
+  without the corresponding (required) metrics header tables, ``hhea`` and
+  ``vhea`` (#1439).
+- [varLib] Added support for localized axes' ``labelname`` and named instances'
+  ``stylename`` (#1438).
+
+3.35.1 (released 2019-01-09)
+----------------------------
+
+- [_m_a_x_p] Include ``maxComponentElements`` in ``maxp`` table's recalculation.
+
+3.35.0 (released 2019-01-07)
+----------------------------
+
+- [psCharStrings] In ``encodeFloat`` function, use float's "general format" with
+  8 digits of precision (i.e. ``%8g``) instead of ``str()``. This works around
+  a macOS rendering issue when real numbers in CFF table are too long, and
+  also makes sure that floats are encoded with the same precision in python 2.7
+  and 3.x (#1430, googlei18n/ufo2ft#306).
+- [_n_a_m_e/fontBuilder] Make ``_n_a_m_e_table.addMultilingualName`` also add
+  Macintosh (platformID=1) names by default. Added options to ``FontBuilder``
+  ``setupNameTable`` method to optionally disable Macintosh or Windows names.
+  (#1359, #1431).
+- [varLib] Make ``build`` optionally accept a ``DesignSpaceDocument`` object,
+  instead of a designspace file path. The caller can now set the ``font``
+  attribute of designspace's sources to a TTFont object, thus allowing to
+  skip filenames manipulation altogether (#1416, #1425).
+- [sfnt] Allow SFNTReader objects to be deep-copied.
+- Require typing>=3.6.4 on py27 to fix issue with singledispatch (#1423).
+- [designspaceLib/t1Lib/macRes] Fixed some cases where pathlib.Path objects were
+  not accepted (#1421).
+- [varLib] Fixed merging of multiple PairPosFormat2 subtables (#1411).
+- [varLib] The default STAT table version is now set to 1.1, to improve
+  compatibility with legacy applications (#1413).
+
+3.34.2 (released 2018-12-17)
+----------------------------
+
+- [merge] Fixed AssertionError when none of the script tables in GPOS/GSUB have
+  a DefaultLangSys record (#1408, 135a4a1).
+
+3.34.1 (released 2018-12-17)
+----------------------------
+
+- [varLib] Work around macOS rendering issue for composites without gvar entry (#1381).
+
+3.34.0 (released 2018-12-14)
+----------------------------
+
+- [varLib] Support generation of CFF2 variable fonts. ``model.reorderMasters()``
+  now supports arbitrary mapping. Fix handling of overlapping ranges for feature
+  variations (#1400).
+- [cffLib, subset] Code clean-up and fixing related to CFF2 support.
+- [ttLib.tables.ttProgram] Use raw strings for regex patterns (#1389).
+- [fontbuilder] Initial support for building CFF2 fonts. Set CFF's
+  ``FontMatrix`` automatically from unitsPerEm.
+- [plistLib] Accept the more general ``collections.Mapping`` instead of the
+  specific ``dict`` class to support custom data classes that should serialize
+  to dictionaries.
+
+3.33.0 (released 2018-11-30)
+----------------------------
+- [subset] subsetter bug fix with variable fonts.
+- [varLib.featureVar] Improve FeatureVariations generation with many rules.
+- [varLib] Enable sparse masters when building variable fonts:
+  https://github.com/fonttools/fonttools/pull/1368#issuecomment-437257368
+- [varLib.mutator] Add IDEF for GETVARIATION opcode, for handling hints in an
+  instance.
+- [ttLib] Ignore the length of kern table subtable format 0
+
+3.32.0 (released 2018-11-01)
+----------------------------
+
+- [ufoLib] Make ``UFOWriter`` a subclass of ``UFOReader``, and use mixins
+  for shared methods (#1344).
+- [featureVars] Fixed normalization error when a condition's minimum/maximum
+  attributes are missing in designspace ``<rule>`` (#1366).
+- [setup.py] Added ``[plot]`` to extras, to optionally install ``matplotlib``,
+  needed to use the ``fonTools.varLib.plot`` module.
+- [varLib] Take total bounding box into account when resolving model (7ee81c8).
+  If multiple axes have the same range ratio, cut across both (62003f4).
+- [subset] Don't error if ``STAT`` has no ``AxisValue`` tables.
+- [fontBuilder] Added a new submodule which contains a ``FontBuilder`` wrapper
+  class around ``TTFont`` that makes it easier to create a working TTF or OTF
+  font from scratch with code. NOTE: the API is still experimental and may
+  change in future versions.
+
+3.31.0 (released 2018-10-21)
+----------------------------
+
+- [ufoLib] Merged the `ufoLib <https://github.com/unified-font-objects/ufoLib>`__
+  master branch into a new ``fontTools.ufoLib`` package (#1335, #1095).
+  Moved ``ufoLib.pointPen`` module to ``fontTools.pens.pointPen``.
+  Moved ``ufoLib.etree`` module to ``fontTools.misc.etree``.
+  Moved ``ufoLib.plistlib`` module to ``fontTools.misc.plistlib``.
+  To use the new ``fontTools.ufoLib`` module you need to install fonttools
+  with the ``[ufo]`` extra, or you can manually install the required additional
+  dependencies (cf. README.rst).
+- [morx] Support AAT action type to insert glyphs and clean up compilation
+  of AAT action tables (4a1871f, 2011ccf).
+- [subset] The ``--no-hinting`` on a CFF font now also drops the optional
+  hinting keys in Private dict: ``ForceBold``, ``LanguageGroup``, and
+  ``ExpansionFactor`` (#1322).
+- [subset] Include nameIDs referenced by STAT table (#1327).
+- [loggingTools] Added ``msg=None`` argument to
+  ``CapturingLogHandler.assertRegex`` (0245f2c).
+- [varLib.mutator] Implemented ``FeatureVariations`` instantiation (#1244).
+- [g_l_y_f] Added PointPen support to ``_TTGlyph`` objects (#1334).
+
+3.30.0 (released 2018-09-18)
+----------------------------
+
+- [feaLib] Skip building noop class PairPos subtables when Coverage is NULL
+  (#1318).
+- [ttx] Expose the previously reserved bit flag ``OVERLAP_SIMPLE`` of
+  glyf table's contour points in the TTX dump. This is used in some
+  implementations to specify a non-zero fill with overlapping contours (#1316).
+- [ttLib] Added support for decompiling/compiling ``TS1C`` tables containing
+  VTT sources for ``cvar`` variation table (#1310).
+- [varLib] Use ``fontTools.designspaceLib`` to read DesignSpaceDocument. The
+  ``fontTools.varLib.designspace`` module is now deprecated and will be removed
+  in future versions. The presence of an explicit ``axes`` element is now
+  required in order to build a variable font (#1224, #1313).
+- [varLib] Implemented building GSUB FeatureVariations table from the ``rules``
+  element of DesignSpace document (#1240, #713, #1314).
+- [subset] Added ``--no-layout-closure`` option to not expand the subset with
+  the glyphs produced by OpenType layout features. Instead, OpenType features
+  will be subset to only rules that are relevant to the otherwise-specified
+  glyph set (#43, #1121).
+
+3.29.1 (released 2018-09-10)
+----------------------------
+
+- [feaLib] Fixed issue whereby lookups from DFLT/dflt were not included in the
+  DFLT/non-dflt language systems (#1307).
+- [graphite] Fixed issue on big-endian architectures (e.g. ppc64) (#1311).
+- [subset] Added ``--layout-scripts`` option to add/exclude set of OpenType
+  layout scripts that will be preserved. By default all scripts are retained
+  (``'*'``) (#1303).
+
+3.29.0 (released 2018-07-26)
+----------------------------
+
+- [feaLib] In the OTL table builder, when the ``name`` table is excluded
+  from the list of tables to be build, skip compiling ``featureNames`` blocks,
+  as the records referenced in ``FeatureParams`` table don't exist (68951b7).
+- [otBase] Try ``ExtensionLookup`` if other offset-overflow methods fail
+  (05f95f0).
+- [feaLib] Added support for explicit ``subtable;`` break statements in
+  PairPos lookups; previously these were ignored (#1279, #1300, #1302).
+- [cffLib.specializer] Make sure the stack depth does not exceed maxstack - 1,
+  so that a subroutinizer can insert subroutine calls (#1301,
+  https://github.com/googlei18n/ufo2ft/issues/266).
+- [otTables] Added support for fixing offset overflow errors occurring inside
+  ``MarkBasePos`` subtables (#1297).
+- [subset] Write the default output file extension based on ``--flavor`` option,
+  or the value of ``TTFont.sfntVersion`` (d7ac0ad).
+- [unicodedata] Updated Blocks, Scripts and ScriptExtensions for Unicode 11
+  (452c85e).
+- [xmlWriter] Added context manager to XMLWriter class to autoclose file
+  descriptor on exit (#1290).
+- [psCharStrings] Optimize the charstring's bytecode by encoding as integers
+  all float values that have no decimal portion (8d7774a).
+- [ttFont] Fixed missing import of ``TTLibError`` exception (#1285).
+- [feaLib] Allow any languages other than ``dflt`` under ``DFLT`` script
+  (#1278, #1292).
+
+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)
 ----------------------------
 
diff --git a/README.rst b/README.rst
index 08ef951..78f422e 100644
--- a/README.rst
+++ b/README.rst
@@ -1,5 +1,4 @@
-|Travis Build Status| |Appveyor Build status| |Health| |Coverage Status|
-|PyPI|
+|CI Build Status| |Coverage Status| |PyPI| |Gitter Chat|
 
 What is this?
 ~~~~~~~~~~~~~
@@ -8,15 +7,25 @@
   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 `BSD-style open-source
+  Mac-specific formats. The project has an `MIT open-source
   licence <LICENSE>`__.
 | Among other things this means you can use it free of charge.
 
+`User documentation <https://fonttools.readthedocs.io/en/latest/>`_ and
+`developer documentation <https://fonttools.readthedocs.io/en/latest/developer.html>`_
+are available at `Read the Docs <https://fonttools.readthedocs.io/>`_.
+
 Installation
 ~~~~~~~~~~~~
 
-FontTools requires `Python <http://www.python.org/download/>`__ 2.7, 3.4
-or later.
+FontTools 4.x requires `Python <http://www.python.org/download/>`__ 3.6
+or later. FontTools 3.x requires Python 2.7 or later.
+
+**NOTE** From August 2019, until no later than January 1 2020, the support
+for *Python 2.7* will be limited to only critical bug fixes, and no new features
+will be added to the ``py27`` branch. You can read more `here <https://python3statement.org>`__
+and `here <https://github.com/fonttools/fonttools/issues/765>`__ for the
+reasons behind this decision.
 
 The package is listed in the Python Package Index (PyPI), so you can
 install it with `pip <https://pip.pypa.io>`__:
@@ -26,7 +35,7 @@
     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
+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.
@@ -40,7 +49,7 @@
     # 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`
+    # 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
@@ -49,114 +58,6 @@
     # 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
 ---------------------
 
@@ -164,157 +65,175 @@
 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:
+The ``fonttools`` PyPI distribution also supports so-called "extras", i.e. a
+set of keywords that describe a group of additional dependencies, which can be
+used when installing via pip, or when specifying a requirement.
+For example:
 
 .. code:: sh
 
-    python setup.py test
+    pip install fonttools[ufo,lxml,woff,unicode]
 
-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``.
+This command will install fonttools, as well as the optional dependencies that
+are required to unlock the extra features named "ufo", etc.
 
-You can also use `tox <https://testrun.org/tox/latest/>`__ to
-automatically run tests on different Python versions in isolated virtual
-environments.
+- ``Lib/fontTools/misc/etree.py``
 
-.. code:: sh
+  The module exports a ElementTree-like API for reading/writing XML files, and
+  allows to use as the backend either the built-in ``xml.etree`` module or
+  `lxml <https://lxml.de>`__. The latter is preferred whenever present,
+  as it is generally faster and more secure.
 
-    pip install tox
-    tox
+  *Extra:* ``lxml``
 
-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``.
+- ``Lib/fontTools/ufoLib``
 
-You can specify an alternative environment list via the ``-e`` option,
-or the ``TOXENV`` environment variable:
+  Package for reading and writing UFO source files; it requires:
 
-.. code:: sh
+  * `fs <https://pypi.org/pypi/fs>`__: (aka ``pyfilesystem2``) filesystem
+    abstraction layer.
 
-    tox -e py27-nocov
-    TOXENV="py36-cov,htmlcov" tox
+  * `enum34 <https://pypi.org/pypi/enum34>`__: backport for the built-in ``enum``
+    module (only required on Python < 3.4).
 
-Development Community
-~~~~~~~~~~~~~~~~~~~~~
+  *Extra:* ``ufo``
 
-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.
+- ``Lib/fontTools/ttLib/woff2.py``
 
-Feature requests and bug reports are always welcome at
-https://github.com/fonttools/fonttools/issues/
+  Module to compress/decompress WOFF 2.0 web fonts; it requires:
 
-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
+  * `brotli <https://pypi.python.org/pypi/Brotli>`__: Python bindings of
+    the Brotli compression library.
 
-History
-~~~~~~~
+  *Extra:* ``woff``
 
-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.
+- ``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.
+
+  *Extra:* ``woff``
+
+- ``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.x updated to the latest
+    Unicode version 12.0. Note this is not necessary if you use Python 3.8
+    as the latter already comes with an up-to-date ``unicodedata``.
+
+  *Extra:* ``unicode``
+
+- ``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.
+
+  *Extra:* ``interpolatable``
+
+- ``Lib/fontTools/varLib/plot.py``
+
+  Module for visualizing DesignSpaceDocument and resulting VariationModel.
+
+  * `matplotlib <https://pypi.org/pypi/matplotlib>`__: 2D plotting library.
+
+  *Extra:* ``plot``
+
+- ``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.
+
+  *Extra:* ``symfont``
+
+- ``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).
+
+  *Extra:* ``type1``
+
+- ``Lib/fontTools/ttLib/removeOverlaps.py``
+
+  Simplify TrueType glyphs by merging overlapping contours and components.
+
+  * `skia-pathops <https://pypi.python.org/pypy/skia-pathops>`__: Python
+    bindings for the Skia library's PathOps module, performing boolean
+    operations on paths (union, intersection, etc.).
+
+  *Extra:* ``pathops``
+
+- ``Lib/fontTools/pens/cocoaPen.py`` and ``Lib/fontTools/pens/quartzPen.py``
+
+  Pens for drawing glyphs with Cocoa ``NSBezierPath`` or ``CGPath`` require:
+
+  * `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.
+
+How to make a new release
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1) Update ``NEWS.rst`` with all the changes since the last release. Write a
+   changelog entry for each PR, with one or two short sentences summarizing it,
+   as well as links to the PR and relevant issues addressed by the PR.
+2) Use semantic versioning to decide whether the new release will be a 'major',
+   'minor' or 'patch' release. It's usually one of the latter two, depending on
+   whether new backward compatible APIs were added, or simply some bugs were fixed.
+3) Run ``python setup.py release`` command from the tip of the ``main`` branch.
+   By default this bumps the third or 'patch' digit only, unless you pass ``--major``
+   or ``--minor`` to bump respectively the first or second digit.
+   This bumps the package version string, extracts the changes since the latest
+   version from ``NEWS.rst``, and uses that text to create an annotated git tag
+   (or a signed git tag if you pass the ``--sign`` option and your git and Github
+   account are configured for `signing commits <https://docs.github.com/en/github/authenticating-to-github/managing-commit-signature-verification/signing-commits>`__
+   using a GPG key).
+   It also commits an additional version bump which opens the main branch for
+   the subsequent developmental cycle
+4) Push both the tag and commit to the upstream repository, by running the command
+   ``git push --follow-tags``.
+5) Let the CI build the wheel and source distribution packages and verify both
+   get uploaded to the Python Package Index (PyPI).
+6) [Optional] Go to fonttools `Github Releases <https://github.com/fonttools/fonttools/releases>`__
+   page and create a new release, copy-pasting the content of the git tag
+   message. This way, the release notes are nicely formatted as markdown, and
+   users watching the repo will get an email notification. One day we shall
+   automate that too.
+
 
 Acknowledgements
 ~~~~~~~~~~~~~~~~
@@ -322,16 +241,17 @@
 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.
+Jelle Bosma, Sascha Brawer, Tom Byrer, Antonio Cavedoni, Frédéric 
+Coiffier, Vincent Connare, David Corbett, Simon Cozens, Dave Crossland, 
+Simon Daniels, Peter Dekkers, 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, Chris 
+Simpkins, Miguel Sousa, Adam Twardoch, Adrien Tétar, Vitaly Volkov, 
+Paul Wise.
 
 Copyrights
 ~~~~~~~~~~
@@ -350,13 +270,12 @@
 
 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
+.. |CI Build Status| image:: https://github.com/fonttools/fonttools/workflows/Test/badge.svg
+   :target: https://github.com/fonttools/fonttools/actions?query=workflow%3ATest
 .. |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/Snippets/checksum.py b/Snippets/checksum.py
new file mode 100644
index 0000000..b965a35
--- /dev/null
+++ b/Snippets/checksum.py
@@ -0,0 +1,131 @@
+#!/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)
+            tt.saveXML(temp_ttx_path, 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
index 0cee39c..0a78670 100755
--- a/Snippets/cmap-format.py
+++ b/Snippets/cmap-format.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 
 # Sample script to convert legacy cmap subtables to format-4
 # subtables.  Note that this is rarely what one needs.  You
@@ -10,8 +10,6 @@
 # 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
diff --git a/Snippets/compact_gpos.py b/Snippets/compact_gpos.py
new file mode 100644
index 0000000..a5bd2f8
--- /dev/null
+++ b/Snippets/compact_gpos.py
@@ -0,0 +1,144 @@
+#! /usr/bin/env python3
+
+"""
+Sample script to use the otlLib.optimize.gpos functions to compact GPOS tables
+of existing fonts. This script takes one or more TTF files as arguments and
+will create compacted copies of the fonts using all available modes of the GPOS
+compaction algorithm. For each copy, it will measure the new size of the GPOS
+table and also the new size of the font in WOFF2 format. All results will be
+printed to stdout in CSV format, so the savings provided by the algorithm in
+each mode can be inspected.
+
+This was initially made to debug the algorithm but can also be used to choose
+a mode value for a specific font (trade-off between bytes saved in TTF format
+vs more bytes in WOFF2 format and more subtables).
+
+Run:
+
+python Snippets/compact_gpos.py MyFont.ttf > results.csv
+"""
+
+import argparse
+from collections import defaultdict
+import csv
+import time
+import sys
+from pathlib import Path
+from typing import Any, Iterable, List, Optional, Sequence, Tuple
+
+from fontTools.ttLib import TTFont
+from fontTools.otlLib.optimize import compact
+
+MODES = [str(c) for c in range(1, 10)]
+
+
+def main(args: Optional[List[str]] = None):
+    parser = argparse.ArgumentParser()
+    parser.add_argument("fonts", type=Path, nargs="+", help="Path to TTFs.")
+    parsed_args = parser.parse_args(args)
+
+    runtimes = defaultdict(list)
+    rows = []
+    font_path: Path
+    for font_path in parsed_args.fonts:
+        font = TTFont(font_path)
+        if "GPOS" not in font:
+            print(f"No GPOS in {font_path.name}, skipping.", file=sys.stderr)
+            continue
+        size_orig = len(font.getTableData("GPOS")) / 1024
+        print(f"Measuring {font_path.name}...", file=sys.stderr)
+
+        fonts = {}
+        font_paths = {}
+        sizes = {}
+        for mode in MODES:
+            print(f"    Running mode={mode}", file=sys.stderr)
+            fonts[mode] = TTFont(font_path)
+            before = time.perf_counter()
+            compact(fonts[mode], mode=str(mode))
+            runtimes[mode].append(time.perf_counter() - before)
+            font_paths[mode] = (
+                font_path.parent
+                / "compact"
+                / (font_path.stem + f"_{mode}" + font_path.suffix)
+            )
+            font_paths[mode].parent.mkdir(parents=True, exist_ok=True)
+            fonts[mode].save(font_paths[mode])
+            fonts[mode] = TTFont(font_paths[mode])
+            sizes[mode] = len(fonts[mode].getTableData("GPOS")) / 1024
+
+        print(f"    Runtimes:", file=sys.stderr)
+        for mode, times in runtimes.items():
+            print(
+                f"        {mode:10} {' '.join(f'{t:5.2f}' for t in times)}",
+                file=sys.stderr,
+            )
+
+        # Bonus: measure WOFF2 file sizes.
+        print(f"    Measuring WOFF2 sizes", file=sys.stderr)
+        size_woff_orig = woff_size(font, font_path) / 1024
+        sizes_woff = {
+            mode: woff_size(fonts[mode], font_paths[mode]) / 1024 for mode in MODES
+        }
+
+        rows.append(
+            (
+                font_path.name,
+                size_orig,
+                size_woff_orig,
+                *flatten(
+                    (
+                        sizes[mode],
+                        pct(sizes[mode], size_orig),
+                        sizes_woff[mode],
+                        pct(sizes_woff[mode], size_woff_orig),
+                    )
+                    for mode in MODES
+                ),
+            )
+        )
+
+    write_csv(rows)
+
+
+def woff_size(font: TTFont, path: Path) -> int:
+    font.flavor = "woff2"
+    woff_path = path.with_suffix(".woff2")
+    font.save(woff_path)
+    return woff_path.stat().st_size
+
+
+def write_csv(rows: List[Tuple[Any]]) -> None:
+    sys.stdout.reconfigure(encoding="utf-8")
+    sys.stdout.write("\uFEFF")
+    writer = csv.writer(sys.stdout, lineterminator="\n")
+    writer.writerow(
+        [
+            "File",
+            "Original GPOS Size",
+            "Original WOFF2 Size",
+            *flatten(
+                (
+                    f"mode={mode}",
+                    f"Change {mode}",
+                    f"mode={mode} WOFF2 Size",
+                    f"Change {mode} WOFF2 Size",
+                )
+                for mode in MODES
+            ),
+        ]
+    )
+    for row in rows:
+        writer.writerow(row)
+
+
+def pct(new: float, old: float) -> float:
+    return -(1 - (new / old))
+
+
+def flatten(seq_seq: Iterable[Iterable[Any]]) -> List[Any]:
+    return [thing for seq in seq_seq for thing in seq]
+
+
+if __name__ == "__main__":
+    main()
diff --git a/Snippets/decompose-ttf.py b/Snippets/decompose-ttf.py
new file mode 100755
index 0000000..bccaf72
--- /dev/null
+++ b/Snippets/decompose-ttf.py
@@ -0,0 +1,53 @@
+#! /usr/bin/env python3
+
+# Example script to decompose the composite glyphs in a TTF into
+# non-composite outlines.
+
+
+import sys
+from fontTools.ttLib import TTFont
+from fontTools.pens.recordingPen import DecomposingRecordingPen
+from fontTools.pens.ttGlyphPen import TTGlyphPen
+
+try:
+    import pathops
+except ImportError:
+    sys.exit(
+        "This script requires the skia-pathops module. "
+        "`pip install skia-pathops` and then retry."
+    )
+
+
+if len(sys.argv) != 3:
+    print("usage: decompose-ttf.py fontfile.ttf outfile.ttf")
+    sys.exit(1)
+
+src = sys.argv[1]
+dst = sys.argv[2]
+
+with TTFont(src) as f:
+    glyfTable = f["glyf"]
+    glyphSet = f.getGlyphSet()
+
+    for glyphName in glyphSet.keys():
+        if not glyfTable[glyphName].isComposite():
+            continue
+
+        # record TTGlyph outlines without components
+        dcPen = DecomposingRecordingPen(glyphSet)
+        glyphSet[glyphName].draw(dcPen)
+
+        # replay recording onto a skia-pathops Path
+        path = pathops.Path()
+        pathPen = path.getPen()
+        dcPen.replay(pathPen)
+
+        # remove overlaps
+        path.simplify()
+
+        # create new TTGlyph from Path
+        ttPen = TTGlyphPen(None)
+        path.draw(ttPen)
+        glyfTable[glyphName] = ttPen.glyph()
+
+    f.save(dst)
diff --git a/Snippets/dump_woff_metadata.py b/Snippets/dump_woff_metadata.py
index 0023a10..c9ea574 100644
--- a/Snippets/dump_woff_metadata.py
+++ b/Snippets/dump_woff_metadata.py
@@ -1,4 +1,3 @@
-from __future__ import print_function
 import sys
 from fontTools.ttx import makeOutputFileName
 from fontTools.ttLib import TTFont
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
index d8eccb4..c072117 100644
--- a/Snippets/fix-dflt-langsys.py
+++ b/Snippets/fix-dflt-langsys.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import argparse
 import logging
diff --git a/Snippets/interpolate.py b/Snippets/interpolate.py
index 7ed822d..063046c 100755
--- a/Snippets/interpolate.py
+++ b/Snippets/interpolate.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 
 # Illustrates how a fonttools script can construct variable fonts.
 #
@@ -21,8 +21,6 @@
 # $ ./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
diff --git a/Snippets/layout-features.py b/Snippets/layout-features.py
index 5e6074e..53e9735 100755
--- a/Snippets/layout-features.py
+++ b/Snippets/layout-features.py
@@ -1,7 +1,5 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 
-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
@@ -10,7 +8,11 @@
 	print("usage: layout-features.py fontfile.ttf")
 	sys.exit(1)
 fontfile = sys.argv[1]
-font = TTFont(fontfile)
+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
diff --git a/Snippets/merge_woff_metadata.py b/Snippets/merge_woff_metadata.py
index 669de46..d6e858f 100644
--- a/Snippets/merge_woff_metadata.py
+++ b/Snippets/merge_woff_metadata.py
@@ -1,4 +1,3 @@
-from __future__ import print_function
 import sys
 import os
 from fontTools.ttx import makeOutputFileName
diff --git a/Snippets/name-viewer.ipynb b/Snippets/name-viewer.ipynb
new file mode 100644
index 0000000..11722bc
--- /dev/null
+++ b/Snippets/name-viewer.ipynb
@@ -0,0 +1,117 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## name-viewer.ipynb\n",
+    "\n",
+    "### Usage\n",
+    "\n",
+    "1. Install `jupyter`, `plotly`, `fonttools` dependencies with pip\n",
+    "2. Modify the `FONT_PATH` setting in the Python source block below by clicking next to it and typing a new path in your web browser window\n",
+    "3. Execute the block of Python source by selecting the code block below and clicking the \"Run\" button above\n",
+    "4. Repeat from step 2 with modified font paths to view tables in other fonts\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import plotly as py\n",
+    "import plotly.graph_objs as go\n",
+    "\n",
+    "from fontTools.ttLib import TTFont\n",
+    "\n",
+    "py.offline.init_notebook_mode(connected=True)\n",
+    "\n",
+    "# EDIT HERE ------------------------------------------------------\n",
+    "#\n",
+    "# Path to font file\n",
+    "FONT_PATH = \"path/to/font.ttf\"\n",
+    "#\n",
+    "# Table height\n",
+    "#    - adjust for the length of output from the font file\n",
+    "HEIGHT = 700\n",
+    "#\n",
+    "# END EDIT -------------------------------------------------------\n",
+    "\n",
+    "record_list = []\n",
+    "nameID_list = []\n",
+    "ppelangID_list = []\n",
+    "value_list = []\n",
+    "\n",
+    "tt = TTFont(FONT_PATH)\n",
+    "namerecord_list = tt[\"name\"].names\n",
+    "\n",
+    "for record in namerecord_list:\n",
+    "    nameID_list.append(record.nameID)\n",
+    "    ppelangID_list.append(\"{} {} {}\".format(record.platformID, \n",
+    "                                            record.platEncID, \n",
+    "                                            record.langID))\n",
+    "    value_list.append(\"{}\".format(record.string.decode('utf-8').strip()))\n",
+    "    \n",
+    "\n",
+    "record_list.append(nameID_list)\n",
+    "record_list.append(ppelangID_list)\n",
+    "record_list.append(value_list)\n",
+    "\n",
+    "\n",
+    "trace0 = go.Table(\n",
+    "  columnorder = [1,2,3],\n",
+    "  columnwidth = [80,80,400],\n",
+    "  header = dict(\n",
+    "    values = [['<b>nameID</b>'],\n",
+    "              ['<b>p-pE-lang</b>'],\n",
+    "              ['<b>Value</b>']\n",
+    "             ],\n",
+    "    line = dict(color = '#506784'),\n",
+    "    fill = dict(color = '#FFDE00'),\n",
+    "    align = ['center','center', 'center'],\n",
+    "    font = dict(color = 'black', size = 16),\n",
+    "  ),\n",
+    "  cells = dict(\n",
+    "    values = record_list,\n",
+    "    line = dict(color = '#506784'),\n",
+    "    fill = dict(color = ['#000000', 'white']),\n",
+    "    align = ['center', 'center', 'left'],\n",
+    "    font = dict(color = ['#F8F8F5', '#000000', '#000000'], size = 14),\n",
+    "    height = 30,\n",
+    "    ))\n",
+    "\n",
+    "data1 = [trace0]\n",
+    "\n",
+    "layout1 = go.Layout(\n",
+    "    autosize=True,\n",
+    "    height=HEIGHT,\n",
+    ")\n",
+    "\n",
+    "fig1 = dict(data=data1, layout=layout1)\n",
+    "py.offline.iplot(fig1)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.2"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/Snippets/otf2ttf.py b/Snippets/otf2ttf.py
index 60acd54..b925b33 100755
--- a/Snippets/otf2ttf.py
+++ b/Snippets/otf2ttf.py
@@ -1,12 +1,18 @@
-#!/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
+#!/usr/bin/env python3
 
+import argparse
+import logging
+import os
+import sys
+
+from fontTools.pens.cu2quPen import Cu2QuPen
+from fontTools import configLogger
+from fontTools.misc.cliTools import makeOutputFileName
+from fontTools.pens.ttGlyphPen import TTGlyphPen
+from fontTools.ttLib import TTFont, newTable
+
+
+log = logging.getLogger()
 
 # default approximation error, measured in UPEM
 MAX_ERR = 1.0
@@ -32,6 +38,13 @@
     return quadGlyphs
 
 
+def update_hmtx(ttFont, glyf):
+    hmtx = ttFont["hmtx"]
+    for glyphName, glyph in glyf.glyphs.items():
+        if hasattr(glyph, 'xMin'):
+            hmtx[glyphName] = (hmtx[glyphName][0], glyph.xMin)
+
+
 def otf_to_ttf(ttFont, post_format=POST_FORMAT, **kwargs):
     assert ttFont.sfntVersion == "OTTO"
     assert "CFF " in ttFont
@@ -43,6 +56,8 @@
     glyf.glyphOrder = glyphOrder
     glyf.glyphs = glyphs_to_quadratic(ttFont.getGlyphSet(), **kwargs)
     del ttFont["CFF "]
+    glyf.compile(ttFont)
+    update_hmtx(ttFont, glyf)
 
     ttFont["maxp"] = maxp = newTable("maxp")
     maxp.tableVersion = 0x00010000
@@ -56,35 +71,55 @@
     maxp.maxComponentElements = max(
         len(g.components if hasattr(g, 'components') else [])
         for g in glyf.glyphs.values())
+    maxp.compile(ttFont)
 
     post = ttFont["post"]
     post.formatType = post_format
     post.extraNames = []
     post.mapping = {}
     post.glyphOrder = glyphOrder
+    try:
+        post.compile(ttFont)
+    except OverflowError:
+        post.formatType = 3
+        log.warning("Dropping glyph names, they do not fit in 'post' table.")
 
     ttFont.sfntVersion = "\000\001\000\000"
 
 
 def main(args=None):
+    configLogger(logger=log)
+
     parser = argparse.ArgumentParser()
-    parser.add_argument("input", metavar="INPUT")
+    parser.add_argument("input", nargs='+', 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')
+    parser.add_argument("--face-index", type=int, default=0)
+    parser.add_argument("--overwrite", action='store_true')
     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 options.output and len(options.input) > 1:
+        if not os.path.isdir(options.output):
+            parser.error("-o/--output option must be a directory when "
+                         "processing multiple fonts")
+
+    for path in options.input:
+        if options.output and not os.path.isdir(options.output):
+            output = options.output
+        else:
+            output = makeOutputFileName(path, outputDir=options.output,
+                                        extension='.ttf',
+                                        overWrite=options.overwrite)
+
+        font = TTFont(path, fontNumber=options.face_index)
+        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__":
diff --git a/Snippets/rename-fonts.py b/Snippets/rename-fonts.py
new file mode 100755
index 0000000..0a43dc2
--- /dev/null
+++ b/Snippets/rename-fonts.py
@@ -0,0 +1,169 @@
+#!/usr/bin/env python3
+"""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.
+"""
+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
index c20c05f..d06c3f5 100755
--- a/Snippets/subset-fpgm.py
+++ b/Snippets/subset-fpgm.py
@@ -1,7 +1,5 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.ttLib import TTFont
 import sys
 
diff --git a/Snippets/svg2glif.py b/Snippets/svg2glif.py
index 2dd6402..22fcc7d 100755
--- a/Snippets/svg2glif.py
+++ b/Snippets/svg2glif.py
@@ -1,15 +1,14 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 """ Convert SVG paths to UFO glyphs. """
 
-from __future__ import print_function, absolute_import
 
-__requires__ = ["FontTools", "ufoLib"]
+__requires__ = ["fontTools"]
 
 from fontTools.misc.py23 import SimpleNamespace
 from fontTools.svgLib import SVGPath
 
-from ufoLib.pointPen import SegmentToPointPen
-from ufoLib.glifLib import writeGlyphToString
+from fontTools.pens.pointPen import SegmentToPointPen
+from fontTools.ufoLib.glifLib import writeGlyphToString
 
 
 __all__ = ["svg2glif"]
diff --git a/Snippets/woff2_compress.py b/Snippets/woff2_compress.py
deleted file mode 100755
index 689ebdc..0000000
--- a/Snippets/woff2_compress.py
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/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
deleted file mode 100755
index e7c1bea..0000000
--- a/Snippets/woff2_decompress.py
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/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
index 97fcf8d..3e9d9d8 100644
--- a/Tests/afmLib/afmLib_test.py
+++ b/Tests/afmLib/afmLib_test.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 import unittest
 import os
 from fontTools import afmLib
diff --git a/Tests/agl_test.py b/Tests/agl_test.py
index 8754e6a..f2fb72d 100644
--- a/Tests/agl_test.py
+++ b/Tests/agl_test.py
@@ -1,7 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import (print_function, division, absolute_import,
-                        unicode_literals)
-from fontTools.misc.py23 import *
 from fontTools import agl
 import unittest
 
@@ -9,21 +5,15 @@
 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("Lcommaaccent"), "Ļ")
         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"), "ĮĮĮ")
+            agl.toUnicode("Lcommaaccent_uni20AC0308_u1040C.alternate"),
+            "\u013B\u20AC\u0308\U0001040C")
+        self.assertEqual(agl.toUnicode("Lcommaaccent_uni013B_u013B"), "ĻĻĻ")
         self.assertEqual(agl.toUnicode("foo"), "")
         self.assertEqual(agl.toUnicode(".notdef"), "")
 
diff --git a/Tests/cffLib/cffLib_test.py b/Tests/cffLib/cffLib_test.py
index 0d1258d..7a6e921 100644
--- a/Tests/cffLib/cffLib_test.py
+++ b/Tests/cffLib/cffLib_test.py
@@ -1,12 +1,15 @@
-from __future__ import print_function, division, absolute_import
 from fontTools.cffLib import TopDict, PrivateDict, CharStrings
-from fontTools.misc.testTools import parseXML
+from fontTools.misc.testTools import parseXML, DataFilesHandler
+from fontTools.ttLib import TTFont
+import copy
+import os
+import sys
 import unittest
 
 
-class TopDictTest(unittest.TestCase):
+class CffLibTest(DataFilesHandler):
 
-    def test_recalcFontBBox(self):
+    def test_topDict_recalcFontBBox(self):
         topDict = TopDict()
         topDict.CharStrings = CharStrings(None, None, None, PrivateDict(), None, None)
         topDict.CharStrings.fromXML(None, None, parseXML("""
@@ -27,7 +30,7 @@
         topDict.recalcFontBBox()
         self.assertEqual(topDict.FontBBox, [-56, -100, 300, 200])
 
-    def test_recalcFontBBox_empty(self):
+    def test_topDict_recalcFontBBox_empty(self):
         topDict = TopDict()
         topDict.CharStrings = CharStrings(None, None, None, PrivateDict(), None, None)
         topDict.CharStrings.fromXML(None, None, parseXML("""
@@ -42,7 +45,68 @@
         topDict.recalcFontBBox()
         self.assertEqual(topDict.FontBBox, [0, 0, 0, 0])
 
+    def test_topDict_set_Encoding(self):
+        ttx_path = self.getpath('TestOTF.ttx')
+        font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+        font.importXML(ttx_path)
+
+        topDict = font["CFF "].cff.topDictIndex[0]
+        encoding = [".notdef"] * 256
+        encoding[0x20] = "space"
+        topDict.Encoding = encoding
+        
+        self.temp_dir()
+        save_path = os.path.join(self.tempdir, 'TestOTF.otf')
+        font.save(save_path)
+
+        font2 = TTFont(save_path)
+        topDict2 = font2["CFF "].cff.topDictIndex[0]
+        self.assertEqual(topDict2.Encoding[32], "space")
+
+    def test_CFF_deepcopy(self):
+        """Test that deepcopying a TTFont with a CFF table does not recurse
+        infinitely."""
+        ttx_path = os.path.join(
+            os.path.dirname(__file__),
+            "..",
+            "varLib",
+            "data",
+            "master_ttx_interpolatable_otf",
+            "TestFamily2-Master0.ttx",
+        )
+        font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+        font.importXML(ttx_path)
+        copy.deepcopy(font)
+
+    def test_FDSelect_format_4(self):
+        ttx_path = self.getpath('TestFDSelect4.ttx')
+        font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+        font.importXML(ttx_path)
+
+        self.temp_dir()
+        save_path = os.path.join(self.tempdir, 'TestOTF.otf')
+        font.save(save_path)
+
+        font2 = TTFont(save_path)
+        topDict2 = font2["CFF2"].cff.topDictIndex[0]
+        self.assertEqual(topDict2.FDSelect.format, 4)
+        self.assertEqual(topDict2.FDSelect.gidArray, [0, 0, 1])
+
+    def test_unique_glyph_names(self):
+        font_path = self.getpath('LinLibertine_RBI.otf')
+        font = TTFont(font_path, recalcBBoxes=False, recalcTimestamp=False)
+
+        glyphOrder = font.getGlyphOrder()
+        self.assertEqual(len(glyphOrder), len(set(glyphOrder)))
+
+        self.temp_dir()
+        save_path = os.path.join(self.tempdir, 'TestOTF.otf')
+        font.save(save_path)
+
+        font2 = TTFont(save_path)
+        glyphOrder = font2.getGlyphOrder()
+        self.assertEqual(len(glyphOrder), len(set(glyphOrder)))
+
 
 if __name__ == "__main__":
-    import sys
     sys.exit(unittest.main())
diff --git a/Tests/cffLib/data/LinLibertine_RBI.otf b/Tests/cffLib/data/LinLibertine_RBI.otf
new file mode 100755
index 0000000..c1a4ff7
--- /dev/null
+++ b/Tests/cffLib/data/LinLibertine_RBI.otf
Binary files differ
diff --git a/Tests/cffLib/data/TestCFF2Widths.ttx b/Tests/cffLib/data/TestCFF2Widths.ttx
new file mode 100644
index 0000000..e3a3c9c
--- /dev/null
+++ b/Tests/cffLib/data/TestCFF2Widths.ttx
@@ -0,0 +1,707 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.43">
+
+  <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"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0xb0062f2d"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Jul 11 13:52:47 2019"/>
+    <modified value="Thu Jul 11 20:52:49 2019"/>
+    <xMin value="-180"/>
+    <yMin value="-454"/>
+    <xMax value="780"/>
+    <yMax value="1060"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <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="600"/>
+    <minLeftSideBearing value="-180"/>
+    <minRightSideBearing value="-180"/>
+    <xMaxExtent value="780"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="4"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="600"/>
+    <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="286"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="3"/>
+      <bProportion value="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <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="ADBO"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="66"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="01100000 00000000 00000001 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="478"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="84"/>
+    <mtx name="A" width="600" lsb="50"/>
+    <mtx name="B" width="600" lsb="120"/>
+    <mtx name="space" width="600" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x42" name="B"/><!-- LATIN CAPITAL LETTER B -->
+    </cmap_format_12>
+  </cmap>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      © 2010 - 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.111;makeotfexe 2.5.65597
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="277" platformID="3" platEncID="1" langID="0x409">
+      Roman
+    </namerecord>
+    <namerecord nameID="278" platformID="3" platEncID="1" langID="0x409">
+      Italic
+    </namerecord>
+    <namerecord nameID="279" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="280" platformID="3" platEncID="1" langID="0x409">
+      ExtraLight
+    </namerecord>
+    <namerecord nameID="281" platformID="3" platEncID="1" langID="0x409">
+      SourceCodeRoman-ExtraLight
+    </namerecord>
+    <namerecord nameID="282" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+    <namerecord nameID="283" platformID="3" platEncID="1" langID="0x409">
+      SourceCodeRoman-Light
+    </namerecord>
+    <namerecord nameID="284" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="285" platformID="3" platEncID="1" langID="0x409">
+      SourceCodeRoman-Regular
+    </namerecord>
+    <namerecord nameID="286" platformID="3" platEncID="1" langID="0x409">
+      Medium
+    </namerecord>
+    <namerecord nameID="287" platformID="3" platEncID="1" langID="0x409">
+      SourceCodeRoman-Medium
+    </namerecord>
+    <namerecord nameID="288" platformID="3" platEncID="1" langID="0x409">
+      Semibold
+    </namerecord>
+    <namerecord nameID="289" platformID="3" platEncID="1" langID="0x409">
+      SourceCodeRoman-Semibold
+    </namerecord>
+    <namerecord nameID="290" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="291" platformID="3" platEncID="1" langID="0x409">
+      SourceCodeRoman-Bold
+    </namerecord>
+    <namerecord nameID="292" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+    <namerecord nameID="293" platformID="3" platEncID="1" langID="0x409">
+      SourceCodeRoman-Black
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </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="-12 0 0"/>
+                <blend value="0 0 0"/>
+                <blend value="478 8 22"/>
+                <blend value="490 0 0"/>
+                <blend value="570 -4 -12"/>
+                <blend value="582 0 0"/>
+                <blend value="640 -6 -16"/>
+                <blend value="652 0 0"/>
+                <blend value="660 -2 -4"/>
+                <blend value="672 0 0"/>
+                <blend value="722 -6 -16"/>
+                <blend value="734 0 0"/>
+            </BlueValues>
+            <OtherBlues>
+                <blend value="-234 17 46"/>
+                <blend value="-222 0 0"/>
+            </OtherBlues>
+            <FamilyBlues value="-12 0 486 498 574 586 638 650 656 668 712 724"/>
+            <FamilyOtherBlues value="-217 -205"/>
+            <BlueScale value="0.0625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW>
+                <blend value="28 39 106"/>
+            </StdHW>
+            <StdVW>
+                <blend value="34 51 138"/>
+            </StdVW>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+        </CharString>
+        <CharString name="A">
+          1 vsindex
+          50 -50 1 blend
+          hmoveto
+          32 144 1 blend
+          hlineto
+          140 396 28 80 24 68 24 82 -67 -80 -14 -18 -7 10 -10 -16 8 blend
+          rlinecurve
+          4 hlineto
+          24 -82 24 -68 28 -80 -10 16 -5 -10 -14 18 6 blend
+          rrcurveto
+          138 -396 34 -65 80 148 3 blend
+          0 -236 660 -28 40 -10 -180 3 blend
+          0 -236 -660 40 10 2 blend
+          rlineto
+          102 236 39 -98 2 blend
+          rmoveto
+          293 28 -293 -28 23 105 -23 -105 4 blend
+          hlineto
+        </CharString>
+        <CharString name="B">
+          120 -21 -56 1 blend
+          hmoveto
+          172 29 80 1 blend
+          hlineto
+          154 88 66 126 92 -61 54 -98 14 -7 -14 12 24 -3 -8 3 8 -2 -6 5 15 -2 -6 2 6 1 4 9 blend
+          hvcurveto
+          4 vlineto
+          78 0 -2 1 blend
+          20 39 54 70 -2 -5 4 10 -5 -14 3 blend
+          vvcurveto
+          108 -76 52 -136 7 20 -15 -32 -7 -18 2 0 4 blend
+          vhcurveto
+          -160 -660 -26 -72 4 10 2 blend
+          hlineto
+          32 366 51 140 14 28 2 blend
+          rmoveto
+          266 114 -60 -142 -21 -58 2 blend
+          vlineto
+          132 62 -38 -94 -88 -56 -46 -144 -31 -76 -10 -38 12 22 23 54 15 48 11 32 10 18 32 88 8 blend
+          hvcurveto
+          -108 19 52 1 blend
+          hlineto
+          -338 28 76 1 blend
+          vmoveto
+          310 128 -70 -168 -22 -60 2 blend
+          vlineto
+          142 80 -48 -100 -112 -80 -50 -142 -27 -74 -18 -52 15 28 19 52 23 64 15 52 13 24 30 74 8 blend
+          hvcurveto
+          -128 22 60 1 blend
+          hlineto
+        </CharString>
+        <CharString name="space">
+        </CharString>
+      </CharStrings>
+      <VarStore Format="1">
+        <Format value="1"/>
+        <VarRegionList>
+          <!-- RegionAxisCount=1 -->
+          <!-- RegionCount=3 -->
+          <Region index="0">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.368"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="1">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.368"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="2">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+        </VarRegionList>
+        <!-- VarDataCount=2 -->
+        <VarData index="0">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=2 -->
+          <VarRegionIndex index="0" value="0"/>
+          <VarRegionIndex index="1" value="1"/>
+        </VarData>
+        <VarData index="1">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=1 -->
+          <VarRegionIndex index="0" value="2"/>
+        </VarData>
+      </VarStore>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="B" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <ScriptTag value="latn"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=0 -->
+        <!-- RegionCount=0 -->
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=1 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=0 -->
+        <Item index="0" value="[]"/>
+      </VarData>
+    </VarStore>
+    <AdvWidthMap>
+      <Map glyph=".notdef" outer="0" inner="0"/>
+      <Map glyph="A" outer="0" inner="0"/>
+      <Map glyph="B" outer="0" inner="0"/>
+      <Map glyph="space" outer="0" inner="0"/>
+    </AdvWidthMap>
+  </HVAR>
+
+  <MVAR>
+    <Version value="0x00010000"/>
+    <Reserved value="0"/>
+    <ValueRecordSize value="8"/>
+    <!-- ValueRecordCount=8 -->
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=2 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.368"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.368"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=7 -->
+        <NumShorts value="1"/>
+        <!-- VarRegionCount=2 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+        <Item index="0" value="[-478, 22]"/>
+        <Item index="1" value="[-209, 0]"/>
+        <Item index="2" value="[-94, 0]"/>
+        <Item index="3" value="[-66, 14]"/>
+        <Item index="4" value="[-48, 0]"/>
+        <Item index="5" value="[-4, 0]"/>
+        <Item index="6" value="[200, 0]"/>
+      </VarData>
+    </VarStore>
+    <ValueRecord index="0">
+      <ValueTag value="cpht"/>
+      <VarIdx value="5"/>
+    </ValueRecord>
+    <ValueRecord index="1">
+      <ValueTag value="hasc"/>
+      <VarIdx value="2"/>
+    </ValueRecord>
+    <ValueRecord index="2">
+      <ValueTag value="hcla"/>
+      <VarIdx value="1"/>
+    </ValueRecord>
+    <ValueRecord index="3">
+      <ValueTag value="hcld"/>
+      <VarIdx value="4"/>
+    </ValueRecord>
+    <ValueRecord index="4">
+      <ValueTag value="hdsc"/>
+      <VarIdx value="2"/>
+    </ValueRecord>
+    <ValueRecord index="5">
+      <ValueTag value="hlgp"/>
+      <VarIdx value="6"/>
+    </ValueRecord>
+    <ValueRecord index="6">
+      <ValueTag value="stro"/>
+      <VarIdx value="3"/>
+    </ValueRecord>
+    <ValueRecord index="7">
+      <ValueTag value="xhgt"/>
+      <VarIdx value="0"/>
+    </ValueRecord>
+  </MVAR>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=2 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="279"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="ital"/>
+        <AxisNameID value="278"/>  <!-- Italic -->
+        <AxisOrdering value="1"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=9 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="280"/>  <!-- ExtraLight -->
+        <NominalValue value="200.0"/>
+        <RangeMinValue value="200.0"/>
+        <RangeMaxValue value="250.0"/>
+      </AxisValue>
+      <AxisValue index="1" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="282"/>  <!-- Light -->
+        <NominalValue value="300.0"/>
+        <RangeMinValue value="250.0"/>
+        <RangeMaxValue value="350.0"/>
+      </AxisValue>
+      <AxisValue index="2" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="2"/>
+        <ValueNameID value="284"/>  <!-- Regular -->
+        <NominalValue value="400.0"/>
+        <RangeMinValue value="350.0"/>
+        <RangeMaxValue value="450.0"/>
+      </AxisValue>
+      <AxisValue index="3" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="286"/>  <!-- Medium -->
+        <NominalValue value="500.0"/>
+        <RangeMinValue value="450.0"/>
+        <RangeMaxValue value="550.0"/>
+      </AxisValue>
+      <AxisValue index="4" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="288"/>  <!-- Semibold -->
+        <NominalValue value="600.0"/>
+        <RangeMinValue value="550.0"/>
+        <RangeMaxValue value="650.0"/>
+      </AxisValue>
+      <AxisValue index="5" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="290"/>  <!-- Bold -->
+        <NominalValue value="700.0"/>
+        <RangeMinValue value="650.0"/>
+        <RangeMaxValue value="800.0"/>
+      </AxisValue>
+      <AxisValue index="6" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="292"/>  <!-- Black -->
+        <NominalValue value="900.0"/>
+        <RangeMinValue value="800.0"/>
+        <RangeMaxValue value="900.0"/>
+      </AxisValue>
+      <AxisValue index="7" Format="3">
+        <AxisIndex value="0"/>
+        <Flags value="2"/>
+        <ValueNameID value="284"/>  <!-- Regular -->
+        <Value value="400.0"/>
+        <LinkedValue value="700.0"/>
+      </AxisValue>
+      <AxisValue index="8" Format="3">
+        <AxisIndex value="1"/>
+        <Flags value="2"/>
+        <ValueNameID value="277"/>  <!-- Roman -->
+        <Value value="0.0"/>
+        <LinkedValue value="1.0"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <avar>
+    <segment axis="wght">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="0.1429" to="0.1"/>
+      <mapping from="0.2857" to="0.368"/>
+      <mapping from="0.4286" to="0.486"/>
+      <mapping from="0.5714" to="0.6"/>
+      <mapping from="0.7143" to="0.824"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+  </avar>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>200.0</MinValue>
+      <DefaultValue>200.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>279</AxisNameID>
+    </Axis>
+
+    <!-- ExtraLight -->
+    <!-- PostScript: SourceCodeRoman-ExtraLight -->
+    <NamedInstance flags="0x0" postscriptNameID="281" subfamilyNameID="280">
+      <coord axis="wght" value="200.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <!-- PostScript: SourceCodeRoman-Light -->
+    <NamedInstance flags="0x0" postscriptNameID="283" subfamilyNameID="282">
+      <coord axis="wght" value="300.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <!-- PostScript: SourceCodeRoman-Regular -->
+    <NamedInstance flags="0x0" postscriptNameID="285" subfamilyNameID="284">
+      <coord axis="wght" value="400.0"/>
+    </NamedInstance>
+
+    <!-- Medium -->
+    <!-- PostScript: SourceCodeRoman-Medium -->
+    <NamedInstance flags="0x0" postscriptNameID="287" subfamilyNameID="286">
+      <coord axis="wght" value="500.0"/>
+    </NamedInstance>
+
+    <!-- Semibold -->
+    <!-- PostScript: SourceCodeRoman-Semibold -->
+    <NamedInstance flags="0x0" postscriptNameID="289" subfamilyNameID="288">
+      <coord axis="wght" value="600.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <!-- PostScript: SourceCodeRoman-Bold -->
+    <NamedInstance flags="0x0" postscriptNameID="291" subfamilyNameID="290">
+      <coord axis="wght" value="700.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <!-- PostScript: SourceCodeRoman-Black -->
+    <NamedInstance flags="0x0" postscriptNameID="293" subfamilyNameID="292">
+      <coord axis="wght" value="900.0"/>
+    </NamedInstance>
+  </fvar>
+
+</ttFont>
diff --git a/Tests/cffLib/data/TestFDSelect4.ttx b/Tests/cffLib/data/TestFDSelect4.ttx
new file mode 100644
index 0000000..f80c8a1
--- /dev/null
+++ b/Tests/cffLib/data/TestFDSelect4.ttx
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.43">
+
+  <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"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.004"/>
+    <checkSumAdjustment value="0x61e1f145"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Sat Oct 10 16:55:59 2015"/>
+    <modified value="Mon Jul 29 22:04:10 2019"/>
+    <xMin value="-454"/>
+    <yMin value="-322"/>
+    <xMax value="2159"/>
+    <yMax value="968"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="900"/>
+    <descent value="-300"/>
+    <lineGap value="96"/>
+    <advanceWidthMax value="2200"/>
+    <minLeftSideBearing value="-454"/>
+    <minRightSideBearing value="-454"/>
+    <xMaxExtent value="2159"/>
+    <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="3"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="3"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="564"/>
+    <usWeightClass value="500"/>
+    <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="294"/>
+    <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="ALIF"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="900"/>
+    <sTypoDescender value="-300"/>
+    <sTypoLineGap value="100"/>
+    <usWinAscent value="790"/>
+    <usWinDescent value="330"/>
+    <ulCodePageRange1 value="00100000 00000000 00000001 11011111"/>
+    <ulCodePageRange2 value="00000000 00001000 00000000 00000000"/>
+    <sxHeight value="489"/>
+    <sCapHeight value="655"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="663" lsb="86"/>
+    <mtx name="A" width="552" lsb="0"/>
+    <mtx name="a" width="510" lsb="49"/>
+  </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="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2015-2019 The Mada Project Authors, with Reserved Font Name “Source”.
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Mada Medium
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      Mada-Medium
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-200"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF2>
+    <major value="2"/>
+    <minor value="0"/>
+    <CFFFont name="CFF2Font">
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FDSelect format="4"/>
+      <FDArray>
+        <FontDict index="0">
+          <Private>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="1"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <Private>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="1"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+        </CharString>
+        <CharString name="A" fdSelectIndex="0">
+          0 hmoveto
+          103 hlineto
+          107 357 22 71 20 73 19 74 rlinecurve
+          4 hlineto
+          20 -74 20 -73 22 -71 rrcurveto
+          106 -357 108 0 -217 655 rlineto
+          -117 hlineto
+          -96 -467 rmoveto
+          306 hlineto
+          0 80 rlineto
+          -306 hlineto
+        </CharString>
+        <CharString name="a" fdSelectIndex="1">
+          193 -12 rmoveto
+          60 52 30 38 45 hvcurveto
+          3 hlineto
+          8 -56 82 0 0 294 rlineto
+          130 0 -56 77 -121 hhcurveto
+          -78 -69 -32 -32 -50 hvcurveto
+          38 -68 rlineto
+          26 41 48 24 52 hhcurveto
+          73 20 -51 -57 1 hvcurveto
+          -204 -22 -89 -54 0 -106 rrcurveto
+          -86 0 60 -55 84 hhcurveto
+          31 79 rmoveto
+          -44 -34 21 49 0 hvcurveto
+          0 55 50 37 146 18 0 -122 rcurveline
+          -38 -41 -34 -21 -43 hhcurveto
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+</ttFont>
diff --git a/Tests/cffLib/data/TestOTF.ttx b/Tests/cffLib/data/TestOTF.ttx
new file mode 100644
index 0000000..0e0c4d7
--- /dev/null
+++ b/Tests/cffLib/data/TestOTF.ttx
@@ -0,0 +1,325 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <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="0x4e5f578f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Jun  4 14:29:11 2015"/>
+    <modified value="Sun Mar 26 22:38:12 2017"/>
+    <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="0xd" name="CR"/>
+      <map code="0x20" name="space"/>
+      <map code="0x2e" name="period"/>
+      <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>
+    <major value="1"/>
+    <minor value="0"/>
+    <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"/>
+      <UnderlinePosition value="-100"/>
+      <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/cffLib/data/TestSparseCFF2VF.ttx b/Tests/cffLib/data/TestSparseCFF2VF.ttx
new file mode 100644
index 0000000..f1ae063
--- /dev/null
+++ b/Tests/cffLib/data/TestSparseCFF2VF.ttx
@@ -0,0 +1,1980 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uni0000"/>
+    <GlyphID id="2" name="exclam"/>
+    <GlyphID id="3" name="uni610F"/>
+    <GlyphID id="4" name="uni85A8"/>
+    <GlyphID id="5" name="uni8E59"/>
+    <GlyphID id="6" name="uni9A40"/>
+    <GlyphID id="7" name="uniF9DC"/>
+    <GlyphID id="8" name="uni50CE"/>
+    <GlyphID id="9" name="uni6A1A"/>
+    <GlyphID id="10" name="uni82E8"/>
+    <GlyphID id="11" name="uni8552"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0x65319c93"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:06:21 2018"/>
+    <modified value="Tue Mar 19 15:57:18 2019"/>
+    <xMin value="14"/>
+    <yMin value="-120"/>
+    <xMax value="976"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="14"/>
+    <minRightSideBearing value="24"/>
+    <xMaxExtent value="976"/>
+    <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="12"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="977"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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 00000011"/>
+    <ulUnicodeRange2 value="00101000 00000000 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="63964"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="exclam" width="222" lsb="74"/>
+    <mtx name="uni0000" width="231" lsb="0"/>
+    <mtx name="uni50CE" width="1000" lsb="14"/>
+    <mtx name="uni610F" width="1000" lsb="48"/>
+    <mtx name="uni6A1A" width="1000" lsb="30"/>
+    <mtx name="uni82E8" width="1000" lsb="35"/>
+    <mtx name="uni8552" width="1000" lsb="53"/>
+    <mtx name="uni85A8" width="1000" lsb="52"/>
+    <mtx name="uni8E59" width="1000" lsb="34"/>
+    <mtx name="uni9A40" width="1000" lsb="36"/>
+    <mtx name="uniF9DC" width="1000" lsb="82"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x0" name="uni0000"/><!-- ???? -->
+      <map code="0x1" name="uni0000"/><!-- ???? -->
+      <map code="0x2" name="uni0000"/><!-- ???? -->
+      <map code="0x3" name="uni0000"/><!-- ???? -->
+      <map code="0x4" name="uni0000"/><!-- ???? -->
+      <map code="0x5" name="uni0000"/><!-- ???? -->
+      <map code="0x6" name="uni0000"/><!-- ???? -->
+      <map code="0x7" name="uni0000"/><!-- ???? -->
+      <map code="0x8" name="uni0000"/><!-- ???? -->
+      <map code="0x9" name="uni0000"/><!-- ???? -->
+      <map code="0xa" name="uni0000"/><!-- ???? -->
+      <map code="0xb" name="uni0000"/><!-- ???? -->
+      <map code="0xc" name="uni0000"/><!-- ???? -->
+      <map code="0xd" name="uni0000"/><!-- ???? -->
+      <map code="0xe" name="uni0000"/><!-- ???? -->
+      <map code="0xf" name="uni0000"/><!-- ???? -->
+      <map code="0x10" name="uni0000"/><!-- ???? -->
+      <map code="0x11" name="uni0000"/><!-- ???? -->
+      <map code="0x12" name="uni0000"/><!-- ???? -->
+      <map code="0x13" name="uni0000"/><!-- ???? -->
+      <map code="0x14" name="uni0000"/><!-- ???? -->
+      <map code="0x15" name="uni0000"/><!-- ???? -->
+      <map code="0x16" name="uni0000"/><!-- ???? -->
+      <map code="0x17" name="uni0000"/><!-- ???? -->
+      <map code="0x18" name="uni0000"/><!-- ???? -->
+      <map code="0x19" name="uni0000"/><!-- ???? -->
+      <map code="0x1a" name="uni0000"/><!-- ???? -->
+      <map code="0x1b" name="uni0000"/><!-- ???? -->
+      <map code="0x1c" name="uni0000"/><!-- ???? -->
+      <map code="0x1d" name="uni0000"/><!-- ???? -->
+      <map code="0x1e" name="uni0000"/><!-- ???? -->
+      <map code="0x1f" name="uni0000"/><!-- ???? -->
+      <map code="0x20" name="uni0000"/><!-- SPACE -->
+      <map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
+      <map code="0xa0" name="uni0000"/><!-- NO-BREAK SPACE -->
+      <map code="0x50ce" name="uni50CE"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x610f" name="uni610F"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x6a1a" name="uni6A1A"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+      <map code="0x82e8" name="uni82E8"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8552" name="uni8552"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="uni85A8"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+      <map code="0x8e59" name="uni8E59"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+      <map code="0x9a40" name="uni9A40"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+      <map code="0xf9dc" name="uniF9DC"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="532" language="0" nGroups="43">
+      <map code="0x0" name="uni0000"/><!-- ???? -->
+      <map code="0x1" name="uni0000"/><!-- ???? -->
+      <map code="0x2" name="uni0000"/><!-- ???? -->
+      <map code="0x3" name="uni0000"/><!-- ???? -->
+      <map code="0x4" name="uni0000"/><!-- ???? -->
+      <map code="0x5" name="uni0000"/><!-- ???? -->
+      <map code="0x6" name="uni0000"/><!-- ???? -->
+      <map code="0x7" name="uni0000"/><!-- ???? -->
+      <map code="0x8" name="uni0000"/><!-- ???? -->
+      <map code="0x9" name="uni0000"/><!-- ???? -->
+      <map code="0xa" name="uni0000"/><!-- ???? -->
+      <map code="0xb" name="uni0000"/><!-- ???? -->
+      <map code="0xc" name="uni0000"/><!-- ???? -->
+      <map code="0xd" name="uni0000"/><!-- ???? -->
+      <map code="0xe" name="uni0000"/><!-- ???? -->
+      <map code="0xf" name="uni0000"/><!-- ???? -->
+      <map code="0x10" name="uni0000"/><!-- ???? -->
+      <map code="0x11" name="uni0000"/><!-- ???? -->
+      <map code="0x12" name="uni0000"/><!-- ???? -->
+      <map code="0x13" name="uni0000"/><!-- ???? -->
+      <map code="0x14" name="uni0000"/><!-- ???? -->
+      <map code="0x15" name="uni0000"/><!-- ???? -->
+      <map code="0x16" name="uni0000"/><!-- ???? -->
+      <map code="0x17" name="uni0000"/><!-- ???? -->
+      <map code="0x18" name="uni0000"/><!-- ???? -->
+      <map code="0x19" name="uni0000"/><!-- ???? -->
+      <map code="0x1a" name="uni0000"/><!-- ???? -->
+      <map code="0x1b" name="uni0000"/><!-- ???? -->
+      <map code="0x1c" name="uni0000"/><!-- ???? -->
+      <map code="0x1d" name="uni0000"/><!-- ???? -->
+      <map code="0x1e" name="uni0000"/><!-- ???? -->
+      <map code="0x1f" name="uni0000"/><!-- ???? -->
+      <map code="0x20" name="uni0000"/><!-- SPACE -->
+      <map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
+      <map code="0xa0" name="uni0000"/><!-- NO-BREAK SPACE -->
+      <map code="0x50ce" name="uni50CE"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x610f" name="uni610F"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x6a1a" name="uni6A1A"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+      <map code="0x82e8" name="uni82E8"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8552" name="uni8552"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="uni85A8"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+      <map code="0x8e59" name="uni8E59"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+      <map code="0x9a40" name="uni9A40"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+      <map code="0xf9dc" name="uniF9DC"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_12>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uv="0x9686" uvs="0xfe00" name="uniF9DC"/>
+      <map uv="0x9686" uvs="0xe0101" name="uniF9DC"/>
+    </cmap_format_14>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x0" name="uni0000"/><!-- ???? -->
+      <map code="0x1" name="uni0000"/><!-- ???? -->
+      <map code="0x2" name="uni0000"/><!-- ???? -->
+      <map code="0x3" name="uni0000"/><!-- ???? -->
+      <map code="0x4" name="uni0000"/><!-- ???? -->
+      <map code="0x5" name="uni0000"/><!-- ???? -->
+      <map code="0x6" name="uni0000"/><!-- ???? -->
+      <map code="0x7" name="uni0000"/><!-- ???? -->
+      <map code="0x8" name="uni0000"/><!-- ???? -->
+      <map code="0x9" name="uni0000"/><!-- ???? -->
+      <map code="0xa" name="uni0000"/><!-- ???? -->
+      <map code="0xb" name="uni0000"/><!-- ???? -->
+      <map code="0xc" name="uni0000"/><!-- ???? -->
+      <map code="0xd" name="uni0000"/><!-- ???? -->
+      <map code="0xe" name="uni0000"/><!-- ???? -->
+      <map code="0xf" name="uni0000"/><!-- ???? -->
+      <map code="0x10" name="uni0000"/><!-- ???? -->
+      <map code="0x11" name="uni0000"/><!-- ???? -->
+      <map code="0x12" name="uni0000"/><!-- ???? -->
+      <map code="0x13" name="uni0000"/><!-- ???? -->
+      <map code="0x14" name="uni0000"/><!-- ???? -->
+      <map code="0x15" name="uni0000"/><!-- ???? -->
+      <map code="0x16" name="uni0000"/><!-- ???? -->
+      <map code="0x17" name="uni0000"/><!-- ???? -->
+      <map code="0x18" name="uni0000"/><!-- ???? -->
+      <map code="0x19" name="uni0000"/><!-- ???? -->
+      <map code="0x1a" name="uni0000"/><!-- ???? -->
+      <map code="0x1b" name="uni0000"/><!-- ???? -->
+      <map code="0x1c" name="uni0000"/><!-- ???? -->
+      <map code="0x1d" name="uni0000"/><!-- ???? -->
+      <map code="0x1e" name="uni0000"/><!-- ???? -->
+      <map code="0x1f" name="uni0000"/><!-- ???? -->
+      <map code="0x20" name="uni0000"/><!-- SPACE -->
+      <map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
+      <map code="0xa0" name="uni0000"/><!-- NO-BREAK SPACE -->
+      <map code="0x50ce" name="uni50CE"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x610f" name="uni610F"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x6a1a" name="uni6A1A"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+      <map code="0x82e8" name="uni82E8"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8552" name="uni8552"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="uni85A8"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+      <map code="0x8e59" name="uni8E59"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+      <map code="0x9a40" name="uni9A40"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+      <map code="0xf9dc" name="uniF9DC"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="532" language="0" nGroups="43">
+      <map code="0x0" name="uni0000"/><!-- ???? -->
+      <map code="0x1" name="uni0000"/><!-- ???? -->
+      <map code="0x2" name="uni0000"/><!-- ???? -->
+      <map code="0x3" name="uni0000"/><!-- ???? -->
+      <map code="0x4" name="uni0000"/><!-- ???? -->
+      <map code="0x5" name="uni0000"/><!-- ???? -->
+      <map code="0x6" name="uni0000"/><!-- ???? -->
+      <map code="0x7" name="uni0000"/><!-- ???? -->
+      <map code="0x8" name="uni0000"/><!-- ???? -->
+      <map code="0x9" name="uni0000"/><!-- ???? -->
+      <map code="0xa" name="uni0000"/><!-- ???? -->
+      <map code="0xb" name="uni0000"/><!-- ???? -->
+      <map code="0xc" name="uni0000"/><!-- ???? -->
+      <map code="0xd" name="uni0000"/><!-- ???? -->
+      <map code="0xe" name="uni0000"/><!-- ???? -->
+      <map code="0xf" name="uni0000"/><!-- ???? -->
+      <map code="0x10" name="uni0000"/><!-- ???? -->
+      <map code="0x11" name="uni0000"/><!-- ???? -->
+      <map code="0x12" name="uni0000"/><!-- ???? -->
+      <map code="0x13" name="uni0000"/><!-- ???? -->
+      <map code="0x14" name="uni0000"/><!-- ???? -->
+      <map code="0x15" name="uni0000"/><!-- ???? -->
+      <map code="0x16" name="uni0000"/><!-- ???? -->
+      <map code="0x17" name="uni0000"/><!-- ???? -->
+      <map code="0x18" name="uni0000"/><!-- ???? -->
+      <map code="0x19" name="uni0000"/><!-- ???? -->
+      <map code="0x1a" name="uni0000"/><!-- ???? -->
+      <map code="0x1b" name="uni0000"/><!-- ???? -->
+      <map code="0x1c" name="uni0000"/><!-- ???? -->
+      <map code="0x1d" name="uni0000"/><!-- ???? -->
+      <map code="0x1e" name="uni0000"/><!-- ???? -->
+      <map code="0x1f" name="uni0000"/><!-- ???? -->
+      <map code="0x20" name="uni0000"/><!-- SPACE -->
+      <map code="0x21" name="exclam"/><!-- EXCLAMATION MARK -->
+      <map code="0xa0" name="uni0000"/><!-- NO-BREAK SPACE -->
+      <map code="0x50ce" name="uni50CE"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x610f" name="uni610F"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x6a1a" name="uni6A1A"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+      <map code="0x82e8" name="uni82E8"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8552" name="uni8552"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="uni85A8"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+      <map code="0x8e59" name="uni8E59"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+      <map code="0x9a40" name="uni9A40"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+      <map code="0xf9dc" name="uniF9DC"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_12>
+  </cmap>
+
+  <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">
+      Kanji-w0.00
+    </namerecord>
+    <namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w0.00
+    </namerecord>
+    <namerecord nameID="259" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w239.00
+    </namerecord>
+    <namerecord nameID="260" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w239.00
+    </namerecord>
+    <namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w240.00
+    </namerecord>
+    <namerecord nameID="262" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w240.00
+    </namerecord>
+    <namerecord nameID="263" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w439.00
+    </namerecord>
+    <namerecord nameID="264" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w439.00
+    </namerecord>
+    <namerecord nameID="265" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w440.00
+    </namerecord>
+    <namerecord nameID="266" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w440.00
+    </namerecord>
+    <namerecord nameID="267" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w499.00
+    </namerecord>
+    <namerecord nameID="268" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w499.00
+    </namerecord>
+    <namerecord nameID="269" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w500.00
+    </namerecord>
+    <namerecord nameID="270" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w500.00
+    </namerecord>
+    <namerecord nameID="271" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w599.00
+    </namerecord>
+    <namerecord nameID="272" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w599.00
+    </namerecord>
+    <namerecord nameID="273" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w600.00
+    </namerecord>
+    <namerecord nameID="274" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w600.00
+    </namerecord>
+    <namerecord nameID="275" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w669.00
+    </namerecord>
+    <namerecord nameID="276" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w669.00
+    </namerecord>
+    <namerecord nameID="277" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w670.00
+    </namerecord>
+    <namerecord nameID="278" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w670.00
+    </namerecord>
+    <namerecord nameID="279" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w699.00
+    </namerecord>
+    <namerecord nameID="280" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w699.00
+    </namerecord>
+    <namerecord nameID="281" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w700.00
+    </namerecord>
+    <namerecord nameID="282" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w700.00
+    </namerecord>
+    <namerecord nameID="283" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-799.00
+    </namerecord>
+    <namerecord nameID="284" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-799.00
+    </namerecord>
+    <namerecord nameID="285" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w800.00
+    </namerecord>
+    <namerecord nameID="286" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w800.00
+    </namerecord>
+    <namerecord nameID="287" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w889.00
+    </namerecord>
+    <namerecord nameID="288" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w889.00
+    </namerecord>
+    <namerecord nameID="289" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w890.00
+    </namerecord>
+    <namerecord nameID="290" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w890.00
+    </namerecord>
+    <namerecord nameID="291" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Kanji-w1000.00
+    </namerecord>
+    <namerecord nameID="292" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansJPVFTest-Kanji-w1000.00
+    </namerecord>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w0.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w0.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w0.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w0.00
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w0.00
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w0.00
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w239.00
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w239.00
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w240.00
+    </namerecord>
+    <namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w240.00
+    </namerecord>
+    <namerecord nameID="263" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w439.00
+    </namerecord>
+    <namerecord nameID="264" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w439.00
+    </namerecord>
+    <namerecord nameID="265" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w440.00
+    </namerecord>
+    <namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w440.00
+    </namerecord>
+    <namerecord nameID="267" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w499.00
+    </namerecord>
+    <namerecord nameID="268" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w499.00
+    </namerecord>
+    <namerecord nameID="269" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w500.00
+    </namerecord>
+    <namerecord nameID="270" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w500.00
+    </namerecord>
+    <namerecord nameID="271" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w599.00
+    </namerecord>
+    <namerecord nameID="272" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w599.00
+    </namerecord>
+    <namerecord nameID="273" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w600.00
+    </namerecord>
+    <namerecord nameID="274" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w600.00
+    </namerecord>
+    <namerecord nameID="275" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w669.00
+    </namerecord>
+    <namerecord nameID="276" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w669.00
+    </namerecord>
+    <namerecord nameID="277" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w670.00
+    </namerecord>
+    <namerecord nameID="278" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w670.00
+    </namerecord>
+    <namerecord nameID="279" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w699.00
+    </namerecord>
+    <namerecord nameID="280" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w699.00
+    </namerecord>
+    <namerecord nameID="281" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w700.00
+    </namerecord>
+    <namerecord nameID="282" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w700.00
+    </namerecord>
+    <namerecord nameID="283" platformID="3" platEncID="1" langID="0x409">
+      Kanji-799.00
+    </namerecord>
+    <namerecord nameID="284" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-799.00
+    </namerecord>
+    <namerecord nameID="285" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w800.00
+    </namerecord>
+    <namerecord nameID="286" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w800.00
+    </namerecord>
+    <namerecord nameID="287" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w889.00
+    </namerecord>
+    <namerecord nameID="288" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w889.00
+    </namerecord>
+    <namerecord nameID="289" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w890.00
+    </namerecord>
+    <namerecord nameID="290" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w890.00
+    </namerecord>
+    <namerecord nameID="291" platformID="3" platEncID="1" langID="0x409">
+      Kanji-w1000.00
+    </namerecord>
+    <namerecord nameID="292" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVFTest-Kanji-w1000.00
+    </namerecord>
+  </name>
+
+  <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>
+
+  <CFF2>
+    <major value="2"/>
+    <minor value="0"/>
+    <CFFFont name="CFF2Font">
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+          </Private>
+        </FontDict>
+        <FontDict index="2">
+          <Private>
+            <BlueValues value="0 0"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="1"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          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
+        </CharString>
+        <CharString name="exclam" fdSelectIndex="2">
+          134 755 107 18 2 blend
+          rmoveto
+          -48 -137 1 blend
+          hlineto
+          8 -565 16 50 2 blend
+          rlineto
+          32 106 1 blend
+          hlineto
+          -17 -202 -52 -67 2 blend
+          rmoveto
+          24 14 18 22 25 -14 18 -22 -23 -14 -21 -24 -18 13 -20 22 38 25 26 38 37 -28 25 -37 -34 -30 -22 -38 -40 27 -26 39 16 blend
+          hvcurveto
+        </CharString>
+        <CharString name="uni0000" fdSelectIndex="2">
+        </CharString>
+        <CharString name="uni50CE" fdSelectIndex="1">
+          5 vsindex
+          326 793 1 0 2 17 0 29 2 blend
+          rmoveto
+          -280 24 0 40 1 blend
+          vlineto
+          -47 16 -8 59 -31 0 -53 6 0 10 -13 0 -21 20 0 33 4 blend
+          vhcurveto
+          13 120 4 0 6 -46 0 -76 2 blend
+          0 13 4 0 7 1 blend
+          hhcurveto
+          49 10 20 82 4 12 0 19 12 0 20 3 0 5 2 0 3 4 0 8 5 blend
+          hvcurveto
+          -10 2 -11 5 -8 6 -12 0 -21 3 0 5 -21 0 -35 6 0 11 -9 0 -14 7 0 11 6 blend
+          rrcurveto
+          -75 19 0 32 1 blend
+          -3 -5 -10 -29 -24 -102 1 0 1 1 0 2 7 0 12 9 0 14 42 0 70 5 blend
+          0 -18 6 0 10 1 blend
+          hhcurveto
+          -38 -6 4 21 10 0 18 2 0 3 0 0 -1 4 0 7 4 blend
+          hvcurveto
+          280 -25 0 -41 1 blend
+          vlineto
+          -41 -464 -40 -8 -74 10 20 41 2 blend
+          rmoveto
+          -30 617 30 -50 -4 -90 -5 12 5 50 4 90 3 blend
+          vlineto
+          -661 -178 11 -4 12 4 -13 -7 2 blend
+          rmoveto
+          -30 689 30 -52 -3 -92 -11 0 -18 52 3 92 3 blend
+          vlineto
+          -481 284 -27 -2 -48 -32 36 -21 2 blend
+          rmoveto
+          -306 30 306 0 -13 0 60 2 103 0 13 0 3 blend
+          vlineto
+          218 0 -61 0 -102 -1 0 -1 2 blend
+          rmoveto
+          -306 30 306 0 -13 0 61 1 104 0 13 0 3 blend
+          vlineto
+          -417 358 -17 -1 -30 19 -43 -12 2 blend
+          rmoveto
+          -30 217 -116 -217 -30 247 176 -36 0 -61 -52 0 -87 50 0 84 52 0 87 -37 0 -62 -6 0 -10 23 0 39 7 blend
+          vlineto
+          75 -26 0 -44 1 blend
+          hmoveto
+          -280 24 0 40 1 blend
+          vlineto
+          -47 17 -8 60 -31 0 -53 5 0 9 -13 0 -21 20 0 33 4 blend
+          vhcurveto
+          12 125 5 0 8 -47 0 -78 2 blend
+          0 14 4 0 7 1 blend
+          hhcurveto
+          49 11 20 82 3 12 0 20 12 0 19 3 1 6 2 1 6 5 0 9 5 blend
+          hvcurveto
+          -9 2 -12 4 -8 7 -14 1 -22 3 0 5 -19 -1 -34 7 0 12 -9 0 -14 6 0 10 6 blend
+          rrcurveto
+          -75 19 -1 29 1 blend
+          -3 -5 -10 -30 -25 -105 1 -1 1 8 0 13 8 0 14 42 0 70 4 blend
+          0 -18 6 0 9 1 blend
+          hhcurveto
+          -40 -6 4 21 11 0 19 2 0 3 0 0 -1 4 1 8 4 blend
+          hvcurveto
+          280 -25 -1 -42 1 blend
+          vlineto
+          -16 -29 0 -48 1 blend
+          hmoveto
+          -30 217 -116 -217 -30 247 176 -36 0 -61 -50 0 -84 50 0 84 50 0 84 -37 0 -62 -3 0 -5 23 0 39 7 blend
+          vlineto
+          -424 -714 -19 0 -32 -12 0 -21 2 blend
+          rmoveto
+          -52 -54 -91 -49 -81 -33 8 -5 11 -13 4 -6 80 36 94 56 56 58 7 0 11 9 0 15 5 0 9 11 0 18 -2 0 -3 9 0 15 13 0 22 -11 0 -18 24 0 39 -22 0 -36 11 0 19 -12 0 -21 4 0 7 -4 0 -6 2 0 2 -2 0 -4 -1 0 -1 3 0 5 18 blend
+          rrcurveto
+          200 -7 -92 0 -154 -5 0 -8 2 blend
+          rmoveto
+          76 -41 90 -62 46 -42 -6 0 -10 5 0 8 -5 0 -7 6 0 10 -4 0 -7 4 0 7 6 blend
+          rrcurveto
+          22 23 -46 42 -91 60 -75 39 60 0 100 29 0 48 0 0 -1 -3 0 -5 3 0 5 -7 0 -11 6 0 11 -7 0 -11 8 blend
+          rlinecurve
+          -499 750 -48 0 -81 6 0 10 2 blend
+          rmoveto
+          -54 -167 -87 -164 -96 -108 7 -6 11 -12 4 -6 98 116 88 165 58 175 7 0 13 15 0 25 10 0 16 14 0 22 11 0 19 10 0 17 9 0 15 -20 0 -33 15 0 24 -44 0 -73 4 0 7 -18 0 -30 4 0 6 4 0 6 3 0 6 19 0 32 0 0 -1 1 0 1 18 blend
+          rrcurveto
+          -113 -214 -60 0 -100 -23 0 -37 2 blend
+          rmoveto
+          -691 30 718 20 0 33 64 0 108 43 0 72 3 blend
+          vlineto
+          -1 -1 0 -3 1 blend
+          2 rlineto
+        </CharString>
+        <CharString name="uni610F" fdSelectIndex="1">
+          1 vsindex
+          306 142 -19 1 -27 7 0 10 2 blend
+          rmoveto
+          -156 45 -2 64 1 blend
+          vlineto
+          -50 22 -8 79 -42 2 -59 8 -1 11 -18 0 -27 43 -1 62 4 blend
+          vhcurveto
+          17 186 8 -1 11 -68 3 -97 2 blend
+          0 18 8 0 12 1 blend
+          hhcurveto
+          70 13 25 114 5 22 -1 31 17 -1 24 2 1 4 0 -1 -1 7 0 10 5 blend
+          hvcurveto
+          -9 3 -12 4 -9 6 -20 1 -28 3 0 4 -31 1 -45 10 0 15 -13 0 -19 9 -1 13 6 blend
+          rrcurveto
+          -109 -4 -7 -13 -49 -38 -156 33 -1 47 -1 0 -1 1 0 1 2 0 3 10 0 15 9 0 13 60 -3 85 7 blend
+          0 -27 5 0 8 1 blend
+          hhcurveto
+          -59 -10 5 22 11 0 16 2 -1 2 -1 0 -2 4 0 6 4 blend
+          hvcurveto
+          157 -47 2 -67 1 blend
+          vlineto
+          63 34 -74 3 -106 -25 1 -36 2 blend
+          rmoveto
+          65 -30 74 -47 37 -37 -7 1 -10 5 -1 7 -3 0 -4 5 0 7 -3 0 -4 6 0 9 6 blend
+          rrcurveto
+          20 22 -37 36 -75 47 -65 28 48 -2 68 47 -2 67 -1 0 -1 -6 1 -8 2 0 3 -8 0 -11 8 -1 11 -6 0 -9 8 blend
+          rlinecurve
+          320 -64 -49 3 -69 -32 1 -46 2 blend
+          rmoveto
+          76 -49 83 -75 38 -12 0 -18 -6 1 -8 -13 0 -19 -4 0 -6 -9 1 -12 5 blend
+          -54 rrcurveto
+          23 19 -38 54 -84 73 -76 49 69 -3 98 35 -2 50 5 0 8 2 0 3 11 0 16 2 0 2 13 -1 18 2 0 4 8 blend
+          rlinecurve
+          -557 -5 -85 4 -121 -6 0 -9 2 blend
+          rmoveto
+          -28 -68 -50 -72 -77 -40 5 -1 6 1 0 1 3 1 5 6 0 9 13 -1 18 1 0 1 6 blend
+          rrcurveto
+          24 -17 79 42 47 74 31 71 62 -3 89 -42 2 -60 -7 1 -10 5 -1 7 -6 0 -8 1 0 1 -2 -1 -4 4 1 7 8 blend
+          rlinecurve
+          -117 625 -26 2 -36 42 -3 59 2 blend
+          rmoveto
+          -30 775 30 -57 3 -81 -3 0 -5 57 -3 81 3 blend
+          vlineto
+          -818 -176 -1 0 -2 12 0 18 2 blend
+          rmoveto
+          -30 869 30 -56 2 -81 3 0 5 56 -2 81 3 blend
+          vlineto
+          -455 258 -40 2 -57 -38 2 -54 2 blend
+          rmoveto
+          -99 30 99 -18 0 -27 81 -3 116 18 0 27 3 blend
+          vlineto
+          -236 -127 -60 2 -86 -18 0 -27 2 blend
+          rmoveto
+          26 -40 25 -53 9 -36 -12 1 -17 10 0 15 -10 -1 -15 13 0 19 -4 0 -6 11 -1 15 6 blend
+          rrcurveto
+          29 12 -10 35 -25 53 -27 39 76 -3 109 12 0 18 3 1 5 -10 0 -15 10 -1 14 -15 1 -21 11 0 16 -11 0 -16 8 blend
+          rlinecurve
+          393 2 -112 4 -161 -1 0 -2 2 blend
+          rmoveto
+          -16 -38 -31 -57 -23 -35 7 0 11 12 -1 17 13 -1 18 19 0 28 10 0 14 8 -1 11 6 blend
+          rrcurveto
+          27 -12 24 36 26 48 23 46 70 -2 101 -10 1 -14 -8 0 -12 -13 1 -18 -6 -1 -9 -17 0 -25 -1 1 -1 -10 1 -14 8 blend
+          rlinecurve
+          -504 -378 27 -1 39 -8 0 -12 2 blend
+          rmoveto
+          559 -94 -559 -110 5 -157 44 -2 63 110 -5 157 3 blend
+          hlineto
+          216 -52 2 -74 1 blend
+          vmoveto
+          559 -92 -559 -110 5 -157 43 -1 62 110 -5 157 3 blend
+          hlineto
+          -30 122 -75 3 -107 -4 0 -6 2 blend
+          rmoveto
+          -276 619 276 -26 0 -38 45 -2 64 26 0 38 3 blend
+          vlineto
+        </CharString>
+        <CharString name="uni6A1A" fdSelectIndex="1">
+          5 vsindex
+          51 612 -8 0 -14 29 0 49 2 blend
+          rmoveto
+          -30 -60 0 -100 1 blend
+          307 30 60 0 100 1 blend
+          vlineto
+          -149 228 -32 0 -53 -20 0 -34 2 blend
+          rmoveto
+          -918 30 918 -19 0 -32 62 0 103 19 0 32 3 blend
+          vlineto
+          -36 -238 -55 0 -91 -32 0 -53 2 blend
+          rmoveto
+          -31 -160 -74 -193 -68 -95 7 -5 10 -11 6 -8 70 101 74 203 33 160 6 0 10 25 0 42 13 0 21 23 0 37 4 0 7 1 0 2 8 0 14 -18 0 -30 13 0 21 -27 0 -44 4 0 7 -19 0 -32 1 0 2 6 0 10 -12 0 -20 -2 0 -3 -2 0 -4 -1 0 -2 18 blend
+          rrcurveto
+          4 -143 19 0 32 77 0 128 2 blend
+          rmoveto
+          -21 -16 25 -26 72 -92 21 -33 -23 0 -38 -34 0 -57 1 0 2 -15 0 -24 -12 0 -21 -6 0 -11 2 0 3 -18 0 -29 8 blend
+          rlinecurve
+          24 24 -18 25 -81 96 -22 22 28 0 48 63 0 105 2 0 2 -1 0 -2 1 0 3 10 0 16 1 0 1 1 0 2 8 blend
+          rlinecurve
+          157 278 1 0 1 -14 0 -23 2 blend
+          rmoveto
+          -30 559 30 -54 0 -90 -17 3 -23 54 0 90 3 blend
+          vlineto
+          -457 -518 29 -3 43 -9 -3 -20 2 blend
+          rmoveto
+          -30 176 30 -46 0 -77 -17 0 -27 46 0 77 3 blend
+          vlineto
+          -194 120 -3 0 -5 -42 37 -35 2 blend
+          rmoveto
+          -365 30 365 38 -29 45 53 0 88 -38 29 -45 3 blend
+          vlineto
+          135 508 -87 0 -146 33 -34 24 2 blend
+          rmoveto
+          -122 30 122 -19 0 -31 63 0 106 19 0 31 3 blend
+          vlineto
+          -115 -172 -60 0 -100 -27 34 -19 2 blend
+          rmoveto
+          -288 30 288 11 -24 18 50 0 83 -11 24 -18 3 blend
+          vlineto
+          148 -62 -2 -106 1 blend
+          hmoveto
+          -288 30 288 11 -24 18 50 0 83 -11 24 -18 3 blend
+          vlineto
+          156 -394 -30 2 -47 19 -34 6 2 blend
+          rmoveto
+          -52 -36 -89 -48 -61 -29 7 0 12 2 0 4 14 0 23 3 0 4 11 0 18 4 0 8 6 blend
+          rrcurveto
+          15 -21 62 28 86 41 57 44 25 0 42 -39 0 -66 -10 0 -17 -4 0 -6 -12 0 -19 -3 0 -5 -6 0 -11 -5 0 -9 8 blend
+          rlinecurve
+          -541 323 10 0 17 44 5 84 2 blend
+          rmoveto
+          -30 517 -150 -517 -30 547 210 -46 -2 -81 -74 0 -123 54 -13 80 74 0 123 -46 -2 -81 -19 0 -32 38 17 82 7 blend
+          vlineto
+          -232 -242 -10 0 -16 -28 29 -27 2 blend
+          rmoveto
+          -344 58 -32 71 1 blend
+          vlineto
+          -47 15 -9 54 -33 -2 -58 3 0 4 -15 0 -25 22 0 37 4 blend
+          vhcurveto
+          12 100 3 0 5 -47 0 -78 2 blend
+          0 12 4 0 6 1 blend
+          hhcurveto
+          48 10 25 102 3 12 0 20 11 0 19 4 1 9 11 -1 16 5 0 8 5 blend
+          hvcurveto
+          -9 3 -11 4 -8 6 -14 0 -23 3 -1 5 -23 1 -37 8 1 15 -8 -1 -15 8 0 12 6 blend
+          rrcurveto
+          -97 11 1 20 1 blend
+          -3 -4 -14 -29 -21 -84 0 0 1 1 -1 1 10 0 16 10 1 17 43 -1 71 5 blend
+          0 -16 7 0 12 1 blend
+          hhcurveto
+          -33 -6 5 22 13 0 22 3 0 5 -1 0 -2 4 0 7 4 blend
+          hvcurveto
+          344 -59 34 -71 1 blend
+          vlineto
+          -346 -371 -24 0 -41 65 -34 78 2 blend
+          rmoveto
+          10 -31 77 16 100 22 99 21 3 0 5 -54 0 -90 -2 0 -3 -3 0 -5 -9 0 -15 -6 0 -10 -10 0 -17 -5 0 -8 8 blend
+          rlinecurve
+          -2 29 -108 -22 -104 -22 -72 -13 -3 0 -5 52 0 86 9 0 16 6 0 10 8 0 13 6 0 11 4 0 6 4 0 6 8 blend
+          rlinecurve
+          -16 767 -44 0 -72 -13 0 -21 2 blend
+          rmoveto
+          -316 -6 0 -11 1 blend
+          vlineto
+          -142 -7 -194 -74 -141 2 0 2 -2 0 -2 2 0 4 6 0 9 4 blend
+          vhcurveto
+          8 -3 13 -7 5 -6 13 0 21 -7 0 -11 25 0 43 -20 0 -34 11 0 17 -10 0 -17 6 blend
+          rrcurveto
+          75 143 10 205 145 4 0 7 3 0 5 2 0 4 21 0 35 9 0 15 5 blend
+          vvcurveto
+          316 6 0 11 1 blend
+          vlineto
+        </CharString>
+        <CharString name="uni82E8" fdSelectIndex="1">
+          3 vsindex
+          58 743 -1 0 -2 26 0 60 2 blend
+          rmoveto
+          -30 887 30 -43 0 -99 2 0 5 43 0 99 3 blend
+          vlineto
+          -630 96 -29 0 -66 -19 0 -44 2 blend
+          rmoveto
+          -207 30 207 -2 -2 -9 50 0 114 2 2 9 3 blend
+          vlineto
+          305 -44 0 -100 1 blend
+          hmoveto
+          -207 30 207 -2 -2 -9 51 0 115 2 2 9 3 blend
+          vlineto
+          -521 -240 -36 0 -82 2 0 4 2 blend
+          rmoveto
+          -206 -5 0 -10 1 blend
+          vlineto
+          -137 -15 -184 -109 -136 5 0 11 3 0 6 5 0 10 -1 0 -1 8 0 19 5 blend
+          vhcurveto
+          7 -3 12 -9 5 -6 12 0 27 -6 0 -13 22 0 51 -15 0 -35 10 0 21 -8 0 -19 6 blend
+          rrcurveto
+          112 139 18 194 141 3 0 7 -4 0 -8 1 0 3 11 0 24 4 0 10 5 blend
+          vvcurveto
+          207 5 0 11 1 blend
+          vlineto
+          -19 -18 0 -42 1 blend
+          hmoveto
+          -30 670 -153 -670 -30 700 213 -38 0 -87 -64 0 -144 48 0 111 64 0 144 -38 0 -87 -14 0 -30 28 0 63 7 blend
+          vlineto
+          -531 -249 -15 0 -36 -23 0 -51 2 blend
+          rmoveto
+          -343 50 0 112 1 blend
+          vlineto
+          -66 31 -12 105 -29 0 -66 6 0 14 -13 0 -28 29 0 66 4 blend
+          vhcurveto
+          23 278 5 0 12 -59 0 -134 2 blend
+          0 24 6 0 14 1 blend
+          hhcurveto
+          96 15 31 123 8 20 0 44 11 0 26 4 0 8 14 0 32 5 0 11 5 blend
+          hvcurveto
+          -9 3 -13 4 -9 7 -13 0 -30 2 0 5 -21 0 -48 8 0 17 -10 -1 -23 6 0 15 6 blend
+          rrcurveto
+          -117 -6 -11 -21 -69 -56 -236 8 0 18 -1 1 -1 1 -1 1 3 0 7 3 1 9 7 0 15 49 0 112 7 blend
+          0 -41 4 0 8 1 blend
+          hhcurveto
+          -84 -16 11 37 4 0 10 2 0 5 -3 0 -7 1 0 2 4 blend
+          hvcurveto
+          343 -51 0 -115 1 blend
+          vlineto
+          444 -47 -59 0 -135 26 0 59 2 blend
+          rmoveto
+          -101 -52 -195 -56 -169 -40 4 -7 5 -10 3 -7 172 40 193 54 120 56 4 0 8 3 0 7 18 0 43 9 0 19 12 0 26 8 0 19 5 0 12 -10 0 -22 7 0 15 -18 0 -41 1 0 3 -11 0 -25 -8 0 -19 -9 0 -21 -8 0 -18 -8 0 -19 5 0 11 0 0 1 18 blend
+          rrcurveto
+        </CharString>
+        <CharString name="uni8552" fdSelectIndex="1">
+          2 vsindex
+          53 761 -3 0 -3 36 0 40 2 blend
+          rmoveto
+          -30 896 30 -65 -1 -73 5 0 5 65 1 73 3 blend
+          vlineto
+          -631 78 -46 0 -52 -22 0 -24 2 blend
+          rmoveto
+          -162 30 162 -8 -32 -41 98 1 111 8 32 41 3 blend
+          vlineto
+          296 -105 -1 -118 1 blend
+          hmoveto
+          -162 30 162 -8 -32 -41 100 1 112 8 32 41 3 blend
+          vlineto
+          -47 -217 -23 0 -26 -57 -1 -64 2 blend
+          rmoveto
+          209 -109 -209 -101 -1 -113 72 1 81 101 1 113 3 blend
+          hlineto
+          -235 109 24 0 27 -72 -1 -81 2 blend
+          rmoveto
+          205 -109 -205 -99 -1 -111 72 1 81 99 1 111 3 blend
+          hlineto
+          -227 109 18 1 21 -72 -1 -81 2 blend
+          rmoveto
+          197 -109 -197 -93 -2 -105 72 1 81 93 2 105 3 blend
+          hlineto
+          -30 139 -87 -2 -98 -15 0 -17 2 blend
+          rmoveto
+          -169 731 169 -41 0 -46 38 0 42 41 0 46 3 blend
+          vlineto
+          -650 -375 62 1 70 -32 0 -36 2 blend
+          rmoveto
+          571 -76 -571 -159 -1 -179 48 0 54 159 1 179 3 blend
+          hlineto
+          -30 -38 0 -43 1 blend
+          vmoveto
+          571 -77 -571 -159 -1 -179 48 0 54 159 1 179 3 blend
+          hlineto
+          287 -66 -1 -74 1 blend
+          vmoveto
+          571 -74 -571 -159 -1 -179 46 0 52 159 1 179 3 blend
+          hlineto
+          -30 104 -99 -1 -111 -4 0 -5 2 blend
+          rmoveto
+          -347 631 347 -18 0 -20 45 0 50 18 0 20 3 blend
+          vlineto
+          -216 -389 -86 -1 -96 -31 0 -35 2 blend
+          rmoveto
+          127 -34 121 -39 72 -31 -17 0 -19 2 0 2 -13 0 -15 -2 0 -2 -13 0 -15 3 0 3 6 blend
+          rrcurveto
+          31 22 -78 32 -126 39 -121 136 1 153 39 0 44 1 0 1 -3 0 -3 -8 0 -9 4 0 5 9 0 10 7 blend
+          31 rlinecurve
+          -258 -1 -67 -1 -75 0 0 -1 2 blend
+          rmoveto
+          -81 -39 -128 -36 -107 -23 8 -6 12 -12 5 -6 103 25 130 41 86 43 9 0 10 6 0 7 3 0 4 7 0 8 -4 0 -5 7 0 8 19 0 22 -14 0 -16 32 0 36 -32 0 -36 17 0 19 -19 0 -21 3 0 3 -1 0 -1 5 0 6 2 0 2 1 0 1 4 0 5 18 blend
+          rrcurveto
+        </CharString>
+        <CharString name="uni85A8" fdSelectIndex="1">
+          2 vsindex
+          53 761 -3 0 -3 31 0 35 2 blend
+          rmoveto
+          -30 896 30 -76 -1 -86 5 0 5 76 1 86 3 blend
+          vlineto
+          -802 -461 2 0 2 -23 0 -26 2 blend
+          rmoveto
+          -30 703 30 -53 0 -60 3 0 4 53 0 60 3 blend
+          vlineto
+          -532 539 -58 -1 -65 6 0 7 2 blend
+          rmoveto
+          -171 30 171 -16 -19 -36 102 1 114 16 19 36 3 blend
+          vlineto
+          299 -100 -1 -112 1 blend
+          hmoveto
+          -171 30 171 -16 -19 -36 102 1 115 16 19 36 3 blend
+          vlineto
+          -46 -219 -34 0 -39 -64 -1 -72 2 blend
+          rmoveto
+          204 -121 -204 -110 -1 -123 83 1 93 110 1 123 3 blend
+          hlineto
+          -230 121 33 1 38 -83 -1 -93 2 blend
+          rmoveto
+          200 -121 -200 -108 -2 -122 83 1 93 108 2 122 3 blend
+          hlineto
+          -222 121 27 -1 30 -83 -1 -93 2 blend
+          rmoveto
+          192 -121 -192 -101 -1 -114 83 1 93 101 1 114 3 blend
+          hlineto
+          -30 151 -87 -1 -98 -29 0 -33 2 blend
+          rmoveto
+          -181 716 181 -24 0 -27 11 0 12 24 0 27 3 blend
+          vlineto
+          -788 -240 -17 0 -19 9 0 11 2 blend
+          rmoveto
+          -130 30 100 786 -100 30 130 -37 0 -42 88 1 99 -20 0 -23 -150 -1 -168 20 0 23 95 1 106 37 0 42 7 blend
+          vlineto
+          -610 -123 -56 -1 -63 -44 0 -50 2 blend
+          rmoveto
+          -50 -62 -93 -73 -118 -54 8 -4 10 -9 6 -7 121 58 92 75 59 70 9 0 11 13 0 15 19 0 21 29 0 32 9 0 10 22 0 25 12 0 14 -11 0 -12 19 0 21 -26 0 -30 7 0 8 -16 0 -18 3 0 3 -13 0 -14 -10 0 -11 -19 0 -21 -2 0 -2 8 0 8 18 blend
+          rrcurveto
+          124 -78 -89 -1 -100 32 0 36 2 blend
+          rmoveto
+          -7 -6 0 -6 1 blend
+          vlineto
+          -65 -139 -176 -81 -162 -31 6 -6 8 -12 3 -8 168 37 178 84 72 154 16 0 17 30 0 34 36 0 41 26 0 29 -7 0 -8 12 0 13 12 0 14 -16 0 -18 15 0 16 -30 0 -33 5 0 6 -18 0 -21 26 0 29 -5 0 -5 -23 0 -26 -12 0 -14 -5 0 -5 6 0 7 18 blend
+          rrcurveto
+          -19 11 -6 -2 -47 0 -53 13 0 15 -13 0 -15 0 0 -1 4 blend
+          rlineto
+          -333 -72 75 1 85 -55 0 -61 2 blend
+          rmoveto
+          65 -25 75 -46 38 -35 -35 0 -40 8 0 9 -38 0 -42 15 0 17 -18 0 -21 14 0 15 6 blend
+          rrcurveto
+          26 19 -39 34 -76 45 -64 25 49 0 56 31 0 35 19 0 21 -14 0 -16 39 0 44 -18 0 -20 32 0 36 -9 0 -10 8 blend
+          rlinecurve
+          72 55 -55 0 -62 28 0 31 2 blend
+          rmoveto
+          -30 -30 -42 0 -47 -42 0 -47 2 blend
+          rlineto
+          269 30 -14 0 -16 42 0 47 2 blend
+          hlineto
+          74 74 13 0 15 -22 0 -24 2 blend
+          rmoveto
+          -276 80 1 90 1 blend
+          vlineto
+          -52 21 -9 77 -48 0 -54 8 0 9 -21 0 -24 44 0 49 4 blend
+          vhcurveto
+          16 182 8 0 9 -90 -1 -101 2 blend
+          0 18 8 0 9 1 blend
+          hhcurveto
+          62 12 21 88 4 25 0 28 20 0 22 6 0 7 10 0 11 9 0 10 5 blend
+          hvcurveto
+          -9 2 -12 5 -8 6 -24 0 -26 4 0 5 -34 0 -39 12 0 13 -16 0 -18 10 0 11 6 blend
+          rrcurveto
+          -81 25 0 28 1 blend
+          -4 -6 -11 -41 -37 -154 -1 0 -1 0 1 1 11 -1 12 15 1 17 79 1 89 5 blend
+          0 -26 9 0 10 1 blend
+          hhcurveto
+          -56 -9 6 25 17 0 19 2 0 3 -1 -1 -2 4 0 5 4 blend
+          hvcurveto
+          276 -81 -1 -91 1 blend
+          vlineto
+          278 -62 -114 -1 -128 32 0 36 2 blend
+          rmoveto
+          -66 -32 -126 -33 -107 -23 5 -7 5 -10 2 -7 110 22 126 32 81 36 10 0 11 7 0 8 30 0 34 11 0 12 21 0 23 9 0 10 7 0 8 -14 0 -16 9 0 10 -27 0 -30 3 0 4 -15 0 -17 -15 0 -17 -10 0 -11 -12 0 -14 -11 0 -12 3 0 4 -3 0 -4 18 blend
+          rrcurveto
+        </CharString>
+        <CharString name="uni8E59" fdSelectIndex="1">
+          3 vsindex
+          193 296 41 0 93 -8 0 -19 2 blend
+          rmoveto
+          625 -94 -625 -84 -1 -192 27 0 63 84 1 192 3 blend
+          hlineto
+          -30 124 -48 0 -110 -6 0 -14 2 blend
+          rmoveto
+          -154 685 154 -15 0 -34 16 0 38 15 0 34 3 blend
+          vlineto
+          -365 -132 -33 0 -76 1 1 3 2 blend
+          rmoveto
+          -232 -7 -1 -16 1 blend
+          vlineto
+          30 -5 51 0 117 -11 0 -27 2 blend
+          rlineto
+          237 18 1 43 1 blend
+          vlineto
+          -11 -92 -27 0 -62 1 -1 2 2 blend
+          rmoveto
+          -30 397 30 -22 0 -52 -12 0 -27 22 0 52 3 blend
+          vlineto
+          -760 647 25 0 56 -4 0 -9 2 blend
+          rmoveto
+          -30 811 30 -28 0 -65 -12 0 -27 28 0 65 3 blend
+          vlineto
+          -823 -13 0 -29 1 blend
+          hmoveto
+          -143 12 0 27 1 blend
+          vlineto
+          -83 -13 -107 -75 -82 4 0 9 3 0 6 5 1 12 -1 0 -1 5 -1 11 5 blend
+          vhcurveto
+          7 -4 11 -9 5 -6 10 0 21 -5 0 -12 20 0 46 -17 0 -38 6 0 15 -8 0 -18 6 blend
+          rrcurveto
+          79 5 0 11 1 blend
+          85 16 118 88 1 1 3 9 0 19 6 0 15 3 blend
+          vvcurveto
+          143 -11 0 -25 1 blend
+          vlineto
+          199 -25 -46 -1 -106 -23 0 -54 2 blend
+          rmoveto
+          -167 30 37 0 85 1 blend
+          167 vlineto
+          -14 -59 -18 0 -42 8 0 18 2 blend
+          rmoveto
+          -30 185 30 -12 0 -26 -4 0 -9 12 0 26 3 blend
+          vlineto
+          -365 -96 10 0 22 7 0 17 2 blend
+          rmoveto
+          -30 392 30 -17 0 -39 -4 0 -9 17 0 39 3 blend
+          vlineto
+          -218 -10 -15 0 -33 -6 0 -13 2 blend
+          rmoveto
+          -160 23 0 51 1 blend
+          vlineto
+          -8 -2 0 0 -1 1 blend
+          -3 -11 -1 1 0 3 0 0 1 2 blend
+          vhcurveto
+          -11 -1 -30 2 0 4 1 0 1 4 0 10 3 blend
+          0 -47 13 0 30 1 blend
+          1 5 -9 6 -10 2 -9 4 0 8 -6 0 -13 6 0 13 -11 0 -25 2 0 6 -8 0 -19 6 blend
+          rrcurveto
+          50 30 -5 0 -11 1 0 2 2 blend
+          0 6 17 3 0 8 5 1 12 2 blend
+          hvcurveto
+          17 5 4 9 21 6 -1 12 4 0 9 1 0 3 4 0 8 11 0 25 5 blend
+          vvcurveto
+          159 -21 0 -46 1 blend
+          vlineto
+          -132 -50 -39 0 -88 1 0 1 2 blend
+          rmoveto
+          -25 -42 -40 -39 -44 -30 8 -4 13 -10 5 -4 41 6 0 12 3 0 8 7 0 16 3 0 5 5 0 13 1 0 4 6 0 13 -3 0 -8 10 0 22 -6 0 -14 5 0 12 -5 0 -10 -3 0 -8 13 blend
+          30 45 -7 0 -14 1 blend
+          47 26 45 -3 0 -8 1 0 1 2 blend
+          rrcurveto
+          153 -7 -13 0 -30 -1 0 -2 2 blend
+          rmoveto
+          35 -27 38 -39 18 -28 -8 3 -11 3 -5 -3 -9 3 -14 6 -7 -3 -5 1 -9 4 -4 0 6 blend
+          rrcurveto
+          24 18 -18 27 -39 39 -34 25 23 1 55 6 -1 12 4 -1 8 -3 4 1 9 -3 13 -6 7 2 7 -3 9 -4 5 4 8 blend
+          rlinecurve
+          115 330 -53 -1 -124 9 1 21 2 blend
+          rmoveto
+          14 -286 131 -209 160 0 50 1 18 34 6 108 -9 3 -11 5 -9 7 -4 -92 -9 -34 -31 -1 -137 -2 -126 185 -12 281 3 0 8 6 0 14 5 0 10 -10 0 -22 -3 0 -6 0 0 -1 14 0 33 -1 0 -1 11 0 23 -3 0 -8 5 0 12 10 0 24 -10 0 -23 3 0 7 -14 0 -32 8 0 18 -8 0 -17 8 0 17 0 0 -1 11 0 26 0 0 1 4 0 9 5 0 11 1 0 1 29 0 67 0 1 2 8 0 17 0 -1 -1 -2 0 -4 -37 0 -85 30 blend
+          rrcurveto
+          207 -169 -37 0 -85 -4 0 -9 2 blend
+          rmoveto
+          -61 -129 -111 -108 -121 -69 7 -5 12 -11 5 -6 119 74 113 110 66 136 4 15 19 8 14 33 4 28 29 8 5 22 2 28 27 6 2 17 8 1 20 -6 0 -14 14 1 34 -13 -1 -31 6 0 15 -7 0 -17 0 -28 -23 -4 -2 -10 0 -27 -20 -1 -3 -5 -1 -16 -12 -1 -15 -19 18 blend
+          rrcurveto
+          -156 153 -20 -2 -49 -2 0 -3 2 blend
+          rmoveto
+          52 -15 63 -26 34 -1 0 -3 -1 0 -1 0 0 1 0 0 -2 0 0 -2 5 blend
+          -21 rrcurveto
+          15 27 -34 20 -64 24 -51 14 21 0 48 20 0 47 -1 0 -1 1 0 1 0 0 -1 0 0 1 1 0 3 -1 0 -2 8 blend
+          rlinecurve
+          -453 -763 1 0 2 12 0 27 2 blend
+          rmoveto
+          -25 -16 -31 0 -71 -7 0 -17 2 blend
+          rlineto
+          -100 89 146 -18 233 -21 0 -46 -5 0 -12 -13 0 -29 -4 0 -9 -8 0 -18 5 blend
+          hhcurveto
+          249 23 0 53 1 blend
+          hlineto
+          2 8 6 14 6 8 -35 0 -207 2 -1 3 11 0 25 5 1 12 17 0 38 4 0 10 8 0 18 -16 0 -37 -1 0 -3 -1 0 -2 9 blend
+          0 -22 -14 0 -32 1 blend
+          0 -214 0 -150 15 -78 89 24 0 55 0 0 1 18 0 40 -2 0 -5 12 0 28 -1 0 -2 6 blend
+          rrcurveto
+          5 62 -50 0 -114 -10 0 -22 2 blend
+          rmoveto
+          -30 -97 -92 -60 -107 -36 8 -6 12 -11 4 -6 105 41 99 65 32 106 5 0 12 7 0 15 15 0 34 1 0 3 7 0 16 1 0 2 10 0 22 -6 0 -15 18 0 41 -17 0 -37 8 0 18 -8 0 -19 -2 0 -5 4 0 9 -12 0 -27 7 0 16 -1 0 -2 6 0 14 18 blend
+          rrcurveto
+        </CharString>
+        <CharString name="uni9A40" fdSelectIndex="1">
+          1 vsindex
+          55 767 2 0 3 37 -2 55 2 blend
+          rmoveto
+          -30 892 30 -44 2 -62 -6 0 -9 44 -2 62 3 blend
+          vlineto
+          -637 72 -28 1 -40 -26 2 -39 2 blend
+          rmoveto
+          -153 30 153 -27 0 -27 77 -2 111 27 0 27 3 blend
+          vlineto
+          315 -89 3 -128 1 blend
+          hmoveto
+          -153 30 153 -27 0 -27 79 -3 113 27 0 27 3 blend
+          vlineto
+          -462 -288 8 0 12 -11 0 -16 2 blend
+          rmoveto
+          571 -62 -571 -102 3 -147 27 -1 39 102 -3 147 3 blend
+          hlineto
+          152 -29 1 -42 1 blend
+          vmoveto
+          571 -60 -571 -102 3 -147 26 -1 37 102 -3 147 3 blend
+          hlineto
+          -30 -71 2 -102 1 blend
+          90 rmoveto
+          -212 631 212 -23 1 -32 45 -2 64 23 -1 32 3 blend
+          vlineto
+          -776 -263 -22 1 -31 -4 0 -5 2 blend
+          rmoveto
+          -30 905 30 -42 2 -60 10 0 14 42 -2 60 3 blend
+          vlineto
+          -716 -160 36 -1 52 -26 2 -37 2 blend
+          rmoveto
+          -30 554 30 -13 0 -19 -59 2 -85 13 0 19 3 blend
+          vlineto
+          -554 -78 59 -2 85 5 -1 7 2 blend
+          rmoveto
+          -30 563 30 -13 1 -19 -56 1 -81 13 -1 19 3 blend
+          vlineto
+          -578 -79 2 1 4 6 0 8 2 blend
+          rmoveto
+          -30 617 30 -27 1 -39 4 -1 5 27 -1 39 3 blend
+          vlineto
+          -477 382 -24 2 -34 8 0 12 2 blend
+          rmoveto
+          -46 -92 -113 -104 -167 -65 7 -5 10 -9 5 -8 172 70 111 106 55 101 6 -1 8 -5 -1 -8 17 0 25 11 0 16 -3 -1 -5 6 0 9 12 0 18 -11 0 -16 18 0 26 -27 1 -39 6 -1 8 -16 1 -23 14 0 20 3 0 5 -6 0 -8 1 0 1 3 0 4 28 -1 41 18 blend
+          rrcurveto
+          298 -65 -24 0 -35 3 0 4 2 blend
+          rmoveto
+          -25 -12 -55 2 -79 -15 0 -22 2 blend
+          rlineto
+          62 -80 121 -81 100 -38 5 8 9 11 7 6 -101 33 -119 76 -59 77 2 0 3 -14 1 -20 -10 1 -14 2 0 3 20 0 29 1 0 2 9 -1 13 18 -1 25 20 -1 28 26 -1 38 14 0 21 14 -1 19 -13 0 -19 -7 0 -10 9 0 13 -18 2 -25 4 -1 5 -7 0 -10 18 blend
+          rrcurveto
+          -211 -88 -39 3 -55 -12 1 -17 2 blend
+          rmoveto
+          -239 30 239 -2 -1 -4 69 -4 98 2 1 4 3 blend
+          vlineto
+          316 -223 -74 3 -106 11 -1 15 2 blend
+          rmoveto
+          -6 -4 0 -6 1 blend
+          vlineto
+          -8 -87 -7 -34 -10 -10 2 0 3 24 -1 35 -1 1 -1 6 0 9 1 -1 1 1 0 1 6 blend
+          rrcurveto
+          -6 -1 0 -1 1 blend
+          -6 -6 -1 -12 2 0 3 1 blend
+          hhcurveto
+          -11 -31 1 0 1 10 0 15 2 blend
+          1 3 -34 0 -1 -1 9 0 13 2 blend
+          hvcurveto
+          5 -8 3 -13 6 -1 8 -11 1 -16 5 0 8 -19 1 -26 4 blend
+          1 -8 28 -2 30 -1 14 1 -14 1 -20 7 -1 9 1 0 1 2 0 3 2 -1 2 3 1 5 0 1 1 7 blend
+          21 0 10 4 10 9 16 15 7 35 2 -1 2 8 -1 11 2 0 3 5 0 7 5 0 8 3 -1 4 3 0 4 2 1 4 3 0 4 9 blend
+          9 89 -15 1 -21 1 blend
+          rrcurveto
+          7 1 1 12 6 -1 8 1 0 1 1 -1 1 9 0 13 4 blend
+          0 hhcurveto
+          -660 -34 -57 3 -82 -8 0 -11 2 blend
+          rmoveto
+          -17 -46 1 0 2 7 0 10 2 blend
+          -32 -46 -46 5 0 7 5 0 7 2 blend
+          -23 rrcurveto
+          20 -21 52 28 31 51 17 46 56 -2 81 -24 0 -35 -4 -1 -7 0 1 1 -4 1 -5 -7 0 -10 1 -1 1 0 0 -1 8 blend
+          rlinecurve
+          110 -3 -67 3 -96 1 0 2 2 blend
+          rmoveto
+          13 -38 10 -49 0 -32 -3 0 -4 4 -1 5 -2 0 -3 2 1 4 -1 1 -1 3 0 4 6 blend
+          rrcurveto
+          29 6 55 -3 78 8 -1 11 2 blend
+          -1 31 -10 50 -15 37 -3 0 -4 0 1 1 -4 0 -6 3 -1 4 -4 1 -5 5 blend
+          rlinecurve
+          113 -6 -56 3 -80 -7 0 -10 2 blend
+          rmoveto
+          22 -32 20 -44 7 -30 2 0 3 1 -1 1 3 -1 3 1 0 1 2 0 3 5 blend
+          rrcurveto
+          28 10 -8 29 -21 44 -23 32 48 -2 69 15 0 22 -2 1 -2 -1 0 -2 0 -1 -1 -5 1 -6 -1 1 -1 -4 0 -6 8 blend
+          rlinecurve
+          117 -5 -45 1 -65 -17 1 -24 2 blend
+          rmoveto
+          25 -23 -1 0 -1 2 -1 2 2 blend
+          27 -32 13 -23 -2 1 -2 1 0 2 2 blend
+          rrcurveto
+          21 14 -12 44 -2 63 20 -1 28 0 0 -1 3 blend
+          22 -27 32 -26 22 -2 0 -2 -2 0 -3 1 0 1 -2 1 -2 4 blend
+          rlinecurve
+          -381 267 39 -1 56 7 -1 10 2 blend
+          rmoveto
+          -16 -30 -33 1 -47 -23 1 -33 2 blend
+          rlineto
+          498 30 -42 1 -61 23 -1 33 2 blend
+          hlineto
+          -516 -23 21 0 31 -14 0 -21 2 blend
+          rmoveto
+          -224 30 247 6 0 9 75 -4 106 10 0 14 3 blend
+          vlineto
+        </CharString>
+        <CharString name="uniF9DC" fdSelectIndex="1">
+          4 vsindex
+          529 746 23 0 29 30 4 43 2 blend
+          rmoveto
+          -30 320 30 -58 0 -73 -29 0 -36 58 0 73 3 blend
+          vlineto
+          -397 -495 15 0 18 12 -4 10 2 blend
+          rmoveto
+          -30 442 30 -56 0 -71 21 0 27 56 0 71 3 blend
+          vlineto
+          -420 149 -6 0 -8 6 -4 2 2 blend
+          rmoveto
+          -30 374 30 -54 0 -68 -25 0 -31 54 0 68 3 blend
+          vlineto
+          -514 -420 34 0 42 -3 4 1 2 blend
+          rmoveto
+          -30 626 30 -66 0 -82 -29 0 -36 66 0 82 3 blend
+          vlineto
+          -531 144 15 0 19 -9 0 -11 2 blend
+          rmoveto
+          -30 460 30 -55 0 -69 -4 0 -5 55 0 69 3 blend
+          vlineto
+          -53 622 -42 0 -53 -6 4 -2 2 blend
+          rmoveto
+          -7 -9 0 -12 1 blend
+          vlineto
+          -86 -171 -222 -118 -188 -45 7 -7 8 -11 3 -8 192 51 224 119 94 187 14 0 18 37 0 46 27 0 34 19 0 24 -7 0 -9 5 0 7 15 0 18 -16 0 -20 17 0 22 -32 0 -40 9 0 11 -19 0 -24 21 0 26 3 0 3 -17 0 -21 -9 0 -11 2 0 2 -3 0 -4 18 blend
+          rrcurveto
+          -19 12 -6 -2 -55 0 -68 27 0 34 -12 0 -15 -3 0 -3 4 blend
+          rlineto
+          -323 -32 55 0 69 -25 0 -32 2 blend
+          rmoveto
+          -25 -11 -68 0 -86 -23 0 -28 2 blend
+          rlineto
+          83 -154 177 -116 201 -44 4 8 9 12 7 6 -200 39 -177 113 -79 147 11 0 14 12 0 15 -18 0 -22 21 0 26 -1 0 -1 4 0 5 11 0 13 21 0 26 21 0 27 32 0 40 17 0 21 16 0 20 9 0 11 -10 0 -12 17 0 21 -36 0 -45 1 0 2 -37 0 -47 18 blend
+          rrcurveto
+          59 127 -46 0 -58 9 -4 6 2 blend
+          rmoveto
+          -40 -82 -80 -104 -112 -75 8 -4 10 -9 6 -7 115 80 2 0 2 8 0 10 7 0 9 23 0 29 2 0 3 16 0 20 16 0 20 -12 0 -15 26 0 32 -30 0 -37 10 0 13 -18 0 -23 8 0 10 -4 0 -5 14 blend
+          80 106 47 90 -13 0 -16 11 0 13 14 0 17 3 blend
+          rrcurveto
+          -129 -493 -106 -5 -137 21 6 34 2 blend
+          rmoveto
+          -27 -73 -43 -71 -51 -50 8 -5 13 -9 5 -5 49 52 47 77 29 77 6 0 8 11 0 14 7 0 8 8 0 10 5 0 7 8 0 10 16 0 20 -8 0 -10 28 0 35 -17 -1 -22 15 0 18 -11 0 -14 -3 0 -4 -4 0 -5 -2 0 -2 -1 0 -1 -3 0 -4 -3 1 -3 18 blend
+          rrcurveto
+          124 -1 -66 4 -77 10 15 31 2 blend
+          rmoveto
+          -374 30 374 4 0 5 84 0 105 -4 0 -5 3 blend
+          vlineto
+          -586 460 -72 0 -90 2 -21 -24 2 blend
+          rmoveto
+          -875 30 845 209 30 -27 0 -33 77 0 96 -53 0 -66 -79 0 -99 80 0 99 5 blend
+          vlineto
+          -8 -29 0 -36 1 blend
+          hmoveto
+          -7 -29 0 -36 1 blend
+          vlineto
+          -28 -75 -43 -102 -46 -95 14 0 17 10 0 13 11 0 14 -41 0 -51 17 0 22 4 0 5 6 blend
+          rrcurveto
+          89 -91 24 -74 -63 -32 0 -40 23 0 28 -11 0 -14 10 0 13 17 0 21 5 blend
+          vvcurveto
+          -33 -6 -35 -19 -13 3 0 4 1 0 1 15 0 18 7 0 9 4 0 5 5 blend
+          vhcurveto
+          -10 -6 -12 3 0 3 0 0 1 1 0 2 3 blend
+          -3 -14 -1 -20 -2 -26 1 -29 4 0 5 1 0 1 7 0 9 2 0 2 13 0 16 -1 0 -1 11 0 14 7 blend
+          2 7 -9 4 -13 11 0 13 -21 0 -26 5 0 6 -33 0 -41 4 blend
+          1 -8 22 -2 27 0 22 -21 0 -27 1 0 2 1 0 1 -3 0 -4 0 0 1 -4 0 -5 6 blend
+          2 19 2 17 5 12 9 3 0 4 2 0 2 3 0 3 2 0 2 4 0 5 3 0 4 6 blend
+          rrcurveto
+          25 17 10 7 0 9 5 0 7 4 0 5 3 blend
+          43 44 22 0 27 1 blend
+          vvcurveto
+          67 -22 76 -86 89 -8 0 -10 9 0 12 -6 0 -8 24 0 30 -11 0 -13 5 blend
+          vhcurveto
+          39 84 42 98 33 81 -10 0 -13 -4 0 -5 -8 0 -10 14 0 17 -6 0 -8 7 0 9 6 blend
+          rrcurveto
+          -20 14 -6 -2 -60 0 -75 32 0 40 -12 0 -14 -2 0 -3 4 blend
+          rlineto
+        </CharString>
+      </CharStrings>
+      <VarStore Format="1">
+        <Format value="1"/>
+        <VarRegionList>
+          <!-- RegionAxisCount=1 -->
+          <!-- RegionCount=16 -->
+          <Region index="0">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="1">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.669"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="2">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.669"/>
+              <PeakCoord value="0.67"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="3">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.67"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="4">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.889"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="5">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.889"/>
+              <PeakCoord value="0.89"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="6">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.89"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="7">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.439"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="8">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.439"/>
+              <PeakCoord value="0.44"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="9">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.44"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="10">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.799"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="11">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.799"/>
+              <PeakCoord value="0.8"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="12">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.8"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="13">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.599"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="14">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.599"/>
+              <PeakCoord value="0.6"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="15">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.6"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+        </VarRegionList>
+        <!-- VarDataCount=6 -->
+        <VarData index="0">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=1 -->
+          <VarRegionIndex index="0" value="0"/>
+        </VarData>
+        <VarData index="1">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=3 -->
+          <VarRegionIndex index="0" value="1"/>
+          <VarRegionIndex index="1" value="2"/>
+          <VarRegionIndex index="2" value="3"/>
+        </VarData>
+        <VarData index="2">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=3 -->
+          <VarRegionIndex index="0" value="4"/>
+          <VarRegionIndex index="1" value="5"/>
+          <VarRegionIndex index="2" value="6"/>
+        </VarData>
+        <VarData index="3">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=3 -->
+          <VarRegionIndex index="0" value="7"/>
+          <VarRegionIndex index="1" value="8"/>
+          <VarRegionIndex index="2" value="9"/>
+        </VarData>
+        <VarData index="4">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=3 -->
+          <VarRegionIndex index="0" value="10"/>
+          <VarRegionIndex index="1" value="11"/>
+          <VarRegionIndex index="2" value="12"/>
+        </VarData>
+        <VarData index="5">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=3 -->
+          <VarRegionIndex index="0" value="13"/>
+          <VarRegionIndex index="1" value="14"/>
+          <VarRegionIndex index="2" value="15"/>
+        </VarData>
+      </VarStore>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=20 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.439"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.439"/>
+            <PeakCoord value="0.44"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.44"/>
+            <PeakCoord value="0.599"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="3">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.599"/>
+            <PeakCoord value="0.6"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="4">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.6"/>
+            <PeakCoord value="0.669"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="5">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.669"/>
+            <PeakCoord value="0.67"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="6">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.67"/>
+            <PeakCoord value="0.799"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="7">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.799"/>
+            <PeakCoord value="0.8"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="8">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.8"/>
+            <PeakCoord value="0.889"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="9">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.889"/>
+            <PeakCoord value="0.89"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="10">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.89"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="11">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="12">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.669"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="13">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.67"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="14">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.889"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="15">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.44"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="16">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.799"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="17">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.8"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="18">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.599"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="19">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.6"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=3 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="11"/>
+        <Item index="0" value="[-22]"/>
+        <Item index="1" value="[0]"/>
+        <Item index="2" value="[76]"/>
+      </VarData>
+    </VarStore>
+    <AdvWidthMap>
+      <Map glyph=".notdef" outer="0" inner="1"/>
+      <Map glyph="exclam" outer="0" inner="2"/>
+      <Map glyph="uni0000" outer="0" inner="0"/>
+      <Map glyph="uni50CE" outer="0" inner="1"/>
+      <Map glyph="uni610F" outer="0" inner="1"/>
+      <Map glyph="uni6A1A" outer="0" inner="1"/>
+      <Map glyph="uni82E8" outer="0" inner="1"/>
+      <Map glyph="uni8552" outer="0" inner="1"/>
+      <Map glyph="uni85A8" outer="0" inner="1"/>
+      <Map glyph="uni8E59" outer="0" inner="1"/>
+      <Map glyph="uni9A40" outer="0" inner="1"/>
+      <Map glyph="uniF9DC" outer="0" inner="1"/>
+    </AdvWidthMap>
+  </HVAR>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=1 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=0 -->
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <avar>
+    <segment axis="wght">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="0.1429" to="0.16"/>
+      <mapping from="0.2143" to="0.32"/>
+      <mapping from="0.2857" to="0.39"/>
+      <mapping from="0.4286" to="0.56"/>
+      <mapping from="0.7143" to="0.78"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+  </avar>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>200.0</MinValue>
+      <DefaultValue>200.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- Kanji-w0.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w0.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="258" subfamilyNameID="257">
+      <coord axis="wght" value="200.0"/>
+    </NamedInstance>
+
+    <!-- Kanji-w239.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w239.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="260" subfamilyNameID="259">
+      <coord axis="wght" value="324.6875"/>
+    </NamedInstance>
+
+    <!-- Kanji-w240.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w240.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="262" subfamilyNameID="261">
+      <coord axis="wght" value="325.0"/>
+    </NamedInstance>
+
+    <!-- Kanji-w439.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w439.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="264" subfamilyNameID="263">
+      <coord axis="wght" value="428.82353"/>
+    </NamedInstance>
+
+    <!-- Kanji-w440.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w440.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="266" subfamilyNameID="265">
+      <coord axis="wght" value="429.41176"/>
+    </NamedInstance>
+
+    <!-- Kanji-w499.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w499.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="268" subfamilyNameID="267">
+      <coord axis="wght" value="464.11765"/>
+    </NamedInstance>
+
+    <!-- Kanji-w500.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w500.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="270" subfamilyNameID="269">
+      <coord axis="wght" value="464.70589"/>
+    </NamedInstance>
+
+    <!-- Kanji-w599.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w599.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="272" subfamilyNameID="271">
+      <coord axis="wght" value="535.45454"/>
+    </NamedInstance>
+
+    <!-- Kanji-w600.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w600.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="274" subfamilyNameID="273">
+      <coord axis="wght" value="536.36363"/>
+    </NamedInstance>
+
+    <!-- Kanji-w669.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w669.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="276" subfamilyNameID="275">
+      <coord axis="wght" value="599.09091"/>
+    </NamedInstance>
+
+    <!-- Kanji-w670.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w670.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="278" subfamilyNameID="277">
+      <coord axis="wght" value="600.0"/>
+    </NamedInstance>
+
+    <!-- Kanji-w699.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w699.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="280" subfamilyNameID="279">
+      <coord axis="wght" value="626.36363"/>
+    </NamedInstance>
+
+    <!-- Kanji-w700.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w700.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="282" subfamilyNameID="281">
+      <coord axis="wght" value="627.27272"/>
+    </NamedInstance>
+
+    <!-- Kanji-799.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-799.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="284" subfamilyNameID="283">
+      <coord axis="wght" value="717.27272"/>
+    </NamedInstance>
+
+    <!-- Kanji-w800.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w800.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="286" subfamilyNameID="285">
+      <coord axis="wght" value="718.18182"/>
+    </NamedInstance>
+
+    <!-- Kanji-w889.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w889.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="288" subfamilyNameID="287">
+      <coord axis="wght" value="799.09091"/>
+    </NamedInstance>
+
+    <!-- Kanji-w890.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w890.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="290" subfamilyNameID="289">
+      <coord axis="wght" value="800.0"/>
+    </NamedInstance>
+
+    <!-- Kanji-w1000.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w1000.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="292" subfamilyNameID="291">
+      <coord axis="wght" value="900.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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="exclam" height="1000" tsb="125"/>
+    <mtx name="uni0000" height="1000" tsb="880"/>
+    <mtx name="uni50CE" height="1000" tsb="49"/>
+    <mtx name="uni610F" height="1000" tsb="40"/>
+    <mtx name="uni6A1A" height="1000" tsb="40"/>
+    <mtx name="uni82E8" height="1000" tsb="41"/>
+    <mtx name="uni8552" height="1000" tsb="41"/>
+    <mtx name="uni85A8" height="1000" tsb="41"/>
+    <mtx name="uni8E59" height="1000" tsb="40"/>
+    <mtx name="uni9A40" height="1000" tsb="41"/>
+    <mtx name="uniF9DC" height="1000" tsb="39"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/cffLib/specializer_test.py b/Tests/cffLib/specializer_test.py
index aa50611..a9b778c 100644
--- a/Tests/cffLib/specializer_test.py
+++ b/Tests/cffLib/specializer_test.py
@@ -1,7 +1,12 @@
-from __future__ import print_function, division, absolute_import
 from fontTools.cffLib.specializer import (programToString, stringToProgram,
-                                          generalizeProgram, specializeProgram)
+                                          generalizeProgram, specializeProgram,
+                                          programToCommands, commandsToProgram,
+                                          generalizeCommands,
+                                          specializeCommands)
+from fontTools.ttLib import TTFont
+import os
 import unittest
+from fontTools.misc.testTools import parseXML, DataFilesHandler
 
 # TODO
 # https://github.com/fonttools/fonttools/pull/959#commitcomment-22059841
@@ -904,15 +909,53 @@
         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
+# maxstack CFF=48, specializer uses up to 47
     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()
+        xpct_charstr = (operands*2 + operator + operands*7 + operator).rstrip()
         self.assertEqual(get_specialized_charstr(test_charstr), xpct_charstr)
 
 
+class CFF2VFTestSpecialize(DataFilesHandler):
+
+    def test_blend_round_trip(self):
+        ttx_path = self.getpath('TestSparseCFF2VF.ttx')
+        ttf_font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+        ttf_font.importXML(ttx_path)
+        fontGlyphList = ttf_font.getGlyphOrder()
+        topDict = ttf_font['CFF2'].cff.topDictIndex[0]
+        charstrings = topDict.CharStrings
+        for glyphName in fontGlyphList:
+            cs = charstrings[glyphName]
+            cs.decompile()
+            cmds = programToCommands(cs.program, getNumRegions=cs.getNumRegions)
+            cmds_g = generalizeCommands(cmds)
+            cmds = specializeCommands(cmds_g, generalizeFirst=False)
+            program = commandsToProgram(cmds)
+            self.assertEqual(program, cs.program)
+            program = specializeProgram(program, getNumRegions=cs.getNumRegions)
+            self.assertEqual(program, cs.program)
+            program_g = generalizeProgram(program, getNumRegions=cs.getNumRegions)
+            program = commandsToProgram(cmds_g)
+            self.assertEqual(program, program_g)
+
+    def test_blend_programToCommands(self):
+        ttx_path = self.getpath('TestCFF2Widths.ttx')
+        ttf_font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
+        ttf_font.importXML(ttx_path)
+        fontGlyphList = ttf_font.getGlyphOrder()
+        topDict = ttf_font['CFF2'].cff.topDictIndex[0]
+        charstrings = topDict.CharStrings
+        for glyphName in fontGlyphList:
+            cs = charstrings[glyphName]
+            cs.decompile()
+            cmds = programToCommands(cs.program, getNumRegions=cs.getNumRegions)
+            program = commandsToProgram(cmds)
+            self.assertEqual(program, cs.program)
+
+
 if __name__ == "__main__":
     import sys
     sys.exit(unittest.main())
diff --git a/Tests/colorLib/__init__.py b/Tests/colorLib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/colorLib/__init__.py
diff --git a/Tests/colorLib/builder_test.py b/Tests/colorLib/builder_test.py
new file mode 100644
index 0000000..f0a0992
--- /dev/null
+++ b/Tests/colorLib/builder_test.py
@@ -0,0 +1,1820 @@
+from fontTools.ttLib import newTable
+from fontTools.ttLib.tables import otTables as ot
+from fontTools.colorLib import builder
+from fontTools.colorLib.geometry import round_start_circle_stable_containment, Circle
+from fontTools.colorLib.builder import LayerListBuilder, _build_n_ary_tree
+from fontTools.colorLib.table_builder import TableBuilder
+from fontTools.colorLib.errors import ColorLibError
+import pytest
+from typing import List
+
+
+def _build(cls, source):
+    return LayerListBuilder().tableBuilder.build(cls, source)
+
+
+def _buildPaint(source):
+    return LayerListBuilder().buildPaint(source)
+
+
+def test_buildCOLR_v0():
+    color_layer_lists = {
+        "a": [("a.color0", 0), ("a.color1", 1)],
+        "b": [("b.color1", 1), ("b.color0", 0)],
+    }
+
+    colr = builder.buildCOLR(color_layer_lists)
+
+    assert colr.tableTag == "COLR"
+    assert colr.version == 0
+    assert colr.ColorLayers["a"][0].name == "a.color0"
+    assert colr.ColorLayers["a"][0].colorID == 0
+    assert colr.ColorLayers["a"][1].name == "a.color1"
+    assert colr.ColorLayers["a"][1].colorID == 1
+    assert colr.ColorLayers["b"][0].name == "b.color1"
+    assert colr.ColorLayers["b"][0].colorID == 1
+    assert colr.ColorLayers["b"][1].name == "b.color0"
+    assert colr.ColorLayers["b"][1].colorID == 0
+
+
+def test_buildCOLR_v0_layer_as_list():
+    # when COLRv0 layers are encoded as plist in UFO lib, both python tuples and
+    # lists are encoded as plist array elements; but the latter are always decoded
+    # as python lists, thus after roundtripping a plist tuples become lists.
+    # Before FontTools 4.17.0 we were treating tuples and lists as equivalent;
+    # with 4.17.0, a paint of type list is used to identify a PaintColrLayers.
+    # This broke backward compatibility as ufo2ft is simply passing through the
+    # color layers as read from the UFO lib plist, and as such the latter use lists
+    # instead of tuples for COLRv0 layers (layerGlyph, paletteIndex) combo.
+    # We restore backward compat by accepting either tuples or lists (of length 2
+    # and only containing a str and an int) as individual top-level layers.
+    # https://github.com/googlefonts/ufo2ft/issues/426
+    color_layer_lists = {
+        "a": [["a.color0", 0], ["a.color1", 1]],
+        "b": [["b.color1", 1], ["b.color0", 0]],
+    }
+
+    colr = builder.buildCOLR(color_layer_lists)
+
+    assert colr.tableTag == "COLR"
+    assert colr.version == 0
+    assert colr.ColorLayers["a"][0].name == "a.color0"
+    assert colr.ColorLayers["a"][0].colorID == 0
+    assert colr.ColorLayers["a"][1].name == "a.color1"
+    assert colr.ColorLayers["a"][1].colorID == 1
+    assert colr.ColorLayers["b"][0].name == "b.color1"
+    assert colr.ColorLayers["b"][0].colorID == 1
+    assert colr.ColorLayers["b"][1].name == "b.color0"
+    assert colr.ColorLayers["b"][1].colorID == 0
+
+
+def test_buildCPAL_v0():
+    palettes = [
+        [(0.68, 0.20, 0.32, 1.0), (0.45, 0.68, 0.21, 1.0)],
+        [(0.68, 0.20, 0.32, 0.6), (0.45, 0.68, 0.21, 0.6)],
+        [(0.68, 0.20, 0.32, 0.3), (0.45, 0.68, 0.21, 0.3)],
+    ]
+
+    cpal = builder.buildCPAL(palettes)
+
+    assert cpal.tableTag == "CPAL"
+    assert cpal.version == 0
+    assert cpal.numPaletteEntries == 2
+
+    assert len(cpal.palettes) == 3
+    assert [tuple(c) for c in cpal.palettes[0]] == [
+        (82, 51, 173, 255),
+        (54, 173, 115, 255),
+    ]
+    assert [tuple(c) for c in cpal.palettes[1]] == [
+        (82, 51, 173, 153),
+        (54, 173, 115, 153),
+    ]
+    assert [tuple(c) for c in cpal.palettes[2]] == [
+        (82, 51, 173, 76),
+        (54, 173, 115, 76),
+    ]
+
+
+def test_buildCPAL_palettes_different_lengths():
+    with pytest.raises(ColorLibError, match="have different lengths"):
+        builder.buildCPAL([[(1, 1, 1, 1)], [(0, 0, 0, 1), (0.5, 0.5, 0.5, 1)]])
+
+
+def test_buildPaletteLabels():
+    name_table = newTable("name")
+    name_table.names = []
+
+    name_ids = builder.buildPaletteLabels(
+        [None, "hi", {"en": "hello", "de": "hallo"}], name_table
+    )
+
+    assert name_ids == [0xFFFF, 256, 257]
+
+    assert len(name_table.names) == 3
+    assert str(name_table.names[0]) == "hi"
+    assert name_table.names[0].nameID == 256
+
+    assert str(name_table.names[1]) == "hallo"
+    assert name_table.names[1].nameID == 257
+
+    assert str(name_table.names[2]) == "hello"
+    assert name_table.names[2].nameID == 257
+
+
+def test_build_CPAL_v1_types_no_labels():
+    palettes = [
+        [(0.1, 0.2, 0.3, 1.0), (0.4, 0.5, 0.6, 1.0)],
+        [(0.1, 0.2, 0.3, 0.6), (0.4, 0.5, 0.6, 0.6)],
+        [(0.1, 0.2, 0.3, 0.3), (0.4, 0.5, 0.6, 0.3)],
+    ]
+    paletteTypes = [
+        builder.ColorPaletteType.USABLE_WITH_LIGHT_BACKGROUND,
+        builder.ColorPaletteType.USABLE_WITH_DARK_BACKGROUND,
+        builder.ColorPaletteType.USABLE_WITH_LIGHT_BACKGROUND
+        | builder.ColorPaletteType.USABLE_WITH_DARK_BACKGROUND,
+    ]
+
+    cpal = builder.buildCPAL(palettes, paletteTypes=paletteTypes)
+
+    assert cpal.tableTag == "CPAL"
+    assert cpal.version == 1
+    assert cpal.numPaletteEntries == 2
+    assert len(cpal.palettes) == 3
+
+    assert cpal.paletteTypes == paletteTypes
+    assert cpal.paletteLabels == [cpal.NO_NAME_ID] * len(palettes)
+    assert cpal.paletteEntryLabels == [cpal.NO_NAME_ID] * cpal.numPaletteEntries
+
+
+def test_build_CPAL_v1_labels():
+    palettes = [
+        [(0.1, 0.2, 0.3, 1.0), (0.4, 0.5, 0.6, 1.0)],
+        [(0.1, 0.2, 0.3, 0.6), (0.4, 0.5, 0.6, 0.6)],
+        [(0.1, 0.2, 0.3, 0.3), (0.4, 0.5, 0.6, 0.3)],
+    ]
+    paletteLabels = ["First", {"en": "Second", "it": "Seconda"}, None]
+    paletteEntryLabels = ["Foo", "Bar"]
+
+    with pytest.raises(TypeError, match="nameTable is required"):
+        builder.buildCPAL(palettes, paletteLabels=paletteLabels)
+    with pytest.raises(TypeError, match="nameTable is required"):
+        builder.buildCPAL(palettes, paletteEntryLabels=paletteEntryLabels)
+
+    name_table = newTable("name")
+    name_table.names = []
+
+    cpal = builder.buildCPAL(
+        palettes,
+        paletteLabels=paletteLabels,
+        paletteEntryLabels=paletteEntryLabels,
+        nameTable=name_table,
+    )
+
+    assert cpal.tableTag == "CPAL"
+    assert cpal.version == 1
+    assert cpal.numPaletteEntries == 2
+    assert len(cpal.palettes) == 3
+
+    assert cpal.paletteTypes == [cpal.DEFAULT_PALETTE_TYPE] * len(palettes)
+    assert cpal.paletteLabels == [256, 257, cpal.NO_NAME_ID]
+    assert cpal.paletteEntryLabels == [258, 259]
+
+    assert name_table.getDebugName(256) == "First"
+    assert name_table.getDebugName(257) == "Second"
+    assert name_table.getDebugName(258) == "Foo"
+    assert name_table.getDebugName(259) == "Bar"
+
+
+def test_invalid_ColorPaletteType():
+    with pytest.raises(ValueError, match="not a valid ColorPaletteType"):
+        builder.ColorPaletteType(-1)
+    with pytest.raises(ValueError, match="not a valid ColorPaletteType"):
+        builder.ColorPaletteType(4)
+    with pytest.raises(ValueError, match="not a valid ColorPaletteType"):
+        builder.ColorPaletteType("abc")
+
+
+def test_buildCPAL_v1_invalid_args_length():
+    with pytest.raises(ColorLibError, match="Expected 2 paletteTypes, got 1"):
+        builder.buildCPAL([[(0, 0, 0, 0)], [(1, 1, 1, 1)]], paletteTypes=[1])
+
+    with pytest.raises(ColorLibError, match="Expected 2 paletteLabels, got 1"):
+        builder.buildCPAL(
+            [[(0, 0, 0, 0)], [(1, 1, 1, 1)]],
+            paletteLabels=["foo"],
+            nameTable=newTable("name"),
+        )
+
+    with pytest.raises(ColorLibError, match="Expected 1 paletteEntryLabels, got 0"):
+        cpal = builder.buildCPAL(
+            [[(0, 0, 0, 0)], [(1, 1, 1, 1)]],
+            paletteEntryLabels=[],
+            nameTable=newTable("name"),
+        )
+
+
+def test_buildCPAL_invalid_color():
+    with pytest.raises(
+        ColorLibError,
+        match=r"In palette\[0\]\[1\]: expected \(R, G, B, A\) tuple, got \(1, 1, 1\)",
+    ):
+        builder.buildCPAL([[(1, 1, 1, 1), (1, 1, 1)]])
+
+    with pytest.raises(
+        ColorLibError,
+        match=(
+            r"palette\[1\]\[0\] has invalid out-of-range "
+            r"\[0..1\] color: \(1, 1, -1, 2\)"
+        ),
+    ):
+        builder.buildCPAL([[(0, 0, 0, 0)], [(1, 1, -1, 2)]])
+
+
+def test_buildPaintSolid():
+    p = _buildPaint((ot.PaintFormat.PaintSolid, 0))
+    assert p.Format == ot.PaintFormat.PaintSolid
+    assert p.PaletteIndex == 0
+    assert p.Alpha == 1.0
+
+
+def test_buildPaintSolid_Alpha():
+    p = _buildPaint((ot.PaintFormat.PaintSolid, 1, 0.5))
+    assert p.Format == ot.PaintFormat.PaintSolid
+    assert p.PaletteIndex == 1
+    assert p.Alpha == 0.5
+
+
+def test_buildPaintVarSolid():
+    p = _buildPaint((ot.PaintFormat.PaintVarSolid, 3, 0.5, 2))
+    assert p.Format == ot.PaintFormat.PaintVarSolid
+    assert p.PaletteIndex == 3
+    assert p.Alpha == 0.5
+    assert p.VarIndexBase == 2
+
+
+def test_buildVarColorStop_DefaultAlpha():
+    s = _build(ot.ColorStop, (0.1, 2))
+    assert s.StopOffset == 0.1
+    assert s.PaletteIndex == 2
+    assert s.Alpha == builder._DEFAULT_ALPHA
+
+
+def test_buildVarColorStop_DefaultAlpha():
+    s = _build(ot.VarColorStop, (0.1, 2))
+    assert s.StopOffset == 0.1
+    assert s.PaletteIndex == 2
+    assert s.Alpha == builder._DEFAULT_ALPHA
+
+
+def test_buildColorStop():
+    s = _build(ot.ColorStop, {"StopOffset": 0.2, "PaletteIndex": 3, "Alpha": 0.4})
+    assert s.StopOffset == 0.2
+    assert s.PaletteIndex == 3
+    assert s.Alpha == 0.4
+
+
+def test_buildColorStop_Variable():
+    s = _build(
+        ot.VarColorStop,
+        {
+            "StopOffset": 0.0,
+            "PaletteIndex": 0,
+            "Alpha": 0.3,
+            "VarIndexBase": 1,
+        },
+    )
+    assert s.StopOffset == 0.0
+    assert s.PaletteIndex == 0
+    assert s.Alpha == 0.3
+    assert s.VarIndexBase == 1
+
+
+def test_buildColorLine_StopList():
+    stops = [(0.0, 0), (0.5, 1), (1.0, 2)]
+
+    cline = _build(ot.ColorLine, {"ColorStop": stops})
+    assert cline.Extend == builder.ExtendMode.PAD
+    assert cline.StopCount == 3
+    assert [(cs.StopOffset, cs.PaletteIndex) for cs in cline.ColorStop] == stops
+
+    cline = _build(ot.ColorLine, {"Extend": "pad", "ColorStop": stops})
+    assert cline.Extend == builder.ExtendMode.PAD
+
+    cline = _build(
+        ot.ColorLine, {"ColorStop": stops, "Extend": builder.ExtendMode.REPEAT}
+    )
+    assert cline.Extend == builder.ExtendMode.REPEAT
+
+    cline = _build(
+        ot.ColorLine, {"ColorStop": stops, "Extend": builder.ExtendMode.REFLECT}
+    )
+    assert cline.Extend == builder.ExtendMode.REFLECT
+
+    cline = _build(
+        ot.ColorLine, {"ColorStop": [_build(ot.ColorStop, s) for s in stops]}
+    )
+    assert [(cs.StopOffset, cs.PaletteIndex) for cs in cline.ColorStop] == stops
+
+
+def test_buildVarColorLine_StopMap():
+    stops = [
+        {"StopOffset": 0.0, "PaletteIndex": 0, "Alpha": 0.5, "VarIndexBase": 1},
+        {"StopOffset": 1.0, "PaletteIndex": 1, "Alpha": 0.3, "VarIndexBase": 3},
+    ]
+    cline = _build(ot.VarColorLine, {"ColorStop": stops})
+    assert [
+        {
+            "StopOffset": cs.StopOffset,
+            "PaletteIndex": cs.PaletteIndex,
+            "Alpha": cs.Alpha,
+            "VarIndexBase": cs.VarIndexBase,
+        }
+        for cs in cline.ColorStop
+    ] == stops
+
+
+def checkBuildAffine2x3(cls, variable=False):
+    matrix = _build(cls, (1.5, 0, 0.5, 2.0, 1.0, -3.0))
+    assert matrix.xx == 1.5
+    assert matrix.yx == 0.0
+    assert matrix.xy == 0.5
+    assert matrix.yy == 2.0
+    assert matrix.dx == 1.0
+    assert matrix.dy == -3.0
+    if variable:
+        assert matrix.VarIndexBase == 0xFFFFFFFF
+
+
+def test_buildAffine2x3():
+    checkBuildAffine2x3(ot.Affine2x3)
+
+
+def test_buildVarAffine2x3():
+    checkBuildAffine2x3(ot.VarAffine2x3, variable=True)
+
+
+def _sample_stops(variable):
+    cls = ot.ColorStop if not variable else ot.VarColorStop
+    stop_sources = [
+        {"StopOffset": 0.0, "PaletteIndex": 0},
+        {"StopOffset": 0.5, "PaletteIndex": 1},
+        {"StopOffset": 1.0, "PaletteIndex": 2, "Alpha": 0.8},
+    ]
+    if variable:
+        for i, src in enumerate(stop_sources, start=123):
+            src["VarIndexBase"] = i
+    return [_build(cls, src) for src in stop_sources]
+
+
+def _is_var(fmt):
+    return fmt.name.startswith("PaintVar")
+
+
+def _is_around_center(fmt):
+    return fmt.name.endswith("AroundCenter")
+
+
+def _is_uniform_scale(fmt):
+    return "ScaleUniform" in fmt.name
+
+
+def checkBuildPaintLinearGradient(fmt):
+    variable = _is_var(fmt)
+    color_stops = _sample_stops(variable)
+
+    x0, y0, x1, y1, x2, y2 = (1, 2, 3, 4, 5, 6)
+    source = {
+        "Format": fmt,
+        "ColorLine": {"ColorStop": color_stops},
+        "x0": x0,
+        "y0": y0,
+        "x1": x1,
+        "y1": y1,
+        "x2": x2,
+        "y2": y2,
+    }
+    if variable:
+        source["VarIndexBase"] = 7
+    gradient = _buildPaint(source)
+    assert gradient.ColorLine.Extend == builder.ExtendMode.PAD
+    assert gradient.ColorLine.ColorStop == color_stops
+
+    gradient = _buildPaint(gradient)
+    assert (gradient.x0, gradient.y0) == (1, 2)
+    assert (gradient.x1, gradient.y1) == (3, 4)
+    assert (gradient.x2, gradient.y2) == (5, 6)
+    if variable:
+        assert gradient.VarIndexBase == 7
+
+
+def test_buildPaintLinearGradient():
+    assert not _is_var(ot.PaintFormat.PaintLinearGradient)
+    checkBuildPaintLinearGradient(ot.PaintFormat.PaintLinearGradient)
+
+
+def test_buildPaintVarLinearGradient():
+    assert _is_var(ot.PaintFormat.PaintVarLinearGradient)
+    checkBuildPaintLinearGradient(ot.PaintFormat.PaintVarLinearGradient)
+
+
+def checkBuildPaintRadialGradient(fmt):
+    variable = _is_var(fmt)
+    color_stops = _sample_stops(variable)
+    line_cls = ot.VarColorLine if variable else ot.ColorLine
+
+    color_line = _build(
+        line_cls, {"ColorStop": color_stops, "Extend": builder.ExtendMode.REPEAT}
+    )
+    c0 = (100, 200)
+    c1 = (150, 250)
+    r0 = 10
+    r1 = 5
+    varIndexBase = 0
+
+    source = [fmt, color_line, *c0, r0, *c1, r1]
+    if variable:
+        source.append(varIndexBase)
+
+    gradient = _build(ot.Paint, tuple(source))
+    assert gradient.Format == fmt
+    assert gradient.ColorLine == color_line
+    assert (gradient.x0, gradient.y0) == c0
+    assert (gradient.x1, gradient.y1) == c1
+    assert gradient.r0 == r0
+    assert gradient.r1 == r1
+    if variable:
+        assert gradient.VarIndexBase == varIndexBase
+
+    source = {
+        "Format": fmt,
+        "ColorLine": {"ColorStop": color_stops},
+        "x0": c0[0],
+        "y0": c0[1],
+        "x1": c1[0],
+        "y1": c1[1],
+        "r0": r0,
+        "r1": r1,
+    }
+    if variable:
+        source["VarIndexBase"] = varIndexBase
+    gradient = _build(ot.Paint, source)
+    assert gradient.ColorLine.Extend == builder.ExtendMode.PAD
+    assert gradient.ColorLine.ColorStop == color_stops
+    assert (gradient.x0, gradient.y0) == c0
+    assert (gradient.x1, gradient.y1) == c1
+    assert gradient.r0 == r0
+    assert gradient.r1 == r1
+    if variable:
+        assert gradient.VarIndexBase == varIndexBase
+
+
+def test_buildPaintRadialGradient():
+    assert not _is_var(ot.PaintFormat.PaintRadialGradient)
+    checkBuildPaintRadialGradient(ot.PaintFormat.PaintRadialGradient)
+
+
+def test_buildPaintVarRadialGradient():
+    assert _is_var(ot.PaintFormat.PaintVarRadialGradient)
+    checkBuildPaintRadialGradient(ot.PaintFormat.PaintVarRadialGradient)
+
+
+def checkPaintSweepGradient(fmt):
+    variable = _is_var(fmt)
+    source = {
+        "Format": fmt,
+        "ColorLine": {"ColorStop": _sample_stops(variable)},
+        "centerX": 127,
+        "centerY": 129,
+        "startAngle": 15,
+        "endAngle": 42,
+    }
+    if variable:
+        source["VarIndexBase"] = 666
+    paint = _buildPaint(source)
+
+    assert paint.Format == fmt
+    assert paint.centerX == 127
+    assert paint.centerY == 129
+    assert paint.startAngle == 15
+    assert paint.endAngle == 42
+    if variable:
+        assert paint.VarIndexBase == 666
+
+
+def test_buildPaintSweepGradient():
+    assert not _is_var(ot.PaintFormat.PaintSweepGradient)
+    checkPaintSweepGradient(ot.PaintFormat.PaintSweepGradient)
+
+
+def test_buildPaintVarSweepGradient():
+    assert _is_var(ot.PaintFormat.PaintVarSweepGradient)
+    checkPaintSweepGradient(ot.PaintFormat.PaintVarSweepGradient)
+
+
+def test_buildPaintGlyph_Solid():
+    layer = _build(
+        ot.Paint,
+        (
+            ot.PaintFormat.PaintGlyph,
+            (
+                ot.PaintFormat.PaintSolid,
+                2,
+            ),
+            "a",
+        ),
+    )
+    assert layer.Format == ot.PaintFormat.PaintGlyph
+    assert layer.Glyph == "a"
+    assert layer.Paint.Format == ot.PaintFormat.PaintSolid
+    assert layer.Paint.PaletteIndex == 2
+
+    layer = _build(
+        ot.Paint,
+        (
+            ot.PaintFormat.PaintGlyph,
+            (ot.PaintFormat.PaintSolid, 3, 0.9),
+            "a",
+        ),
+    )
+    assert layer.Paint.Format == ot.PaintFormat.PaintSolid
+    assert layer.Paint.PaletteIndex == 3
+    assert layer.Paint.Alpha == 0.9
+
+
+def test_buildPaintGlyph_VarLinearGradient():
+    layer = _build(
+        ot.Paint,
+        {
+            "Format": ot.PaintFormat.PaintGlyph,
+            "Glyph": "a",
+            "Paint": {
+                "Format": ot.PaintFormat.PaintVarLinearGradient,
+                "ColorLine": {"ColorStop": [(0.0, 3), (1.0, 4)]},
+                "x0": 100,
+                "y0": 200,
+                "x1": 150,
+                "y1": 250,
+            },
+        },
+    )
+
+    assert layer.Format == ot.PaintFormat.PaintGlyph
+    assert layer.Glyph == "a"
+    assert layer.Paint.Format == ot.PaintFormat.PaintVarLinearGradient
+    assert layer.Paint.ColorLine.ColorStop[0].StopOffset == 0.0
+    assert layer.Paint.ColorLine.ColorStop[0].PaletteIndex == 3
+    assert layer.Paint.ColorLine.ColorStop[1].StopOffset == 1.0
+    assert layer.Paint.ColorLine.ColorStop[1].PaletteIndex == 4
+    assert layer.Paint.x0 == 100
+    assert layer.Paint.y0 == 200
+    assert layer.Paint.x1 == 150
+    assert layer.Paint.y1 == 250
+
+
+def test_buildPaintGlyph_RadialGradient():
+    layer = _build(
+        ot.Paint,
+        (
+            int(ot.PaintFormat.PaintGlyph),
+            (
+                ot.PaintFormat.PaintRadialGradient,
+                (
+                    "pad",
+                    [
+                        (0.0, 5),
+                        {"StopOffset": 0.5, "PaletteIndex": 6, "Alpha": 0.8},
+                        (1.0, 7),
+                    ],
+                ),
+                50,
+                50,
+                30,
+                75,
+                75,
+                10,
+            ),
+            "a",
+        ),
+    )
+    assert layer.Format == ot.PaintFormat.PaintGlyph
+    assert layer.Paint.Format == ot.PaintFormat.PaintRadialGradient
+    assert layer.Paint.ColorLine.ColorStop[0].StopOffset == 0.0
+    assert layer.Paint.ColorLine.ColorStop[0].PaletteIndex == 5
+    assert layer.Paint.ColorLine.ColorStop[1].StopOffset == 0.5
+    assert layer.Paint.ColorLine.ColorStop[1].PaletteIndex == 6
+    assert layer.Paint.ColorLine.ColorStop[1].Alpha == 0.8
+    assert layer.Paint.ColorLine.ColorStop[2].StopOffset == 1.0
+    assert layer.Paint.ColorLine.ColorStop[2].PaletteIndex == 7
+    assert layer.Paint.x0 == 50
+    assert layer.Paint.y0 == 50
+    assert layer.Paint.r0 == 30
+    assert layer.Paint.x1 == 75
+    assert layer.Paint.y1 == 75
+    assert layer.Paint.r1 == 10
+
+
+def test_buildPaintGlyph_Dict_Solid():
+    layer = _build(
+        ot.Paint,
+        (
+            int(ot.PaintFormat.PaintGlyph),
+            (int(ot.PaintFormat.PaintSolid), 1),
+            "a",
+        ),
+    )
+    assert layer.Format == ot.PaintFormat.PaintGlyph
+    assert layer.Format == ot.PaintFormat.PaintGlyph
+    assert layer.Glyph == "a"
+    assert layer.Paint.Format == ot.PaintFormat.PaintSolid
+    assert layer.Paint.PaletteIndex == 1
+
+
+def test_buildPaintGlyph_Dict_VarLinearGradient():
+    layer = _build(
+        ot.Paint,
+        {
+            "Format": ot.PaintFormat.PaintGlyph,
+            "Glyph": "a",
+            "Paint": {
+                "Format": int(ot.PaintFormat.PaintVarLinearGradient),
+                "ColorLine": {"ColorStop": [(0.0, 0), (1.0, 1)]},
+                "x0": 0,
+                "y0": 0,
+                "x1": 10,
+                "y1": 10,
+            },
+        },
+    )
+    assert layer.Format == ot.PaintFormat.PaintGlyph
+    assert layer.Glyph == "a"
+    assert layer.Paint.Format == ot.PaintFormat.PaintVarLinearGradient
+    assert layer.Paint.ColorLine.ColorStop[0].StopOffset == 0.0
+
+
+def test_buildPaintGlyph_Dict_RadialGradient():
+    layer = _buildPaint(
+        {
+            "Glyph": "a",
+            "Paint": {
+                "Format": int(ot.PaintFormat.PaintRadialGradient),
+                "ColorLine": {"ColorStop": [(0.0, 0), (1.0, 1)]},
+                "x0": 0,
+                "y0": 0,
+                "r0": 4,
+                "x1": 10,
+                "y1": 10,
+                "r1": 0,
+            },
+            "Format": int(ot.PaintFormat.PaintGlyph),
+        },
+    )
+    assert layer.Paint.Format == ot.PaintFormat.PaintRadialGradient
+    assert layer.Paint.r0 == 4
+
+
+def test_buildPaintColrGlyph():
+    paint = _buildPaint((int(ot.PaintFormat.PaintColrGlyph), "a"))
+    assert paint.Format == ot.PaintFormat.PaintColrGlyph
+    assert paint.Glyph == "a"
+
+
+def checkBuildPaintTransform(fmt):
+    variable = _is_var(fmt)
+    if variable:
+        affine_cls = ot.VarAffine2x3
+    else:
+        affine_cls = ot.Affine2x3
+
+    affine_src = [1, 2, 3, 4, 5, 6]
+    if variable:
+        affine_src.append(7)
+
+    paint = _buildPaint(
+        (
+            int(fmt),
+            (ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintSolid, 0, 1.0), "a"),
+            _build(affine_cls, tuple(affine_src)),
+        ),
+    )
+
+    assert paint.Format == fmt
+    assert paint.Paint.Format == ot.PaintFormat.PaintGlyph
+    assert paint.Paint.Paint.Format == ot.PaintFormat.PaintSolid
+
+    assert paint.Transform.xx == 1.0
+    assert paint.Transform.yx == 2.0
+    assert paint.Transform.xy == 3.0
+    assert paint.Transform.yy == 4.0
+    assert paint.Transform.dx == 5.0
+    assert paint.Transform.dy == 6.0
+    if variable:
+        assert paint.Transform.VarIndexBase == 7
+
+    affine_src = [1, 2, 3, 0.3333, 10, 10]
+    if variable:
+        affine_src.append(456)  # VarIndexBase
+    paint = _build(
+        ot.Paint,
+        {
+            "Format": fmt,
+            "Transform": tuple(affine_src),
+            "Paint": {
+                "Format": int(ot.PaintFormat.PaintRadialGradient),
+                "ColorLine": {"ColorStop": [(0.0, 0), (1.0, 1)]},
+                "x0": 100,
+                "y0": 101,
+                "x1": 102,
+                "y1": 103,
+                "r0": 0,
+                "r1": 50,
+            },
+        },
+    )
+
+    assert paint.Format == fmt
+    assert paint.Transform.xx == 1.0
+    assert paint.Transform.yx == 2.0
+    assert paint.Transform.xy == 3.0
+    assert paint.Transform.yy == 0.3333
+    assert paint.Transform.dx == 10
+    assert paint.Transform.dy == 10
+    if variable:
+        assert paint.Transform.VarIndexBase == 456
+    assert paint.Paint.Format == ot.PaintFormat.PaintRadialGradient
+
+
+def test_buildPaintTransform():
+    assert not _is_var(ot.PaintFormat.PaintTransform)
+    checkBuildPaintTransform(ot.PaintFormat.PaintTransform)
+
+
+def test_buildPaintVarTransform():
+    assert _is_var(ot.PaintFormat.PaintVarTransform)
+    checkBuildPaintTransform(ot.PaintFormat.PaintVarTransform)
+
+
+def test_buildPaintComposite():
+    composite = _build(
+        ot.Paint,
+        {
+            "Format": int(ot.PaintFormat.PaintComposite),
+            "CompositeMode": "src_over",
+            "SourcePaint": {
+                "Format": ot.PaintFormat.PaintComposite,
+                "CompositeMode": "src_over",
+                "SourcePaint": {
+                    "Format": int(ot.PaintFormat.PaintGlyph),
+                    "Glyph": "c",
+                    "Paint": (ot.PaintFormat.PaintSolid, 2),
+                },
+                "BackdropPaint": {
+                    "Format": int(ot.PaintFormat.PaintGlyph),
+                    "Glyph": "b",
+                    "Paint": (ot.PaintFormat.PaintSolid, 1),
+                },
+            },
+            "BackdropPaint": {
+                "Format": ot.PaintFormat.PaintGlyph,
+                "Glyph": "a",
+                "Paint": {
+                    "Format": ot.PaintFormat.PaintSolid,
+                    "PaletteIndex": 0,
+                    "Alpha": 0.5,
+                },
+            },
+        },
+    )
+
+    assert composite.Format == ot.PaintFormat.PaintComposite
+    assert composite.SourcePaint.Format == ot.PaintFormat.PaintComposite
+    assert composite.SourcePaint.SourcePaint.Format == ot.PaintFormat.PaintGlyph
+    assert composite.SourcePaint.SourcePaint.Glyph == "c"
+    assert composite.SourcePaint.SourcePaint.Paint.Format == ot.PaintFormat.PaintSolid
+    assert composite.SourcePaint.SourcePaint.Paint.PaletteIndex == 2
+    assert composite.SourcePaint.CompositeMode == ot.CompositeMode.SRC_OVER
+    assert composite.SourcePaint.BackdropPaint.Format == ot.PaintFormat.PaintGlyph
+    assert composite.SourcePaint.BackdropPaint.Glyph == "b"
+    assert composite.SourcePaint.BackdropPaint.Paint.Format == ot.PaintFormat.PaintSolid
+    assert composite.SourcePaint.BackdropPaint.Paint.PaletteIndex == 1
+    assert composite.CompositeMode == ot.CompositeMode.SRC_OVER
+    assert composite.BackdropPaint.Format == ot.PaintFormat.PaintGlyph
+    assert composite.BackdropPaint.Glyph == "a"
+    assert composite.BackdropPaint.Paint.Format == ot.PaintFormat.PaintSolid
+    assert composite.BackdropPaint.Paint.PaletteIndex == 0
+    assert composite.BackdropPaint.Paint.Alpha == 0.5
+
+
+def checkBuildPaintTranslate(fmt):
+    variable = _is_var(fmt)
+
+    source = {
+        "Format": fmt,
+        "Paint": (
+            ot.PaintFormat.PaintGlyph,
+            (ot.PaintFormat.PaintSolid, 0, 1.0),
+            "a",
+        ),
+        "dx": 123,
+        "dy": -345,
+    }
+    if variable:
+        source["VarIndexBase"] = 678
+
+    paint = _build(ot.Paint, source)
+
+    assert paint.Format == fmt
+    assert paint.Paint.Format == ot.PaintFormat.PaintGlyph
+    assert paint.dx == 123
+    assert paint.dy == -345
+    if variable:
+        assert paint.VarIndexBase == 678
+
+
+def test_buildPaintTranslate():
+    assert not _is_var(ot.PaintFormat.PaintTranslate)
+    checkBuildPaintTranslate(ot.PaintFormat.PaintTranslate)
+
+
+def test_buildPaintVarTranslate():
+    assert _is_var(ot.PaintFormat.PaintVarTranslate)
+    checkBuildPaintTranslate(ot.PaintFormat.PaintVarTranslate)
+
+
+def checkBuildPaintScale(fmt):
+    variable = _is_var(fmt)
+    around_center = _is_around_center(fmt)
+    uniform = _is_uniform_scale(fmt)
+
+    source = {
+        "Format": fmt,
+        "Paint": (
+            ot.PaintFormat.PaintGlyph,
+            (ot.PaintFormat.PaintSolid, 0, 1.0),
+            "a",
+        ),
+    }
+    if uniform:
+        source["scale"] = 1.5
+    else:
+        source["scaleX"] = 1.0
+        source["scaleY"] = 2.0
+    if around_center:
+        source["centerX"] = 127
+        source["centerY"] = 129
+    if variable:
+        source["VarIndexBase"] = 666
+
+    paint = _build(ot.Paint, source)
+
+    assert paint.Format == fmt
+    assert paint.Paint.Format == ot.PaintFormat.PaintGlyph
+    if uniform:
+        assert paint.scale == 1.5
+    else:
+        assert paint.scaleX == 1.0
+        assert paint.scaleY == 2.0
+    if around_center:
+        assert paint.centerX == 127
+        assert paint.centerY == 129
+    if variable:
+        assert paint.VarIndexBase == 666
+
+
+def test_buildPaintScale():
+    assert not _is_var(ot.PaintFormat.PaintScale)
+    assert not _is_uniform_scale(ot.PaintFormat.PaintScale)
+    assert not _is_around_center(ot.PaintFormat.PaintScale)
+    checkBuildPaintScale(ot.PaintFormat.PaintScale)
+
+
+def test_buildPaintVarScale():
+    assert _is_var(ot.PaintFormat.PaintVarScale)
+    assert not _is_uniform_scale(ot.PaintFormat.PaintVarScale)
+    assert not _is_around_center(ot.PaintFormat.PaintVarScale)
+    checkBuildPaintScale(ot.PaintFormat.PaintVarScale)
+
+
+def test_buildPaintScaleAroundCenter():
+    assert not _is_var(ot.PaintFormat.PaintScaleAroundCenter)
+    assert not _is_uniform_scale(ot.PaintFormat.PaintScaleAroundCenter)
+    assert _is_around_center(ot.PaintFormat.PaintScaleAroundCenter)
+    checkBuildPaintScale(ot.PaintFormat.PaintScaleAroundCenter)
+
+
+def test_buildPaintVarScaleAroundCenter():
+    assert _is_var(ot.PaintFormat.PaintVarScaleAroundCenter)
+    assert not _is_uniform_scale(ot.PaintFormat.PaintScaleAroundCenter)
+    assert _is_around_center(ot.PaintFormat.PaintVarScaleAroundCenter)
+    checkBuildPaintScale(ot.PaintFormat.PaintVarScaleAroundCenter)
+
+
+def test_buildPaintScaleUniform():
+    assert not _is_var(ot.PaintFormat.PaintScaleUniform)
+    assert _is_uniform_scale(ot.PaintFormat.PaintScaleUniform)
+    assert not _is_around_center(ot.PaintFormat.PaintScaleUniform)
+    checkBuildPaintScale(ot.PaintFormat.PaintScaleUniform)
+
+
+def test_buildPaintVarScaleUniform():
+    assert _is_var(ot.PaintFormat.PaintVarScaleUniform)
+    assert _is_uniform_scale(ot.PaintFormat.PaintVarScaleUniform)
+    assert not _is_around_center(ot.PaintFormat.PaintVarScaleUniform)
+    checkBuildPaintScale(ot.PaintFormat.PaintVarScaleUniform)
+
+
+def test_buildPaintScaleUniformAroundCenter():
+    assert not _is_var(ot.PaintFormat.PaintScaleUniformAroundCenter)
+    assert _is_uniform_scale(ot.PaintFormat.PaintScaleUniformAroundCenter)
+    assert _is_around_center(ot.PaintFormat.PaintScaleUniformAroundCenter)
+    checkBuildPaintScale(ot.PaintFormat.PaintScaleUniformAroundCenter)
+
+
+def test_buildPaintVarScaleUniformAroundCenter():
+    assert _is_var(ot.PaintFormat.PaintVarScaleUniformAroundCenter)
+    assert _is_uniform_scale(ot.PaintFormat.PaintVarScaleUniformAroundCenter)
+    assert _is_around_center(ot.PaintFormat.PaintVarScaleUniformAroundCenter)
+    checkBuildPaintScale(ot.PaintFormat.PaintVarScaleUniformAroundCenter)
+
+
+def checkBuildPaintRotate(fmt):
+    variable = _is_var(fmt)
+    around_center = _is_around_center(fmt)
+
+    source = {
+        "Format": fmt,
+        "Paint": (
+            ot.PaintFormat.PaintGlyph,
+            (ot.PaintFormat.PaintSolid, 0, 1.0),
+            "a",
+        ),
+        "angle": 15,
+    }
+    if around_center:
+        source["centerX"] = 127
+        source["centerY"] = 129
+
+    paint = _build(ot.Paint, source)
+
+    assert paint.Format == fmt
+    assert paint.Paint.Format == ot.PaintFormat.PaintGlyph
+    assert paint.angle == 15
+    if around_center:
+        assert paint.centerX == 127
+        assert paint.centerY == 129
+    if variable:
+        assert paint.VarIndexBase == 0xFFFFFFFF
+
+
+def test_buildPaintRotate():
+    assert not _is_var(ot.PaintFormat.PaintRotate)
+    assert not _is_around_center(ot.PaintFormat.PaintRotate)
+    checkBuildPaintRotate(ot.PaintFormat.PaintRotate)
+
+
+def test_buildPaintVarRotate():
+    assert _is_var(ot.PaintFormat.PaintVarRotate)
+    assert not _is_around_center(ot.PaintFormat.PaintVarRotate)
+    checkBuildPaintRotate(ot.PaintFormat.PaintVarRotate)
+
+
+def test_buildPaintRotateAroundCenter():
+    assert not _is_var(ot.PaintFormat.PaintRotateAroundCenter)
+    assert _is_around_center(ot.PaintFormat.PaintRotateAroundCenter)
+    checkBuildPaintRotate(ot.PaintFormat.PaintRotateAroundCenter)
+
+
+def test_buildPaintVarRotateAroundCenter():
+    assert _is_var(ot.PaintFormat.PaintVarRotateAroundCenter)
+    assert _is_around_center(ot.PaintFormat.PaintVarRotateAroundCenter)
+    checkBuildPaintRotate(ot.PaintFormat.PaintVarRotateAroundCenter)
+
+
+def checkBuildPaintSkew(fmt):
+    variable = _is_var(fmt)
+    around_center = _is_around_center(fmt)
+
+    source = {
+        "Format": fmt,
+        "Paint": (
+            ot.PaintFormat.PaintGlyph,
+            (ot.PaintFormat.PaintSolid, 0, 1.0),
+            "a",
+        ),
+        "xSkewAngle": 15,
+        "ySkewAngle": 42,
+    }
+    if around_center:
+        source["centerX"] = 127
+        source["centerY"] = 129
+    if variable:
+        source["VarIndexBase"] = 0
+
+    paint = _build(ot.Paint, source)
+
+    assert paint.Format == fmt
+    assert paint.Paint.Format == ot.PaintFormat.PaintGlyph
+    assert paint.xSkewAngle == 15
+    assert paint.ySkewAngle == 42
+    if around_center:
+        assert paint.centerX == 127
+        assert paint.centerY == 129
+    if variable:
+        assert paint.VarIndexBase == 0
+
+
+def test_buildPaintSkew():
+    assert not _is_var(ot.PaintFormat.PaintSkew)
+    assert not _is_around_center(ot.PaintFormat.PaintSkew)
+    checkBuildPaintSkew(ot.PaintFormat.PaintSkew)
+
+
+def test_buildPaintVarSkew():
+    assert _is_var(ot.PaintFormat.PaintVarSkew)
+    assert not _is_around_center(ot.PaintFormat.PaintVarSkew)
+    checkBuildPaintSkew(ot.PaintFormat.PaintVarSkew)
+
+
+def test_buildPaintSkewAroundCenter():
+    assert not _is_var(ot.PaintFormat.PaintSkewAroundCenter)
+    assert _is_around_center(ot.PaintFormat.PaintSkewAroundCenter)
+    checkBuildPaintSkew(ot.PaintFormat.PaintSkewAroundCenter)
+
+
+def test_buildPaintVarSkewAroundCenter():
+    assert _is_var(ot.PaintFormat.PaintVarSkewAroundCenter)
+    assert _is_around_center(ot.PaintFormat.PaintVarSkewAroundCenter)
+    checkBuildPaintSkew(ot.PaintFormat.PaintVarSkewAroundCenter)
+
+
+def test_buildColrV1():
+    colorGlyphs = {
+        "a": (
+            ot.PaintFormat.PaintColrLayers,
+            [
+                (ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintSolid, 0), "b"),
+                (ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintVarSolid, 1), "c"),
+            ],
+        ),
+        "d": (
+            ot.PaintFormat.PaintColrLayers,
+            [
+                (
+                    ot.PaintFormat.PaintGlyph,
+                    {
+                        "Format": int(ot.PaintFormat.PaintSolid),
+                        "PaletteIndex": 2,
+                        "Alpha": 0.8,
+                    },
+                    "e",
+                ),
+                (
+                    ot.PaintFormat.PaintGlyph,
+                    {
+                        "Format": int(ot.PaintFormat.PaintVarRadialGradient),
+                        "ColorLine": {
+                            "ColorStop": [(0.0, 3), (1.0, 4)],
+                            "Extend": "reflect",
+                        },
+                        "x0": 0,
+                        "y0": 0,
+                        "x1": 0,
+                        "y1": 0,
+                        "r0": 10,
+                        "r1": 0,
+                    },
+                    "f",
+                ),
+            ],
+        ),
+        "g": (
+            ot.PaintFormat.PaintColrLayers,
+            [(ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintSolid, 5), "h")],
+        ),
+    }
+    glyphMap = {
+        ".notdef": 0,
+        "a": 4,
+        "b": 3,
+        "c": 2,
+        "d": 1,
+        "e": 5,
+        "f": 6,
+        "g": 7,
+        "h": 8,
+    }
+
+    # TODO(anthrotype) should we split into two tests? - seems two distinct validations
+    layers, baseGlyphs = builder.buildColrV1(colorGlyphs, glyphMap)
+    assert baseGlyphs.BaseGlyphCount == len(colorGlyphs)
+    assert baseGlyphs.BaseGlyphPaintRecord[0].BaseGlyph == "d"
+    assert baseGlyphs.BaseGlyphPaintRecord[1].BaseGlyph == "a"
+    assert baseGlyphs.BaseGlyphPaintRecord[2].BaseGlyph == "g"
+
+    layers, baseGlyphs = builder.buildColrV1(colorGlyphs)
+    assert baseGlyphs.BaseGlyphCount == len(colorGlyphs)
+    assert baseGlyphs.BaseGlyphPaintRecord[0].BaseGlyph == "a"
+    assert baseGlyphs.BaseGlyphPaintRecord[1].BaseGlyph == "d"
+    assert baseGlyphs.BaseGlyphPaintRecord[2].BaseGlyph == "g"
+
+
+def test_buildColrV1_more_than_255_paints():
+    num_paints = 364
+    colorGlyphs = {
+        "a": (
+            ot.PaintFormat.PaintColrLayers,
+            [
+                {
+                    "Format": int(ot.PaintFormat.PaintGlyph),
+                    "Paint": (ot.PaintFormat.PaintSolid, 0),
+                    "Glyph": name,
+                }
+                for name in (f"glyph{i}" for i in range(num_paints))
+            ],
+        ),
+    }
+    layers, baseGlyphs = builder.buildColrV1(colorGlyphs)
+    paints = layers.Paint
+
+    assert len(paints) == num_paints + 1
+
+    assert all(paints[i].Format == ot.PaintFormat.PaintGlyph for i in range(255))
+
+    assert paints[255].Format == ot.PaintFormat.PaintColrLayers
+    assert paints[255].FirstLayerIndex == 0
+    assert paints[255].NumLayers == 255
+
+    assert all(
+        paints[i].Format == ot.PaintFormat.PaintGlyph
+        for i in range(256, num_paints + 1)
+    )
+
+    assert baseGlyphs.BaseGlyphCount == len(colorGlyphs)
+    assert baseGlyphs.BaseGlyphPaintRecord[0].BaseGlyph == "a"
+    assert (
+        baseGlyphs.BaseGlyphPaintRecord[0].Paint.Format
+        == ot.PaintFormat.PaintColrLayers
+    )
+    assert baseGlyphs.BaseGlyphPaintRecord[0].Paint.FirstLayerIndex == 255
+    assert baseGlyphs.BaseGlyphPaintRecord[0].Paint.NumLayers == num_paints + 1 - 255
+
+
+def test_split_color_glyphs_by_version():
+    layerBuilder = LayerListBuilder()
+    colorGlyphs = {
+        "a": [
+            ("b", 0),
+            ("c", 1),
+            ("d", 2),
+            ("e", 3),
+        ]
+    }
+
+    colorGlyphsV0, colorGlyphsV1 = builder._split_color_glyphs_by_version(colorGlyphs)
+
+    assert colorGlyphsV0 == {"a": [("b", 0), ("c", 1), ("d", 2), ("e", 3)]}
+    assert not colorGlyphsV1
+
+    colorGlyphs = {"a": (ot.PaintFormat.PaintGlyph, 0, "b")}
+
+    colorGlyphsV0, colorGlyphsV1 = builder._split_color_glyphs_by_version(colorGlyphs)
+
+    assert not colorGlyphsV0
+    assert colorGlyphsV1 == colorGlyphs
+
+    colorGlyphs = {
+        "a": [("b", 0)],
+        "c": [
+            ("d", 1),
+            (
+                "e",
+                {
+                    "format": 3,
+                    "colorLine": {"stops": [(0.0, 2), (1.0, 3)]},
+                    "p0": (0, 0),
+                    "p1": (10, 10),
+                },
+            ),
+        ],
+    }
+
+    colorGlyphsV0, colorGlyphsV1 = builder._split_color_glyphs_by_version(colorGlyphs)
+
+    assert colorGlyphsV0 == {"a": [("b", 0)]}
+    assert "a" not in colorGlyphsV1
+    assert "c" in colorGlyphsV1
+    assert len(colorGlyphsV1["c"]) == 2
+
+
+def assertIsColrV1(colr):
+    assert colr.version == 1
+    assert not hasattr(colr, "ColorLayers")
+    assert hasattr(colr, "table")
+    assert isinstance(colr.table, ot.COLR)
+
+
+def assertNoV0Content(colr):
+    assert colr.table.BaseGlyphRecordCount == 0
+    assert colr.table.BaseGlyphRecordArray is None
+    assert colr.table.LayerRecordCount == 0
+    assert colr.table.LayerRecordArray is None
+
+
+def test_build_layerv1list_empty():
+    # Nobody uses PaintColrLayers, no layerlist
+    colr = builder.buildCOLR(
+        {
+            # BaseGlyph, tuple form
+            "a": (
+                int(ot.PaintFormat.PaintGlyph),
+                (int(ot.PaintFormat.PaintSolid), 2, 0.8),
+                "b",
+            ),
+            # BaseGlyph, map form
+            "b": {
+                "Format": int(ot.PaintFormat.PaintGlyph),
+                "Paint": {
+                    "Format": int(ot.PaintFormat.PaintLinearGradient),
+                    "ColorLine": {
+                        "ColorStop": [(0.0, 2), (1.0, 3)],
+                        "Extend": "reflect",
+                    },
+                    "x0": 1,
+                    "y0": 2,
+                    "x1": 3,
+                    "y1": 4,
+                    "x2": 2,
+                    "y2": 2,
+                },
+                "Glyph": "bb",
+            },
+        },
+        version=1,
+    )
+
+    assertIsColrV1(colr)
+    assertNoV0Content(colr)
+
+    # 2 v1 glyphs, none in LayerList
+    assert colr.table.BaseGlyphList.BaseGlyphCount == 2
+    assert len(colr.table.BaseGlyphList.BaseGlyphPaintRecord) == 2
+    assert colr.table.LayerList is None
+
+
+def _paint_names(paints) -> List[str]:
+    # prints a predictable string from a paint list to enable
+    # semi-readable assertions on a LayerList order.
+    result = []
+    for paint in paints:
+        if paint.Format == int(ot.PaintFormat.PaintGlyph):
+            result.append(paint.Glyph)
+        elif paint.Format == int(ot.PaintFormat.PaintColrLayers):
+            result.append(
+                f"Layers[{paint.FirstLayerIndex}:{paint.FirstLayerIndex+paint.NumLayers}]"
+            )
+    return result
+
+
+def test_build_layerv1list_simple():
+    # Two colr glyphs, each with two layers the first of which is common
+    # All layers use the same solid paint
+    solid_paint = {
+        "Format": int(ot.PaintFormat.PaintSolid),
+        "PaletteIndex": 2,
+        "Alpha": 0.8,
+    }
+    backdrop = {
+        "Format": int(ot.PaintFormat.PaintGlyph),
+        "Paint": solid_paint,
+        "Glyph": "back",
+    }
+    a_foreground = {
+        "Format": int(ot.PaintFormat.PaintGlyph),
+        "Paint": solid_paint,
+        "Glyph": "a_fore",
+    }
+    b_foreground = {
+        "Format": int(ot.PaintFormat.PaintGlyph),
+        "Paint": solid_paint,
+        "Glyph": "b_fore",
+    }
+
+    # list => PaintColrLayers, contents should land in LayerList
+    colr = builder.buildCOLR(
+        {
+            "a": (
+                ot.PaintFormat.PaintColrLayers,
+                [
+                    backdrop,
+                    a_foreground,
+                ],
+            ),
+            "b": {
+                "Format": ot.PaintFormat.PaintColrLayers,
+                "Layers": [
+                    backdrop,
+                    b_foreground,
+                ],
+            },
+        },
+        version=1,
+    )
+
+    assertIsColrV1(colr)
+    assertNoV0Content(colr)
+
+    # 2 v1 glyphs, 4 paints in LayerList
+    # A single shared backdrop isn't worth accessing by slice
+    assert colr.table.BaseGlyphList.BaseGlyphCount == 2
+    assert len(colr.table.BaseGlyphList.BaseGlyphPaintRecord) == 2
+    assert colr.table.LayerList.LayerCount == 4
+    assert _paint_names(colr.table.LayerList.Paint) == [
+        "back",
+        "a_fore",
+        "back",
+        "b_fore",
+    ]
+
+
+def test_build_layerv1list_with_sharing():
+    # Three colr glyphs, each with two layers in common
+    solid_paint = {
+        "Format": int(ot.PaintFormat.PaintSolid),
+        "PaletteIndex": 2,
+        "Alpha": 0.8,
+    }
+    backdrop = [
+        {
+            "Format": int(ot.PaintFormat.PaintGlyph),
+            "Paint": solid_paint,
+            "Glyph": "back1",
+        },
+        {
+            "Format": ot.PaintFormat.PaintGlyph,
+            "Paint": solid_paint,
+            "Glyph": "back2",
+        },
+    ]
+    a_foreground = {
+        "Format": ot.PaintFormat.PaintGlyph,
+        "Paint": solid_paint,
+        "Glyph": "a_fore",
+    }
+    b_background = {
+        "Format": ot.PaintFormat.PaintGlyph,
+        "Paint": solid_paint,
+        "Glyph": "b_back",
+    }
+    b_foreground = {
+        "Format": ot.PaintFormat.PaintGlyph,
+        "Paint": solid_paint,
+        "Glyph": "b_fore",
+    }
+    c_background = {
+        "Format": ot.PaintFormat.PaintGlyph,
+        "Paint": solid_paint,
+        "Glyph": "c_back",
+    }
+
+    # list => PaintColrLayers, which means contents should be in LayerList
+    colr = builder.buildCOLR(
+        {
+            "a": (ot.PaintFormat.PaintColrLayers, backdrop + [a_foreground]),
+            "b": (
+                ot.PaintFormat.PaintColrLayers,
+                [b_background] + backdrop + [b_foreground],
+            ),
+            "c": (ot.PaintFormat.PaintColrLayers, [c_background] + backdrop),
+        },
+        version=1,
+    )
+
+    assertIsColrV1(colr)
+    assertNoV0Content(colr)
+
+    # 2 v1 glyphs, 4 paints in LayerList
+    # A single shared backdrop isn't worth accessing by slice
+    baseGlyphs = colr.table.BaseGlyphList.BaseGlyphPaintRecord
+    assert colr.table.BaseGlyphList.BaseGlyphCount == 3
+    assert len(baseGlyphs) == 3
+    assert _paint_names([b.Paint for b in baseGlyphs]) == [
+        "Layers[0:3]",
+        "Layers[3:6]",
+        "Layers[6:8]",
+    ]
+    assert _paint_names(colr.table.LayerList.Paint) == [
+        "back1",
+        "back2",
+        "a_fore",
+        "b_back",
+        "Layers[0:2]",
+        "b_fore",
+        "c_back",
+        "Layers[0:2]",
+    ]
+    assert colr.table.LayerList.LayerCount == 8
+
+
+def test_build_layerv1list_with_overlaps():
+    paints = [
+        {
+            "Format": ot.PaintFormat.PaintGlyph,
+            "Paint": {
+                "Format": ot.PaintFormat.PaintSolid,
+                "PaletteIndex": 2,
+                "Alpha": 0.8,
+            },
+            "Glyph": c,
+        }
+        for c in "abcdefghi"
+    ]
+
+    # list => PaintColrLayers, which means contents should be in LayerList
+    colr = builder.buildCOLR(
+        {
+            "a": (ot.PaintFormat.PaintColrLayers, paints[0:4]),
+            "b": (ot.PaintFormat.PaintColrLayers, paints[0:6]),
+            "c": (ot.PaintFormat.PaintColrLayers, paints[2:8]),
+        },
+        version=1,
+    )
+
+    assertIsColrV1(colr)
+    assertNoV0Content(colr)
+
+    baseGlyphs = colr.table.BaseGlyphList.BaseGlyphPaintRecord
+    # assert colr.table.BaseGlyphList.BaseGlyphCount == 2
+
+    assert _paint_names(colr.table.LayerList.Paint) == [
+        "a",
+        "b",
+        "c",
+        "d",
+        "Layers[0:4]",
+        "e",
+        "f",
+        "Layers[2:4]",
+        "Layers[5:7]",
+        "g",
+        "h",
+    ]
+    assert _paint_names([b.Paint for b in baseGlyphs]) == [
+        "Layers[0:4]",
+        "Layers[4:7]",
+        "Layers[7:11]",
+    ]
+    assert colr.table.LayerList.LayerCount == 11
+
+
+def test_explicit_version_1():
+    colr = builder.buildCOLR(
+        {
+            "a": (
+                ot.PaintFormat.PaintColrLayers,
+                [
+                    (ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintSolid, 0), "b"),
+                    (ot.PaintFormat.PaintGlyph, (ot.PaintFormat.PaintSolid, 1), "c"),
+                ],
+            )
+        },
+        version=1,
+    )
+    assert colr.version == 1
+    assert not hasattr(colr, "ColorLayers")
+    assert hasattr(colr, "table")
+    assert isinstance(colr.table, ot.COLR)
+    assert colr.table.VarStore is None
+
+
+class BuildCOLRTest(object):
+    def test_automatic_version_all_solid_color_glyphs(self):
+        colr = builder.buildCOLR({"a": [("b", 0), ("c", 1)]})
+        assert colr.version == 0
+        assert hasattr(colr, "ColorLayers")
+        assert colr.ColorLayers["a"][0].name == "b"
+        assert colr.ColorLayers["a"][1].name == "c"
+
+    def test_automatic_version_no_solid_color_glyphs(self):
+        colr = builder.buildCOLR(
+            {
+                "a": (
+                    ot.PaintFormat.PaintColrLayers,
+                    [
+                        (
+                            ot.PaintFormat.PaintGlyph,
+                            {
+                                "Format": int(ot.PaintFormat.PaintRadialGradient),
+                                "ColorLine": {
+                                    "ColorStop": [(0.0, 0), (1.0, 1)],
+                                    "Extend": "repeat",
+                                },
+                                "x0": 1,
+                                "y0": 0,
+                                "x1": 10,
+                                "y1": 0,
+                                "r0": 4,
+                                "r1": 2,
+                            },
+                            "b",
+                        ),
+                        (
+                            ot.PaintFormat.PaintGlyph,
+                            {
+                                "Format": ot.PaintFormat.PaintSolid,
+                                "PaletteIndex": 2,
+                                "Alpha": 0.8,
+                            },
+                            "c",
+                        ),
+                    ],
+                ),
+                "d": (
+                    ot.PaintFormat.PaintColrLayers,
+                    [
+                        {
+                            "Format": ot.PaintFormat.PaintGlyph,
+                            "Glyph": "e",
+                            "Paint": {
+                                "Format": ot.PaintFormat.PaintLinearGradient,
+                                "ColorLine": {
+                                    "ColorStop": [(0.0, 2), (1.0, 3)],
+                                    "Extend": "reflect",
+                                },
+                                "x0": 1,
+                                "y0": 2,
+                                "x1": 3,
+                                "y1": 4,
+                                "x2": 2,
+                                "y2": 2,
+                            },
+                        }
+                    ],
+                ),
+            }
+        )
+        assertIsColrV1(colr)
+        assert colr.table.BaseGlyphRecordCount == 0
+        assert colr.table.BaseGlyphRecordArray is None
+        assert colr.table.LayerRecordCount == 0
+        assert colr.table.LayerRecordArray is None
+
+    def test_automatic_version_mixed_solid_and_gradient_glyphs(self):
+        colr = builder.buildCOLR(
+            {
+                "a": [("b", 0), ("c", 1)],
+                "d": (
+                    ot.PaintFormat.PaintColrLayers,
+                    [
+                        (
+                            ot.PaintFormat.PaintGlyph,
+                            {
+                                "Format": ot.PaintFormat.PaintLinearGradient,
+                                "ColorLine": {"ColorStop": [(0.0, 2), (1.0, 3)]},
+                                "x0": 1,
+                                "y0": 2,
+                                "x1": 3,
+                                "y1": 4,
+                                "x2": 2,
+                                "y2": 2,
+                            },
+                            "e",
+                        ),
+                        (
+                            ot.PaintFormat.PaintGlyph,
+                            (ot.PaintFormat.PaintSolid, 2, 0.8),
+                            "f",
+                        ),
+                    ],
+                ),
+            }
+        )
+        assertIsColrV1(colr)
+        assert colr.table.VarStore is None
+
+        assert colr.table.BaseGlyphRecordCount == 1
+        assert isinstance(colr.table.BaseGlyphRecordArray, ot.BaseGlyphRecordArray)
+        assert colr.table.LayerRecordCount == 2
+        assert isinstance(colr.table.LayerRecordArray, ot.LayerRecordArray)
+
+        assert isinstance(colr.table.BaseGlyphList, ot.BaseGlyphList)
+        assert colr.table.BaseGlyphList.BaseGlyphCount == 1
+        assert isinstance(
+            colr.table.BaseGlyphList.BaseGlyphPaintRecord[0], ot.BaseGlyphPaintRecord
+        )
+        assert colr.table.BaseGlyphList.BaseGlyphPaintRecord[0].BaseGlyph == "d"
+        assert isinstance(colr.table.LayerList, ot.LayerList)
+        assert colr.table.LayerList.Paint[0].Glyph == "e"
+
+    def test_explicit_version_0(self):
+        colr = builder.buildCOLR({"a": [("b", 0), ("c", 1)]}, version=0)
+        assert colr.version == 0
+        assert hasattr(colr, "ColorLayers")
+
+    def test_explicit_version_1(self):
+        colr = builder.buildCOLR(
+            {
+                "a": (
+                    ot.PaintFormat.PaintColrLayers,
+                    [
+                        (
+                            ot.PaintFormat.PaintGlyph,
+                            (ot.PaintFormat.PaintSolid, 0),
+                            "b",
+                        ),
+                        (
+                            ot.PaintFormat.PaintGlyph,
+                            (ot.PaintFormat.PaintSolid, 1),
+                            "c",
+                        ),
+                    ],
+                )
+            },
+            version=1,
+        )
+        assert colr.version == 1
+        assert not hasattr(colr, "ColorLayers")
+        assert hasattr(colr, "table")
+        assert isinstance(colr.table, ot.COLR)
+        assert colr.table.VarStore is None
+
+    def test_paint_one_colr_layers(self):
+        # A set of one layers should flip to just that layer
+        colr = builder.buildCOLR(
+            {
+                "a": (
+                    ot.PaintFormat.PaintColrLayers,
+                    [
+                        (
+                            ot.PaintFormat.PaintGlyph,
+                            (ot.PaintFormat.PaintSolid, 0),
+                            "b",
+                        ),
+                    ],
+                )
+            },
+        )
+
+        assert colr.table.LayerList is None, "PaintColrLayers should be gone"
+        assert colr.table.BaseGlyphList.BaseGlyphCount == 1
+        paint = colr.table.BaseGlyphList.BaseGlyphPaintRecord[0].Paint
+        assert paint.Format == ot.PaintFormat.PaintGlyph
+        assert paint.Paint.Format == ot.PaintFormat.PaintSolid
+
+    def test_build_clip_list(self):
+        colr = builder.buildCOLR(
+            {
+                "a": (
+                    ot.PaintFormat.PaintGlyph,
+                    (ot.PaintFormat.PaintSolid, 0),
+                    "b",
+                ),
+                "c": (
+                    ot.PaintFormat.PaintGlyph,
+                    (ot.PaintFormat.PaintSolid, 1),
+                    "d",
+                ),
+            },
+            clipBoxes={
+                "a": (0, 0, 1000, 1000, 0),  # optional 5th: varIndexBase
+                "c": (-100.8, -200.4, 1100.1, 1200.5),  # floats get rounded
+                "e": (0, 0, 10, 10),  # missing base glyph 'e' is ignored
+            },
+        )
+
+        assert colr.table.ClipList.Format == 1
+        clipBoxes = colr.table.ClipList.clips
+        assert [
+            (baseGlyph, clipBox.as_tuple()) for baseGlyph, clipBox in clipBoxes.items()
+        ] == [
+            ("a", (0, 0, 1000, 1000, 0)),
+            ("c", (-101, -201, 1101, 1201)),
+        ]
+        assert clipBoxes["a"].Format == 2
+        assert clipBoxes["c"].Format == 1
+
+
+class TrickyRadialGradientTest:
+    @staticmethod
+    def circle_inside_circle(c0, r0, c1, r1, rounded=False):
+        if rounded:
+            return Circle(c0, r0).round().inside(Circle(c1, r1).round())
+        else:
+            return Circle(c0, r0).inside(Circle(c1, r1))
+
+    def round_start_circle(self, c0, r0, c1, r1, inside=True):
+        assert self.circle_inside_circle(c0, r0, c1, r1) is inside
+        assert self.circle_inside_circle(c0, r0, c1, r1, rounded=True) is not inside
+        r = round_start_circle_stable_containment(c0, r0, c1, r1)
+        assert (
+            self.circle_inside_circle(r.centre, r.radius, c1, r1, rounded=True)
+            is inside
+        )
+        return r.centre, r.radius
+
+    def test_noto_emoji_mosquito_u1f99f(self):
+        # https://github.com/googlefonts/picosvg/issues/158
+        c0 = (385.23508, 70.56727999999998)
+        r0 = 0
+        c1 = (642.99108, 104.70327999999995)
+        r1 = 260.0072
+        assert self.round_start_circle(c0, r0, c1, r1, inside=True) == ((386, 71), 0)
+
+    @pytest.mark.parametrize(
+        "c0, r0, c1, r1, inside, expected",
+        [
+            # inside before round, outside after round
+            ((1.4, 0), 0, (2.6, 0), 1.3, True, ((2, 0), 0)),
+            ((1, 0), 0.6, (2.8, 0), 2.45, True, ((2, 0), 1)),
+            ((6.49, 6.49), 0, (0.49, 0.49), 8.49, True, ((5, 5), 0)),
+            # outside before round, inside after round
+            ((0, 0), 0, (2, 0), 1.5, False, ((-1, 0), 0)),
+            ((0, -0.5), 0, (0, -2.5), 1.5, False, ((0, 1), 0)),
+            # the following ones require two nudges to round correctly
+            ((0.5, 0), 0, (9.4, 0), 8.8, False, ((-1, 0), 0)),
+            ((1.5, 1.5), 0, (0.49, 0.49), 1.49, True, ((0, 0), 0)),
+            # limit case when circle almost exactly overlap
+            ((0.5000001, 0), 0.5000001, (0.499999, 0), 0.4999999, True, ((0, 0), 0)),
+            # concentrical circles, r0 > r1
+            ((0, 0), 1.49, (0, 0), 1, False, ((0, 0), 2)),
+        ],
+    )
+    def test_nudge_start_circle_position(self, c0, r0, c1, r1, inside, expected):
+        assert self.round_start_circle(c0, r0, c1, r1, inside) == expected
+
+
+@pytest.mark.parametrize(
+    "lst, n, expected",
+    [
+        ([0], 2, [0]),
+        ([0, 1], 2, [0, 1]),
+        ([0, 1, 2], 2, [[0, 1], 2]),
+        ([0, 1, 2], 3, [0, 1, 2]),
+        ([0, 1, 2, 3], 2, [[0, 1], [2, 3]]),
+        ([0, 1, 2, 3], 3, [[0, 1, 2], 3]),
+        ([0, 1, 2, 3, 4], 3, [[0, 1, 2], 3, 4]),
+        ([0, 1, 2, 3, 4, 5], 3, [[0, 1, 2], [3, 4, 5]]),
+        (list(range(7)), 3, [[0, 1, 2], [3, 4, 5], 6]),
+        (list(range(8)), 3, [[0, 1, 2], [3, 4, 5], [6, 7]]),
+        (list(range(9)), 3, [[0, 1, 2], [3, 4, 5], [6, 7, 8]]),
+        (list(range(10)), 3, [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], 9]),
+        (list(range(11)), 3, [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], 9, 10]),
+        (list(range(12)), 3, [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [9, 10, 11]]),
+        (list(range(13)), 3, [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [9, 10, 11], 12]),
+        (
+            list(range(14)),
+            3,
+            [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [[9, 10, 11], 12, 13]],
+        ),
+        (
+            list(range(15)),
+            3,
+            [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [9, 10, 11], [12, 13, 14]],
+        ),
+        (
+            list(range(16)),
+            3,
+            [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [[9, 10, 11], [12, 13, 14], 15]],
+        ),
+        (
+            list(range(23)),
+            3,
+            [
+                [[0, 1, 2], [3, 4, 5], [6, 7, 8]],
+                [[9, 10, 11], [12, 13, 14], [15, 16, 17]],
+                [[18, 19, 20], 21, 22],
+            ],
+        ),
+        (
+            list(range(27)),
+            3,
+            [
+                [[0, 1, 2], [3, 4, 5], [6, 7, 8]],
+                [[9, 10, 11], [12, 13, 14], [15, 16, 17]],
+                [[18, 19, 20], [21, 22, 23], [24, 25, 26]],
+            ],
+        ),
+        (
+            list(range(28)),
+            3,
+            [
+                [
+                    [[0, 1, 2], [3, 4, 5], [6, 7, 8]],
+                    [[9, 10, 11], [12, 13, 14], [15, 16, 17]],
+                    [[18, 19, 20], [21, 22, 23], [24, 25, 26]],
+                ],
+                27,
+            ],
+        ),
+        (list(range(257)), 256, [list(range(256)), 256]),
+        (list(range(258)), 256, [list(range(256)), 256, 257]),
+        (list(range(512)), 256, [list(range(256)), list(range(256, 512))]),
+        (list(range(512 + 1)), 256, [list(range(256)), list(range(256, 512)), 512]),
+        (
+            list(range(256 ** 2)),
+            256,
+            [list(range(k * 256, k * 256 + 256)) for k in range(256)],
+        ),
+    ],
+)
+def test_build_n_ary_tree(lst, n, expected):
+    assert _build_n_ary_tree(lst, n) == expected
diff --git a/Tests/colorLib/table_builder_test.py b/Tests/colorLib/table_builder_test.py
new file mode 100644
index 0000000..d0a76f5
--- /dev/null
+++ b/Tests/colorLib/table_builder_test.py
@@ -0,0 +1,15 @@
+from fontTools.ttLib.tables import otTables  # trigger setup to occur
+from fontTools.ttLib.tables.otConverters import UShort
+from fontTools.colorLib.table_builder import TableBuilder
+import pytest
+
+
+class WriteMe:
+    value = None
+
+
+def test_intValue_otRound():
+    dest = WriteMe()
+    converter = UShort("value", None, None)
+    TableBuilder()._convert(dest, "value", converter, 85.6)
+    assert dest.value == 86, "Should have used otRound"
diff --git a/Tests/colorLib/unbuilder_test.py b/Tests/colorLib/unbuilder_test.py
new file mode 100644
index 0000000..b5af732
--- /dev/null
+++ b/Tests/colorLib/unbuilder_test.py
@@ -0,0 +1,231 @@
+from fontTools.ttLib.tables import otTables as ot
+from fontTools.colorLib.builder import buildColrV1
+from fontTools.colorLib.unbuilder import unbuildColrV1
+import pytest
+
+
+TEST_COLOR_GLYPHS = {
+    "glyph00010": {
+        "Format": int(ot.PaintFormat.PaintColrLayers),
+        "Layers": [
+            {
+                "Format": int(ot.PaintFormat.PaintGlyph),
+                "Paint": {
+                    "Format": int(ot.PaintFormat.PaintSolid),
+                    "PaletteIndex": 2,
+                    "Alpha": 0.5,
+                },
+                "Glyph": "glyph00011",
+            },
+            {
+                "Format": int(ot.PaintFormat.PaintGlyph),
+                "Paint": {
+                    "Format": int(ot.PaintFormat.PaintVarLinearGradient),
+                    "ColorLine": {
+                        "Extend": "repeat",
+                        "ColorStop": [
+                            {
+                                "StopOffset": 0.0,
+                                "PaletteIndex": 3,
+                                "Alpha": 1.0,
+                                "VarIndexBase": 0,
+                            },
+                            {
+                                "StopOffset": 0.5,
+                                "PaletteIndex": 4,
+                                "Alpha": 1.0,
+                                "VarIndexBase": 1,
+                            },
+                            {
+                                "StopOffset": 1.0,
+                                "PaletteIndex": 5,
+                                "Alpha": 1.0,
+                                "VarIndexBase": 2,
+                            },
+                        ],
+                    },
+                    "x0": 1,
+                    "y0": 2,
+                    "x1": -3,
+                    "y1": -4,
+                    "x2": 5,
+                    "y2": 6,
+                    "VarIndexBase": 0xFFFFFFFF,
+                },
+                "Glyph": "glyph00012",
+            },
+            {
+                "Format": int(ot.PaintFormat.PaintGlyph),
+                "Paint": {
+                    "Format": int(ot.PaintFormat.PaintVarTransform),
+                    "Paint": {
+                        "Format": int(ot.PaintFormat.PaintRadialGradient),
+                        "ColorLine": {
+                            "Extend": "pad",
+                            "ColorStop": [
+                                {
+                                    "StopOffset": 0,
+                                    "PaletteIndex": 6,
+                                    "Alpha": 1.0,
+                                },
+                                {
+                                    "StopOffset": 1.0,
+                                    "PaletteIndex": 7,
+                                    "Alpha": 0.4,
+                                },
+                            ],
+                        },
+                        "x0": 7,
+                        "y0": 8,
+                        "r0": 9,
+                        "x1": 10,
+                        "y1": 11,
+                        "r1": 12,
+                    },
+                    "Transform": {
+                        "xx": -13.0,
+                        "yx": 14.0,
+                        "xy": 15.0,
+                        "yy": -17.0,
+                        "dx": 18.0,
+                        "dy": 19.0,
+                        "VarIndexBase": 3,
+                    },
+                },
+                "Glyph": "glyph00013",
+            },
+            {
+                "Format": int(ot.PaintFormat.PaintVarTranslate),
+                "Paint": {
+                    "Format": int(ot.PaintFormat.PaintRotate),
+                    "Paint": {
+                        "Format": int(ot.PaintFormat.PaintVarSkew),
+                        "Paint": {
+                            "Format": int(ot.PaintFormat.PaintGlyph),
+                            "Paint": {
+                                "Format": int(ot.PaintFormat.PaintSolid),
+                                "PaletteIndex": 2,
+                                "Alpha": 0.5,
+                            },
+                            "Glyph": "glyph00011",
+                        },
+                        "xSkewAngle": -11.0,
+                        "ySkewAngle": 5.0,
+                        "VarIndexBase": 4,
+                    },
+                    "angle": 45.0,
+                },
+                "dx": 257.0,
+                "dy": 258.0,
+                "VarIndexBase": 5,
+            },
+        ],
+    },
+    "glyph00014": {
+        "Format": int(ot.PaintFormat.PaintComposite),
+        "SourcePaint": {
+            "Format": int(ot.PaintFormat.PaintColrGlyph),
+            "Glyph": "glyph00010",
+        },
+        "CompositeMode": "src_over",
+        "BackdropPaint": {
+            "Format": int(ot.PaintFormat.PaintTransform),
+            "Paint": {
+                "Format": int(ot.PaintFormat.PaintColrGlyph),
+                "Glyph": "glyph00010",
+            },
+            "Transform": {
+                "xx": 1.0,
+                "yx": 0.0,
+                "xy": 0.0,
+                "yy": 1.0,
+                "dx": 300.0,
+                "dy": 0.0,
+            },
+        },
+    },
+    "glyph00015": {
+        "Format": int(ot.PaintFormat.PaintGlyph),
+        "Paint": {
+            "Format": int(ot.PaintFormat.PaintSweepGradient),
+            "ColorLine": {
+                "Extend": "pad",
+                "ColorStop": [
+                    {
+                        "StopOffset": 0.0,
+                        "PaletteIndex": 3,
+                        "Alpha": 1.0,
+                    },
+                    {
+                        "StopOffset": 1.0,
+                        "PaletteIndex": 5,
+                        "Alpha": 1.0,
+                    },
+                ],
+            },
+            "centerX": 259,
+            "centerY": 300,
+            "startAngle": 45.0,
+            "endAngle": 135.0,
+        },
+        "Glyph": "glyph00011",
+    },
+    "glyph00016": {
+        "Format": int(ot.PaintFormat.PaintColrLayers),
+        "Layers": [
+            {
+                "Format": int(ot.PaintFormat.PaintGlyph),
+                "Paint": {
+                    "Format": int(ot.PaintFormat.PaintVarSolid),
+                    "PaletteIndex": 2,
+                    "Alpha": 0.5,
+                    "VarIndexBase": 6,
+                },
+                "Glyph": "glyph00011",
+            },
+            {
+                "Format": int(ot.PaintFormat.PaintGlyph),
+                "Paint": {
+                    "Format": int(ot.PaintFormat.PaintVarLinearGradient),
+                    "ColorLine": {
+                        "Extend": "repeat",
+                        "ColorStop": [
+                            {
+                                "StopOffset": 0.0,
+                                "PaletteIndex": 3,
+                                "Alpha": 1.0,
+                                "VarIndexBase": 7,
+                            },
+                            {
+                                "StopOffset": 0.5,
+                                "PaletteIndex": 4,
+                                "Alpha": 1.0,
+                                "VarIndexBase": 8,
+                            },
+                            {
+                                "StopOffset": 1.0,
+                                "PaletteIndex": 5,
+                                "Alpha": 1.0,
+                                "VarIndexBase": 9,
+                            },
+                        ],
+                    },
+                    "x0": 1,
+                    "y0": 2,
+                    "x1": -3,
+                    "y1": -4,
+                    "x2": 5,
+                    "y2": 6,
+                    "VarIndexBase": 0xFFFFFFFF,
+                },
+                "Glyph": "glyph00012",
+            },
+        ],
+    },
+}
+
+
+def test_unbuildColrV1():
+    layersV1, baseGlyphsV1 = buildColrV1(TEST_COLOR_GLYPHS)
+    colorGlyphs = unbuildColrV1(layersV1, baseGlyphsV1)
+    assert colorGlyphs == TEST_COLOR_GLYPHS
diff --git a/Tests/conftest.py b/Tests/conftest.py
new file mode 100644
index 0000000..4928614
--- /dev/null
+++ b/Tests/conftest.py
@@ -0,0 +1,28 @@
+import fontTools
+import pytest
+
+
+@pytest.fixture(autouse=True, scope="session")
+def disableConfigLogger():
+    """Session-scoped fixture to make fontTools.configLogger function no-op.
+
+    Logging in python maintains a global state. When in the tests we call a main()
+    function from modules subset or ttx, a call to configLogger is made that modifies
+    this global state (to configures a handler for the fontTools logger).
+    To prevent that, we monkey-patch the `configLogger` attribute of the `fontTools`
+    module (the one used in the scripts main() functions) so that it does nothing,
+    to avoid any side effects.
+
+    NOTE: `fontTools.configLogger` is only an alias for the configLogger function in
+    `fontTools.misc.loggingTools` module; the original function is not modified.
+    """
+
+    def noop(*args, **kwargs):
+        return
+
+    originalConfigLogger = fontTools.configLogger
+    fontTools.configLogger = noop
+    try:
+        yield
+    finally:
+        fontTools.configLogger = originalConfigLogger
diff --git a/Tests/cu2qu/cli_test.py b/Tests/cu2qu/cli_test.py
new file mode 100644
index 0000000..f6798a6
--- /dev/null
+++ b/Tests/cu2qu/cli_test.py
@@ -0,0 +1,86 @@
+import os
+
+import pytest
+import py
+
+ufoLib2 = pytest.importorskip("ufoLib2")
+
+from fontTools.cu2qu.ufo import CURVE_TYPE_LIB_KEY
+from fontTools.cu2qu.cli import main
+
+
+DATADIR = os.path.join(os.path.dirname(__file__), 'data')
+
+TEST_UFOS = [
+    py.path.local(DATADIR).join("RobotoSubset-Regular.ufo"),
+    py.path.local(DATADIR).join("RobotoSubset-Bold.ufo"),
+]
+
+
+@pytest.fixture
+def test_paths(tmpdir):
+    result = []
+    for path in TEST_UFOS:
+        new_path = tmpdir / path.basename
+        path.copy(new_path)
+        result.append(new_path)
+    return result
+
+
+class MainTest(object):
+
+    @staticmethod
+    def run_main(*args):
+        main([str(p) for p in args if p])
+
+    def test_single_input_no_output(self, test_paths):
+        ufo_path = test_paths[0]
+
+        self.run_main(ufo_path)
+
+        font = ufoLib2.Font.open(ufo_path)
+        assert font.lib[CURVE_TYPE_LIB_KEY] == "quadratic"
+
+    def test_single_input_output_file(self, tmpdir):
+        input_path = TEST_UFOS[0]
+        output_path = tmpdir / input_path.basename
+        self.run_main('-o', output_path, input_path)
+
+        assert output_path.check(dir=1)
+
+    def test_multiple_inputs_output_dir(self, tmpdir):
+        output_dir = tmpdir / "output_dir"
+        self.run_main('-d', output_dir, *TEST_UFOS)
+
+        assert output_dir.check(dir=1)
+        outputs = set(p.basename for p in output_dir.listdir())
+        assert "RobotoSubset-Regular.ufo" in outputs
+        assert "RobotoSubset-Bold.ufo" in outputs
+
+    def test_interpolatable_inplace(self, test_paths):
+        self.run_main('-i', *test_paths)
+        self.run_main('-i', *test_paths)  # idempotent
+
+    @pytest.mark.parametrize(
+        "mode", ["", "-i"], ids=["normal", "interpolatable"])
+    def test_copytree(self, mode, tmpdir):
+        output_dir = tmpdir / "output_dir"
+        self.run_main(mode, '-d', output_dir, *TEST_UFOS)
+
+        output_dir_2 = tmpdir / "output_dir_2"
+        # no conversion when curves are already quadratic, just copy
+        self.run_main(mode, '-d', output_dir_2, *output_dir.listdir())
+        # running again overwrites existing with the copy
+        self.run_main(mode, '-d', output_dir_2, *output_dir.listdir())
+
+    def test_multiprocessing(self, tmpdir, test_paths):
+        self.run_main(*(test_paths + ["-j"]))
+
+    def test_keep_direction(self, test_paths):
+        self.run_main('--keep-direction', *test_paths)
+
+    def test_conversion_error(self, test_paths):
+        self.run_main('--conversion-error', 0.002, *test_paths)
+
+    def test_conversion_error_short(self, test_paths):
+        self.run_main('-e', 0.003, test_paths[0])
diff --git a/Tests/cu2qu/cu2qu_test.py b/Tests/cu2qu/cu2qu_test.py
new file mode 100644
index 0000000..456d210
--- /dev/null
+++ b/Tests/cu2qu/cu2qu_test.py
@@ -0,0 +1,178 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+import collections
+import math
+import unittest
+import os
+import json
+
+from fontTools.cu2qu import curve_to_quadratic, curves_to_quadratic
+
+
+DATADIR = os.path.join(os.path.dirname(__file__), 'data')
+
+MAX_ERR = 5
+
+
+class CurveToQuadraticTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        """Do the curve conversion ahead of time, and run tests on results."""
+        with open(os.path.join(DATADIR, "curves.json"), "r") as fp:
+            curves = json.load(fp)
+
+        cls.single_splines = [
+            curve_to_quadratic(c, MAX_ERR) for c in curves]
+        cls.single_errors = [
+            cls.curve_spline_dist(c, s)
+            for c, s in zip(curves, cls.single_splines)]
+
+        curve_groups = [curves[i:i + 3] for i in range(0, 300, 3)]
+        cls.compat_splines = [
+            curves_to_quadratic(c, [MAX_ERR] * 3) for c in curve_groups]
+        cls.compat_errors = [
+            [cls.curve_spline_dist(c, s) for c, s in zip(curve_group, splines)]
+            for curve_group, splines in zip(curve_groups, cls.compat_splines)]
+
+        cls.results = []
+
+    @classmethod
+    def tearDownClass(cls):
+        """Print stats from conversion, as determined during tests."""
+
+        for tag, results in cls.results:
+            print('\n%s\n%s' % (
+                tag, '\n'.join(
+                    '%s: %s (%d)' % (k, '#' * (v // 10 + 1), v)
+                    for k, v in sorted(results.items()))))
+
+    def test_results_unchanged(self):
+        """Tests that the results of conversion haven't changed since the time
+        of this test's writing. Useful as a quick check whenever one modifies
+        the conversion algorithm.
+        """
+
+        expected = {
+            2: 6,
+            3: 26,
+            4: 82,
+            5: 232,
+            6: 360,
+            7: 266,
+            8: 28}
+
+        results = collections.defaultdict(int)
+        for spline in self.single_splines:
+            n = len(spline) - 2
+            results[n] += 1
+        self.assertEqual(results, expected)
+        self.results.append(('single spline lengths', results))
+
+    def test_results_unchanged_multiple(self):
+        """Test that conversion results are unchanged for multiple curves."""
+
+        expected = {
+            5: 11,
+            6: 35,
+            7: 49,
+            8: 5}
+
+        results = collections.defaultdict(int)
+        for splines in self.compat_splines:
+            n = len(splines[0]) - 2
+            for spline in splines[1:]:
+                self.assertEqual(len(spline) - 2, n,
+                    'Got incompatible conversion results')
+            results[n] += 1
+        self.assertEqual(results, expected)
+        self.results.append(('compatible spline lengths', results))
+
+    def test_does_not_exceed_tolerance(self):
+        """Test that conversion results do not exceed given error tolerance."""
+
+        results = collections.defaultdict(int)
+        for error in self.single_errors:
+            results[round(error, 1)] += 1
+            self.assertLessEqual(error, MAX_ERR)
+        self.results.append(('single errors', results))
+
+    def test_does_not_exceed_tolerance_multiple(self):
+        """Test that error tolerance isn't exceeded for multiple curves."""
+
+        results = collections.defaultdict(int)
+        for errors in self.compat_errors:
+            for error in errors:
+                results[round(error, 1)] += 1
+                self.assertLessEqual(error, MAX_ERR)
+        self.results.append(('compatible errors', results))
+
+    @classmethod
+    def curve_spline_dist(cls, bezier, spline, total_steps=20):
+        """Max distance between a bezier and quadratic spline at sampled points."""
+
+        error = 0
+        n = len(spline) - 2
+        steps = total_steps // n
+        for i in range(0, n - 1):
+            p1 = spline[0] if i == 0 else p3
+            p2 = spline[i + 1]
+            if i < n - 1:
+                p3 = cls.lerp(spline[i + 1], spline[i + 2], 0.5)
+            else:
+                p3 = spline[n + 2]
+            segment = p1, p2, p3
+            for j in range(steps):
+                error = max(error, cls.dist(
+                    cls.cubic_bezier_at(bezier, (j / steps + i) / n),
+                    cls.quadratic_bezier_at(segment, j / steps)))
+        return error
+
+    @classmethod
+    def lerp(cls, p1, p2, t):
+        (x1, y1), (x2, y2) = p1, p2
+        return x1 + (x2 - x1) * t, y1 + (y2 - y1) * t
+
+    @classmethod
+    def dist(cls, p1, p2):
+        (x1, y1), (x2, y2) = p1, p2
+        return math.hypot(x1 - x2, y1 - y2)
+
+    @classmethod
+    def quadratic_bezier_at(cls, b, t):
+        (x1, y1), (x2, y2), (x3, y3) = b
+        _t = 1 - t
+        t2 = t * t
+        _t2 = _t * _t
+        _2_t_t = 2 * t * _t
+        return (_t2 * x1 + _2_t_t * x2 + t2 * x3,
+                _t2 * y1 + _2_t_t * y2 + t2 * y3)
+
+    @classmethod
+    def cubic_bezier_at(cls, b, t):
+        (x1, y1), (x2, y2), (x3, y3), (x4, y4) = b
+        _t = 1 - t
+        t2 = t * t
+        _t2 = _t * _t
+        t3 = t * t2
+        _t3 = _t * _t2
+        _3_t2_t = 3 * t2 * _t
+        _3_t_t2 = 3 * t * _t2
+        return (_t3 * x1 + _3_t_t2 * x2 + _3_t2_t * x3 + t3 * x4,
+                _t3 * y1 + _3_t_t2 * y2 + _3_t2_t * y3 + t3 * y4)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/fontinfo.plist b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/fontinfo.plist
new file mode 100644
index 0000000..21621a2
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/fontinfo.plist
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ascender</key>
+	<integer>2146</integer>
+	<key>capHeight</key>
+	<integer>1456</integer>
+	<key>copyright</key>
+	<string>Copyright 2011 Google Inc. All Rights Reserved.</string>
+	<key>descender</key>
+	<integer>-555</integer>
+	<key>familyName</key>
+	<string>RobotoSubset</string>
+	<key>italicAngle</key>
+	<integer>0</integer>
+	<key>openTypeHeadCreated</key>
+	<string>2008/09/12 12:29:34</string>
+	<key>openTypeHeadFlags</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+		<integer>3</integer>
+		<integer>4</integer>
+	</array>
+	<key>openTypeHeadLowestRecPPEM</key>
+	<integer>9</integer>
+	<key>openTypeHheaAscender</key>
+	<integer>1900</integer>
+	<key>openTypeHheaDescender</key>
+	<integer>-500</integer>
+	<key>openTypeHheaLineGap</key>
+	<integer>0</integer>
+	<key>openTypeNameDescription</key>
+	<string></string>
+	<key>openTypeNameDesigner</key>
+	<string></string>
+	<key>openTypeNameDesignerURL</key>
+	<string></string>
+	<key>openTypeNameLicense</key>
+	<string></string>
+	<key>openTypeNameLicenseURL</key>
+	<string></string>
+	<key>openTypeNameManufacturer</key>
+	<string></string>
+	<key>openTypeNameManufacturerURL</key>
+	<string></string>
+	<key>openTypeNameSampleText</key>
+	<string></string>
+	<key>openTypeOS2CodePageRanges</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+		<integer>2</integer>
+		<integer>3</integer>
+		<integer>4</integer>
+		<integer>7</integer>
+		<integer>8</integer>
+		<integer>29</integer>
+	</array>
+	<key>openTypeOS2FamilyClass</key>
+	<array>
+		<integer>0</integer>
+		<integer>0</integer>
+	</array>
+	<key>openTypeOS2Panose</key>
+	<array>
+		<integer>0</integer>
+		<integer>0</integer>
+		<integer>0</integer>
+		<integer>0</integer>
+		<integer>0</integer>
+		<integer>0</integer>
+		<integer>0</integer>
+		<integer>0</integer>
+		<integer>0</integer>
+		<integer>0</integer>
+	</array>
+	<key>openTypeOS2Selection</key>
+	<array>
+	</array>
+	<key>openTypeOS2StrikeoutPosition</key>
+	<integer>512</integer>
+	<key>openTypeOS2StrikeoutSize</key>
+	<integer>102</integer>
+	<key>openTypeOS2SubscriptXOffset</key>
+	<integer>0</integer>
+	<key>openTypeOS2SubscriptXSize</key>
+	<integer>1434</integer>
+	<key>openTypeOS2SubscriptYOffset</key>
+	<integer>287</integer>
+	<key>openTypeOS2SubscriptYSize</key>
+	<integer>1331</integer>
+	<key>openTypeOS2SuperscriptXOffset</key>
+	<integer>0</integer>
+	<key>openTypeOS2SuperscriptXSize</key>
+	<integer>1434</integer>
+	<key>openTypeOS2SuperscriptYOffset</key>
+	<integer>977</integer>
+	<key>openTypeOS2SuperscriptYSize</key>
+	<integer>1331</integer>
+	<key>openTypeOS2Type</key>
+	<array>
+	</array>
+	<key>openTypeOS2TypoAscender</key>
+	<integer>2146</integer>
+	<key>openTypeOS2TypoDescender</key>
+	<integer>-555</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>11</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>37</integer>
+		<integer>38</integer>
+		<integer>40</integer>
+		<integer>45</integer>
+		<integer>60</integer>
+		<integer>62</integer>
+		<integer>64</integer>
+		<integer>69</integer>
+	</array>
+	<key>openTypeOS2VendorID</key>
+	<string>GOOG</string>
+	<key>openTypeOS2WeightClass</key>
+	<integer>700</integer>
+	<key>openTypeOS2WidthClass</key>
+	<integer>5</integer>
+	<key>openTypeOS2WinAscent</key>
+	<integer>2146</integer>
+	<key>openTypeOS2WinDescent</key>
+	<integer>555</integer>
+	<key>postscriptBlueFuzz</key>
+	<integer>1</integer>
+	<key>postscriptBlueScale</key>
+	<real>0.039625</real>
+	<key>postscriptBlueShift</key>
+	<integer>7</integer>
+	<key>postscriptBlueValues</key>
+	<array>
+		<integer>-20</integer>
+		<integer>0</integer>
+		<integer>1082</integer>
+		<integer>1102</integer>
+		<integer>1456</integer>
+		<integer>1476</integer>
+	</array>
+	<key>postscriptDefaultCharacter</key>
+	<string>space</string>
+	<key>postscriptForceBold</key>
+	<false/>
+	<key>postscriptIsFixedPitch</key>
+	<false/>
+	<key>postscriptOtherBlues</key>
+	<array>
+		<integer>-436</integer>
+		<integer>-416</integer>
+	</array>
+	<key>postscriptStemSnapH</key>
+	<array>
+		<integer>250</integer>
+	</array>
+	<key>postscriptStemSnapV</key>
+	<array>
+		<integer>316</integer>
+	</array>
+	<key>postscriptUnderlinePosition</key>
+	<integer>-150</integer>
+	<key>postscriptUnderlineThickness</key>
+	<integer>100</integer>
+	<key>postscriptUniqueID</key>
+	<integer>-1</integer>
+	<key>styleName</key>
+	<string>Bold</string>
+	<key>trademark</key>
+	<string></string>
+	<key>unitsPerEm</key>
+	<integer>2048</integer>
+	<key>versionMajor</key>
+	<integer>1</integer>
+	<key>versionMinor</key>
+	<integer>0</integer>
+	<key>xHeight</key>
+	<integer>1082</integer>
+	<key>year</key>
+	<integer>2017</integer>
+</dict>
+</plist>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/A_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/A_.glif
new file mode 100644
index 0000000..481f006
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/A_.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="2">
+	<unicode hex="0041"/>
+	<advance width="1390"/>
+	<outline>
+		<contour>
+			<point x="727" y="1150" type="line"/>
+			<point x="764" y="1456" type="line"/>
+			<point x="537" y="1456" type="line"/>
+			<point x="0" y="0" type="line"/>
+			<point x="358" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1032" y="0" type="line"/>
+			<point x="1391" y="0" type="line"/>
+			<point x="851" y="1456" type="line"/>
+			<point x="621" y="1456" type="line"/>
+			<point x="662" y="1150" type="line"/>
+		</contour>
+		<contour>
+			<point x="1018" y="543" type="line"/>
+			<point x="262" y="543" type="line"/>
+			<point x="262" y="272" type="line"/>
+			<point x="1018" y="272" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/B_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/B_.glif
new file mode 100644
index 0000000..a4c9f25
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/B_.glif
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="B" format="2">
+	<unicode hex="0042"/>
+	<advance width="1317"/>
+	<outline>
+		<contour>
+			<point x="703" y="619" type="line"/>
+			<point x="797" y="710" type="line"/>
+			<point x="1092" y="713"/>
+			<point x="1199" y="875"/>
+			<point x="1199" y="1054" type="curve"/>
+			<point x="1199" y="1326"/>
+			<point x="988" y="1456"/>
+			<point x="636" y="1456" type="curve"/>
+			<point x="117" y="1456" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="452" y="0" type="line"/>
+			<point x="452" y="1185" type="line"/>
+			<point x="636" y="1185" type="line"/>
+			<point x="795" y="1185"/>
+			<point x="864" y="1135"/>
+			<point x="864" y="1012" type="curve"/>
+			<point x="864" y="904"/>
+			<point x="797" y="849"/>
+			<point x="635" y="849" type="curve"/>
+			<point x="328" y="849" type="line"/>
+			<point x="330" y="619" type="line"/>
+		</contour>
+		<contour>
+			<point x="690" y="0" type="line"/>
+			<point x="1042" y="0"/>
+			<point x="1230" y="145"/>
+			<point x="1230" y="431" type="curve" smooth="yes"/>
+			<point x="1230" y="598"/>
+			<point x="1129" y="769"/>
+			<point x="846" y="757" type="curve"/>
+			<point x="768" y="849" type="line"/>
+			<point x="412" y="849" type="line"/>
+			<point x="410" y="619" type="line"/>
+			<point x="703" y="619" type="line" smooth="yes"/>
+			<point x="842" y="619"/>
+			<point x="896" y="548"/>
+			<point x="896" y="436" type="curve" smooth="yes"/>
+			<point x="896" y="344"/>
+			<point x="836" y="270"/>
+			<point x="690" y="270" type="curve" smooth="yes"/>
+			<point x="365" y="270" type="line"/>
+			<point x="245" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/C_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/C_.glif
new file mode 100644
index 0000000..1d8e1e1
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/C_.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="C" format="2">
+	<unicode hex="0043"/>
+	<advance width="1343"/>
+	<outline>
+		<contour>
+			<point x="950" y="493" type="line"/>
+			<point x="942" y="329"/>
+			<point x="856" y="255"/>
+			<point x="687" y="255" type="curve" smooth="yes"/>
+			<point x="489" y="255"/>
+			<point x="415" y="379"/>
+			<point x="415" y="688" type="curve" smooth="yes"/>
+			<point x="415" y="769" type="line" smooth="yes"/>
+			<point x="415" y="1079"/>
+			<point x="503" y="1202"/>
+			<point x="685" y="1202" type="curve" smooth="yes"/>
+			<point x="875" y="1202"/>
+			<point x="944" y="1117"/>
+			<point x="952" y="953" type="curve"/>
+			<point x="1286" y="953" type="line"/>
+			<point x="1259" y="1255"/>
+			<point x="1060" y="1477"/>
+			<point x="685" y="1477" type="curve" smooth="yes"/>
+			<point x="316" y="1477"/>
+			<point x="75" y="1205"/>
+			<point x="75" y="767" type="curve"/>
+			<point x="75" y="688" type="line"/>
+			<point x="75" y="250"/>
+			<point x="303" y="-20"/>
+			<point x="687" y="-20" type="curve" smooth="yes"/>
+			<point x="1046" y="-20"/>
+			<point x="1268" y="188"/>
+			<point x="1284" y="493" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/D_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/D_.glif
new file mode 100644
index 0000000..1d82e15
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/D_.glif
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="D" format="2">
+	<unicode hex="0044"/>
+	<advance width="1327"/>
+	<outline>
+		<contour>
+			<point x="581" y="0" type="line"/>
+			<point x="973" y="0"/>
+			<point x="1251" y="285"/>
+			<point x="1251" y="697" type="curve"/>
+			<point x="1251" y="758" type="line"/>
+			<point x="1251" y="1170"/>
+			<point x="973" y="1456"/>
+			<point x="579" y="1456" type="curve"/>
+			<point x="255" y="1456" type="line"/>
+			<point x="255" y="1185" type="line"/>
+			<point x="579" y="1185" type="line"/>
+			<point x="794" y="1185"/>
+			<point x="910" y="1039"/>
+			<point x="910" y="760" type="curve"/>
+			<point x="910" y="697" type="line" smooth="yes"/>
+			<point x="910" y="416"/>
+			<point x="793" y="270"/>
+			<point x="581" y="270" type="curve"/>
+			<point x="263" y="270" type="line"/>
+			<point x="261" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="452" y="1456" type="line"/>
+			<point x="117" y="1456" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="452" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/E_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/E_.glif
new file mode 100644
index 0000000..4d867ee
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/E_.glif
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="E" format="2">
+	<unicode hex="0045"/>
+	<advance width="1148"/>
+	<outline>
+		<contour>
+			<point x="1111" y="270" type="line"/>
+			<point x="337" y="270" type="line"/>
+			<point x="337" y="0" type="line"/>
+			<point x="1111" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="452" y="1456" type="line"/>
+			<point x="117" y="1456" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="452" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1011" y="878" type="line"/>
+			<point x="337" y="878" type="line"/>
+			<point x="337" y="617" type="line"/>
+			<point x="1011" y="617" type="line"/>
+		</contour>
+		<contour>
+			<point x="1112" y="1456" type="line"/>
+			<point x="337" y="1456" type="line"/>
+			<point x="337" y="1185" type="line"/>
+			<point x="1112" y="1185" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/F_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/F_.glif
new file mode 100644
index 0000000..2e0d20c
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/F_.glif
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="F" format="2">
+	<unicode hex="0046"/>
+	<advance width="1121"/>
+	<outline>
+		<contour>
+			<point x="452" y="1456" type="line"/>
+			<point x="117" y="1456" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="452" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1021" y="850" type="line"/>
+			<point x="359" y="850" type="line"/>
+			<point x="359" y="580" type="line"/>
+			<point x="1021" y="580" type="line"/>
+		</contour>
+		<contour>
+			<point x="1083" y="1456" type="line"/>
+			<point x="359" y="1456" type="line"/>
+			<point x="359" y="1185" type="line"/>
+			<point x="1083" y="1185" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/G_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/G_.glif
new file mode 100644
index 0000000..6e65a0c
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/G_.glif
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="G" format="2">
+	<unicode hex="0047"/>
+	<advance width="1396"/>
+	<outline>
+		<contour>
+			<point x="1296" y="778" type="line"/>
+			<point x="708" y="778" type="line"/>
+			<point x="708" y="537" type="line"/>
+			<point x="961" y="537" type="line"/>
+			<point x="961" y="311" type="line"/>
+			<point x="931" y="284"/>
+			<point x="868" y="250"/>
+			<point x="746" y="250" type="curve" smooth="yes"/>
+			<point x="529" y="250"/>
+			<point x="426" y="396"/>
+			<point x="426" y="687" type="curve" smooth="yes"/>
+			<point x="426" y="770" type="line" smooth="yes"/>
+			<point x="426" y="1063"/>
+			<point x="535" y="1207"/>
+			<point x="715" y="1207" type="curve" smooth="yes"/>
+			<point x="883" y="1207"/>
+			<point x="951" y="1125"/>
+			<point x="972" y="981" type="curve"/>
+			<point x="1295" y="981" type="line"/>
+			<point x="1265" y="1272"/>
+			<point x="1098" y="1477"/>
+			<point x="704" y="1477" type="curve" smooth="yes"/>
+			<point x="340" y="1477"/>
+			<point x="86" y="1221"/>
+			<point x="86" y="768" type="curve" smooth="yes"/>
+			<point x="86" y="687" type="line" smooth="yes"/>
+			<point x="86" y="234"/>
+			<point x="340" y="-20"/>
+			<point x="724" y="-20" type="curve" smooth="yes"/>
+			<point x="1040" y="-20"/>
+			<point x="1223" y="98"/>
+			<point x="1296" y="180" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/H_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/H_.glif
new file mode 100644
index 0000000..4ae896d
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/H_.glif
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="H" format="2">
+	<unicode hex="0048"/>
+	<advance width="1442"/>
+	<outline>
+		<contour>
+			<point x="1094" y="878" type="line"/>
+			<point x="345" y="878" type="line"/>
+			<point x="345" y="608" type="line"/>
+			<point x="1094" y="608" type="line"/>
+		</contour>
+		<contour>
+			<point x="452" y="1456" type="line"/>
+			<point x="117" y="1456" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="452" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1324" y="1456" type="line"/>
+			<point x="990" y="1456" type="line"/>
+			<point x="990" y="0" type="line"/>
+			<point x="1324" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/I_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/I_.glif
new file mode 100644
index 0000000..c22d48a
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/I_.glif
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="I" format="2">
+	<unicode hex="0049"/>
+	<advance width="612"/>
+	<outline>
+		<contour>
+			<point x="473" y="1456" type="line"/>
+			<point x="139" y="1456" type="line"/>
+			<point x="139" y="0" type="line"/>
+			<point x="473" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/J_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/J_.glif
new file mode 100644
index 0000000..599404b
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/J_.glif
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="J" format="2">
+	<unicode hex="004A"/>
+	<advance width="1149"/>
+	<outline>
+		<contour>
+			<point x="699" y="457" type="line"/>
+			<point x="699" y="327"/>
+			<point x="638" y="250"/>
+			<point x="536" y="250" type="curve" smooth="yes"/>
+			<point x="433" y="250"/>
+			<point x="374" y="292"/>
+			<point x="374" y="440" type="curve"/>
+			<point x="38" y="440" type="line"/>
+			<point x="38" y="124"/>
+			<point x="246" y="-20"/>
+			<point x="536" y="-20" type="curve" smooth="yes"/>
+			<point x="816" y="-20"/>
+			<point x="1033" y="165"/>
+			<point x="1033" y="457" type="curve"/>
+			<point x="1033" y="1456" type="line"/>
+			<point x="699" y="1456" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/K_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/K_.glif
new file mode 100644
index 0000000..15d08e1
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/K_.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="K" format="2">
+	<unicode hex="004B"/>
+	<advance width="1307"/>
+	<outline>
+		<contour>
+			<point x="452" y="1456" type="line"/>
+			<point x="117" y="1456" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="452" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1322" y="1456" type="line"/>
+			<point x="909" y="1456" type="line"/>
+			<point x="577" y="999" type="line"/>
+			<point x="361" y="679" type="line"/>
+			<point x="422" y="357" type="line"/>
+			<point x="754" y="718" type="line"/>
+		</contour>
+		<contour>
+			<point x="930" y="0" type="line"/>
+			<point x="1327" y="0" type="line"/>
+			<point x="794" y="857" type="line"/>
+			<point x="538" y="656" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/L_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/L_.glif
new file mode 100644
index 0000000..eb22f5f
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/L_.glif
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="L" format="2">
+	<unicode hex="004C"/>
+	<advance width="1110"/>
+	<outline>
+		<contour>
+			<point x="1071" y="270" type="line"/>
+			<point x="337" y="270" type="line"/>
+			<point x="337" y="0" type="line"/>
+			<point x="1071" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="452" y="1456" type="line"/>
+			<point x="117" y="1456" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="452" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/M_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/M_.glif
new file mode 100644
index 0000000..9c1a8e5
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/M_.glif
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="M" format="2">
+	<unicode hex="004D"/>
+	<advance width="1795"/>
+	<outline>
+		<contour>
+			<point x="279" y="1456" type="line"/>
+			<point x="784" y="0" type="line"/>
+			<point x="1008" y="0" type="line"/>
+			<point x="1513" y="1456" type="line"/>
+			<point x="1236" y="1456" type="line"/>
+			<point x="896" y="443" type="line"/>
+			<point x="556" y="1456" type="line"/>
+		</contour>
+		<contour>
+			<point x="117" y="1456" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="452" y="0" type="line"/>
+			<point x="452" y="340" type="line"/>
+			<point x="400" y="1456" type="line"/>
+		</contour>
+		<contour>
+			<point x="1392" y="1456" type="line"/>
+			<point x="1340" y="340" type="line"/>
+			<point x="1340" y="0" type="line"/>
+			<point x="1676" y="0" type="line"/>
+			<point x="1676" y="1456" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/N_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/N_.glif
new file mode 100644
index 0000000..4b335f3
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/N_.glif
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="N" format="2">
+	<unicode hex="004E"/>
+	<advance width="1441"/>
+	<outline>
+		<contour>
+			<point x="1323" y="1456" type="line"/>
+			<point x="989" y="1456" type="line"/>
+			<point x="989" y="550" type="line"/>
+			<point x="452" y="1456" type="line"/>
+			<point x="117" y="1456" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="452" y="0" type="line"/>
+			<point x="452" y="906" type="line"/>
+			<point x="989" y="0" type="line"/>
+			<point x="1323" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/O_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/O_.glif
new file mode 100644
index 0000000..8e6d182
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/O_.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="O" format="2">
+	<unicode hex="004F"/>
+	<advance width="1414"/>
+	<outline>
+		<contour>
+			<point x="1338" y="757" type="line"/>
+			<point x="1338" y="1202"/>
+			<point x="1076" y="1476"/>
+			<point x="706" y="1476" type="curve" smooth="yes"/>
+			<point x="334" y="1476"/>
+			<point x="75" y="1202"/>
+			<point x="75" y="757" type="curve"/>
+			<point x="75" y="698" type="line"/>
+			<point x="75" y="253"/>
+			<point x="336" y="-20"/>
+			<point x="708" y="-20" type="curve" smooth="yes"/>
+			<point x="1078" y="-20"/>
+			<point x="1338" y="253"/>
+			<point x="1338" y="698" type="curve"/>
+		</contour>
+		<contour>
+			<point x="998" y="698" type="line"/>
+			<point x="998" y="413"/>
+			<point x="894" y="254"/>
+			<point x="708" y="254" type="curve" smooth="yes"/>
+			<point x="517" y="254"/>
+			<point x="415" y="413"/>
+			<point x="415" y="698" type="curve" smooth="yes"/>
+			<point x="415" y="759" type="line" smooth="yes"/>
+			<point x="415" y="1047"/>
+			<point x="515" y="1201"/>
+			<point x="706" y="1201" type="curve" smooth="yes"/>
+			<point x="892" y="1201"/>
+			<point x="998" y="1047"/>
+			<point x="998" y="759" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/P_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/P_.glif
new file mode 100644
index 0000000..5b00197
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/P_.glif
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="P" format="2">
+	<unicode hex="0050"/>
+	<advance width="1330"/>
+	<outline>
+		<contour>
+			<point x="694" y="494" type="line"/>
+			<point x="1042" y="494"/>
+			<point x="1253" y="680"/>
+			<point x="1253" y="962" type="curve"/>
+			<point x="1253" y="1247"/>
+			<point x="1042" y="1456"/>
+			<point x="694" y="1456" type="curve"/>
+			<point x="117" y="1456" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="452" y="0" type="line"/>
+			<point x="452" y="1185" type="line"/>
+			<point x="694" y="1185" type="line"/>
+			<point x="849" y="1185"/>
+			<point x="914" y="1079"/>
+			<point x="914" y="960" type="curve"/>
+			<point x="914" y="847"/>
+			<point x="849" y="765"/>
+			<point x="694" y="765" type="curve"/>
+			<point x="330" y="765" type="line"/>
+			<point x="330" y="494" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/Q_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/Q_.glif
new file mode 100644
index 0000000..28a9616
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/Q_.glif
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="Q" format="2">
+	<unicode hex="0051"/>
+	<advance width="1414"/>
+	<outline>
+		<contour>
+			<point x="922" y="240" type="line"/>
+			<point x="720" y="56" type="line"/>
+			<point x="1116" y="-266" type="line"/>
+			<point x="1325" y="-82" type="line"/>
+		</contour>
+		<contour>
+			<point x="1339" y="757" type="line"/>
+			<point x="1339" y="1202"/>
+			<point x="1077" y="1476"/>
+			<point x="707" y="1476" type="curve" smooth="yes"/>
+			<point x="335" y="1476"/>
+			<point x="76" y="1202"/>
+			<point x="76" y="757" type="curve"/>
+			<point x="76" y="698" type="line"/>
+			<point x="76" y="253"/>
+			<point x="337" y="-20"/>
+			<point x="709" y="-20" type="curve" smooth="yes"/>
+			<point x="1079" y="-20"/>
+			<point x="1339" y="253"/>
+			<point x="1339" y="698" type="curve"/>
+		</contour>
+		<contour>
+			<point x="999" y="698" type="line"/>
+			<point x="999" y="413"/>
+			<point x="895" y="254"/>
+			<point x="709" y="254" type="curve" smooth="yes"/>
+			<point x="518" y="254"/>
+			<point x="416" y="413"/>
+			<point x="416" y="698" type="curve" smooth="yes"/>
+			<point x="416" y="759" type="line" smooth="yes"/>
+			<point x="416" y="1047"/>
+			<point x="516" y="1201"/>
+			<point x="707" y="1201" type="curve" smooth="yes"/>
+			<point x="893" y="1201"/>
+			<point x="999" y="1047"/>
+			<point x="999" y="759" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/R_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/R_.glif
new file mode 100644
index 0000000..b76ad56
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/R_.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="R" format="2">
+	<unicode hex="0052"/>
+	<advance width="1327"/>
+	<outline>
+		<contour>
+			<point name="hr00" x="117" y="1456" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="452" y="0" type="line"/>
+			<point x="452" y="1185" type="line"/>
+			<point x="680" y="1185" type="line" smooth="yes"/>
+			<point x="820" y="1185"/>
+			<point x="892" y="1109"/>
+			<point x="892" y="984" type="curve" smooth="yes"/>
+			<point x="892" y="860"/>
+			<point x="821" y="785"/>
+			<point x="680" y="785" type="curve" smooth="yes"/>
+			<point x="328" y="785" type="line"/>
+			<point x="330" y="514" type="line"/>
+			<point x="806" y="514" type="line"/>
+			<point x="915" y="579" type="line"/>
+			<point x="1102" y="649"/>
+			<point x="1227" y="766"/>
+			<point x="1227" y="1016" type="curve" smooth="yes"/>
+			<point x="1227" y="1303"/>
+			<point x="1016" y="1456"/>
+			<point x="680" y="1456" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="919" y="0" type="line"/>
+			<point x="1278" y="0" type="line"/>
+			<point x="1278" y="15" type="line"/>
+			<point x="949" y="646" type="line"/>
+			<point x="594" y="644" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/S_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/S_.glif
new file mode 100644
index 0000000..a2ab850
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/S_.glif
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="S" format="2">
+	<unicode hex="0053"/>
+	<advance width="1275"/>
+	<outline>
+		<contour>
+			<point name="hr00" x="869" y="387" type="curve" smooth="yes"/>
+			<point x="869" y="309"/>
+			<point x="809" y="244"/>
+			<point x="669" y="244" type="curve" smooth="yes"/>
+			<point x="502" y="244"/>
+			<point x="402" y="301"/>
+			<point x="402" y="472" type="curve"/>
+			<point x="66" y="472" type="line"/>
+			<point x="66" y="127"/>
+			<point x="373" y="-20"/>
+			<point x="669" y="-20" type="curve" smooth="yes"/>
+			<point x="993" y="-20"/>
+			<point x="1204" y="127"/>
+			<point x="1204" y="389" type="curve"/>
+			<point x="1204" y="634"/>
+			<point x="1030" y="772"/>
+			<point x="718" y="870" type="curve"/>
+			<point x="545" y="924"/>
+			<point x="444" y="977"/>
+			<point x="444" y="1064" type="curve"/>
+			<point x="444" y="1144"/>
+			<point x="515" y="1211"/>
+			<point x="656" y="1211" type="curve" smooth="yes"/>
+			<point x="800" y="1211"/>
+			<point x="870" y="1134"/>
+			<point x="870" y="1024" type="curve"/>
+			<point x="1204" y="1024" type="line"/>
+			<point x="1204" y="1299"/>
+			<point x="983" y="1476"/>
+			<point x="663" y="1476" type="curve" smooth="yes"/>
+			<point x="343" y="1476"/>
+			<point x="110" y="1317"/>
+			<point x="110" y="1067" type="curve"/>
+			<point x="110" y="805"/>
+			<point x="344" y="685"/>
+			<point x="603" y="600" type="curve"/>
+			<point x="827" y="527"/>
+			<point x="869" y="478"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/T_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/T_.glif
new file mode 100644
index 0000000..5e0a1c9
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/T_.glif
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="T" format="2">
+	<unicode hex="0054"/>
+	<advance width="1284"/>
+	<outline>
+		<contour>
+			<point x="805" y="1456" type="line"/>
+			<point x="470" y="1456" type="line"/>
+			<point x="470" y="0" type="line"/>
+			<point x="805" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1245" y="1456" type="line"/>
+			<point x="38" y="1456" type="line"/>
+			<point x="38" y="1185" type="line"/>
+			<point x="1245" y="1185" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/U_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/U_.glif
new file mode 100644
index 0000000..202f92f
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/U_.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="U" format="2">
+	<unicode hex="0055"/>
+	<advance width="1357"/>
+	<outline>
+		<contour>
+			<point x="911" y="1456" type="line"/>
+			<point x="911" y="505" type="line" smooth="yes"/>
+			<point x="911" y="325"/>
+			<point x="829" y="250"/>
+			<point x="679" y="250" type="curve" smooth="yes"/>
+			<point x="530" y="250"/>
+			<point x="445" y="325"/>
+			<point x="445" y="505" type="curve" smooth="yes"/>
+			<point x="445" y="1456" type="line"/>
+			<point x="109" y="1456" type="line"/>
+			<point x="109" y="505" type="line"/>
+			<point x="109" y="164"/>
+			<point x="342" y="-20"/>
+			<point x="679" y="-20" type="curve" smooth="yes"/>
+			<point x="1017" y="-20"/>
+			<point x="1246" y="164"/>
+			<point x="1246" y="505" type="curve"/>
+			<point x="1246" y="1456" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/V_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/V_.glif
new file mode 100644
index 0000000..c242ddd
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/V_.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="V" format="2">
+	<unicode hex="0056"/>
+	<advance width="1349"/>
+	<outline>
+		<contour>
+			<point x="659" y="343" type="line"/>
+			<point x="610" y="0" type="line"/>
+			<point x="854" y="0" type="line"/>
+			<point x="1350" y="1456" type="line"/>
+			<point x="976" y="1456" type="line"/>
+		</contour>
+		<contour>
+			<point x="372" y="1456" type="line"/>
+			<point x="0" y="1456" type="line"/>
+			<point x="493" y="0" type="line"/>
+			<point x="739" y="0" type="line"/>
+			<point x="688" y="343" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/W_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/W_.glif
new file mode 100644
index 0000000..5cf2b3a
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/W_.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="W" format="2">
+	<unicode hex="0057"/>
+	<advance width="1784"/>
+	<outline>
+		<contour>
+			<point x="459" y="132" type="line"/>
+			<point x="498" y="0" type="line"/>
+			<point x="684" y="0" type="line"/>
+			<point x="993" y="1343" type="line"/>
+			<point x="918" y="1456" type="line"/>
+			<point x="748" y="1456" type="line"/>
+		</contour>
+		<contour>
+			<point x="359" y="1456" type="line"/>
+			<point x="26" y="1456" type="line"/>
+			<point x="340" y="0" type="line"/>
+			<point x="552" y="0" type="line"/>
+			<point x="601" y="122" type="line"/>
+		</contour>
+		<contour>
+			<point x="1183" y="129" type="line"/>
+			<point x="1230" y="0" type="line"/>
+			<point x="1442" y="0" type="line"/>
+			<point x="1755" y="1456" type="line"/>
+			<point x="1423" y="1456" type="line"/>
+		</contour>
+		<contour>
+			<point x="1032" y="1456" type="line"/>
+			<point x="863" y="1456" type="line"/>
+			<point x="785" y="1345" type="line"/>
+			<point x="1098" y="0" type="line"/>
+			<point x="1284" y="0" type="line"/>
+			<point x="1326" y="124" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/X_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/X_.glif
new file mode 100644
index 0000000..9682d90
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/X_.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="X" format="2">
+	<unicode hex="0058"/>
+	<advance width="1306"/>
+	<outline>
+		<contour>
+			<point x="404" y="1456" type="line"/>
+			<point x="21" y="1456" type="line"/>
+			<point x="433" y="734" type="line"/>
+			<point x="10" y="0" type="line"/>
+			<point x="397" y="0" type="line"/>
+			<point x="653" y="493" type="line"/>
+			<point x="909" y="0" type="line"/>
+			<point x="1296" y="0" type="line"/>
+			<point x="873" y="734" type="line"/>
+			<point x="1285" y="1456" type="line"/>
+			<point x="902" y="1456" type="line"/>
+			<point x="653" y="972" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/Y_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/Y_.glif
new file mode 100644
index 0000000..0b738ae
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/Y_.glif
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="Y" format="2">
+	<unicode hex="0059"/>
+	<advance width="1280"/>
+	<outline>
+		<contour>
+			<point x="361" y="1456" type="line"/>
+			<point x="-2" y="1456" type="line"/>
+			<point x="470" y="523" type="line"/>
+			<point x="470" y="0" type="line"/>
+			<point x="810" y="0" type="line"/>
+			<point x="810" y="523" type="line"/>
+			<point x="1282" y="1456" type="line"/>
+			<point x="919" y="1456" type="line"/>
+			<point x="640" y="824" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/Z_.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/Z_.glif
new file mode 100644
index 0000000..82d6647
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/Z_.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="Z" format="2">
+	<unicode hex="005A"/>
+	<advance width="1247"/>
+	<outline>
+		<contour>
+			<point x="1195" y="270" type="line"/>
+			<point x="149" y="270" type="line"/>
+			<point x="149" y="0" type="line"/>
+			<point x="1195" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1185" y="1276" type="line"/>
+			<point x="1185" y="1456" type="line"/>
+			<point x="954" y="1456" type="line"/>
+			<point x="69" y="185" type="line"/>
+			<point x="69" y="0" type="line"/>
+			<point x="309" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1075" y="1456" type="line"/>
+			<point x="66" y="1456" type="line"/>
+			<point x="66" y="1185" type="line"/>
+			<point x="1075" y="1185" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/a.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/a.glif
new file mode 100644
index 0000000..885bf08
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/a.glif
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="2">
+	<unicode hex="0061"/>
+	<advance width="1091"/>
+	<outline>
+		<contour>
+			<point name="hr00" x="670" y="272" type="line"/>
+			<point x="670" y="169"/>
+			<point x="684" y="66"/>
+			<point x="715" y="0" type="curve"/>
+			<point x="1038" y="0" type="line"/>
+			<point x="1038" y="17" type="line"/>
+			<point x="1009" y="71"/>
+			<point x="993" y="132"/>
+			<point x="993" y="273" type="curve" smooth="yes"/>
+			<point x="993" y="716" type="line"/>
+			<point x="993" y="973"/>
+			<point x="803" y="1102"/>
+			<point x="548" y="1102" type="curve" smooth="yes"/>
+			<point x="262" y="1102"/>
+			<point x="78" y="950"/>
+			<point x="78" y="749" type="curve"/>
+			<point x="400" y="749" type="line"/>
+			<point x="400" y="828"/>
+			<point x="448" y="867"/>
+			<point x="531" y="867" type="curve" smooth="yes"/>
+			<point x="629" y="867"/>
+			<point x="670" y="809"/>
+			<point x="670" y="718" type="curve"/>
+		</contour>
+		<contour>
+			<point x="710" y="661" type="line"/>
+			<point x="558" y="661" type="line"/>
+			<point x="216" y="661"/>
+			<point x="53" y="528"/>
+			<point x="53" y="305" type="curve" smooth="yes"/>
+			<point x="53" y="113"/>
+			<point x="218" y="-20"/>
+			<point x="420" y="-20" type="curve" smooth="yes"/>
+			<point x="627" y="-20"/>
+			<point x="709" y="108"/>
+			<point x="758" y="214" type="curve"/>
+			<point x="683" y="352" type="line"/>
+			<point x="683" y="297"/>
+			<point x="612" y="220"/>
+			<point x="495" y="220" type="curve" smooth="yes"/>
+			<point x="424" y="220"/>
+			<point x="375" y="262"/>
+			<point x="375" y="323" type="curve" smooth="yes"/>
+			<point x="375" y="405"/>
+			<point x="425" y="481"/>
+			<point x="560" y="481" type="curve"/>
+			<point x="712" y="481" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/b.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/b.glif
new file mode 100644
index 0000000..728335c
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/b.glif
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="b" format="2">
+	<unicode hex="0062"/>
+	<advance width="1153"/>
+	<outline>
+		<contour>
+			<point x="102" y="1536" type="line"/>
+			<point x="102" y="0" type="line"/>
+			<point x="391" y="0" type="line"/>
+			<point x="424" y="266" type="line"/>
+			<point x="424" y="1536" type="line"/>
+		</contour>
+		<contour>
+			<point x="1095" y="553" type="line"/>
+			<point x="1095" y="870"/>
+			<point x="961" y="1102"/>
+			<point x="673" y="1102" type="curve" smooth="yes"/>
+			<point x="412" y="1102"/>
+			<point x="306" y="856"/>
+			<point x="264" y="554" type="curve"/>
+			<point x="264" y="529" type="line"/>
+			<point x="306" y="225"/>
+			<point x="412" y="-20"/>
+			<point x="675" y="-20" type="curve" smooth="yes"/>
+			<point x="961" y="-20"/>
+			<point x="1095" y="205"/>
+			<point x="1095" y="532" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="773" y="532" type="line"/>
+			<point x="773" y="358"/>
+			<point x="745" y="239"/>
+			<point x="594" y="239" type="curve" smooth="yes"/>
+			<point x="445" y="239"/>
+			<point x="394" y="335"/>
+			<point x="394" y="502" type="curve" smooth="yes"/>
+			<point x="394" y="581" type="line" smooth="yes"/>
+			<point x="394" y="744"/>
+			<point x="445" y="842"/>
+			<point x="592" y="842" type="curve" smooth="yes"/>
+			<point x="741" y="842"/>
+			<point x="773" y="710"/>
+			<point x="773" y="553" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/c.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/c.glif
new file mode 100644
index 0000000..33db728
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/c.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="c" format="2">
+	<unicode hex="0063"/>
+	<advance width="1066"/>
+	<outline>
+		<contour>
+			<point x="555" y="240" type="curve" smooth="yes"/>
+			<point x="404" y="240"/>
+			<point x="379" y="369"/>
+			<point x="379" y="529" type="curve" smooth="yes"/>
+			<point x="379" y="552" type="line" smooth="yes"/>
+			<point x="379" y="708"/>
+			<point x="405" y="842"/>
+			<point x="553" y="842" type="curve"/>
+			<point x="661" y="842"/>
+			<point x="714" y="765"/>
+			<point x="714" y="668" type="curve"/>
+			<point x="1016" y="668" type="line"/>
+			<point x="1016" y="943"/>
+			<point x="829" y="1102"/>
+			<point x="560" y="1102" type="curve" smooth="yes"/>
+			<point x="225" y="1102"/>
+			<point x="57" y="865"/>
+			<point x="57" y="552" type="curve" smooth="yes"/>
+			<point x="57" y="529" type="line" smooth="yes"/>
+			<point x="57" y="216"/>
+			<point x="225" y="-20"/>
+			<point x="562" y="-20" type="curve"/>
+			<point x="819" y="-20"/>
+			<point x="1016" y="142"/>
+			<point x="1016" y="386" type="curve"/>
+			<point x="714" y="386" type="line"/>
+			<point x="714" y="294"/>
+			<point x="653" y="240"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/contents.plist b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..431aca7
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/contents.plist
@@ -0,0 +1,112 @@
+<?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>A</key>
+		<string>A_.glif</string>
+		<key>B</key>
+		<string>B_.glif</string>
+		<key>C</key>
+		<string>C_.glif</string>
+		<key>D</key>
+		<string>D_.glif</string>
+		<key>E</key>
+		<string>E_.glif</string>
+		<key>F</key>
+		<string>F_.glif</string>
+		<key>G</key>
+		<string>G_.glif</string>
+		<key>H</key>
+		<string>H_.glif</string>
+		<key>I</key>
+		<string>I_.glif</string>
+		<key>J</key>
+		<string>J_.glif</string>
+		<key>K</key>
+		<string>K_.glif</string>
+		<key>L</key>
+		<string>L_.glif</string>
+		<key>M</key>
+		<string>M_.glif</string>
+		<key>N</key>
+		<string>N_.glif</string>
+		<key>O</key>
+		<string>O_.glif</string>
+		<key>P</key>
+		<string>P_.glif</string>
+		<key>Q</key>
+		<string>Q_.glif</string>
+		<key>R</key>
+		<string>R_.glif</string>
+		<key>S</key>
+		<string>S_.glif</string>
+		<key>T</key>
+		<string>T_.glif</string>
+		<key>U</key>
+		<string>U_.glif</string>
+		<key>V</key>
+		<string>V_.glif</string>
+		<key>W</key>
+		<string>W_.glif</string>
+		<key>X</key>
+		<string>X_.glif</string>
+		<key>Y</key>
+		<string>Y_.glif</string>
+		<key>Z</key>
+		<string>Z_.glif</string>
+		<key>a</key>
+		<string>a.glif</string>
+		<key>b</key>
+		<string>b.glif</string>
+		<key>c</key>
+		<string>c.glif</string>
+		<key>d</key>
+		<string>d.glif</string>
+		<key>e</key>
+		<string>e.glif</string>
+		<key>f</key>
+		<string>f.glif</string>
+		<key>g</key>
+		<string>g.glif</string>
+		<key>h</key>
+		<string>h.glif</string>
+		<key>i</key>
+		<string>i.glif</string>
+		<key>j</key>
+		<string>j.glif</string>
+		<key>k</key>
+		<string>k.glif</string>
+		<key>l</key>
+		<string>l.glif</string>
+		<key>m</key>
+		<string>m.glif</string>
+		<key>n</key>
+		<string>n.glif</string>
+		<key>o</key>
+		<string>o.glif</string>
+		<key>p</key>
+		<string>p.glif</string>
+		<key>q</key>
+		<string>q.glif</string>
+		<key>r</key>
+		<string>r.glif</string>
+		<key>s</key>
+		<string>s.glif</string>
+		<key>space</key>
+		<string>space.glif</string>
+		<key>t</key>
+		<string>t.glif</string>
+		<key>u</key>
+		<string>u.glif</string>
+		<key>v</key>
+		<string>v.glif</string>
+		<key>w</key>
+		<string>w.glif</string>
+		<key>x</key>
+		<string>x.glif</string>
+		<key>y</key>
+		<string>y.glif</string>
+		<key>z</key>
+		<string>z.glif</string>
+	</dict>
+</plist>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/d.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/d.glif
new file mode 100644
index 0000000..9c7ac79
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/d.glif
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="d" format="2">
+	<unicode hex="0064"/>
+	<advance width="1153"/>
+	<outline>
+		<contour>
+			<point x="728" y="248" type="line"/>
+			<point x="761" y="0" type="line"/>
+			<point x="1051" y="0" type="line"/>
+			<point x="1051" y="1536" type="line"/>
+			<point x="728" y="1536" type="line"/>
+		</contour>
+		<contour>
+			<point x="57" y="528" type="line"/>
+			<point x="57" y="213"/>
+			<point x="205" y="-20"/>
+			<point x="477" y="-20" type="curve" smooth="yes"/>
+			<point x="728" y="-20"/>
+			<point x="848" y="227"/>
+			<point x="889" y="520" type="curve"/>
+			<point x="889" y="545" type="line"/>
+			<point x="848" y="858"/>
+			<point x="728" y="1102"/>
+			<point x="479" y="1102" type="curve" smooth="yes"/>
+			<point x="205" y="1102"/>
+			<point x="57" y="878"/>
+			<point x="57" y="549" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="379" y="549" type="line"/>
+			<point x="379" y="717"/>
+			<point x="427" y="842"/>
+			<point x="561" y="842" type="curve" smooth="yes"/>
+			<point x="695" y="842"/>
+			<point x="759" y="748"/>
+			<point x="759" y="572" type="curve" smooth="yes"/>
+			<point x="759" y="493" type="line" smooth="yes"/>
+			<point x="759" y="339"/>
+			<point x="694" y="240"/>
+			<point x="559" y="240" type="curve" smooth="yes"/>
+			<point x="423" y="240"/>
+			<point x="379" y="366"/>
+			<point x="379" y="528" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/e.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/e.glif
new file mode 100644
index 0000000..71ee028
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/e.glif
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="e" format="2">
+	<unicode hex="0065"/>
+	<advance width="1113"/>
+	<outline>
+		<contour>
+			<point x="616" y="-20" type="curve" smooth="yes"/>
+			<point x="825" y="-20"/>
+			<point x="973" y="75"/>
+			<point x="1041" y="170" type="curve"/>
+			<point x="891" y="352" type="line"/>
+			<point x="827" y="272"/>
+			<point x="733" y="240"/>
+			<point x="637" y="240" type="curve" smooth="yes"/>
+			<point x="481" y="240"/>
+			<point x="387" y="345"/>
+			<point x="387" y="505" type="curve" smooth="yes"/>
+			<point x="387" y="543" type="line" smooth="yes"/>
+			<point x="387" y="704"/>
+			<point x="430" y="842"/>
+			<point x="579" y="842" type="curve" smooth="yes"/>
+			<point x="694" y="842"/>
+			<point x="753" y="779"/>
+			<point x="753" y="672" type="curve" smooth="yes"/>
+			<point x="753" y="646" type="line"/>
+			<point name="hr01" x="192" y="646" type="line"/>
+			<point x="192" y="435" type="line"/>
+			<point x="1068" y="435" type="line"/>
+			<point x="1068" y="572" type="line" smooth="yes"/>
+			<point x="1068" y="897"/>
+			<point x="890" y="1102"/>
+			<point x="581" y="1102" type="curve" smooth="yes"/>
+			<point x="246" y="1102"/>
+			<point x="65" y="859"/>
+			<point name="hr02" x="65" y="543" type="curve" smooth="yes"/>
+			<point x="65" y="505" type="line" smooth="yes"/>
+			<point x="65" y="221"/>
+			<point x="268" y="-20"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/f.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/f.glif
new file mode 100644
index 0000000..deeb2d6
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/f.glif
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="f" format="2">
+	<unicode hex="0066"/>
+	<advance width="740"/>
+	<outline>
+		<contour>
+			<point name="hr00" x="499" y="0" type="line"/>
+			<point x="499" y="1170" type="line"/>
+			<point x="499" y="1252"/>
+			<point x="555" y="1297"/>
+			<point x="655" y="1297" type="curve" smooth="yes"/>
+			<point x="691" y="1297"/>
+			<point x="716" y="1294"/>
+			<point x="740" y="1288" type="curve"/>
+			<point x="740" y="1536" type="line"/>
+			<point x="692" y="1548"/>
+			<point x="641" y="1557"/>
+			<point x="585" y="1557" type="curve" smooth="yes"/>
+			<point x="334" y="1557"/>
+			<point x="176" y="1421"/>
+			<point x="176" y="1170" type="curve"/>
+			<point x="176" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="711" y="1082" type="line"/>
+			<point x="18" y="1082" type="line"/>
+			<point x="18" y="848" type="line"/>
+			<point x="711" y="848" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/g.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/g.glif
new file mode 100644
index 0000000..7618721
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/g.glif
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="g" format="2">
+	<unicode hex="0067"/>
+	<advance width="1176"/>
+	<outline>
+		<contour>
+			<point x="782" y="1082" type="line"/>
+			<point x="751" y="826" type="line"/>
+			<point x="751" y="44" type="line" smooth="yes"/>
+			<point x="751" y="-96"/>
+			<point x="669" y="-177"/>
+			<point x="521" y="-177" type="curve" smooth="yes"/>
+			<point x="411" y="-177"/>
+			<point x="326" y="-128"/>
+			<point x="268" y="-66" type="curve"/>
+			<point x="131" y="-264" type="line"/>
+			<point x="221" y="-370"/>
+			<point x="392" y="-426"/>
+			<point x="533" y="-426" type="curve" smooth="yes"/>
+			<point x="856" y="-426"/>
+			<point x="1074" y="-258"/>
+			<point x="1074" y="42" type="curve" smooth="yes"/>
+			<point x="1074" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="60" y="528" type="line"/>
+			<point x="60" y="213"/>
+			<point x="228" y="-20"/>
+			<point x="500" y="-20" type="curve" smooth="yes"/>
+			<point x="763" y="-20"/>
+			<point x="869" y="227"/>
+			<point x="912" y="520" type="curve"/>
+			<point x="912" y="545" type="line"/>
+			<point x="869" y="858"/>
+			<point x="795" y="1102"/>
+			<point x="502" y="1102" type="curve" smooth="yes"/>
+			<point x="228" y="1102"/>
+			<point x="60" y="878"/>
+			<point x="60" y="549" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="382" y="549" type="line"/>
+			<point x="382" y="717"/>
+			<point x="450" y="842"/>
+			<point x="584" y="842" type="curve" smooth="yes"/>
+			<point x="731" y="842"/>
+			<point x="792" y="748"/>
+			<point x="792" y="572" type="curve" smooth="yes"/>
+			<point x="792" y="493" type="line" smooth="yes"/>
+			<point x="792" y="339"/>
+			<point x="731" y="240"/>
+			<point x="582" y="240" type="curve" smooth="yes"/>
+			<point x="446" y="240"/>
+			<point x="382" y="366"/>
+			<point x="382" y="528" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/h.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/h.glif
new file mode 100644
index 0000000..58e9754
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/h.glif
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="h" format="2">
+	<unicode hex="0068"/>
+	<advance width="1153"/>
+	<outline>
+		<contour>
+			<point x="415" y="1536" type="line"/>
+			<point x="93" y="1536" type="line"/>
+			<point x="93" y="0" type="line"/>
+			<point x="415" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="374" y="578" type="line"/>
+			<point x="374" y="729"/>
+			<point x="413" y="842"/>
+			<point x="573" y="842" type="curve" smooth="yes"/>
+			<point x="674" y="842"/>
+			<point x="733" y="806"/>
+			<point x="733" y="674" type="curve" smooth="yes"/>
+			<point x="733" y="0" type="line"/>
+			<point x="1056" y="0" type="line"/>
+			<point x="1056" y="672" type="line" smooth="yes"/>
+			<point x="1056" y="986"/>
+			<point x="909" y="1102"/>
+			<point x="695" y="1102" type="curve" smooth="yes"/>
+			<point x="454" y="1102"/>
+			<point x="295" y="880"/>
+			<point x="295" y="576" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/i.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/i.glif
new file mode 100644
index 0000000..be7bc25
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/i.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="i" format="2">
+	<unicode hex="0069"/>
+	<advance width="557"/>
+	<outline>
+		<contour>
+			<point x="440" y="1082" type="line"/>
+			<point x="117" y="1082" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="440" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="98" y="1361" type="curve" smooth="yes"/>
+			<point x="98" y="1265"/>
+			<point x="170" y="1197"/>
+			<point x="277" y="1197" type="curve" smooth="yes"/>
+			<point x="384" y="1197"/>
+			<point x="456" y="1265"/>
+			<point x="456" y="1361" type="curve" smooth="yes"/>
+			<point x="456" y="1457"/>
+			<point x="384" y="1525"/>
+			<point x="277" y="1525" type="curve" smooth="yes"/>
+			<point x="170" y="1525"/>
+			<point x="98" y="1457"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/j.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/j.glif
new file mode 100644
index 0000000..1ac2ef3
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/j.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="j" format="2">
+	<unicode hex="006A"/>
+	<advance width="547"/>
+	<outline>
+		<contour>
+			<point x="122" y="1082" type="line"/>
+			<point x="122" y="-35" type="line"/>
+			<point x="122" y="-129"/>
+			<point x="73" y="-174"/>
+			<point x="-16" y="-174" type="curve" smooth="yes"/>
+			<point x="-49" y="-174"/>
+			<point x="-77" y="-170"/>
+			<point x="-110" y="-165" type="curve"/>
+			<point x="-110" y="-420" type="line"/>
+			<point x="-55" y="-433"/>
+			<point x="-10" y="-437"/>
+			<point x="45" y="-437" type="curve" smooth="yes"/>
+			<point x="294" y="-437"/>
+			<point x="445" y="-295"/>
+			<point x="445" y="-35" type="curve"/>
+			<point x="445" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="98" y="1361" type="curve" smooth="yes"/>
+			<point x="98" y="1265"/>
+			<point x="170" y="1197"/>
+			<point x="277" y="1197" type="curve" smooth="yes"/>
+			<point x="384" y="1197"/>
+			<point x="456" y="1265"/>
+			<point x="456" y="1361" type="curve" smooth="yes"/>
+			<point x="456" y="1457"/>
+			<point x="384" y="1525"/>
+			<point x="277" y="1525" type="curve" smooth="yes"/>
+			<point x="170" y="1525"/>
+			<point x="98" y="1457"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/k.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/k.glif
new file mode 100644
index 0000000..9f329d4
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/k.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="k" format="2">
+	<unicode hex="006B"/>
+	<advance width="1112"/>
+	<outline>
+		<contour>
+			<point x="424" y="1537" type="line"/>
+			<point x="102" y="1537" type="line"/>
+			<point x="102" y="0" type="line"/>
+			<point x="424" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1112" y="1082" type="line"/>
+			<point x="726" y="1082" type="line"/>
+			<point x="465" y="766" type="line"/>
+			<point x="261" y="499" type="line"/>
+			<point x="394" y="285" type="line"/>
+			<point x="643" y="531" type="line"/>
+		</contour>
+		<contour>
+			<point x="771" y="0" type="line"/>
+			<point x="1140" y="0" type="line"/>
+			<point x="706" y="670" type="line"/>
+			<point x="473" y="493" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/l.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/l.glif
new file mode 100644
index 0000000..3156475
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/l.glif
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="l" format="2">
+	<unicode hex="006C"/>
+	<advance width="557"/>
+	<outline>
+		<contour>
+			<point x="440" y="1536" type="line"/>
+			<point x="117" y="1536" type="line"/>
+			<point x="117" y="0" type="line"/>
+			<point x="440" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/m.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/m.glif
new file mode 100644
index 0000000..93ed63a
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/m.glif
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="m" format="2">
+	<unicode hex="006D"/>
+	<advance width="1767"/>
+	<outline>
+		<contour>
+			<point x="424" y="853" type="line"/>
+			<point x="404" y="1082" type="line"/>
+			<point x="102" y="1082" type="line"/>
+			<point x="102" y="0" type="line"/>
+			<point x="424" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="383" y="578" type="line"/>
+			<point x="383" y="729"/>
+			<point x="452" y="842"/>
+			<point x="581" y="842" type="curve" smooth="yes"/>
+			<point x="670" y="842"/>
+			<point x="722" y="816"/>
+			<point x="722" y="679" type="curve"/>
+			<point x="722" y="0" type="line"/>
+			<point x="1044" y="0" type="line"/>
+			<point x="1044" y="722" type="line"/>
+			<point x="1044" y="992"/>
+			<point x="914" y="1102"/>
+			<point x="724" y="1102" type="curve" smooth="yes"/>
+			<point x="450" y="1102"/>
+			<point x="304" y="880"/>
+			<point x="304" y="576" type="curve"/>
+		</contour>
+		<contour>
+			<point x="1010" y="578" type="line"/>
+			<point x="1010" y="729"/>
+			<point x="1072" y="842"/>
+			<point x="1202" y="842" type="curve" smooth="yes"/>
+			<point x="1289" y="842"/>
+			<point x="1342" y="815"/>
+			<point x="1342" y="681" type="curve" smooth="yes"/>
+			<point x="1342" y="0" type="line"/>
+			<point x="1665" y="0" type="line"/>
+			<point x="1665" y="681" type="line" smooth="yes"/>
+			<point x="1665" y="995"/>
+			<point x="1526" y="1102"/>
+			<point x="1324" y="1102" type="curve" smooth="yes"/>
+			<point x="1050" y="1102"/>
+			<point x="911" y="880"/>
+			<point x="911" y="576" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/n.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/n.glif
new file mode 100644
index 0000000..092570b
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/n.glif
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="n" format="2">
+	<unicode hex="006E"/>
+	<advance width="1153"/>
+	<outline>
+		<contour>
+			<point x="416" y="851" type="line"/>
+			<point x="396" y="1082" type="line"/>
+			<point x="94" y="1082" type="line"/>
+			<point x="94" y="0" type="line"/>
+			<point x="416" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="375" y="578" type="line"/>
+			<point x="375" y="729"/>
+			<point x="431" y="842"/>
+			<point x="574" y="842" type="curve" smooth="yes"/>
+			<point x="675" y="842"/>
+			<point x="733" y="812"/>
+			<point x="733" y="682" type="curve"/>
+			<point x="733" y="0" type="line"/>
+			<point x="1056" y="0" type="line"/>
+			<point x="1056" y="681" type="line"/>
+			<point x="1056" y="995"/>
+			<point x="918" y="1102"/>
+			<point x="716" y="1102" type="curve" smooth="yes"/>
+			<point x="464" y="1102"/>
+			<point x="296" y="907"/>
+			<point x="296" y="576" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/o.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/o.glif
new file mode 100644
index 0000000..b5836f7
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/o.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="o" format="2">
+	<unicode hex="006F"/>
+	<advance width="1153"/>
+	<outline>
+		<contour>
+			<point x="57" y="530" type="line"/>
+			<point x="57" y="214"/>
+			<point x="241" y="-20"/>
+			<point x="577" y="-20" type="curve" smooth="yes"/>
+			<point x="912" y="-20"/>
+			<point x="1095" y="214"/>
+			<point x="1095" y="530" type="curve"/>
+			<point x="1095" y="551" type="line"/>
+			<point x="1095" y="867"/>
+			<point x="912" y="1102"/>
+			<point x="575" y="1102" type="curve" smooth="yes"/>
+			<point x="241" y="1102"/>
+			<point x="57" y="867"/>
+			<point x="57" y="551" type="curve"/>
+		</contour>
+		<contour>
+			<point x="379" y="551" type="line"/>
+			<point x="379" y="709"/>
+			<point x="427" y="842"/>
+			<point x="575" y="842" type="curve" smooth="yes"/>
+			<point x="726" y="842"/>
+			<point x="773" y="709"/>
+			<point x="773" y="551" type="curve"/>
+			<point x="773" y="530" type="line"/>
+			<point x="773" y="367"/>
+			<point x="725" y="240"/>
+			<point x="577" y="240" type="curve" smooth="yes"/>
+			<point x="426" y="240"/>
+			<point x="379" y="367"/>
+			<point x="379" y="530" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/p.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/p.glif
new file mode 100644
index 0000000..4989434
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/p.glif
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="p" format="2">
+	<unicode hex="0070"/>
+	<advance width="1153"/>
+	<outline>
+		<contour>
+			<point x="424" y="874" type="line"/>
+			<point x="402" y="1082" type="line"/>
+			<point x="102" y="1082" type="line"/>
+			<point x="102" y="-416" type="line"/>
+			<point x="424" y="-416" type="line"/>
+		</contour>
+		<contour>
+			<point x="1095" y="554" type="line"/>
+			<point x="1095" y="883"/>
+			<point x="948" y="1102"/>
+			<point x="673" y="1102" type="curve" smooth="yes"/>
+			<point x="412" y="1102"/>
+			<point x="306" y="863"/>
+			<point x="264" y="550" type="curve"/>
+			<point x="264" y="523" type="line"/>
+			<point x="306" y="231"/>
+			<point x="412" y="-20"/>
+			<point x="675" y="-20" type="curve" smooth="yes"/>
+			<point x="948" y="-20"/>
+			<point x="1095" y="218"/>
+			<point x="1095" y="533" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="773" y="533" type="line"/>
+			<point x="773" y="371"/>
+			<point x="729" y="240"/>
+			<point x="594" y="240" type="curve" smooth="yes"/>
+			<point x="445" y="240"/>
+			<point x="394" y="343"/>
+			<point x="394" y="495" type="curve" smooth="yes"/>
+			<point x="394" y="577" type="line" smooth="yes"/>
+			<point x="394" y="753"/>
+			<point x="445" y="842"/>
+			<point x="592" y="842" type="curve" smooth="yes"/>
+			<point x="725" y="842"/>
+			<point x="773" y="722"/>
+			<point x="773" y="554" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/q.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/q.glif
new file mode 100644
index 0000000..78a3914
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/q.glif
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="q" format="2">
+	<unicode hex="0071"/>
+	<advance width="1153"/>
+	<outline>
+		<contour>
+			<point x="728" y="-416" type="line"/>
+			<point x="1051" y="-416" type="line"/>
+			<point x="1051" y="1082" type="line"/>
+			<point x="771" y="1082" type="line"/>
+			<point x="728" y="855" type="line"/>
+		</contour>
+		<contour>
+			<point x="57" y="531" type="line"/>
+			<point x="57" y="216"/>
+			<point x="205" y="-20"/>
+			<point x="477" y="-20" type="curve" smooth="yes"/>
+			<point x="740" y="-20"/>
+			<point x="846" y="230"/>
+			<point x="889" y="523" type="curve"/>
+			<point x="889" y="548" type="line"/>
+			<point x="846" y="861"/>
+			<point x="740" y="1102"/>
+			<point x="479" y="1102" type="curve" smooth="yes"/>
+			<point x="205" y="1102"/>
+			<point x="57" y="881"/>
+			<point x="57" y="552" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="379" y="552" type="line"/>
+			<point x="379" y="720"/>
+			<point x="427" y="842"/>
+			<point x="561" y="842" type="curve" smooth="yes"/>
+			<point x="708" y="842"/>
+			<point x="759" y="751"/>
+			<point x="759" y="575" type="curve" smooth="yes"/>
+			<point x="759" y="496" type="line" smooth="yes"/>
+			<point x="759" y="342"/>
+			<point x="708" y="240"/>
+			<point x="559" y="240" type="curve" smooth="yes"/>
+			<point x="423" y="240"/>
+			<point x="379" y="369"/>
+			<point x="379" y="531" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/r.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/r.glif
new file mode 100644
index 0000000..e4e388a
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/r.glif
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="r" format="2">
+	<unicode hex="0072"/>
+	<advance width="767"/>
+	<outline>
+		<contour>
+			<point x="424" y="814" type="line"/>
+			<point x="404" y="1082" type="line"/>
+			<point x="102" y="1082" type="line"/>
+			<point x="102" y="0" type="line"/>
+			<point x="424" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="745" y="1090" type="line"/>
+			<point x="721" y="1098"/>
+			<point x="685" y="1102"/>
+			<point x="653" y="1102" type="curve"/>
+			<point x="459" y="1102"/>
+			<point x="343" y="902"/>
+			<point x="343" y="612" type="curve"/>
+			<point x="403" y="572" type="line"/>
+			<point x="403" y="714"/>
+			<point x="472" y="785"/>
+			<point x="631" y="785" type="curve"/>
+			<point x="661" y="785"/>
+			<point x="712" y="780"/>
+			<point x="740" y="777" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/s.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/s.glif
new file mode 100644
index 0000000..c46d75c
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/s.glif
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="s" format="2">
+	<unicode hex="0073"/>
+	<advance width="1051"/>
+	<outline>
+		<contour>
+			<point x="673" y="304" type="curve" smooth="yes"/>
+			<point x="673" y="244"/>
+			<point x="622" y="203"/>
+			<point x="525" y="203" type="curve" smooth="yes"/>
+			<point x="424" y="203"/>
+			<point x="348" y="247"/>
+			<point x="344" y="347" type="curve"/>
+			<point x="42" y="347" type="line"/>
+			<point x="42" y="173"/>
+			<point x="208" y="-20"/>
+			<point x="517" y="-20" type="curve" smooth="yes"/>
+			<point x="803" y="-20"/>
+			<point x="984" y="123"/>
+			<point x="984" y="314" type="curve"/>
+			<point x="984" y="543"/>
+			<point x="792" y="619"/>
+			<point x="569" y="659" type="curve" smooth="yes"/>
+			<point x="435" y="683"/>
+			<point x="382" y="719"/>
+			<point x="382" y="777" type="curve"/>
+			<point x="382" y="839"/>
+			<point x="438" y="880"/>
+			<point x="516" y="880" type="curve" smooth="yes"/>
+			<point x="620" y="880"/>
+			<point x="662" y="832"/>
+			<point x="662" y="750" type="curve"/>
+			<point x="984" y="750" type="line"/>
+			<point x="984" y="958"/>
+			<point x="805" y="1102"/>
+			<point x="517" y="1102" type="curve" smooth="yes"/>
+			<point x="238" y="1102"/>
+			<point x="76" y="943"/>
+			<point x="76" y="760" type="curve"/>
+			<point x="76" y="570"/>
+			<point x="243" y="472"/>
+			<point x="458" y="426" type="curve" smooth="yes"/>
+			<point x="629" y="389"/>
+			<point x="673" y="359"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/space.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/space.glif
new file mode 100644
index 0000000..52850f2
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/space.glif
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="space" format="1">
+  <advance width="510"/>
+  <unicode hex="0020"/>
+  <outline>
+  </outline>
+  <lib>
+  <dict>
+  <key>com.typemytype.robofont.guides</key>
+  <array>
+    <dict>
+      <key>angle</key>
+      <real>0</real>
+      <key>isGlobal</key>
+      <false/>
+      <key>magnetic</key>
+      <integer>5</integer>
+      <key>x</key>
+      <real>0</real>
+      <key>y</key>
+      <real>901</real>
+    </dict>
+    <dict>
+      <key>angle</key>
+      <real>0</real>
+      <key>isGlobal</key>
+      <false/>
+      <key>magnetic</key>
+      <integer>5</integer>
+      <key>x</key>
+      <real>0</real>
+      <key>y</key>
+      <real>555</real>
+    </dict>
+  </array>
+  </dict>
+  </lib>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/t.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/t.glif
new file mode 100644
index 0000000..2639d1d
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/t.glif
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="t" format="2">
+	<unicode hex="0074"/>
+	<advance width="700"/>
+	<outline>
+		<contour>
+			<point x="658" y="1082" type="line"/>
+			<point x="12" y="1082" type="line"/>
+			<point x="12" y="848" type="line"/>
+			<point x="658" y="848" type="line"/>
+		</contour>
+		<contour>
+			<point x="156" y="1351" type="line"/>
+			<point x="156" y="311" type="line"/>
+			<point x="156" y="76"/>
+			<point x="279" y="-20"/>
+			<point x="487" y="-20" type="curve" smooth="yes"/>
+			<point x="558" y="-20"/>
+			<point x="617" y="-10"/>
+			<point x="672" y="9" type="curve"/>
+			<point x="672" y="250" type="line"/>
+			<point x="650" y="246"/>
+			<point x="625" y="244"/>
+			<point x="588" y="244" type="curve" smooth="yes"/>
+			<point x="508" y="244"/>
+			<point x="478" y="267"/>
+			<point x="478" y="353" type="curve"/>
+			<point x="478" y="1351" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/u.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/u.glif
new file mode 100644
index 0000000..4ee3072
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/u.glif
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="u" format="2">
+	<unicode hex="0075"/>
+	<advance width="1153"/>
+	<outline>
+		<contour>
+			<point x="734" y="263" type="line"/>
+			<point x="755" y="0" type="line"/>
+			<point x="1057" y="0" type="line"/>
+			<point x="1057" y="1082" type="line"/>
+			<point x="734" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="767" y="483" type="line"/>
+			<point x="767" y="344"/>
+			<point x="718" y="240"/>
+			<point x="557" y="240" type="curve" smooth="yes"/>
+			<point x="469" y="240"/>
+			<point x="416" y="285"/>
+			<point x="416" y="380" type="curve"/>
+			<point x="416" y="1082" type="line"/>
+			<point x="94" y="1082" type="line"/>
+			<point x="94" y="382" type="line"/>
+			<point x="94" y="96"/>
+			<point x="241" y="-20"/>
+			<point x="455" y="-20" type="curve" smooth="yes"/>
+			<point x="716" y="-20"/>
+			<point x="854" y="194"/>
+			<point x="854" y="485" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/v.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/v.glif
new file mode 100644
index 0000000..2c06870
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/v.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="v" format="2">
+	<unicode hex="0076"/>
+	<advance width="1051"/>
+	<outline>
+		<contour>
+			<point x="483" y="231" type="line"/>
+			<point x="483" y="0" type="line"/>
+			<point x="685" y="0" type="line"/>
+			<point x="1042" y="1082" type="line"/>
+			<point x="704" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="345" y="1082" type="line"/>
+			<point x="6" y="1082" type="line"/>
+			<point x="363" y="0" type="line"/>
+			<point x="565" y="0" type="line"/>
+			<point x="565" y="231" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/w.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/w.glif
new file mode 100644
index 0000000..08b32ac
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/w.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="w" format="2">
+	<unicode hex="0077"/>
+	<advance width="1493"/>
+	<outline>
+		<contour>
+			<point x="422" y="322" type="line"/>
+			<point x="392" y="0" type="line"/>
+			<point x="557" y="0" type="line"/>
+			<point x="764" y="701" type="line"/>
+			<point x="833" y="1082" type="line"/>
+			<point x="631" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="333" y="1082" type="line"/>
+			<point x="24" y="1082" type="line"/>
+			<point x="286" y="0" type="line"/>
+			<point x="483" y="0" type="line"/>
+			<point x="470" y="328" type="line"/>
+		</contour>
+		<contour>
+			<point x="1019" y="344" type="line"/>
+			<point x="1007" y="0" type="line"/>
+			<point x="1204" y="0" type="line"/>
+			<point x="1465" y="1082" type="line"/>
+			<point x="1156" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="858" y="1082" type="line"/>
+			<point x="661" y="1082" type="line"/>
+			<point x="727" y="699" type="line"/>
+			<point x="932" y="0" type="line"/>
+			<point x="1098" y="0" type="line"/>
+			<point x="1068" y="324" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/x.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/x.glif
new file mode 100644
index 0000000..0a5d8b6
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/x.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="x" format="2">
+	<unicode hex="0078"/>
+	<advance width="1051"/>
+	<outline>
+		<contour>
+			<point x="370" y="1082" type="line"/>
+			<point x="30" y="1082" type="line"/>
+			<point x="321" y="555" type="line"/>
+			<point x="15" y="0" type="line"/>
+			<point x="355" y="0" type="line"/>
+			<point x="530" y="320" type="line"/>
+			<point x="707" y="0" type="line"/>
+			<point x="1046" y="0" type="line"/>
+			<point x="740" y="555" type="line"/>
+			<point x="1032" y="1082" type="line"/>
+			<point x="695" y="1082" type="line"/>
+			<point x="530" y="784" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/y.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/y.glif
new file mode 100644
index 0000000..01015df
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/y.glif
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="y" format="2">
+	<unicode hex="0079"/>
+	<advance width="1051"/>
+	<outline>
+		<contour>
+			<point x="428" y="127" type="line"/>
+			<point x="347" y="-82" type="line"/>
+			<point x="324" y="-143"/>
+			<point x="285" y="-176"/>
+			<point x="175" y="-176" type="curve" smooth="yes"/>
+			<point x="160" y="-176"/>
+			<point x="147" y="-176"/>
+			<point x="131" y="-176" type="curve"/>
+			<point x="131" y="-417" type="line"/>
+			<point x="187" y="-432"/>
+			<point x="205" y="-437"/>
+			<point x="267" y="-437" type="curve" smooth="yes"/>
+			<point x="508" y="-437"/>
+			<point x="583" y="-270"/>
+			<point x="621" y="-161" type="curve" smooth="yes"/>
+			<point x="1055" y="1082" type="line"/>
+			<point x="710" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="343" y="1082" type="line"/>
+			<point x="-2" y="1082" type="line"/>
+			<point x="384" y="-32" type="line"/>
+			<point x="600" y="-32" type="line"/>
+			<point x="562" y="325" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/z.glif b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/z.glif
new file mode 100644
index 0000000..ab555dc
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/glyphs/z.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="z" format="2">
+	<unicode hex="007A"/>
+	<advance width="1051"/>
+	<outline>
+		<contour>
+			<point x="981" y="260" type="line"/>
+			<point x="149" y="260" type="line"/>
+			<point x="149" y="0" type="line"/>
+			<point x="981" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="969" y="900" type="line"/>
+			<point x="969" y="1082" type="line"/>
+			<point x="749" y="1082" type="line"/>
+			<point x="69" y="188" type="line"/>
+			<point x="69" y="0" type="line"/>
+			<point x="288" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="861" y="1082" type="line"/>
+			<point x="88" y="1082" type="line"/>
+			<point x="88" y="822" type="line"/>
+			<point x="861" y="822" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/layercontents.plist b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/layercontents.plist
new file mode 100644
index 0000000..03e5dde
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-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/cu2qu/data/RobotoSubset-Bold.ufo/lib.plist b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/lib.plist
new file mode 100644
index 0000000..5623ed1
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/lib.plist
@@ -0,0 +1,62 @@
+<?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>space</string>
+			<string>A</string>
+			<string>B</string>
+			<string>C</string>
+			<string>D</string>
+			<string>E</string>
+			<string>F</string>
+			<string>G</string>
+			<string>H</string>
+			<string>I</string>
+			<string>J</string>
+			<string>K</string>
+			<string>L</string>
+			<string>M</string>
+			<string>N</string>
+			<string>O</string>
+			<string>P</string>
+			<string>Q</string>
+			<string>R</string>
+			<string>S</string>
+			<string>T</string>
+			<string>U</string>
+			<string>V</string>
+			<string>W</string>
+			<string>X</string>
+			<string>Y</string>
+			<string>Z</string>
+			<string>a</string>
+			<string>b</string>
+			<string>c</string>
+			<string>d</string>
+			<string>e</string>
+			<string>f</string>
+			<string>g</string>
+			<string>h</string>
+			<string>i</string>
+			<string>j</string>
+			<string>k</string>
+			<string>l</string>
+			<string>m</string>
+			<string>n</string>
+			<string>o</string>
+			<string>p</string>
+			<string>q</string>
+			<string>r</string>
+			<string>s</string>
+			<string>t</string>
+			<string>u</string>
+			<string>v</string>
+			<string>w</string>
+			<string>x</string>
+			<string>y</string>
+			<string>z</string>
+		</array>
+	</dict>
+</plist>
diff --git a/Tests/cu2qu/data/RobotoSubset-Bold.ufo/metainfo.plist b/Tests/cu2qu/data/RobotoSubset-Bold.ufo/metainfo.plist
new file mode 100644
index 0000000..edfd637
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-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/cu2qu/data/RobotoSubset-Regular.ufo/fontinfo.plist b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/fontinfo.plist
new file mode 100644
index 0000000..28e318e
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/fontinfo.plist
@@ -0,0 +1,214 @@
+<?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>2146</integer>
+		<key>capHeight</key>
+		<integer>1456</integer>
+		<key>copyright</key>
+		<string>Copyright 2011 Google Inc. All Rights Reserved.</string>
+		<key>descender</key>
+		<integer>-555</integer>
+		<key>familyName</key>
+		<string>RobotoSubset</string>
+		<key>guidelines</key>
+		<array>
+		</array>
+		<key>italicAngle</key>
+		<integer>0</integer>
+		<key>openTypeHeadCreated</key>
+		<string>2008/09/12 12:29:34</string>
+		<key>openTypeHeadFlags</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+			<integer>3</integer>
+			<integer>4</integer>
+		</array>
+		<key>openTypeHeadLowestRecPPEM</key>
+		<integer>9</integer>
+		<key>openTypeHheaAscender</key>
+		<integer>1900</integer>
+		<key>openTypeHheaDescender</key>
+		<integer>-500</integer>
+		<key>openTypeHheaLineGap</key>
+		<integer>0</integer>
+		<key>openTypeNameDescription</key>
+		<string></string>
+		<key>openTypeNameDesigner</key>
+		<string></string>
+		<key>openTypeNameDesignerURL</key>
+		<string></string>
+		<key>openTypeNameLicense</key>
+		<string></string>
+		<key>openTypeNameLicenseURL</key>
+		<string></string>
+		<key>openTypeNameManufacturer</key>
+		<string></string>
+		<key>openTypeNameManufacturerURL</key>
+		<string></string>
+		<key>openTypeNameSampleText</key>
+		<string></string>
+		<key>openTypeOS2CodePageRanges</key>
+		<array>
+			<integer>0</integer>
+			<integer>1</integer>
+			<integer>2</integer>
+			<integer>3</integer>
+			<integer>4</integer>
+			<integer>7</integer>
+			<integer>8</integer>
+			<integer>29</integer>
+		</array>
+		<key>openTypeOS2FamilyClass</key>
+		<array>
+			<integer>0</integer>
+			<integer>0</integer>
+		</array>
+		<key>openTypeOS2Panose</key>
+		<array>
+			<integer>2</integer>
+			<integer>0</integer>
+			<integer>0</integer>
+			<integer>0</integer>
+			<integer>0</integer>
+			<integer>0</integer>
+			<integer>0</integer>
+			<integer>0</integer>
+			<integer>0</integer>
+			<integer>0</integer>
+		</array>
+		<key>openTypeOS2Selection</key>
+		<array>
+		</array>
+		<key>openTypeOS2StrikeoutPosition</key>
+		<integer>512</integer>
+		<key>openTypeOS2StrikeoutSize</key>
+		<integer>102</integer>
+		<key>openTypeOS2SubscriptXOffset</key>
+		<integer>0</integer>
+		<key>openTypeOS2SubscriptXSize</key>
+		<integer>1434</integer>
+		<key>openTypeOS2SubscriptYOffset</key>
+		<integer>287</integer>
+		<key>openTypeOS2SubscriptYSize</key>
+		<integer>1331</integer>
+		<key>openTypeOS2SuperscriptXOffset</key>
+		<integer>0</integer>
+		<key>openTypeOS2SuperscriptXSize</key>
+		<integer>1434</integer>
+		<key>openTypeOS2SuperscriptYOffset</key>
+		<integer>977</integer>
+		<key>openTypeOS2SuperscriptYSize</key>
+		<integer>1331</integer>
+		<key>openTypeOS2Type</key>
+		<array>
+		</array>
+		<key>openTypeOS2TypoAscender</key>
+		<integer>2146</integer>
+		<key>openTypeOS2TypoDescender</key>
+		<integer>-555</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>11</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>37</integer>
+			<integer>38</integer>
+			<integer>40</integer>
+			<integer>45</integer>
+			<integer>60</integer>
+			<integer>62</integer>
+			<integer>64</integer>
+			<integer>69</integer>
+		</array>
+		<key>openTypeOS2VendorID</key>
+		<string>GOOG</string>
+		<key>openTypeOS2WeightClass</key>
+		<integer>400</integer>
+		<key>openTypeOS2WidthClass</key>
+		<integer>5</integer>
+		<key>openTypeOS2WinAscent</key>
+		<integer>2146</integer>
+		<key>openTypeOS2WinDescent</key>
+		<integer>555</integer>
+		<key>postscriptBlueFuzz</key>
+		<integer>1</integer>
+		<key>postscriptBlueScale</key>
+		<real>0.039625</real>
+		<key>postscriptBlueShift</key>
+		<integer>7</integer>
+		<key>postscriptBlueValues</key>
+		<array>
+			<integer>-20</integer>
+			<integer>0</integer>
+			<integer>1082</integer>
+			<integer>1102</integer>
+			<integer>1456</integer>
+			<integer>1476</integer>
+		</array>
+		<key>postscriptDefaultCharacter</key>
+		<string>space</string>
+		<key>postscriptFamilyBlues</key>
+		<array>
+		</array>
+		<key>postscriptFamilyOtherBlues</key>
+		<array>
+		</array>
+		<key>postscriptForceBold</key>
+		<false/>
+		<key>postscriptIsFixedPitch</key>
+		<false/>
+		<key>postscriptOtherBlues</key>
+		<array>
+			<integer>-436</integer>
+			<integer>-416</integer>
+		</array>
+		<key>postscriptStemSnapH</key>
+		<array>
+			<integer>154</integer>
+		</array>
+		<key>postscriptStemSnapV</key>
+		<array>
+			<integer>196</integer>
+		</array>
+		<key>postscriptUnderlinePosition</key>
+		<integer>-150</integer>
+		<key>postscriptUnderlineThickness</key>
+		<integer>100</integer>
+		<key>postscriptUniqueID</key>
+		<integer>-1</integer>
+		<key>styleName</key>
+		<string>Regular</string>
+		<key>trademark</key>
+		<string></string>
+		<key>unitsPerEm</key>
+		<integer>2048</integer>
+		<key>versionMajor</key>
+		<integer>1</integer>
+		<key>versionMinor</key>
+		<integer>0</integer>
+		<key>xHeight</key>
+		<integer>1082</integer>
+		<key>year</key>
+		<integer>2017</integer>
+	</dict>
+</plist>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/A_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/A_.glif
new file mode 100644
index 0000000..8f070f2
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/A_.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="2">
+	<unicode hex="0041"/>
+	<advance width="1349"/>
+	<outline>
+		<contour>
+			<point x="718" y="1320" type="line"/>
+			<point x="720" y="1456" type="line"/>
+			<point x="585" y="1456" type="line"/>
+			<point x="28" y="0" type="line"/>
+			<point x="241" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1110" y="0" type="line"/>
+			<point x="1323" y="0" type="line"/>
+			<point x="765" y="1456" type="line"/>
+			<point x="630" y="1456" type="line"/>
+			<point x="632" y="1320" type="line"/>
+		</contour>
+		<contour>
+			<point x="1099" y="543" type="line"/>
+			<point x="271" y="543" type="line"/>
+			<point x="271" y="376" type="line"/>
+			<point x="1099" y="376" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/B_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/B_.glif
new file mode 100644
index 0000000..2a5194a
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/B_.glif
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="B" format="2">
+	<unicode hex="0042"/>
+	<advance width="1280"/>
+	<outline>
+		<contour>
+			<point x="688" y="678" type="line"/>
+			<point x="755" y="726" type="line"/>
+			<point x="976" y="752"/>
+			<point x="1128" y="887"/>
+			<point x="1128" y="1067" type="curve"/>
+			<point x="1128" y="1339"/>
+			<point x="951" y="1456"/>
+			<point x="653" y="1456" type="curve"/>
+			<point x="166" y="1456" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="374" y="0" type="line"/>
+			<point x="374" y="1289" type="line"/>
+			<point x="653" y="1289" type="line"/>
+			<point x="834" y="1289"/>
+			<point x="920" y="1224"/>
+			<point x="920" y="1068" type="curve"/>
+			<point x="920" y="927"/>
+			<point x="812" y="841"/>
+			<point x="657" y="841" type="curve"/>
+			<point x="327" y="841" type="line"/>
+			<point x="329" y="678" type="line"/>
+		</contour>
+		<contour>
+			<point x="679" y="0" type="line"/>
+			<point x="973" y="0"/>
+			<point x="1164" y="148"/>
+			<point x="1164" y="422" type="curve" smooth="yes"/>
+			<point x="1164" y="610"/>
+			<point x="1048" y="764"/>
+			<point x="835" y="782" type="curve"/>
+			<point x="790" y="841" type="line"/>
+			<point x="411" y="841" type="line"/>
+			<point x="409" y="678" type="line"/>
+			<point x="688" y="678" type="line" smooth="yes"/>
+			<point x="877" y="678"/>
+			<point x="956" y="581"/>
+			<point x="956" y="420" type="curve" smooth="yes"/>
+			<point x="956" y="265"/>
+			<point x="855" y="166"/>
+			<point x="679" y="166" type="curve" smooth="yes"/>
+			<point x="364" y="166" type="line"/>
+			<point x="244" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/C_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/C_.glif
new file mode 100644
index 0000000..eaabcee
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/C_.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="C" format="2">
+	<unicode hex="0043"/>
+	<advance width="1334"/>
+	<outline>
+		<contour>
+			<point x="1038" y="464" type="line"/>
+			<point x="1005" y="266"/>
+			<point x="933" y="146"/>
+			<point x="690" y="146" type="curve" smooth="yes"/>
+			<point x="434" y="146"/>
+			<point x="325" y="378"/>
+			<point x="325" y="658" type="curve" smooth="yes"/>
+			<point x="325" y="799" type="line" smooth="yes"/>
+			<point x="325" y="1104"/>
+			<point x="454" y="1309"/>
+			<point x="709" y="1309" type="curve" smooth="yes"/>
+			<point x="928" y="1309"/>
+			<point x="1009" y="1184"/>
+			<point x="1038" y="987" type="curve"/>
+			<point x="1246" y="987" type="line"/>
+			<point x="1216" y="1272"/>
+			<point x="1043" y="1476"/>
+			<point x="709" y="1476" type="curve" smooth="yes"/>
+			<point x="344" y="1476"/>
+			<point x="117" y="1207"/>
+			<point x="117" y="797" type="curve"/>
+			<point x="117" y="658" type="line"/>
+			<point x="117" y="248"/>
+			<point x="345" y="-20"/>
+			<point x="690" y="-20" type="curve" smooth="yes"/>
+			<point x="1046" y="-20"/>
+			<point x="1215" y="192"/>
+			<point x="1246" y="464" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/D_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/D_.glif
new file mode 100644
index 0000000..b89cff9
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/D_.glif
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="D" format="2">
+	<unicode hex="0044"/>
+	<advance width="1344"/>
+	<outline>
+		<contour>
+			<point x="559" y="0" type="line"/>
+			<point x="969" y="0"/>
+			<point x="1225" y="263"/>
+			<point x="1225" y="688" type="curve"/>
+			<point x="1225" y="767" type="line"/>
+			<point x="1225" y="1192"/>
+			<point x="965" y="1456"/>
+			<point x="578" y="1456" type="curve"/>
+			<point x="254" y="1456" type="line"/>
+			<point x="254" y="1289" type="line"/>
+			<point x="578" y="1289" type="line"/>
+			<point x="863" y="1289"/>
+			<point x="1019" y="1102"/>
+			<point x="1019" y="769" type="curve"/>
+			<point x="1019" y="688" type="line" smooth="yes"/>
+			<point x="1019" y="371"/>
+			<point x="869" y="166"/>
+			<point x="559" y="166" type="curve"/>
+			<point x="262" y="166" type="line"/>
+			<point x="260" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="374" y="1456" type="line"/>
+			<point x="166" y="1456" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="374" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/E_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/E_.glif
new file mode 100644
index 0000000..14f1ad9
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/E_.glif
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="E" format="2">
+	<unicode hex="0045"/>
+	<advance width="1164"/>
+	<outline>
+		<contour>
+			<point x="1095" y="166" type="line"/>
+			<point x="335" y="166" type="line"/>
+			<point x="335" y="0" type="line"/>
+			<point x="1095" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="374" y="1456" type="line"/>
+			<point x="166" y="1456" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="374" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="993" y="835" type="line"/>
+			<point x="335" y="835" type="line"/>
+			<point x="335" y="669" type="line"/>
+			<point x="993" y="669" type="line"/>
+		</contour>
+		<contour>
+			<point x="1084" y="1456" type="line"/>
+			<point x="335" y="1456" type="line"/>
+			<point x="335" y="1289" type="line"/>
+			<point x="1084" y="1289" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/F_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/F_.glif
new file mode 100644
index 0000000..77ffdb8
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/F_.glif
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="F" format="2">
+	<unicode hex="0046"/>
+	<advance width="1128"/>
+	<outline>
+		<contour>
+			<point x="374" y="1456" type="line"/>
+			<point x="166" y="1456" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="374" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="969" y="803" type="line"/>
+			<point x="332" y="803" type="line"/>
+			<point x="332" y="637" type="line"/>
+			<point x="969" y="637" type="line"/>
+		</contour>
+		<contour>
+			<point x="1068" y="1456" type="line"/>
+			<point x="332" y="1456" type="line"/>
+			<point x="332" y="1289" type="line"/>
+			<point x="1068" y="1289" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/G_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/G_.glif
new file mode 100644
index 0000000..dd03d17
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/G_.glif
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="G" format="2">
+	<unicode hex="0047"/>
+	<advance width="1394"/>
+	<outline>
+		<contour>
+			<point x="1247" y="731" type="line"/>
+			<point x="715" y="731" type="line"/>
+			<point x="715" y="566" type="line"/>
+			<point x="1040" y="566" type="line"/>
+			<point x="1040" y="248" type="line"/>
+			<point x="1002" y="206"/>
+			<point x="934" y="146"/>
+			<point x="731" y="146" type="curve" smooth="yes"/>
+			<point x="489" y="146"/>
+			<point x="326" y="341"/>
+			<point x="326" y="676" type="curve" smooth="yes"/>
+			<point x="326" y="781" type="line" smooth="yes"/>
+			<point x="326" y="1108"/>
+			<point x="440" y="1309"/>
+			<point x="708" y="1309" type="curve" smooth="yes"/>
+			<point x="922" y="1309"/>
+			<point x="1012" y="1181"/>
+			<point x="1039" y="1027" type="curve"/>
+			<point x="1247" y="1027" type="line"/>
+			<point x="1209" y="1287"/>
+			<point x="1042" y="1476"/>
+			<point x="707" y="1476" type="curve" smooth="yes"/>
+			<point x="325" y="1476"/>
+			<point x="117" y="1219"/>
+			<point x="117" y="779" type="curve" smooth="yes"/>
+			<point x="117" y="676" type="line" smooth="yes"/>
+			<point x="117" y="236"/>
+			<point x="373" y="-20"/>
+			<point x="730" y="-20" type="curve" smooth="yes"/>
+			<point x="1061" y="-20"/>
+			<point x="1191" y="114"/>
+			<point x="1247" y="195" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/H_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/H_.glif
new file mode 100644
index 0000000..fa6876c
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/H_.glif
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="H" format="2">
+	<unicode hex="0048"/>
+	<advance width="1463"/>
+	<outline>
+		<contour>
+			<point x="1110" y="835" type="line"/>
+			<point x="344" y="835" type="line"/>
+			<point x="344" y="669" type="line"/>
+			<point x="1110" y="669" type="line"/>
+		</contour>
+		<contour>
+			<point x="374" y="1456" type="line"/>
+			<point x="166" y="1456" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="374" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1294" y="1456" type="line"/>
+			<point x="1086" y="1456" type="line"/>
+			<point x="1086" y="0" type="line"/>
+			<point x="1294" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/I_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/I_.glif
new file mode 100644
index 0000000..cdf1ebf
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/I_.glif
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="I" format="2">
+	<unicode hex="0049"/>
+	<advance width="560"/>
+	<outline>
+		<contour>
+			<point x="385" y="1456" type="line"/>
+			<point x="177" y="1456" type="line"/>
+			<point x="177" y="0" type="line"/>
+			<point x="385" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/J_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/J_.glif
new file mode 100644
index 0000000..34346b7
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/J_.glif
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="J" format="2">
+	<unicode hex="004A"/>
+	<advance width="1131"/>
+	<outline>
+		<contour>
+			<point x="769" y="424" type="line"/>
+			<point x="769" y="242"/>
+			<point x="660" y="146"/>
+			<point x="513" y="146" type="curve" smooth="yes"/>
+			<point x="366" y="146"/>
+			<point x="257" y="224"/>
+			<point x="257" y="403" type="curve"/>
+			<point x="49" y="403" type="line"/>
+			<point x="49" y="116"/>
+			<point x="244" y="-20"/>
+			<point x="513" y="-20" type="curve" smooth="yes"/>
+			<point x="784" y="-20"/>
+			<point x="977" y="136"/>
+			<point x="977" y="424" type="curve"/>
+			<point x="977" y="1456" type="line"/>
+			<point x="769" y="1456" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/K_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/K_.glif
new file mode 100644
index 0000000..2c27124
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/K_.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="K" format="2">
+	<unicode hex="004B"/>
+	<advance width="1283"/>
+	<outline>
+		<contour>
+			<point x="374" y="1456" type="line"/>
+			<point x="166" y="1456" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="374" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1248" y="1456" type="line"/>
+			<point x="999" y="1456" type="line"/>
+			<point x="523" y="918" type="line"/>
+			<point x="267" y="632" type="line"/>
+			<point x="303" y="415" type="line"/>
+			<point x="648" y="775" type="line"/>
+		</contour>
+		<contour>
+			<point x="1044" y="0" type="line"/>
+			<point x="1292" y="0" type="line"/>
+			<point x="645" y="866" type="line"/>
+			<point x="521" y="703" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/L_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/L_.glif
new file mode 100644
index 0000000..85d578d
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/L_.glif
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="L" format="2">
+	<unicode hex="004C"/>
+	<advance width="1108"/>
+	<outline>
+		<contour>
+			<point x="1058" y="166" type="line"/>
+			<point x="335" y="166" type="line"/>
+			<point x="335" y="0" type="line"/>
+			<point x="1058" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="374" y="1456" type="line"/>
+			<point x="166" y="1456" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="374" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/M_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/M_.glif
new file mode 100644
index 0000000..f67a855
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/M_.glif
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="M" format="2">
+	<unicode hex="004D"/>
+	<advance width="1792"/>
+	<outline>
+		<contour>
+			<point x="231" y="1456" type="line"/>
+			<point x="816" y="0" type="line"/>
+			<point x="974" y="0" type="line"/>
+			<point x="1560" y="1456" type="line"/>
+			<point x="1358" y="1456" type="line"/>
+			<point x="896" y="285" type="line"/>
+			<point x="433" y="1456" type="line"/>
+		</contour>
+		<contour>
+			<point x="166" y="1456" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="373" y="0" type="line"/>
+			<point x="373" y="556" type="line"/>
+			<point x="343" y="1456" type="line"/>
+		</contour>
+		<contour>
+			<point x="1448" y="1456" type="line"/>
+			<point x="1418" y="556" type="line"/>
+			<point x="1418" y="0" type="line"/>
+			<point x="1625" y="0" type="line"/>
+			<point x="1625" y="1456" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/N_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/N_.glif
new file mode 100644
index 0000000..70b3869
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/N_.glif
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="N" format="2">
+	<unicode hex="004E"/>
+	<advance width="1462"/>
+	<outline>
+		<contour>
+			<point x="1293" y="1456" type="line"/>
+			<point x="1087" y="1456" type="line"/>
+			<point x="1087" y="350" type="line"/>
+			<point x="374" y="1456" type="line"/>
+			<point x="166" y="1456" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="374" y="0" type="line"/>
+			<point x="374" y="1102" type="line"/>
+			<point x="1084" y="0" type="line"/>
+			<point x="1293" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/O_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/O_.glif
new file mode 100644
index 0000000..6d87c26
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/O_.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="O" format="2">
+	<unicode hex="004F"/>
+	<advance width="1414"/>
+	<outline>
+		<contour>
+			<point x="1296" y="768" type="line"/>
+			<point x="1296" y="1209"/>
+			<point x="1063" y="1476"/>
+			<point x="706" y="1476" type="curve" smooth="yes"/>
+			<point x="360" y="1476"/>
+			<point x="117" y="1209"/>
+			<point x="117" y="768" type="curve"/>
+			<point x="117" y="687" type="line"/>
+			<point x="117" y="246"/>
+			<point x="362" y="-20"/>
+			<point x="708" y="-20" type="curve" smooth="yes"/>
+			<point x="1065" y="-20"/>
+			<point x="1296" y="246"/>
+			<point x="1296" y="687" type="curve"/>
+		</contour>
+		<contour>
+			<point x="1090" y="687" type="line"/>
+			<point x="1090" y="338"/>
+			<point x="952" y="153"/>
+			<point x="708" y="153" type="curve" smooth="yes"/>
+			<point x="478" y="153"/>
+			<point x="323" y="338"/>
+			<point x="323" y="687" type="curve" smooth="yes"/>
+			<point x="323" y="770" type="line" smooth="yes"/>
+			<point x="323" y="1117"/>
+			<point x="476" y="1302"/>
+			<point x="706" y="1302" type="curve" smooth="yes"/>
+			<point x="948" y="1302"/>
+			<point x="1090" y="1117"/>
+			<point x="1090" y="770" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/P_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/P_.glif
new file mode 100644
index 0000000..81622d1
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/P_.glif
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="P" format="2">
+	<unicode hex="0050"/>
+	<advance width="1299"/>
+	<outline>
+		<contour>
+			<point x="712" y="567" type="line"/>
+			<point x="1044" y="567"/>
+			<point x="1227" y="727"/>
+			<point x="1227" y="1010" type="curve"/>
+			<point x="1227" y="1269"/>
+			<point x="1044" y="1456"/>
+			<point x="712" y="1456" type="curve"/>
+			<point x="166" y="1456" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="374" y="0" type="line"/>
+			<point x="374" y="1289" type="line"/>
+			<point x="712" y="1289" type="line"/>
+			<point x="930" y="1289"/>
+			<point x="1019" y="1153"/>
+			<point x="1019" y="1008" type="curve"/>
+			<point x="1019" y="846"/>
+			<point x="930" y="733"/>
+			<point x="712" y="733" type="curve"/>
+			<point x="329" y="733" type="line"/>
+			<point x="329" y="567" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/Q_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/Q_.glif
new file mode 100644
index 0000000..9dfc7f6
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/Q_.glif
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="Q" format="2">
+	<unicode hex="0051"/>
+	<advance width="1414"/>
+	<outline>
+		<contour>
+			<point x="918" y="176" type="line"/>
+			<point x="785" y="45" type="line"/>
+			<point x="1156" y="-245" type="line"/>
+			<point x="1297" y="-117" type="line"/>
+		</contour>
+		<contour>
+			<point x="1287" y="768" type="line"/>
+			<point x="1287" y="1209"/>
+			<point x="1054" y="1476"/>
+			<point x="696" y="1476" type="curve" smooth="yes"/>
+			<point x="350" y="1476"/>
+			<point x="107" y="1209"/>
+			<point x="107" y="768" type="curve"/>
+			<point x="107" y="687" type="line"/>
+			<point x="107" y="246"/>
+			<point x="352" y="-20"/>
+			<point x="698" y="-20" type="curve" smooth="yes"/>
+			<point x="1056" y="-20"/>
+			<point x="1287" y="246"/>
+			<point x="1287" y="687" type="curve"/>
+		</contour>
+		<contour>
+			<point x="1080" y="687" type="line"/>
+			<point x="1080" y="338"/>
+			<point x="942" y="153"/>
+			<point x="698" y="153" type="curve" smooth="yes"/>
+			<point x="469" y="153"/>
+			<point x="314" y="338"/>
+			<point x="314" y="687" type="curve" smooth="yes"/>
+			<point x="314" y="770" type="line" smooth="yes"/>
+			<point x="314" y="1117"/>
+			<point x="467" y="1302"/>
+			<point x="696" y="1302" type="curve" smooth="yes"/>
+			<point x="939" y="1302"/>
+			<point x="1080" y="1117"/>
+			<point x="1080" y="770" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/R_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/R_.glif
new file mode 100644
index 0000000..8d35190
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/R_.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="R" format="2">
+	<unicode hex="0052"/>
+	<advance width="1253"/>
+	<outline>
+		<contour>
+			<point x="166" y="1456" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="374" y="0" type="line"/>
+			<point x="374" y="1289" type="line"/>
+			<point x="650" y="1289" type="line" smooth="yes"/>
+			<point x="868" y="1289"/>
+			<point x="956" y="1180"/>
+			<point x="956" y="1017" type="curve" smooth="yes"/>
+			<point x="956" y="869"/>
+			<point x="854" y="753"/>
+			<point x="651" y="753" type="curve" smooth="yes"/>
+			<point x="327" y="753" type="line"/>
+			<point x="329" y="587" type="line"/>
+			<point x="770" y="587" type="line"/>
+			<point x="827" y="609" type="line"/>
+			<point x="1039" y="666"/>
+			<point x="1164" y="818"/>
+			<point x="1164" y="1017" type="curve" smooth="yes"/>
+			<point x="1164" y="1303"/>
+			<point x="983" y="1456"/>
+			<point x="650" y="1456" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="1006" y="0" type="line"/>
+			<point x="1229" y="0" type="line"/>
+			<point x="1229" y="12" type="line"/>
+			<point x="874" y="662" type="line"/>
+			<point x="657" y="662" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/S_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/S_.glif
new file mode 100644
index 0000000..8bc81ce
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/S_.glif
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="S" format="2">
+	<unicode hex="0053"/>
+	<advance width="1216"/>
+	<outline>
+		<contour>
+			<point x="931" y="370" type="curve" smooth="yes"/>
+			<point x="931" y="232"/>
+			<point x="826" y="146"/>
+			<point x="631" y="146" type="curve" smooth="yes"/>
+			<point x="446" y="146"/>
+			<point x="287" y="232"/>
+			<point x="287" y="423" type="curve"/>
+			<point x="79" y="423" type="line"/>
+			<point x="79" y="136"/>
+			<point x="360" y="-20"/>
+			<point x="631" y="-20" type="curve" smooth="yes"/>
+			<point x="942" y="-20"/>
+			<point x="1140" y="135"/>
+			<point x="1140" y="372" type="curve"/>
+			<point x="1140" y="596"/>
+			<point x="998" y="727"/>
+			<point x="663" y="822" type="curve"/>
+			<point x="435" y="886"/>
+			<point x="334" y="959"/>
+			<point x="334" y="1079" type="curve"/>
+			<point x="334" y="1212"/>
+			<point x="423" y="1309"/>
+			<point x="621" y="1309" type="curve" smooth="yes"/>
+			<point x="834" y="1309"/>
+			<point x="930" y="1194"/>
+			<point x="930" y="1035" type="curve"/>
+			<point x="1138" y="1035" type="line"/>
+			<point x="1138" y="1260"/>
+			<point x="955" y="1476"/>
+			<point x="621" y="1476" type="curve" smooth="yes"/>
+			<point x="320" y="1476"/>
+			<point x="125" y="1303"/>
+			<point x="125" y="1076" type="curve"/>
+			<point x="125" y="851"/>
+			<point x="309" y="728"/>
+			<point x="596" y="644" type="curve"/>
+			<point x="865" y="565"/>
+			<point x="931" y="502"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/T_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/T_.glif
new file mode 100644
index 0000000..dff2656
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/T_.glif
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="T" format="2">
+	<unicode hex="0054"/>
+	<advance width="1222"/>
+	<outline>
+		<contour>
+			<point x="715" y="1456" type="line"/>
+			<point x="509" y="1456" type="line"/>
+			<point x="509" y="0" type="line"/>
+			<point x="715" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1176" y="1456" type="line"/>
+			<point x="49" y="1456" type="line"/>
+			<point x="49" y="1289" type="line"/>
+			<point x="1176" y="1289" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/U_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/U_.glif
new file mode 100644
index 0000000..ee592bc
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/U_.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="U" format="2">
+	<unicode hex="0055"/>
+	<advance width="1324"/>
+	<outline>
+		<contour>
+			<point x="988" y="1456" type="line"/>
+			<point x="988" y="471" type="line" smooth="yes"/>
+			<point x="988" y="248"/>
+			<point x="860" y="146"/>
+			<point x="664" y="146" type="curve" smooth="yes"/>
+			<point x="470" y="146"/>
+			<point x="341" y="248"/>
+			<point x="341" y="471" type="curve" smooth="yes"/>
+			<point x="341" y="1456" type="line"/>
+			<point x="135" y="1456" type="line"/>
+			<point x="135" y="471" type="line"/>
+			<point x="135" y="143"/>
+			<point x="366" y="-20"/>
+			<point x="664" y="-20" type="curve" smooth="yes"/>
+			<point x="946" y="-20"/>
+			<point x="1196" y="143"/>
+			<point x="1196" y="471" type="curve"/>
+			<point x="1196" y="1456" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/V_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/V_.glif
new file mode 100644
index 0000000..232cc55
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/V_.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="V" format="2">
+	<unicode hex="0056"/>
+	<advance width="1313"/>
+	<outline>
+		<contour>
+			<point x="639" y="228" type="line"/>
+			<point x="588" y="0" type="line"/>
+			<point x="748" y="0" type="line"/>
+			<point x="1287" y="1456" type="line"/>
+			<point x="1061" y="1456" type="line"/>
+		</contour>
+		<contour>
+			<point x="254" y="1456" type="line"/>
+			<point x="28" y="1456" type="line"/>
+			<point x="566" y="0" type="line"/>
+			<point x="726" y="0" type="line"/>
+			<point x="672" y="228" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/W_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/W_.glif
new file mode 100644
index 0000000..4c1b017
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/W_.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="W" format="2">
+	<unicode hex="0057"/>
+	<advance width="1813"/>
+	<outline>
+		<contour>
+			<point x="552" y="450" type="line"/>
+			<point x="448" y="0" type="line"/>
+			<point x="597" y="0" type="line"/>
+			<point x="902" y="1048" type="line"/>
+			<point x="984" y="1456" type="line"/>
+			<point x="834" y="1456" type="line"/>
+		</contour>
+		<contour>
+			<point x="268" y="1456" type="line"/>
+			<point x="61" y="1456" type="line"/>
+			<point x="409" y="0" type="line"/>
+			<point x="556" y="0" type="line"/>
+			<point x="490" y="471" type="line"/>
+		</contour>
+		<contour>
+			<point x="1346" y="472" type="line"/>
+			<point x="1276" y="0" type="line"/>
+			<point x="1423" y="0" type="line"/>
+			<point x="1771" y="1456" type="line"/>
+			<point x="1563" y="1456" type="line"/>
+		</contour>
+		<contour>
+			<point x="1007" y="1456" type="line"/>
+			<point x="860" y="1456" type="line"/>
+			<point x="942" y="1048" type="line"/>
+			<point x="1235" y="0" type="line"/>
+			<point x="1384" y="0" type="line"/>
+			<point x="1281" y="450" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/X_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/X_.glif
new file mode 100644
index 0000000..a8547b3
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/X_.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="X" format="2">
+	<unicode hex="0058"/>
+	<advance width="1291"/>
+	<outline>
+		<contour>
+			<point x="311" y="1456" type="line"/>
+			<point x="68" y="1456" type="line"/>
+			<point x="524" y="734" type="line"/>
+			<point x="58" y="0" type="line"/>
+			<point x="303" y="0" type="line"/>
+			<point x="648" y="557" type="line"/>
+			<point x="992" y="0" type="line"/>
+			<point x="1237" y="0" type="line"/>
+			<point x="772" y="734" type="line"/>
+			<point x="1227" y="1456" type="line"/>
+			<point x="984" y="1456" type="line"/>
+			<point x="648" y="908" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/Y_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/Y_.glif
new file mode 100644
index 0000000..6d48150
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/Y_.glif
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="Y" format="2">
+	<unicode hex="0059"/>
+	<advance width="1231"/>
+	<outline>
+		<contour>
+			<point x="250" y="1456" type="line"/>
+			<point x="13" y="1456" type="line"/>
+			<point x="510" y="543" type="line"/>
+			<point x="510" y="0" type="line"/>
+			<point x="718" y="0" type="line"/>
+			<point x="718" y="543" type="line"/>
+			<point x="1215" y="1456" type="line"/>
+			<point x="979" y="1456" type="line"/>
+			<point x="614" y="736" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/Z_.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/Z_.glif
new file mode 100644
index 0000000..6b5dfc4
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/Z_.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="Z" format="2">
+	<unicode hex="005A"/>
+	<advance width="1227"/>
+	<outline>
+		<contour>
+			<point x="1148" y="166" type="line"/>
+			<point x="165" y="166" type="line"/>
+			<point x="165" y="0" type="line"/>
+			<point x="1148" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1116" y="1307" type="line"/>
+			<point x="1116" y="1456" type="line"/>
+			<point x="987" y="1456" type="line"/>
+			<point x="86" y="153" type="line"/>
+			<point x="86" y="0" type="line"/>
+			<point x="214" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="1028" y="1456" type="line"/>
+			<point x="96" y="1456" type="line"/>
+			<point x="96" y="1289" type="line"/>
+			<point x="1028" y="1289" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/a.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/a.glif
new file mode 100644
index 0000000..7d8a2cd
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/a.glif
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="2">
+	<unicode hex="0061"/>
+	<advance width="1118"/>
+	<outline>
+		<contour>
+			<point x="771" y="183" type="line"/>
+			<point x="771" y="123"/>
+			<point x="782" y="41"/>
+			<point x="802" y="0" type="curve"/>
+			<point x="1010" y="0" type="line"/>
+			<point x="1010" y="17" type="line"/>
+			<point x="984" y="76"/>
+			<point x="971" y="166"/>
+			<point x="971" y="238" type="curve" smooth="yes"/>
+			<point x="971" y="738" type="line"/>
+			<point x="971" y="981"/>
+			<point x="803" y="1102"/>
+			<point x="566" y="1102" type="curve" smooth="yes"/>
+			<point x="301" y="1102"/>
+			<point x="133" y="935"/>
+			<point x="133" y="782" type="curve"/>
+			<point x="333" y="782" type="line"/>
+			<point x="333" y="867"/>
+			<point x="421" y="945"/>
+			<point x="554" y="945" type="curve" smooth="yes"/>
+			<point x="697" y="945"/>
+			<point x="771" y="864"/>
+			<point x="771" y="740" type="curve"/>
+		</contour>
+		<contour>
+			<point x="804" y="661" type="line"/>
+			<point x="596" y="661" type="line"/>
+			<point x="301" y="661"/>
+			<point x="111" y="539"/>
+			<point x="111" y="303" type="curve" smooth="yes"/>
+			<point x="111" y="123"/>
+			<point x="257" y="-20"/>
+			<point x="480" y="-20" type="curve" smooth="yes"/>
+			<point x="711" y="-20"/>
+			<point x="857" y="170"/>
+			<point x="874" y="277" type="curve"/>
+			<point x="789" y="370" type="line"/>
+			<point x="789" y="279"/>
+			<point x="673" y="151"/>
+			<point x="510" y="151" type="curve" smooth="yes"/>
+			<point x="377" y="151"/>
+			<point x="311" y="230"/>
+			<point x="311" y="331" type="curve" smooth="yes"/>
+			<point x="311" y="462"/>
+			<point x="425" y="524"/>
+			<point x="629" y="524" type="curve"/>
+			<point x="806" y="524" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/b.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/b.glif
new file mode 100644
index 0000000..f009d99
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/b.glif
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="b" format="2">
+	<unicode hex="0062"/>
+	<advance width="1153"/>
+	<outline>
+		<contour>
+			<point x="137" y="1536" type="line"/>
+			<point x="137" y="0" type="line"/>
+			<point x="320" y="0" type="line"/>
+			<point x="337" y="210" type="line"/>
+			<point x="337" y="1536" type="line"/>
+		</contour>
+		<contour>
+			<point x="1063" y="550" type="line"/>
+			<point x="1063" y="879"/>
+			<point x="912" y="1102"/>
+			<point x="639" y="1102" type="curve" smooth="yes"/>
+			<point x="362" y="1102"/>
+			<point x="230" y="901"/>
+			<point x="200" y="572" type="curve"/>
+			<point x="200" y="510" type="line"/>
+			<point x="230" y="181"/>
+			<point x="362" y="-20"/>
+			<point x="641" y="-20" type="curve" smooth="yes"/>
+			<point x="910" y="-20"/>
+			<point x="1063" y="214"/>
+			<point x="1063" y="529" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="863" y="529" type="line"/>
+			<point x="863" y="320"/>
+			<point x="785" y="146"/>
+			<point x="590" y="146" type="curve" smooth="yes"/>
+			<point x="411" y="146"/>
+			<point x="327" y="287"/>
+			<point x="296" y="426" type="curve"/>
+			<point x="296" y="655" type="line"/>
+			<point x="326" y="803"/>
+			<point x="411" y="937"/>
+			<point x="588" y="937" type="curve" smooth="yes"/>
+			<point x="794" y="937"/>
+			<point x="863" y="759"/>
+			<point x="863" y="550" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/c.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/c.glif
new file mode 100644
index 0000000..727fef4
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/c.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="c" format="2">
+	<unicode hex="0063"/>
+	<advance width="1076"/>
+	<outline>
+		<contour>
+			<point x="578" y="140" type="curve" smooth="yes"/>
+			<point x="352" y="140"/>
+			<point x="292" y="337"/>
+			<point x="292" y="520" type="curve" smooth="yes"/>
+			<point x="292" y="562" type="line" smooth="yes"/>
+			<point x="292" y="743"/>
+			<point x="354" y="942"/>
+			<point x="578" y="942" type="curve"/>
+			<point x="723" y="942"/>
+			<point x="813" y="835"/>
+			<point x="823" y="709" type="curve"/>
+			<point x="1012" y="709" type="line"/>
+			<point x="1002" y="929"/>
+			<point x="836" y="1102"/>
+			<point x="578" y="1102" type="curve" smooth="yes"/>
+			<point x="248" y="1102"/>
+			<point x="92" y="850"/>
+			<point x="92" y="562" type="curve" smooth="yes"/>
+			<point x="92" y="520" type="line" smooth="yes"/>
+			<point x="92" y="232"/>
+			<point x="247" y="-20"/>
+			<point x="578" y="-20" type="curve"/>
+			<point x="809" y="-20"/>
+			<point x="1002" y="153"/>
+			<point x="1012" y="343" type="curve"/>
+			<point x="823" y="343" type="line"/>
+			<point x="813" y="229"/>
+			<point x="706" y="140"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/contents.plist b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..431aca7
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/contents.plist
@@ -0,0 +1,112 @@
+<?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>A</key>
+		<string>A_.glif</string>
+		<key>B</key>
+		<string>B_.glif</string>
+		<key>C</key>
+		<string>C_.glif</string>
+		<key>D</key>
+		<string>D_.glif</string>
+		<key>E</key>
+		<string>E_.glif</string>
+		<key>F</key>
+		<string>F_.glif</string>
+		<key>G</key>
+		<string>G_.glif</string>
+		<key>H</key>
+		<string>H_.glif</string>
+		<key>I</key>
+		<string>I_.glif</string>
+		<key>J</key>
+		<string>J_.glif</string>
+		<key>K</key>
+		<string>K_.glif</string>
+		<key>L</key>
+		<string>L_.glif</string>
+		<key>M</key>
+		<string>M_.glif</string>
+		<key>N</key>
+		<string>N_.glif</string>
+		<key>O</key>
+		<string>O_.glif</string>
+		<key>P</key>
+		<string>P_.glif</string>
+		<key>Q</key>
+		<string>Q_.glif</string>
+		<key>R</key>
+		<string>R_.glif</string>
+		<key>S</key>
+		<string>S_.glif</string>
+		<key>T</key>
+		<string>T_.glif</string>
+		<key>U</key>
+		<string>U_.glif</string>
+		<key>V</key>
+		<string>V_.glif</string>
+		<key>W</key>
+		<string>W_.glif</string>
+		<key>X</key>
+		<string>X_.glif</string>
+		<key>Y</key>
+		<string>Y_.glif</string>
+		<key>Z</key>
+		<string>Z_.glif</string>
+		<key>a</key>
+		<string>a.glif</string>
+		<key>b</key>
+		<string>b.glif</string>
+		<key>c</key>
+		<string>c.glif</string>
+		<key>d</key>
+		<string>d.glif</string>
+		<key>e</key>
+		<string>e.glif</string>
+		<key>f</key>
+		<string>f.glif</string>
+		<key>g</key>
+		<string>g.glif</string>
+		<key>h</key>
+		<string>h.glif</string>
+		<key>i</key>
+		<string>i.glif</string>
+		<key>j</key>
+		<string>j.glif</string>
+		<key>k</key>
+		<string>k.glif</string>
+		<key>l</key>
+		<string>l.glif</string>
+		<key>m</key>
+		<string>m.glif</string>
+		<key>n</key>
+		<string>n.glif</string>
+		<key>o</key>
+		<string>o.glif</string>
+		<key>p</key>
+		<string>p.glif</string>
+		<key>q</key>
+		<string>q.glif</string>
+		<key>r</key>
+		<string>r.glif</string>
+		<key>s</key>
+		<string>s.glif</string>
+		<key>space</key>
+		<string>space.glif</string>
+		<key>t</key>
+		<string>t.glif</string>
+		<key>u</key>
+		<string>u.glif</string>
+		<key>v</key>
+		<string>v.glif</string>
+		<key>w</key>
+		<string>w.glif</string>
+		<key>x</key>
+		<string>x.glif</string>
+		<key>y</key>
+		<string>y.glif</string>
+		<key>z</key>
+		<string>z.glif</string>
+	</dict>
+</plist>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/d.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/d.glif
new file mode 100644
index 0000000..c585788
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/d.glif
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="d" format="2">
+	<unicode hex="0064"/>
+	<advance width="1159"/>
+	<outline>
+		<contour>
+			<point x="815" y="210" type="line"/>
+			<point x="832" y="0" type="line"/>
+			<point x="1015" y="0" type="line"/>
+			<point x="1015" y="1536" type="line"/>
+			<point x="815" y="1536" type="line"/>
+		</contour>
+		<contour>
+			<point x="92" y="529" type="line"/>
+			<point x="92" y="214"/>
+			<point x="266" y="-20"/>
+			<point x="520" y="-20" type="curve" smooth="yes"/>
+			<point x="798" y="-20"/>
+			<point x="931" y="181"/>
+			<point x="960" y="510" type="curve"/>
+			<point x="960" y="572" type="line"/>
+			<point x="931" y="901"/>
+			<point x="798" y="1102"/>
+			<point x="522" y="1102" type="curve" smooth="yes"/>
+			<point x="264" y="1102"/>
+			<point x="92" y="879"/>
+			<point x="92" y="550" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="292" y="550" type="line"/>
+			<point x="292" y="759"/>
+			<point x="375" y="937"/>
+			<point x="573" y="937" type="curve" smooth="yes"/>
+			<point x="750" y="937"/>
+			<point x="834" y="803"/>
+			<point x="865" y="655" type="curve"/>
+			<point x="865" y="426" type="line"/>
+			<point x="824" y="277"/>
+			<point x="750" y="146"/>
+			<point x="571" y="146" type="curve" smooth="yes"/>
+			<point x="375" y="146"/>
+			<point x="292" y="320"/>
+			<point x="292" y="529" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/e.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/e.glif
new file mode 100644
index 0000000..29d3ecd
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/e.glif
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="e" format="2">
+	<unicode hex="0065"/>
+	<advance width="1092"/>
+	<outline>
+		<contour>
+			<point x="593" y="-20" type="curve"/>
+			<point x="812" y="-20"/>
+			<point x="936" y="85"/>
+			<point x="1006" y="192" type="curve"/>
+			<point x="885" y="286" type="line"/>
+			<point x="820" y="200"/>
+			<point x="735" y="140"/>
+			<point x="604" y="140" type="curve" smooth="yes"/>
+			<point x="406" y="140"/>
+			<point x="294" y="302"/>
+			<point x="294" y="502" type="curve" smooth="yes"/>
+			<point x="294" y="544" type="line" smooth="yes"/>
+			<point x="294" y="803"/>
+			<point x="407" y="942"/>
+			<point x="569" y="942" type="curve"/>
+			<point x="755" y="942"/>
+			<point x="808" y="792"/>
+			<point x="818" y="655" type="curve"/>
+			<point x="818" y="641" type="line"/>
+			<point x="212" y="641" type="line"/>
+			<point x="212" y="481" type="line"/>
+			<point x="1018" y="481" type="line"/>
+			<point x="1018" y="566" type="line" smooth="yes"/>
+			<point x="1018" y="871"/>
+			<point x="887" y="1102"/>
+			<point x="569" y="1102" type="curve" smooth="yes"/>
+			<point x="326" y="1102"/>
+			<point x="94" y="900"/>
+			<point x="94" y="544" type="curve" smooth="yes"/>
+			<point x="94" y="502" type="line" smooth="yes"/>
+			<point x="94" y="198"/>
+			<point x="287" y="-20"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/f.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/f.glif
new file mode 100644
index 0000000..56e63f7
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/f.glif
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="f" format="2">
+	<unicode hex="0066"/>
+	<advance width="719"/>
+	<outline>
+		<contour>
+			<point x="429" y="0" type="line"/>
+			<point x="429" y="1193" type="line"/>
+			<point x="429" y="1320"/>
+			<point x="496" y="1390"/>
+			<point x="612" y="1390" type="curve" smooth="yes"/>
+			<point x="645" y="1390"/>
+			<point x="683" y="1387"/>
+			<point x="710" y="1382" type="curve"/>
+			<point x="720" y="1541" type="line"/>
+			<point x="679" y="1551"/>
+			<point x="635" y="1557"/>
+			<point x="593" y="1557" type="curve" smooth="yes"/>
+			<point x="367" y="1557"/>
+			<point x="229" y="1428"/>
+			<point x="229" y="1193" type="curve"/>
+			<point x="229" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="653" y="1082" type="line"/>
+			<point x="60" y="1082" type="line"/>
+			<point x="60" y="932" type="line"/>
+			<point x="653" y="932" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/g.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/g.glif
new file mode 100644
index 0000000..130a3ed
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/g.glif
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="g" format="2">
+	<unicode hex="0067"/>
+	<advance width="1153"/>
+	<outline>
+		<contour>
+			<point x="836" y="1082" type="line"/>
+			<point x="817" y="844" type="line"/>
+			<point x="817" y="14" type="line" smooth="yes"/>
+			<point x="817" y="-170"/>
+			<point x="706" y="-266"/>
+			<point x="538" y="-266" type="curve" smooth="yes"/>
+			<point x="444" y="-266"/>
+			<point x="341" y="-232"/>
+			<point x="250" y="-121" type="curve"/>
+			<point x="147" y="-238" type="line"/>
+			<point x="246" y="-382"/>
+			<point x="446" y="-426"/>
+			<point x="553" y="-426" type="curve" smooth="yes"/>
+			<point x="825" y="-426"/>
+			<point x="1017" y="-264"/>
+			<point x="1017" y="24" type="curve" smooth="yes"/>
+			<point x="1017" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="94" y="529" type="line"/>
+			<point x="94" y="214"/>
+			<point x="261" y="-20"/>
+			<point x="521" y="-20" type="curve" smooth="yes"/>
+			<point x="799" y="-20"/>
+			<point x="932" y="181"/>
+			<point x="962" y="510" type="curve"/>
+			<point x="962" y="572" type="line"/>
+			<point x="932" y="901"/>
+			<point x="799" y="1102"/>
+			<point x="523" y="1102" type="curve" smooth="yes"/>
+			<point x="259" y="1102"/>
+			<point x="94" y="879"/>
+			<point x="94" y="550" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="294" y="550" type="line"/>
+			<point x="294" y="759"/>
+			<point x="376" y="937"/>
+			<point x="574" y="937" type="curve" smooth="yes"/>
+			<point x="751" y="937"/>
+			<point x="835" y="803"/>
+			<point x="866" y="655" type="curve"/>
+			<point x="866" y="426" type="line"/>
+			<point x="825" y="277"/>
+			<point x="751" y="146"/>
+			<point x="572" y="146" type="curve" smooth="yes"/>
+			<point x="376" y="146"/>
+			<point x="294" y="320"/>
+			<point x="294" y="529" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/h.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/h.glif
new file mode 100644
index 0000000..85defe3
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/h.glif
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="h" format="2">
+	<unicode hex="0068"/>
+	<advance width="1129"/>
+	<outline>
+		<contour>
+			<point x="337" y="1536" type="line"/>
+			<point x="137" y="1536" type="line"/>
+			<point x="137" y="0" type="line"/>
+			<point x="337" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="289" y="578" type="line"/>
+			<point x="289" y="778"/>
+			<point x="414" y="937"/>
+			<point x="588" y="937" type="curve" smooth="yes"/>
+			<point x="725" y="937"/>
+			<point x="796" y="872"/>
+			<point x="796" y="712" type="curve" smooth="yes"/>
+			<point x="796" y="0" type="line"/>
+			<point x="996" y="0" type="line"/>
+			<point x="996" y="710" type="line" smooth="yes"/>
+			<point x="996" y="993"/>
+			<point x="861" y="1102"/>
+			<point x="649" y="1102" type="curve" smooth="yes"/>
+			<point x="384" y="1102"/>
+			<point x="207" y="880"/>
+			<point x="207" y="576" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/i.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/i.glif
new file mode 100644
index 0000000..2535d41
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/i.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="i" format="2">
+	<unicode hex="0069"/>
+	<advance width="506"/>
+	<outline>
+		<contour>
+			<point x="353" y="1082" type="line"/>
+			<point x="153" y="1082" type="line"/>
+			<point x="153" y="0" type="line"/>
+			<point x="353" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="140" y="1365" type="curve" smooth="yes"/>
+			<point x="140" y="1304"/>
+			<point x="179" y="1256"/>
+			<point x="255" y="1256" type="curve"/>
+			<point x="331" y="1256"/>
+			<point x="371" y="1304"/>
+			<point x="371" y="1365" type="curve"/>
+			<point x="371" y="1427"/>
+			<point x="331" y="1476"/>
+			<point x="255" y="1476" type="curve"/>
+			<point x="179" y="1476"/>
+			<point x="140" y="1427"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/j.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/j.glif
new file mode 100644
index 0000000..64961d3
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/j.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="j" format="2">
+	<unicode hex="006A"/>
+	<advance width="495"/>
+	<outline>
+		<contour>
+			<point x="142" y="1082" type="line"/>
+			<point x="142" y="-129" type="line"/>
+			<point x="142" y="-235"/>
+			<point x="101" y="-271"/>
+			<point x="27" y="-271" type="curve" smooth="yes"/>
+			<point x="5" y="-271"/>
+			<point x="-31" y="-269"/>
+			<point x="-57" y="-263" type="curve"/>
+			<point x="-57" y="-420" type="line"/>
+			<point x="-26" y="-430"/>
+			<point x="25" y="-437"/>
+			<point x="59" y="-437" type="curve" smooth="yes"/>
+			<point x="250" y="-437"/>
+			<point x="342" y="-328"/>
+			<point x="342" y="-129" type="curve"/>
+			<point x="342" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="123" y="1365" type="curve" smooth="yes"/>
+			<point x="123" y="1304"/>
+			<point x="162" y="1256"/>
+			<point x="238" y="1256" type="curve" smooth="yes"/>
+			<point x="314" y="1256"/>
+			<point x="354" y="1304"/>
+			<point x="354" y="1365" type="curve" smooth="yes"/>
+			<point x="354" y="1427"/>
+			<point x="314" y="1476"/>
+			<point x="238" y="1476" type="curve" smooth="yes"/>
+			<point x="162" y="1476"/>
+			<point x="123" y="1427"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/k.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/k.glif
new file mode 100644
index 0000000..708bdec
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/k.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="k" format="2">
+	<unicode hex="006B"/>
+	<advance width="1046"/>
+	<outline>
+		<contour>
+			<point x="338" y="1536" type="line"/>
+			<point x="138" y="1536" type="line"/>
+			<point x="138" y="0" type="line"/>
+			<point x="338" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="995" y="1082" type="line"/>
+			<point x="753" y="1082" type="line"/>
+			<point x="434" y="735" type="line"/>
+			<point x="241" y="502" type="line"/>
+			<point x="256" y="291" type="line"/>
+			<point x="531" y="577" type="line"/>
+		</contour>
+		<contour>
+			<point x="812" y="0" type="line"/>
+			<point x="1046" y="0" type="line"/>
+			<point x="543" y="684" type="line"/>
+			<point x="440" y="508" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/l.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/l.glif
new file mode 100644
index 0000000..26d4ac3
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/l.glif
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="l" format="2">
+	<unicode hex="006C"/>
+	<advance width="506"/>
+	<outline>
+		<contour>
+			<point x="353" y="1536" type="line"/>
+			<point x="153" y="1536" type="line"/>
+			<point x="153" y="0" type="line"/>
+			<point x="353" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/m.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/m.glif
new file mode 100644
index 0000000..22f0363
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/m.glif
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="m" format="2">
+	<unicode hex="006D"/>
+	<advance width="1791"/>
+	<outline>
+		<contour>
+			<point x="337" y="869" type="line"/>
+			<point x="326" y="1082" type="line"/>
+			<point x="137" y="1082" type="line"/>
+			<point x="137" y="0" type="line"/>
+			<point x="337" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="296" y="578" type="line"/>
+			<point x="296" y="778"/>
+			<point x="360" y="937"/>
+			<point x="571" y="937" type="curve" smooth="yes"/>
+			<point x="708" y="937"/>
+			<point x="795" y="872"/>
+			<point x="795" y="711" type="curve"/>
+			<point x="795" y="0" type="line"/>
+			<point x="995" y="0" type="line"/>
+			<point x="995" y="721" type="line"/>
+			<point x="995" y="992"/>
+			<point x="845" y="1102"/>
+			<point x="645" y="1102" type="curve" smooth="yes"/>
+			<point x="350" y="1102"/>
+			<point x="203" y="880"/>
+			<point x="203" y="576" type="curve"/>
+		</contour>
+		<contour>
+			<point x="993" y="681" type="line"/>
+			<point x="993" y="824"/>
+			<point x="1077" y="937"/>
+			<point x="1230" y="937" type="curve" smooth="yes"/>
+			<point x="1367" y="937"/>
+			<point x="1454" y="887"/>
+			<point x="1454" y="714" type="curve" smooth="yes"/>
+			<point x="1454" y="0" type="line"/>
+			<point x="1654" y="0" type="line"/>
+			<point x="1654" y="712" type="line" smooth="yes"/>
+			<point x="1654" y="984"/>
+			<point x="1523" y="1102"/>
+			<point x="1290" y="1102" type="curve" smooth="yes"/>
+			<point x="1009" y="1102"/>
+			<point x="859" y="878"/>
+			<point x="859" y="637" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/n.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/n.glif
new file mode 100644
index 0000000..c85d49b
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/n.glif
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="n" format="2">
+	<unicode hex="006E"/>
+	<advance width="1132"/>
+	<outline>
+		<contour>
+			<point x="337" y="851" type="line"/>
+			<point x="326" y="1082" type="line"/>
+			<point x="137" y="1082" type="line"/>
+			<point x="137" y="0" type="line"/>
+			<point x="337" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="289" y="578" type="line"/>
+			<point x="289" y="778"/>
+			<point x="414" y="937"/>
+			<point x="588" y="937" type="curve" smooth="yes"/>
+			<point x="725" y="937"/>
+			<point x="796" y="872"/>
+			<point x="796" y="712" type="curve" smooth="yes"/>
+			<point x="796" y="0" type="line"/>
+			<point x="996" y="0" type="line"/>
+			<point x="996" y="710" type="line" smooth="yes"/>
+			<point x="996" y="993"/>
+			<point x="861" y="1102"/>
+			<point x="649" y="1102" type="curve" smooth="yes"/>
+			<point x="384" y="1102"/>
+			<point x="207" y="880"/>
+			<point x="207" y="576" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/o.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/o.glif
new file mode 100644
index 0000000..a95b37a
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/o.glif
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="o" format="2">
+	<unicode hex="006F"/>
+	<advance width="1173"/>
+	<outline>
+		<contour>
+			<point x="92" y="530" type="line"/>
+			<point x="92" y="217"/>
+			<point x="281" y="-20"/>
+			<point x="587" y="-20" type="curve" smooth="yes"/>
+			<point x="893" y="-20"/>
+			<point x="1081" y="217"/>
+			<point x="1081" y="530" type="curve"/>
+			<point x="1081" y="551" type="line"/>
+			<point x="1081" y="864"/>
+			<point x="893" y="1102"/>
+			<point x="585" y="1102" type="curve" smooth="yes"/>
+			<point x="281" y="1102"/>
+			<point x="92" y="864"/>
+			<point x="92" y="551" type="curve"/>
+		</contour>
+		<contour>
+			<point x="292" y="551" type="line"/>
+			<point x="292" y="760"/>
+			<point x="388" y="942"/>
+			<point x="585" y="942" type="curve" smooth="yes"/>
+			<point x="784" y="942"/>
+			<point x="881" y="760"/>
+			<point x="881" y="551" type="curve"/>
+			<point x="881" y="530" type="line"/>
+			<point x="881" y="319"/>
+			<point x="784" y="140"/>
+			<point x="587" y="140" type="curve" smooth="yes"/>
+			<point x="388" y="140"/>
+			<point x="292" y="319"/>
+			<point x="292" y="530" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/p.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/p.glif
new file mode 100644
index 0000000..c003eba
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/p.glif
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="p" format="2">
+	<unicode hex="0070"/>
+	<advance width="1153"/>
+	<outline>
+		<contour>
+			<point x="337" y="874" type="line"/>
+			<point x="319" y="1082" type="line"/>
+			<point x="137" y="1082" type="line"/>
+			<point x="137" y="-416" type="line"/>
+			<point x="337" y="-416" type="line"/>
+		</contour>
+		<contour>
+			<point x="1061" y="550" type="line"/>
+			<point x="1061" y="879"/>
+			<point x="911" y="1102"/>
+			<point x="637" y="1102" type="curve" smooth="yes"/>
+			<point x="360" y="1102"/>
+			<point x="219" y="901"/>
+			<point x="189" y="572" type="curve"/>
+			<point x="189" y="489" type="line"/>
+			<point x="219" y="173"/>
+			<point x="360" y="-20"/>
+			<point x="640" y="-20" type="curve" smooth="yes"/>
+			<point x="909" y="-20"/>
+			<point x="1061" y="214"/>
+			<point x="1061" y="529" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="861" y="529" type="line"/>
+			<point x="861" y="320"/>
+			<point x="774" y="140"/>
+			<point x="578" y="140" type="curve" smooth="yes"/>
+			<point x="400" y="140"/>
+			<point x="325" y="267"/>
+			<point x="285" y="406" type="curve"/>
+			<point x="285" y="655" type="line"/>
+			<point x="315" y="803"/>
+			<point x="400" y="937"/>
+			<point x="576" y="937" type="curve" smooth="yes"/>
+			<point x="774" y="937"/>
+			<point x="861" y="759"/>
+			<point x="861" y="550" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/q.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/q.glif
new file mode 100644
index 0000000..04d7b00
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/q.glif
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="q" format="2">
+	<unicode hex="0071"/>
+	<advance width="1169"/>
+	<outline>
+		<contour>
+			<point x="814" y="-416" type="line"/>
+			<point x="1014" y="-416" type="line"/>
+			<point x="1014" y="1082" type="line"/>
+			<point x="831" y="1082" type="line"/>
+			<point x="814" y="874" type="line"/>
+		</contour>
+		<contour>
+			<point x="92" y="529" type="line"/>
+			<point x="92" y="214"/>
+			<point x="255" y="-20"/>
+			<point x="524" y="-20" type="curve" smooth="yes"/>
+			<point x="802" y="-20"/>
+			<point x="941" y="181"/>
+			<point x="970" y="510" type="curve"/>
+			<point x="970" y="572" type="line"/>
+			<point x="941" y="901"/>
+			<point x="802" y="1102"/>
+			<point x="526" y="1102" type="curve" smooth="yes"/>
+			<point x="253" y="1102"/>
+			<point x="92" y="879"/>
+			<point x="92" y="550" type="curve" smooth="yes"/>
+		</contour>
+		<contour>
+			<point x="292" y="550" type="line"/>
+			<point x="292" y="759"/>
+			<point x="379" y="942"/>
+			<point x="577" y="942" type="curve" smooth="yes"/>
+			<point x="754" y="942"/>
+			<point x="844" y="803"/>
+			<point x="875" y="655" type="curve"/>
+			<point x="875" y="426" type="line"/>
+			<point x="834" y="277"/>
+			<point x="754" y="140"/>
+			<point x="575" y="140" type="curve" smooth="yes"/>
+			<point x="379" y="140"/>
+			<point x="292" y="320"/>
+			<point x="292" y="529" type="curve" smooth="yes"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/r.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/r.glif
new file mode 100644
index 0000000..5e222cc
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/r.glif
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="r" format="2">
+	<unicode hex="0072"/>
+	<advance width="695"/>
+	<outline>
+		<contour>
+			<point x="337" y="914" type="line"/>
+			<point x="331" y="1082" type="line"/>
+			<point x="137" y="1082" type="line"/>
+			<point x="137" y="0" type="line"/>
+			<point x="337" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="665" y="1088" type="line"/>
+			<point x="652" y="1094"/>
+			<point x="607" y="1102"/>
+			<point x="582" y="1102" type="curve"/>
+			<point x="352" y="1102"/>
+			<point x="253" y="884"/>
+			<point x="253" y="628" type="curve"/>
+			<point x="307" y="660" type="line"/>
+			<point x="325" y="809"/>
+			<point x="408" y="913"/>
+			<point x="572" y="913" type="curve"/>
+			<point x="608" y="913"/>
+			<point x="632" y="911"/>
+			<point x="665" y="905" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/s.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/s.glif
new file mode 100644
index 0000000..9544403
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/s.glif
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="s" format="2">
+	<unicode hex="0073"/>
+	<advance width="1061"/>
+	<outline>
+		<contour>
+			<point x="763" y="289" type="curve" smooth="yes"/>
+			<point x="763" y="202"/>
+			<point x="685" y="140"/>
+			<point x="541" y="140" type="curve" smooth="yes"/>
+			<point x="430" y="140"/>
+			<point x="302" y="189"/>
+			<point x="294" y="339" type="curve"/>
+			<point x="94" y="339" type="line"/>
+			<point x="94" y="158"/>
+			<point x="251" y="-20"/>
+			<point x="541" y="-20" type="curve" smooth="yes"/>
+			<point x="795" y="-20"/>
+			<point x="963" y="112"/>
+			<point x="963" y="304" type="curve"/>
+			<point x="963" y="479"/>
+			<point x="838" y="567"/>
+			<point x="572" y="628" type="curve" smooth="yes"/>
+			<point x="376" y="672"/>
+			<point x="331" y="710"/>
+			<point x="331" y="788" type="curve"/>
+			<point x="331" y="865"/>
+			<point x="389" y="942"/>
+			<point x="536" y="942" type="curve" smooth="yes"/>
+			<point x="674" y="942"/>
+			<point x="751" y="847"/>
+			<point x="751" y="762" type="curve"/>
+			<point x="951" y="762" type="line"/>
+			<point x="951" y="948"/>
+			<point x="797" y="1102"/>
+			<point x="536" y="1102" type="curve" smooth="yes"/>
+			<point x="291" y="1102"/>
+			<point x="131" y="955"/>
+			<point x="131" y="782" type="curve"/>
+			<point x="131" y="604"/>
+			<point x="283" y="519"/>
+			<point x="523" y="469" type="curve" smooth="yes"/>
+			<point x="731" y="427"/>
+			<point x="763" y="367"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/space.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/space.glif
new file mode 100644
index 0000000..b92e22c
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/space.glif
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="space" format="2">
+	<unicode hex="0020"/>
+	<advance width="510"/>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/t.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/t.glif
new file mode 100644
index 0000000..261e70a
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/t.glif
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="t" format="2">
+	<unicode hex="0074"/>
+	<advance width="672"/>
+	<outline>
+		<contour>
+			<point x="600" y="1082" type="line"/>
+			<point x="6" y="1082" type="line"/>
+			<point x="6" y="932" type="line"/>
+			<point x="600" y="932" type="line"/>
+		</contour>
+		<contour>
+			<point x="203" y="1342" type="line"/>
+			<point x="203" y="270" type="line"/>
+			<point x="203" y="53"/>
+			<point x="319" y="-20"/>
+			<point x="456" y="-20" type="curve" smooth="yes"/>
+			<point x="526" y="-20"/>
+			<point x="573" y="-8"/>
+			<point x="602" y="1" type="curve"/>
+			<point x="602" y="160" type="line"/>
+			<point x="587" y="156"/>
+			<point x="547" y="148"/>
+			<point x="517" y="148" type="curve" smooth="yes"/>
+			<point x="458" y="148"/>
+			<point x="403" y="165"/>
+			<point x="403" y="269" type="curve"/>
+			<point x="403" y="1342" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/u.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/u.glif
new file mode 100644
index 0000000..c17bfd9
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/u.glif
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="u" format="2">
+	<unicode hex="0075"/>
+	<advance width="1130"/>
+	<outline>
+		<contour>
+			<point x="793" y="250" type="line"/>
+			<point x="803" y="0" type="line"/>
+			<point x="993" y="0" type="line"/>
+			<point x="993" y="1082" type="line"/>
+			<point x="793" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="830" y="483" type="line"/>
+			<point x="830" y="293"/>
+			<point x="750" y="146"/>
+			<point x="521" y="146" type="curve" smooth="yes"/>
+			<point x="428" y="146"/>
+			<point x="333" y="192"/>
+			<point x="333" y="381" type="curve"/>
+			<point x="333" y="1082" type="line"/>
+			<point x="133" y="1082" type="line"/>
+			<point x="133" y="383" type="line"/>
+			<point x="133" y="96"/>
+			<point x="276" y="-20"/>
+			<point x="488" y="-20" type="curve" smooth="yes"/>
+			<point x="800" y="-20"/>
+			<point x="911" y="194"/>
+			<point x="911" y="485" type="curve"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/v.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/v.glif
new file mode 100644
index 0000000..bd5b120
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/v.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="v" format="2">
+	<unicode hex="0076"/>
+	<advance width="994"/>
+	<outline>
+		<contour>
+			<point x="469" y="176" type="line"/>
+			<point x="439" y="0" type="line"/>
+			<point x="572" y="0" type="line"/>
+			<point x="957" y="1082" type="line"/>
+			<point x="753" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="236" y="1082" type="line"/>
+			<point x="32" y="1082" type="line"/>
+			<point x="421" y="0" type="line"/>
+			<point x="553" y="0" type="line"/>
+			<point x="531" y="171" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/w.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/w.glif
new file mode 100644
index 0000000..e6b648e
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/w.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="w" format="2">
+	<unicode hex="0077"/>
+	<advance width="1538"/>
+	<outline>
+		<contour>
+			<point x="406" y="182" type="line"/>
+			<point x="388" y="0" type="line"/>
+			<point x="514" y="0" type="line"/>
+			<point x="799" y="913" type="line"/>
+			<point x="818" y="1082" type="line"/>
+			<point x="687" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="237" y="1082" type="line"/>
+			<point x="39" y="1082" type="line"/>
+			<point x="353" y="0" type="line"/>
+			<point x="486" y="0" type="line"/>
+			<point x="476" y="171" type="line"/>
+		</contour>
+		<contour>
+			<point x="1071" y="178" type="line"/>
+			<point x="1049" y="0" type="line"/>
+			<point x="1181" y="0" type="line"/>
+			<point x="1495" y="1082" type="line"/>
+			<point x="1297" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="847" y="1082" type="line"/>
+			<point x="708" y="1082" type="line"/>
+			<point x="727" y="915" type="line"/>
+			<point x="1020" y="0" type="line"/>
+			<point x="1146" y="0" type="line"/>
+			<point x="1122" y="198" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/x.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/x.glif
new file mode 100644
index 0000000..8a60313
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/x.glif
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="x" format="2">
+	<unicode hex="0078"/>
+	<advance width="1020"/>
+	<outline>
+		<contour>
+			<point x="280" y="1082" type="line"/>
+			<point x="50" y="1082" type="line"/>
+			<point x="401" y="547" type="line"/>
+			<point x="40" y="0" type="line"/>
+			<point x="272" y="0" type="line"/>
+			<point x="509" y="397" type="line"/>
+			<point x="746" y="0" type="line"/>
+			<point x="976" y="0" type="line"/>
+			<point x="615" y="547" type="line"/>
+			<point x="966" y="1082" type="line"/>
+			<point x="733" y="1082" type="line"/>
+			<point x="505" y="695" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/y.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/y.glif
new file mode 100644
index 0000000..793d4f2
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/y.glif
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="y" format="2">
+	<unicode hex="0079"/>
+	<advance width="968"/>
+	<outline>
+		<contour>
+			<point x="438" y="113" type="line"/>
+			<point x="363" y="-92" type="line" smooth="yes"/>
+			<point x="319" y="-226"/>
+			<point x="255" y="-266"/>
+			<point x="130" y="-266" type="curve" smooth="yes"/>
+			<point x="121" y="-266"/>
+			<point x="93" y="-264"/>
+			<point x="83" y="-262" type="curve"/>
+			<point x="83" y="-421" type="line"/>
+			<point x="103" y="-426"/>
+			<point x="159" y="-437"/>
+			<point x="190" y="-437" type="curve" smooth="yes"/>
+			<point x="386" y="-437"/>
+			<point x="475" y="-274"/>
+			<point x="515" y="-166" type="curve" smooth="yes"/>
+			<point x="944" y="1082" type="line"/>
+			<point x="731" y="1082" type="line"/>
+		</contour>
+		<contour>
+			<point x="238" y="1082" type="line"/>
+			<point x="20" y="1082" type="line"/>
+			<point x="413" y="-21" type="line"/>
+			<point x="556" y="50" type="line"/>
+			<point x="504" y="258" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/z.glif b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/z.glif
new file mode 100644
index 0000000..e21d52a
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/glyphs/z.glif
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="z" format="2">
+	<unicode hex="007A"/>
+	<advance width="1020"/>
+	<outline>
+		<contour>
+			<point x="949" y="160" type="line"/>
+			<point x="166" y="160" type="line"/>
+			<point x="166" y="0" type="line"/>
+			<point x="949" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="923" y="944" type="line"/>
+			<point x="923" y="1082" type="line"/>
+			<point x="796" y="1082" type="line"/>
+			<point x="89" y="144" type="line"/>
+			<point x="89" y="0" type="line"/>
+			<point x="211" y="0" type="line"/>
+		</contour>
+		<contour>
+			<point x="835" y="1082" type="line"/>
+			<point x="95" y="1082" type="line"/>
+			<point x="95" y="921" type="line"/>
+			<point x="835" y="921" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/layercontents.plist b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/layercontents.plist
new file mode 100644
index 0000000..03e5dde
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-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/cu2qu/data/RobotoSubset-Regular.ufo/lib.plist b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/lib.plist
new file mode 100644
index 0000000..5623ed1
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/lib.plist
@@ -0,0 +1,62 @@
+<?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>space</string>
+			<string>A</string>
+			<string>B</string>
+			<string>C</string>
+			<string>D</string>
+			<string>E</string>
+			<string>F</string>
+			<string>G</string>
+			<string>H</string>
+			<string>I</string>
+			<string>J</string>
+			<string>K</string>
+			<string>L</string>
+			<string>M</string>
+			<string>N</string>
+			<string>O</string>
+			<string>P</string>
+			<string>Q</string>
+			<string>R</string>
+			<string>S</string>
+			<string>T</string>
+			<string>U</string>
+			<string>V</string>
+			<string>W</string>
+			<string>X</string>
+			<string>Y</string>
+			<string>Z</string>
+			<string>a</string>
+			<string>b</string>
+			<string>c</string>
+			<string>d</string>
+			<string>e</string>
+			<string>f</string>
+			<string>g</string>
+			<string>h</string>
+			<string>i</string>
+			<string>j</string>
+			<string>k</string>
+			<string>l</string>
+			<string>m</string>
+			<string>n</string>
+			<string>o</string>
+			<string>p</string>
+			<string>q</string>
+			<string>r</string>
+			<string>s</string>
+			<string>t</string>
+			<string>u</string>
+			<string>v</string>
+			<string>w</string>
+			<string>x</string>
+			<string>y</string>
+			<string>z</string>
+		</array>
+	</dict>
+</plist>
diff --git a/Tests/cu2qu/data/RobotoSubset-Regular.ufo/metainfo.plist b/Tests/cu2qu/data/RobotoSubset-Regular.ufo/metainfo.plist
new file mode 100644
index 0000000..edfd637
--- /dev/null
+++ b/Tests/cu2qu/data/RobotoSubset-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/cu2qu/data/curves.json b/Tests/cu2qu/data/curves.json
new file mode 100644
index 0000000..23af575
--- /dev/null
+++ b/Tests/cu2qu/data/curves.json
@@ -0,0 +1 @@
+[[[550.0,258.0],[1044.0,482.0],[2029.0,1841.0],[1934.0,1554.0]],[[859.0,384.0],[1998.0,116.0],[1596.0,1772.0],[8.0,1824.0]],[[1090.0,937.0],[418.0,1300.0],[125.0,91.0],[104.0,37.0]],[[1561.0,887.0],[1728.0,118.0],[908.0,1793.0],[2030.0,954.0]],[[1415.0,945.0],[896.0,1882.0],[1186.0,88.0],[1704.0,409.0]],[[761.0,1214.0],[495.0,1362.0],[1728.0,777.0],[1242.0,1163.0]],[[2045.0,1611.0],[141.0,1967.0],[994.0,1655.0],[1697.0,708.0]],[[1503.0,1534.0],[354.0,1797.0],[442.0,670.0],[1610.0,1517.0]],[[2005.0,121.0],[1922.0,178.0],[1263.0,1612.0],[697.0,690.0]],[[929.0,50.0],[817.0,950.0],[1656.0,1408.0],[1447.0,1880.0]],[[1102.0,23.0],[1571.0,529.0],[841.0,1745.0],[229.0,1970.0]],[[1493.0,818.0],[1693.0,1986.0],[1461.0,1697.0],[1417.0,6.0]],[[1356.0,1876.0],[114.0,940.0],[725.0,740.0],[375.0,1045.0]],[[132.0,288.0],[340.0,68.0],[1855.0,59.0],[1151.0,1022.0]],[[1100.0,448.0],[756.0,1410.0],[1189.0,284.0],[685.0,653.0]],[[1045.0,688.0],[1117.0,1206.0],[1862.0,1318.0],[2033.0,1940.0]],[[467.0,96.0],[1277.0,1583.0],[1406.0,1724.0],[770.0,1058.0]],[[445.0,1038.0],[856.0,1768.0],[85.0,923.0],[73.0,1627.0]],[[599.0,144.0],[656.0,1825.0],[1747.0,903.0],[1846.0,914.0]],[[125.0,1617.0],[1315.0,1746.0],[240.0,1223.0],[514.0,868.0]],[[194.0,1254.0],[289.0,313.0],[1271.0,1220.0],[648.0,1704.0]],[[1033.0,534.0],[34.0,155.0],[891.0,1887.0],[702.0,153.0]],[[1548.0,820.0],[1421.0,405.0],[842.0,1773.0],[795.0,2016.0]],[[427.0,1597.0],[1212.0,2047.0],[70.0,1332.0],[1647.0,1152.0]],[[74.0,642.0],[822.0,1342.0],[553.0,1388.0],[1758.0,872.0]],[[1091.0,394.0],[1553.0,1408.0],[1984.0,961.0],[267.0,165.0]],[[346.0,544.0],[695.0,682.0],[872.0,1097.0],[1360.0,1045.0]],[[1507.0,1387.0],[1393.0,466.0],[1192.0,963.0],[2002.0,554.0]],[[427.0,1313.0],[160.0,1665.0],[299.0,1557.0],[603.0,512.0]],[[1396.0,469.0],[1548.0,313.0],[916.0,334.0],[1092.0,1494.0]],[[1210.0,468.0],[1875.0,1135.0],[441.0,187.0],[1211.0,50.0]],[[59.0,375.0],[1693.0,471.0],[163.0,769.0],[981.0,1724.0]],[[663.0,473.0],[1846.0,685.0],[988.0,651.0],[421.0,1782.0]],[[1549.0,1204.0],[1037.0,1953.0],[1288.0,410.0],[850.0,1300.0]],[[162.0,111.0],[43.0,1210.0],[1311.0,1842.0],[1602.0,1283.0]],[[1632.0,257.0],[262.0,1299.0],[1867.0,456.0],[1024.0,881.0]],[[1920.0,1457.0],[1061.0,750.0],[851.0,1258.0],[815.0,1009.0]],[[1476.0,333.0],[1150.0,366.0],[1834.0,370.0],[1388.0,931.0]],[[1599.0,1256.0],[168.0,1340.0],[765.0,1297.0],[1240.0,1006.0]],[[1369.0,413.0],[377.0,1003.0],[901.0,83.0],[998.0,1645.0]],[[296.0,1097.0],[290.0,307.0],[88.0,40.0],[1191.0,1471.0]],[[2020.0,1920.0],[631.0,413.0],[1343.0,315.0],[709.0,735.0]],[[612.0,579.0],[1309.0,1251.0],[437.0,1202.0],[517.0,846.0]],[[580.0,130.0],[1294.0,841.0],[729.0,1224.0],[1772.0,646.0]],[[198.0,1012.0],[1034.0,263.0],[1829.0,1761.0],[1024.0,1799.0]],[[1856.0,44.0],[1620.0,1387.0],[702.0,1056.0],[1989.0,99.0]],[[1706.0,77.0],[255.0,1453.0],[566.0,512.0],[567.0,1061.0]],[[1134.0,1629.0],[1642.0,705.0],[365.0,956.0],[1990.0,30.0]],[[727.0,1299.0],[1795.0,924.0],[976.0,1281.0],[2027.0,1961.0]],[[921.0,1688.0],[1380.0,1127.0],[898.0,197.0],[293.0,1510.0]],[[653.0,834.0],[1277.0,1223.0],[1227.0,1522.0],[676.0,1903.0]],[[348.0,504.0],[1545.0,722.0],[638.0,1026.0],[1747.0,891.0]],[[213.0,2027.0],[1612.0,1425.0],[1572.0,675.0],[166.0,370.0]],[[1045.0,413.0],[1095.0,342.0],[569.0,335.0],[1822.0,987.0]],[[1566.0,1773.0],[1627.0,674.0],[1333.0,1794.0],[517.0,1998.0]],[[868.0,488.0],[1766.0,1672.0],[483.0,1210.0],[1137.0,1016.0]],[[1551.0,16.0],[777.0,1797.0],[86.0,126.0],[992.0,1066.0]],[[846.0,708.0],[1166.0,607.0],[821.0,1119.0],[1274.0,1027.0]],[[1828.0,688.0],[1462.0,2010.0],[1720.0,498.0],[855.0,1569.0]],[[838.0,1163.0],[442.0,98.0],[483.0,54.0],[1214.0,559.0]],[[307.0,1530.0],[1274.0,1790.0],[1461.0,1325.0],[3.0,507.0]],[[1811.0,1841.0],[1434.0,1248.0],[1635.0,1390.0],[2016.0,463.0]],[[1546.0,1566.0],[835.0,15.0],[1137.0,814.0],[1890.0,1675.0]],[[1250.0,697.0],[1840.0,808.0],[1472.0,14.0],[1594.0,1744.0]],[[1659.0,1376.0],[277.0,2018.0],[1014.0,1191.0],[85.0,1667.0]],[[639.0,1627.0],[1106.0,729.0],[300.0,41.0],[1431.0,1083.0]],[[1684.0,1243.0],[622.0,1892.0],[1062.0,1984.0],[694.0,1913.0]],[[185.0,1109.0],[403.0,1730.0],[285.0,1454.0],[274.0,1812.0]],[[80.0,672.0],[662.0,381.0],[1646.0,1129.0],[1246.0,855.0]],[[850.0,971.0],[1367.0,1102.0],[280.0,306.0],[1508.0,1916.0]],[[203.0,690.0],[1216.0,1104.0],[1457.0,950.0],[1607.0,1637.0]],[[705.0,1980.0],[1063.0,1350.0],[910.0,1059.0],[1000.0,125.0]],[[1649.0,1296.0],[1768.0,1017.0],[1102.0,777.0],[297.0,678.0]],[[1816.0,606.0],[1073.0,1881.0],[665.0,567.0],[565.0,1805.0]],[[1479.0,1268.0],[1641.0,985.0],[474.0,844.0],[1251.0,279.0]],[[435.0,932.0],[1626.0,1316.0],[2016.0,409.0],[764.0,184.0]],[[226.0,95.0],[887.0,142.0],[2025.0,1811.0],[1402.0,1124.0]],[[483.0,707.0],[390.0,909.0],[1637.0,955.0],[2027.0,1842.0]],[[1547.0,690.0],[949.0,965.0],[1161.0,1894.0],[1595.0,867.0]],[[1850.0,1056.0],[1352.0,2032.0],[454.0,875.0],[322.0,189.0]],[[63.0,21.0],[1967.0,1308.0],[1569.0,1176.0],[802.0,1638.0]],[[655.0,623.0],[124.0,62.0],[1586.0,594.0],[233.0,1554.0]],[[1041.0,532.0],[325.0,1895.0],[1242.0,59.0],[145.0,249.0]],[[528.0,175.0],[1120.0,481.0],[1771.0,372.0],[778.0,113.0]],[[2046.0,533.0],[1143.0,786.0],[1833.0,1596.0],[1350.0,1097.0]],[[1064.0,995.0],[1005.0,246.0],[717.0,1432.0],[1755.0,249.0]],[[1446.0,1690.0],[816.0,1737.0],[287.0,1094.0],[296.0,1030.0]],[[727.0,395.0],[618.0,240.0],[832.0,1753.0],[183.0,216.0]],[[373.0,1921.0],[1516.0,406.0],[1280.0,164.0],[518.0,135.0]],[[1815.0,525.0],[1618.0,1827.0],[100.0,1105.0],[370.0,1024.0]],[[1332.0,351.0],[1236.0,140.0],[1573.0,238.0],[1069.0,1282.0]],[[532.0,1066.0],[1557.0,479.0],[1244.0,385.0],[1740.0,1005.0]],[[841.0,1352.0],[1387.0,1601.0],[1970.0,428.0],[531.0,1837.0]],[[123.0,1193.0],[643.0,819.0],[1516.0,1594.0],[1328.0,398.0]],[[1677.0,1414.0],[517.0,265.0],[178.0,1230.0],[1284.0,1710.0]],[[1221.0,1305.0],[1444.0,1116.0],[1332.0,35.0],[499.0,609.0]],[[1298.0,1333.0],[1341.0,281.0],[1850.0,1145.0],[1964.0,1860.0]],[[1491.0,1558.0],[320.0,229.0],[551.0,199.0],[2015.0,1031.0]],[[1005.0,1387.0],[1481.0,1516.0],[1648.0,1259.0],[1902.0,1394.0]],[[687.0,119.0],[607.0,1024.0],[905.0,546.0],[461.0,756.0]],[[1683.0,205.0],[406.0,1088.0],[438.0,836.0],[1071.0,273.0]],[[321.0,298.0],[890.0,710.0],[1769.0,89.0],[1507.0,1993.0]],[[1162.0,900.0],[820.0,2021.0],[963.0,1742.0],[1852.0,1503.0]],[[773.0,1974.0],[297.0,1050.0],[1668.0,824.0],[33.0,1559.0]],[[1995.0,312.0],[1653.0,1743.0],[164.0,1441.0],[1877.0,26.0]],[[777.0,1226.0],[22.0,491.0],[1239.0,1292.0],[1157.0,1685.0]],[[1672.0,1260.0],[1853.0,1236.0],[536.0,1819.0],[574.0,667.0]],[[1035.0,39.0],[1737.0,148.0],[1508.0,1723.0],[1647.0,1153.0]],[[75.0,370.0],[368.0,19.0],[1570.0,1101.0],[1902.0,1113.0]],[[1526.0,1971.0],[1378.0,1591.0],[1868.0,477.0],[1981.0,1452.0]],[[592.0,1700.0],[607.0,74.0],[704.0,1065.0],[1506.0,520.0]],[[1176.0,1691.0],[1056.0,1176.0],[1723.0,1120.0],[1775.0,1375.0]],[[1989.0,882.0],[2012.0,1646.0],[1741.0,374.0],[263.0,530.0]],[[844.0,612.0],[938.0,107.0],[422.0,1037.0],[637.0,1965.0]],[[405.0,1634.0],[767.0,12.0],[365.0,1751.0],[208.0,894.0]],[[1728.0,1420.0],[192.0,422.0],[1718.0,485.0],[1086.0,1141.0]],[[733.0,1964.0],[195.0,877.0],[357.0,1596.0],[507.0,1832.0]],[[1205.0,2039.0],[1610.0,475.0],[1962.0,433.0],[610.0,1582.0]],[[824.0,684.0],[1055.0,1706.0],[1182.0,2017.0],[879.0,1380.0]],[[1990.0,421.0],[35.0,1420.0],[1095.0,231.0],[1803.0,1228.0]],[[412.0,936.0],[1124.0,1107.0],[1009.0,1686.0],[607.0,533.0]],[[1049.0,799.0],[1670.0,239.0],[609.0,1694.0],[1106.0,1146.0]],[[1966.0,1252.0],[1093.0,2012.0],[878.0,2042.0],[1506.0,1927.0]],[[989.0,1386.0],[721.0,742.0],[1847.0,612.0],[238.0,1335.0]],[[553.0,873.0],[1291.0,2022.0],[1967.0,1351.0],[484.0,523.0]],[[573.0,1050.0],[921.0,360.0],[204.0,704.0],[475.0,926.0]],[[816.0,1261.0],[1729.0,1342.0],[17.0,82.0],[1250.0,902.0]],[[346.0,919.0],[1147.0,1397.0],[1102.0,1553.0],[94.0,498.0]],[[1351.0,1421.0],[571.0,464.0],[1027.0,586.0],[168.0,1421.0]],[[316.0,376.0],[422.0,1228.0],[1298.0,1019.0],[1103.0,203.0]],[[1481.0,127.0],[320.0,569.0],[1635.0,1523.0],[991.0,384.0]],[[1346.0,1120.0],[32.0,1318.0],[459.0,1443.0],[515.0,1110.0]],[[1659.0,373.0],[1947.0,1715.0],[1612.0,1233.0],[898.0,1239.0]],[[545.0,220.0],[450.0,717.0],[985.0,880.0],[1780.0,1124.0]],[[81.0,1025.0],[1109.0,1072.0],[1938.0,516.0],[1651.0,424.0]],[[1529.0,282.0],[1487.0,124.0],[1262.0,1824.0],[541.0,638.0]],[[304.0,581.0],[885.0,1982.0],[1374.0,1495.0],[1197.0,654.0]],[[637.0,1563.0],[1801.0,1661.0],[482.0,594.0],[1104.0,1209.0]],[[33.0,39.0],[543.0,1554.0],[414.0,1882.0],[124.0,1769.0]],[[1729.0,1130.0],[1516.0,1672.0],[1663.0,1892.0],[218.0,406.0]],[[1928.0,153.0],[2.0,172.0],[455.0,571.0],[1459.0,1109.0]],[[1459.0,1941.0],[1004.0,982.0],[432.0,1465.0],[649.0,476.0]],[[166.0,1284.0],[1730.0,1418.0],[1038.0,228.0],[1781.0,1699.0]],[[1541.0,1469.0],[1203.0,1397.0],[1806.0,975.0],[591.0,229.0]],[[1398.0,464.0],[705.0,1996.0],[1396.0,497.0],[88.0,1967.0]],[[856.0,1569.0],[715.0,1627.0],[933.0,408.0],[1017.0,1374.0]],[[1347.0,1004.0],[1889.0,1929.0],[1513.0,2017.0],[793.0,1769.0]],[[1804.0,1633.0],[493.0,1999.0],[1091.0,512.0],[613.0,48.0]],[[1540.0,1698.0],[446.0,107.0],[305.0,749.0],[1879.0,1544.0]],[[1181.0,636.0],[631.0,433.0],[1042.0,76.0],[1902.0,1624.0]],[[935.0,1600.0],[21.0,1021.0],[1732.0,650.0],[733.0,1402.0]],[[979.0,311.0],[659.0,719.0],[1538.0,88.0],[888.0,1750.0]],[[965.0,165.0],[779.0,316.0],[1015.0,1630.0],[1904.0,487.0]],[[198.0,1585.0],[367.0,387.0],[1961.0,184.0],[979.0,49.0]],[[85.0,1277.0],[1910.0,1138.0],[1702.0,682.0],[545.0,1303.0]],[[1837.0,1710.0],[686.0,1619.0],[1593.0,822.0],[2029.0,1140.0]],[[1474.0,620.0],[1062.0,1144.0],[717.0,342.0],[1476.0,1376.0]],[[584.0,1058.0],[1044.0,1033.0],[1430.0,1573.0],[1143.0,1915.0]],[[55.0,610.0],[533.0,1035.0],[925.0,804.0],[288.0,812.0]],[[1758.0,982.0],[570.0,1886.0],[1602.0,802.0],[338.0,316.0]],[[627.0,235.0],[123.0,1660.0],[1567.0,1709.0],[563.0,529.0]],[[303.0,988.0],[1563.0,571.0],[1170.0,829.0],[1626.0,1461.0]],[[730.0,922.0],[1219.0,589.0],[1424.0,2015.0],[1195.0,362.0]],[[1224.0,855.0],[1898.0,89.0],[1189.0,422.0],[1526.0,1816.0]],[[1044.0,238.0],[213.0,1292.0],[654.0,542.0],[423.0,460.0]],[[1782.0,1007.0],[851.0,1625.0],[497.0,869.0],[1572.0,548.0]],[[1042.0,14.0],[495.0,825.0],[1548.0,1974.0],[944.0,1096.0]],[[154.0,687.0],[954.0,1681.0],[1121.0,1725.0],[1632.0,1114.0]],[[2023.0,400.0],[530.0,764.0],[65.0,1859.0],[183.0,2000.0]],[[877.0,1613.0],[1377.0,997.0],[385.0,315.0],[174.0,1731.0]],[[1809.0,773.0],[709.0,778.0],[1576.0,1476.0],[807.0,953.0]],[[1473.0,264.0],[1396.0,212.0],[1877.0,181.0],[724.0,604.0]],[[1169.0,1921.0],[176.0,265.0],[1623.0,376.0],[1638.0,1234.0]],[[1615.0,1097.0],[1442.0,1927.0],[201.0,1954.0],[71.0,1748.0]],[[1247.0,1299.0],[611.0,1137.0],[269.0,1478.0],[1700.0,1601.0]],[[96.0,464.0],[151.0,58.0],[413.0,1360.0],[1379.0,1508.0]],[[141.0,1516.0],[303.0,1986.0],[343.0,1827.0],[1370.0,2048.0]],[[13.0,658.0],[1331.0,1478.0],[876.0,598.0],[607.0,441.0]],[[1654.0,1299.0],[1723.0,1474.0],[1398.0,1064.0],[1509.0,154.0]],[[259.0,1010.0],[1087.0,1626.0],[1162.0,341.0],[306.0,697.0]],[[1094.0,1694.0],[341.0,517.0],[1156.0,1076.0],[961.0,862.0]],[[404.0,1135.0],[1967.0,192.0],[1234.0,835.0],[307.0,1292.0]],[[1391.0,1212.0],[545.0,144.0],[1811.0,1490.0],[152.0,117.0]],[[1292.0,1710.0],[670.0,166.0],[1739.0,755.0],[808.0,953.0]],[[470.0,532.0],[501.0,1091.0],[1877.0,804.0],[226.0,1479.0]],[[1868.0,1371.0],[1452.0,900.0],[38.0,57.0],[2001.0,132.0]],[[673.0,1037.0],[163.0,37.0],[942.0,346.0],[709.0,143.0]],[[820.0,857.0],[1814.0,1182.0],[995.0,2009.0],[1521.0,1330.0]],[[1605.0,300.0],[799.0,743.0],[768.0,1216.0],[1745.0,1941.0]],[[1488.0,94.0],[1996.0,84.0],[429.0,1771.0],[1407.0,1388.0]],[[303.0,1721.0],[799.0,2024.0],[1956.0,1843.0],[1929.0,677.0]],[[1098.0,1235.0],[1623.0,1061.0],[1046.0,1270.0],[60.0,187.0]],[[1874.0,1874.0],[1456.0,950.0],[1819.0,856.0],[1949.0,1374.0]],[[593.0,1572.0],[1791.0,222.0],[455.0,1459.0],[33.0,1047.0]],[[221.0,1255.0],[1551.0,61.0],[1329.0,1385.0],[1264.0,203.0]],[[854.0,334.0],[1346.0,491.0],[271.0,525.0],[1205.0,1677.0]],[[1395.0,952.0],[111.0,749.0],[1498.0,1239.0],[1203.0,1548.0]],[[1722.0,1890.0],[303.0,815.0],[1669.0,948.0],[172.0,986.0]],[[919.0,997.0],[1616.0,1553.0],[860.0,622.0],[1225.0,1474.0]],[[5.0,1258.0],[1819.0,2039.0],[699.0,599.0],[127.0,1518.0]],[[1789.0,1400.0],[2005.0,1300.0],[456.0,1197.0],[1130.0,1759.0]],[[46.0,1272.0],[354.0,2014.0],[470.0,903.0],[1084.0,1789.0]],[[1526.0,944.0],[222.0,419.0],[667.0,531.0],[1196.0,197.0]],[[279.0,893.0],[12.0,253.0],[1732.0,86.0],[271.0,225.0]],[[36.0,142.0],[1389.0,1362.0],[76.0,36.0],[865.0,1920.0]],[[819.0,1090.0],[1209.0,1029.0],[956.0,748.0],[863.0,1603.0]],[[244.0,977.0],[1853.0,144.0],[1357.0,1338.0],[1666.0,490.0]],[[65.0,757.0],[383.0,757.0],[894.0,921.0],[723.0,1245.0]],[[400.0,240.0],[1285.0,599.0],[257.0,1815.0],[614.0,945.0]],[[176.0,1172.0],[1410.0,238.0],[365.0,1812.0],[820.0,933.0]],[[758.0,488.0],[235.0,828.0],[221.0,474.0],[358.0,900.0]],[[1171.0,1032.0],[1731.0,1018.0],[132.0,1031.0],[797.0,1334.0]],[[1433.0,1463.0],[1860.0,1566.0],[1583.0,366.0],[1745.0,1001.0]],[[2004.0,1407.0],[731.0,466.0],[981.0,296.0],[1788.0,1134.0]],[[1244.0,1372.0],[1517.0,1676.0],[1869.0,1492.0],[1441.0,1293.0]],[[1622.0,1930.0],[70.0,1516.0],[521.0,1238.0],[688.0,1237.0]],[[519.0,612.0],[683.0,1874.0],[623.0,553.0],[659.0,326.0]],[[1039.0,964.0],[1457.0,1291.0],[702.0,1135.0],[1937.0,1268.0]],[[316.0,1754.0],[630.0,1446.0],[1841.0,440.0],[638.0,1293.0]],[[283.0,765.0],[1964.0,143.0],[191.0,785.0],[1458.0,1499.0]],[[1455.0,1534.0],[1401.0,493.0],[756.0,1537.0],[133.0,1109.0]],[[860.0,255.0],[1011.0,1246.0],[1339.0,1650.0],[1000.0,1473.0]],[[202.0,949.0],[1190.0,27.0],[800.0,397.0],[554.0,912.0]],[[1510.0,1091.0],[576.0,665.0],[934.0,308.0],[1275.0,1769.0]],[[1799.0,1945.0],[749.0,1456.0],[800.0,1773.0],[303.0,1134.0]],[[840.0,937.0],[582.0,547.0],[852.0,86.0],[670.0,1989.0]],[[1486.0,753.0],[201.0,1475.0],[337.0,972.0],[865.0,356.0]],[[1807.0,804.0],[1402.0,675.0],[73.0,891.0],[1294.0,1967.0]],[[148.0,214.0],[1502.0,2047.0],[1431.0,555.0],[1999.0,279.0]],[[1305.0,1276.0],[1301.0,366.0],[1969.0,1384.0],[1702.0,292.0]],[[1073.0,257.0],[1322.0,78.0],[738.0,1341.0],[924.0,1282.0]],[[1075.0,1033.0],[1254.0,1997.0],[1703.0,49.0],[1206.0,665.0]],[[1191.0,199.0],[474.0,1767.0],[1763.0,890.0],[1139.0,1460.0]],[[2024.0,1152.0],[1048.0,706.0],[1321.0,584.0],[1440.0,387.0]],[[1626.0,1461.0],[787.0,1621.0],[1840.0,614.0],[1970.0,994.0]],[[154.0,1014.0],[323.0,288.0],[157.0,1931.0],[1983.0,1340.0]],[[698.0,2036.0],[1628.0,54.0],[1581.0,1845.0],[677.0,1528.0]],[[211.0,1508.0],[1445.0,1793.0],[972.0,1243.0],[361.0,1809.0]],[[1462.0,799.0],[660.0,551.0],[1811.0,184.0],[1491.0,1381.0]],[[710.0,2008.0],[1959.0,34.0],[958.0,243.0],[1819.0,669.0]],[[853.0,1639.0],[1908.0,505.0],[1289.0,1073.0],[566.0,693.0]],[[1351.0,539.0],[739.0,1262.0],[959.0,1750.0],[1917.0,1875.0]],[[1274.0,695.0],[1264.0,846.0],[1157.0,633.0],[26.0,1394.0]],[[487.0,1742.0],[1556.0,732.0],[1800.0,1840.0],[1811.0,1489.0]],[[845.0,221.0],[348.0,439.0],[398.0,1587.0],[562.0,1816.0]],[[1626.0,745.0],[1945.0,1838.0],[149.0,794.0],[1843.0,2000.0]],[[1596.0,1190.0],[1428.0,710.0],[1119.0,738.0],[112.0,248.0]],[[265.0,941.0],[1825.0,1306.0],[1808.0,1373.0],[416.0,1590.0]],[[220.0,1918.0],[1139.0,1676.0],[1905.0,1356.0],[393.0,672.0]],[[1643.0,1749.0],[1956.0,610.0],[1308.0,597.0],[1433.0,562.0]],[[792.0,921.0],[885.0,1859.0],[637.0,423.0],[421.0,1741.0]],[[215.0,1857.0],[621.0,1534.0],[1317.0,1147.0],[1630.0,58.0]],[[1587.0,1995.0],[1824.0,1235.0],[1241.0,1585.0],[1282.0,1186.0]],[[713.0,410.0],[2004.0,736.0],[1825.0,628.0],[1878.0,432.0]],[[505.0,1304.0],[1295.0,2024.0],[1396.0,1309.0],[1894.0,1324.0]],[[1984.0,1614.0],[893.0,680.0],[987.0,819.0],[1004.0,211.0]],[[1314.0,252.0],[1344.0,1719.0],[121.0,1410.0],[1472.0,1480.0]],[[1674.0,856.0],[1182.0,919.0],[1284.0,1627.0],[1575.0,719.0]],[[34.0,1592.0],[1434.0,910.0],[958.0,269.0],[1311.0,1575.0]],[[834.0,1202.0],[392.0,1777.0],[16.0,1437.0],[381.0,1670.0]],[[627.0,456.0],[734.0,1394.0],[590.0,1538.0],[1789.0,1333.0]],[[1135.0,854.0],[794.0,648.0],[674.0,657.0],[600.0,490.0]],[[1810.0,532.0],[1766.0,548.0],[1367.0,1299.0],[561.0,84.0]],[[1468.0,713.0],[926.0,962.0],[2035.0,2001.0],[140.0,367.0]],[[547.0,1920.0],[584.0,856.0],[1476.0,564.0],[1147.0,1427.0]],[[265.0,1571.0],[1946.0,122.0],[1891.0,806.0],[986.0,844.0]],[[20.0,1245.0],[172.0,1093.0],[775.0,294.0],[433.0,451.0]],[[1639.0,1359.0],[429.0,1824.0],[1977.0,1149.0],[584.0,1766.0]],[[1521.0,1429.0],[1571.0,1685.0],[1786.0,1507.0],[843.0,801.0]],[[267.0,593.0],[974.0,982.0],[85.0,987.0],[1612.0,1870.0]],[[1805.0,390.0],[221.0,705.0],[31.0,181.0],[1762.0,1140.0]],[[1701.0,543.0],[965.0,1533.0],[1698.0,1400.0],[193.0,1861.0]],[[529.0,1491.0],[246.0,1430.0],[480.0,1005.0],[510.0,1788.0]],[[609.0,78.0],[1496.0,532.0],[616.0,1180.0],[101.0,1934.0]],[[109.0,1978.0],[274.0,1765.0],[376.0,1924.0],[396.0,527.0]],[[1612.0,1679.0],[990.0,1555.0],[1956.0,1299.0],[1793.0,478.0]],[[275.0,862.0],[1512.0,427.0],[393.0,1453.0],[432.0,802.0]],[[455.0,358.0],[14.0,1768.0],[960.0,374.0],[1258.0,1997.0]],[[253.0,1757.0],[1221.0,1605.0],[167.0,118.0],[1133.0,1959.0]],[[1793.0,896.0],[1100.0,1317.0],[1956.0,1808.0],[224.0,1101.0]],[[711.0,1793.0],[1865.0,1211.0],[747.0,1314.0],[1629.0,1694.0]],[[1631.0,1955.0],[903.0,1254.0],[70.0,258.0],[605.0,2021.0]],[[474.0,1472.0],[1061.0,1266.0],[1241.0,567.0],[437.0,565.0]],[[1864.0,155.0],[1825.0,1923.0],[1334.0,1520.0],[512.0,59.0]],[[825.0,1100.0],[265.0,1892.0],[1160.0,49.0],[1089.0,88.0]],[[1644.0,458.0],[400.0,1319.0],[1832.0,374.0],[2041.0,1407.0]],[[178.0,769.0],[694.0,227.0],[476.0,174.0],[480.0,1249.0]],[[821.0,663.0],[615.0,933.0],[890.0,367.0],[1445.0,1783.0]],[[1092.0,551.0],[1171.0,1016.0],[284.0,1084.0],[232.0,89.0]],[[1768.0,1156.0],[1944.0,1728.0],[1787.0,278.0],[758.0,879.0]],[[139.0,1758.0],[1697.0,1453.0],[1453.0,607.0],[732.0,925.0]],[[939.0,243.0],[1497.0,274.0],[1828.0,1318.0],[891.0,897.0]],[[1055.0,634.0],[1562.0,439.0],[1956.0,7.0],[1933.0,1278.0]],[[1075.0,1192.0],[854.0,543.0],[1558.0,143.0],[1566.0,1872.0]],[[99.0,539.0],[948.0,2020.0],[405.0,1212.0],[1786.0,822.0]],[[1367.0,402.0],[1019.0,993.0],[2013.0,474.0],[728.0,2033.0]],[[1468.0,1774.0],[1639.0,1727.0],[97.0,1635.0],[579.0,1743.0]],[[521.0,248.0],[1197.0,1591.0],[1761.0,390.0],[825.0,1111.0]],[[1961.0,1729.0],[1082.0,436.0],[1334.0,626.0],[1063.0,100.0]],[[397.0,1518.0],[1860.0,1085.0],[387.0,1163.0],[569.0,346.0]],[[1664.0,1558.0],[114.0,1961.0],[532.0,1603.0],[2015.0,954.0]],[[115.0,1540.0],[253.0,1681.0],[344.0,1023.0],[162.0,1860.0]],[[343.0,1202.0],[162.0,1423.0],[173.0,279.0],[299.0,185.0]],[[1256.0,1451.0],[1261.0,371.0],[1927.0,1464.0],[1338.0,700.0]],[[1454.0,1023.0],[1340.0,953.0],[1017.0,891.0],[1272.0,1253.0]],[[1322.0,1236.0],[19.0,1970.0],[1035.0,942.0],[604.0,989.0]],[[660.0,346.0],[1063.0,1633.0],[829.0,564.0],[675.0,303.0]],[[1295.0,1581.0],[864.0,648.0],[158.0,1825.0],[884.0,1641.0]],[[461.0,1273.0],[900.0,1186.0],[1826.0,1378.0],[341.0,280.0]],[[288.0,945.0],[490.0,1898.0],[1877.0,40.0],[686.0,1876.0]],[[1772.0,449.0],[787.0,63.0],[996.0,1260.0],[877.0,1204.0]],[[1261.0,1081.0],[1431.0,1088.0],[1177.0,194.0],[119.0,43.0]],[[1807.0,173.0],[844.0,315.0],[1292.0,1852.0],[1246.0,468.0]],[[1010.0,454.0],[790.0,123.0],[797.0,555.0],[105.0,1803.0]],[[117.0,931.0],[1946.0,708.0],[36.0,917.0],[566.0,256.0]],[[65.0,561.0],[1312.0,346.0],[1068.0,798.0],[1631.0,32.0]],[[1145.0,1441.0],[1060.0,1578.0],[1654.0,1906.0],[1142.0,362.0]],[[737.0,1963.0],[1613.0,545.0],[853.0,105.0],[211.0,1299.0]],[[592.0,896.0],[1305.0,1625.0],[167.0,1672.0],[1944.0,265.0]],[[140.0,537.0],[1682.0,1595.0],[1112.0,181.0],[891.0,795.0]],[[1246.0,1557.0],[1227.0,93.0],[1092.0,781.0],[665.0,941.0]],[[360.0,863.0],[1967.0,674.0],[215.0,1648.0],[1158.0,60.0]],[[618.0,405.0],[162.0,1761.0],[1945.0,717.0],[893.0,1919.0]],[[441.0,1658.0],[917.0,259.0],[520.0,1384.0],[1944.0,2021.0]],[[1505.0,1772.0],[1016.0,1814.0],[1064.0,1655.0],[1457.0,1582.0]],[[937.0,1545.0],[435.0,740.0],[1411.0,310.0],[105.0,1718.0]],[[2023.0,249.0],[1885.0,451.0],[959.0,1862.0],[1438.0,374.0]],[[1372.0,151.0],[1133.0,1372.0],[531.0,684.0],[1760.0,1276.0]],[[1813.0,996.0],[2004.0,1571.0],[113.0,1043.0],[493.0,1174.0]],[[1063.0,101.0],[345.0,1329.0],[741.0,896.0],[1202.0,331.0]],[[698.0,1865.0],[1523.0,1633.0],[1854.0,1933.0],[420.0,2001.0]],[[341.0,139.0],[242.0,76.0],[1141.0,149.0],[1100.0,1273.0]],[[722.0,1955.0],[1381.0,69.0],[1862.0,1400.0],[972.0,927.0]],[[1416.0,237.0],[93.0,1804.0],[811.0,1612.0],[627.0,734.0]],[[950.0,333.0],[1617.0,167.0],[713.0,1311.0],[19.0,1860.0]],[[665.0,156.0],[1730.0,909.0],[1053.0,1793.0],[773.0,166.0]],[[1546.0,1681.0],[1632.0,1745.0],[1113.0,1813.0],[1380.0,97.0]],[[320.0,1935.0],[1717.0,663.0],[1763.0,656.0],[704.0,1094.0]],[[1691.0,1971.0],[1169.0,1422.0],[1870.0,1628.0],[1542.0,1172.0]],[[983.0,1469.0],[923.0,1084.0],[82.0,296.0],[1078.0,1596.0]],[[657.0,1081.0],[1033.0,2009.0],[64.0,652.0],[1980.0,452.0]],[[899.0,622.0],[462.0,1574.0],[232.0,706.0],[279.0,388.0]],[[1918.0,1918.0],[102.0,237.0],[1111.0,210.0],[1934.0,851.0]],[[1457.0,1793.0],[452.0,1387.0],[1304.0,1565.0],[1593.0,1188.0]],[[338.0,938.0],[1807.0,1431.0],[1750.0,1766.0],[1785.0,1091.0]],[[764.0,617.0],[216.0,1353.0],[1440.0,1542.0],[275.0,1302.0]],[[725.0,595.0],[469.0,836.0],[1954.0,954.0],[1468.0,661.0]],[[832.0,1224.0],[703.0,566.0],[1638.0,1743.0],[2003.0,1437.0]],[[139.0,308.0],[99.0,1507.0],[1019.0,637.0],[874.0,1622.0]],[[1817.0,1117.0],[1745.0,1384.0],[1974.0,1395.0],[332.0,224.0]],[[570.0,1924.0],[721.0,372.0],[33.0,266.0],[98.0,751.0]],[[1141.0,795.0],[1886.0,1647.0],[1111.0,1081.0],[1574.0,431.0]],[[1619.0,1897.0],[988.0,291.0],[1280.0,550.0],[108.0,1550.0]],[[230.0,1189.0],[1413.0,69.0],[1798.0,1299.0],[41.0,1293.0]],[[1605.0,209.0],[1829.0,398.0],[1734.0,1663.0],[504.0,71.0]],[[47.0,1673.0],[1426.0,719.0],[1656.0,166.0],[586.0,1169.0]],[[1683.0,680.0],[1921.0,1202.0],[1049.0,143.0],[1600.0,1687.0]],[[599.0,1327.0],[697.0,1856.0],[1610.0,514.0],[323.0,1609.0]],[[1066.0,1604.0],[2003.0,137.0],[1190.0,653.0],[1101.0,1590.0]],[[1122.0,511.0],[1046.0,36.0],[489.0,437.0],[1916.0,619.0]],[[1908.0,986.0],[973.0,170.0],[920.0,327.0],[443.0,395.0]],[[153.0,467.0],[179.0,1033.0],[1699.0,600.0],[1420.0,467.0]],[[204.0,1595.0],[915.0,652.0],[2004.0,702.0],[1442.0,1630.0]],[[701.0,1335.0],[288.0,203.0],[62.0,1220.0],[407.0,1845.0]],[[356.0,2.0],[194.0,1151.0],[1249.0,1042.0],[1878.0,1569.0]],[[480.0,915.0],[1253.0,514.0],[98.0,1499.0],[1828.0,386.0]],[[1765.0,636.0],[1125.0,466.0],[1528.0,1033.0],[864.0,1345.0]],[[577.0,913.0],[30.0,942.0],[1976.0,1469.0],[521.0,1672.0]],[[1404.0,1750.0],[1802.0,458.0],[1025.0,217.0],[1209.0,1305.0]],[[815.0,852.0],[939.0,991.0],[1540.0,1421.0],[1050.0,6.0]],[[2015.0,575.0],[1751.0,1981.0],[370.0,1130.0],[409.0,898.0]],[[444.0,1745.0],[1659.0,582.0],[469.0,1800.0],[886.0,660.0]],[[882.0,1116.0],[1497.0,1337.0],[1422.0,1031.0],[611.0,127.0]],[[906.0,1053.0],[1974.0,65.0],[1400.0,68.0],[714.0,822.0]],[[1063.0,942.0],[299.0,1745.0],[1511.0,1516.0],[776.0,432.0]],[[18.0,1606.0],[1388.0,1350.0],[1680.0,1405.0],[1054.0,1648.0]],[[1129.0,1446.0],[307.0,1791.0],[913.0,1933.0],[1417.0,1158.0]],[[119.0,434.0],[220.0,700.0],[922.0,1799.0],[1203.0,1733.0]],[[1632.0,18.0],[278.0,1625.0],[625.0,850.0],[1942.0,1612.0]],[[2022.0,404.0],[1679.0,675.0],[2018.0,880.0],[1264.0,148.0]],[[1223.0,1200.0],[568.0,1028.0],[1244.0,1949.0],[546.0,1787.0]],[[1365.0,1315.0],[863.0,1138.0],[162.0,1272.0],[1206.0,2036.0]],[[1224.0,1082.0],[654.0,1186.0],[1077.0,1368.0],[610.0,1060.0]],[[1586.0,1810.0],[2027.0,690.0],[1571.0,162.0],[379.0,842.0]],[[1298.0,209.0],[1251.0,164.0],[1701.0,445.0],[1326.0,529.0]],[[42.0,1410.0],[988.0,1451.0],[1779.0,986.0],[342.0,133.0]],[[1371.0,77.0],[1816.0,106.0],[690.0,1151.0],[857.0,1756.0]],[[1184.0,675.0],[179.0,159.0],[2036.0,1598.0],[456.0,1556.0]],[[1179.0,1786.0],[204.0,938.0],[1366.0,1717.0],[1994.0,832.0]],[[364.0,1378.0],[1657.0,734.0],[964.0,1987.0],[295.0,1716.0]],[[1618.0,893.0],[1047.0,6.0],[1154.0,133.0],[1065.0,349.0]],[[736.0,1034.0],[1838.0,1780.0],[1251.0,411.0],[1217.0,220.0]],[[1954.0,719.0],[1042.0,855.0],[516.0,172.0],[1635.0,40.0]],[[1216.0,11.0],[1563.0,1392.0],[396.0,1051.0],[663.0,818.0]],[[297.0,716.0],[1544.0,95.0],[902.0,1663.0],[73.0,17.0]],[[1702.0,719.0],[218.0,1621.0],[1697.0,781.0],[651.0,909.0]],[[375.0,1851.0],[1369.0,1024.0],[785.0,1047.0],[1591.0,1013.0]],[[1197.0,1055.0],[602.0,1089.0],[1481.0,1128.0],[896.0,775.0]],[[89.0,431.0],[895.0,1129.0],[691.0,1319.0],[899.0,666.0]],[[134.0,897.0],[1588.0,1072.0],[1054.0,869.0],[1070.0,1538.0]],[[171.0,135.0],[630.0,2035.0],[1788.0,1234.0],[1501.0,1661.0]],[[1458.0,810.0],[1173.0,1147.0],[1082.0,1979.0],[625.0,1460.0]],[[579.0,1596.0],[250.0,295.0],[1065.0,308.0],[2021.0,852.0]],[[1858.0,1259.0],[164.0,1101.0],[1380.0,17.0],[2029.0,1767.0]],[[1766.0,1735.0],[1501.0,1990.0],[792.0,1771.0],[1603.0,1184.0]],[[393.0,331.0],[659.0,1381.0],[1496.0,1758.0],[1564.0,504.0]],[[1541.0,212.0],[1776.0,823.0],[461.0,938.0],[1956.0,1571.0]],[[708.0,545.0],[901.0,392.0],[1420.0,1323.0],[1800.0,682.0]],[[1549.0,1983.0],[754.0,153.0],[812.0,1023.0],[537.0,489.0]],[[1121.0,53.0],[20.0,1523.0],[1169.0,879.0],[222.0,1277.0]],[[606.0,521.0],[275.0,640.0],[1712.0,1099.0],[535.0,298.0]],[[791.0,658.0],[1707.0,887.0],[1608.0,2007.0],[709.0,263.0]],[[2041.0,994.0],[835.0,298.0],[621.0,1001.0],[775.0,603.0]],[[1063.0,267.0],[1524.0,323.0],[1456.0,1141.0],[734.0,1923.0]],[[1775.0,904.0],[516.0,481.0],[1783.0,1755.0],[1506.0,917.0]],[[1817.0,1604.0],[1362.0,751.0],[214.0,199.0],[1504.0,1897.0]],[[661.0,1911.0],[1528.0,1442.0],[639.0,1873.0],[823.0,1954.0]],[[1155.0,852.0],[520.0,902.0],[1157.0,433.0],[360.0,929.0]],[[1738.0,877.0],[1231.0,2016.0],[252.0,1538.0],[835.0,218.0]],[[1265.0,1226.0],[863.0,1755.0],[51.0,1870.0],[1333.0,1719.0]],[[999.0,431.0],[690.0,208.0],[1565.0,667.0],[70.0,2031.0]],[[1987.0,1510.0],[1747.0,117.0],[1785.0,1594.0],[972.0,33.0]],[[176.0,818.0],[1292.0,146.0],[552.0,1796.0],[731.0,1851.0]],[[599.0,588.0],[1030.0,1564.0],[380.0,505.0],[290.0,1570.0]],[[1982.0,1501.0],[862.0,154.0],[1676.0,903.0],[1970.0,809.0]],[[691.0,969.0],[807.0,1386.0],[1223.0,1955.0],[963.0,872.0]],[[1153.0,504.0],[41.0,150.0],[1291.0,364.0],[731.0,1868.0]],[[329.0,1678.0],[574.0,177.0],[574.0,1309.0],[1469.0,1851.0]],[[780.0,1639.0],[1898.0,351.0],[1615.0,1498.0],[40.0,1167.0]],[[866.0,1458.0],[1533.0,30.0],[388.0,1824.0],[1660.0,1256.0]],[[709.0,1225.0],[898.0,1351.0],[1361.0,867.0],[167.0,208.0]],[[76.0,745.0],[1894.0,1328.0],[150.0,1089.0],[1112.0,269.0]],[[1023.0,48.0],[532.0,1692.0],[1423.0,1105.0],[2041.0,159.0]],[[1580.0,453.0],[1250.0,1717.0],[1016.0,939.0],[1785.0,1081.0]],[[42.0,23.0],[620.0,1945.0],[610.0,1433.0],[317.0,1013.0]],[[632.0,543.0],[1642.0,568.0],[1352.0,795.0],[530.0,560.0]],[[477.0,521.0],[136.0,1139.0],[1118.0,1443.0],[30.0,595.0]],[[35.0,261.0],[1868.0,1772.0],[1628.0,1267.0],[547.0,1604.0]],[[1745.0,1474.0],[1834.0,1432.0],[1197.0,1946.0],[760.0,1101.0]],[[32.0,1275.0],[941.0,217.0],[2045.0,244.0],[30.0,324.0]],[[1897.0,10.0],[1005.0,542.0],[1605.0,1591.0],[911.0,1133.0]],[[750.0,893.0],[661.0,371.0],[1353.0,1420.0],[352.0,499.0]],[[945.0,838.0],[1345.0,1834.0],[339.0,1766.0],[1459.0,676.0]],[[741.0,416.0],[1451.0,756.0],[2020.0,324.0],[1868.0,1766.0]],[[895.0,259.0],[277.0,1030.0],[1344.0,1605.0],[1508.0,1336.0]],[[1762.0,289.0],[779.0,1686.0],[1423.0,2036.0],[1419.0,488.0]],[[1832.0,1348.0],[35.0,922.0],[1245.0,1682.0],[573.0,784.0]],[[1139.0,189.0],[664.0,1269.0],[233.0,455.0],[1088.0,457.0]],[[736.0,1852.0],[1015.0,1919.0],[1765.0,223.0],[577.0,2018.0]],[[1473.0,1195.0],[1537.0,364.0],[1790.0,531.0],[955.0,1753.0]],[[1993.0,256.0],[1481.0,758.0],[253.0,835.0],[776.0,76.0]],[[1421.0,973.0],[978.0,1682.0],[1699.0,676.0],[961.0,3.0]],[[899.0,232.0],[636.0,369.0],[101.0,582.0],[1091.0,940.0]],[[1477.0,1362.0],[564.0,384.0],[1047.0,1690.0],[1456.0,173.0]],[[254.0,1877.0],[154.0,1288.0],[1275.0,1229.0],[1596.0,1256.0]],[[1576.0,1976.0],[1206.0,491.0],[46.0,433.0],[1736.0,301.0]],[[854.0,504.0],[38.0,1006.0],[1921.0,290.0],[871.0,1396.0]],[[879.0,1212.0],[1170.0,1905.0],[1905.0,861.0],[1895.0,1542.0]],[[342.0,126.0],[302.0,1232.0],[1840.0,839.0],[1209.0,1697.0]],[[757.0,1616.0],[1536.0,1896.0],[906.0,1010.0],[2022.0,57.0]],[[1198.0,1099.0],[1942.0,2021.0],[1441.0,448.0],[491.0,876.0]],[[1828.0,1589.0],[880.0,1724.0],[252.0,720.0],[1583.0,1741.0]],[[1530.0,580.0],[278.0,654.0],[184.0,868.0],[1905.0,1269.0]],[[1232.0,1947.0],[551.0,86.0],[1848.0,1776.0],[1413.0,1784.0]],[[1488.0,892.0],[1106.0,832.0],[1888.0,1940.0],[1111.0,1701.0]],[[1165.0,1134.0],[1817.0,269.0],[455.0,1332.0],[1833.0,1176.0]],[[929.0,1311.0],[1016.0,635.0],[643.0,1041.0],[1016.0,1686.0]],[[123.0,1758.0],[1630.0,923.0],[557.0,294.0],[347.0,676.0]],[[1919.0,1554.0],[900.0,1196.0],[1602.0,1099.0],[32.0,1162.0]],[[613.0,492.0],[1765.0,1153.0],[1232.0,1714.0],[250.0,609.0]],[[398.0,678.0],[1996.0,1773.0],[454.0,235.0],[1469.0,1303.0]],[[1258.0,147.0],[1254.0,1848.0],[128.0,1409.0],[1165.0,870.0]],[[1079.0,1121.0],[691.0,1175.0],[1385.0,367.0],[145.0,527.0]],[[538.0,1571.0],[1323.0,1388.0],[1929.0,685.0],[1199.0,111.0]],[[1086.0,72.0],[94.0,1721.0],[1900.0,99.0],[1593.0,435.0]],[[509.0,67.0],[1571.0,366.0],[2045.0,879.0],[1449.0,133.0]],[[1678.0,1933.0],[1307.0,775.0],[34.0,520.0],[1932.0,1928.0]],[[1026.0,1770.0],[412.0,1704.0],[1853.0,1208.0],[381.0,213.0]],[[1735.0,630.0],[1437.0,811.0],[339.0,1869.0],[1481.0,453.0]],[[1356.0,412.0],[833.0,1301.0],[649.0,657.0],[1353.0,334.0]],[[854.0,1201.0],[269.0,2009.0],[1889.0,2048.0],[1820.0,1581.0]],[[1491.0,1945.0],[714.0,583.0],[1.0,717.0],[1263.0,725.0]],[[614.0,849.0],[550.0,998.0],[1873.0,565.0],[347.0,1984.0]],[[1595.0,1615.0],[1737.0,1786.0],[1927.0,1147.0],[1955.0,513.0]],[[810.0,1548.0],[144.0,1123.0],[575.0,1802.0],[891.0,635.0]],[[1598.0,1811.0],[253.0,1423.0],[925.0,639.0],[1170.0,2005.0]],[[1354.0,627.0],[264.0,1611.0],[299.0,303.0],[0.0,123.0]],[[307.0,357.0],[536.0,1030.0],[251.0,853.0],[1764.0,1383.0]],[[1140.0,1455.0],[820.0,649.0],[1678.0,328.0],[1451.0,472.0]],[[1731.0,1846.0],[1366.0,416.0],[42.0,229.0],[607.0,1670.0]],[[864.0,826.0],[279.0,656.0],[1865.0,97.0],[1339.0,1206.0]],[[1271.0,628.0],[1852.0,214.0],[179.0,1180.0],[652.0,99.0]],[[1293.0,97.0],[605.0,1032.0],[436.0,956.0],[1050.0,2026.0]],[[2044.0,785.0],[316.0,536.0],[1169.0,123.0],[966.0,645.0]],[[716.0,997.0],[1884.0,440.0],[14.0,801.0],[1497.0,698.0]],[[1106.0,388.0],[330.0,1230.0],[950.0,1543.0],[1219.0,555.0]],[[1234.0,570.0],[1219.0,493.0],[1226.0,411.0],[854.0,1802.0]],[[1636.0,400.0],[124.0,1619.0],[1952.0,31.0],[1203.0,1922.0]],[[1920.0,1511.0],[737.0,857.0],[1954.0,817.0],[927.0,564.0]],[[1772.0,813.0],[1493.0,2012.0],[933.0,199.0],[1006.0,470.0]],[[1487.0,273.0],[174.0,870.0],[1769.0,1356.0],[1667.0,1840.0]],[[1809.0,1885.0],[1839.0,1520.0],[160.0,833.0],[1063.0,533.0]],[[443.0,1700.0],[819.0,1341.0],[422.0,21.0],[914.0,812.0]],[[1573.0,798.0],[1241.0,1265.0],[1533.0,953.0],[115.0,1022.0]],[[1136.0,1249.0],[742.0,468.0],[60.0,1425.0],[632.0,1595.0]],[[2011.0,1864.0],[460.0,925.0],[1438.0,252.0],[336.0,988.0]],[[703.0,808.0],[1751.0,575.0],[1550.0,1642.0],[1442.0,336.0]],[[152.0,1865.0],[1448.0,1179.0],[1524.0,1347.0],[1532.0,45.0]],[[468.0,1584.0],[1235.0,1119.0],[239.0,1965.0],[1132.0,213.0]],[[2013.0,1301.0],[1754.0,1866.0],[901.0,698.0],[168.0,1642.0]],[[1050.0,785.0],[1289.0,824.0],[457.0,685.0],[1722.0,1727.0]],[[1035.0,528.0],[380.0,1056.0],[981.0,1144.0],[753.0,1856.0]],[[1743.0,69.0],[592.0,1250.0],[567.0,581.0],[1685.0,150.0]],[[1913.0,2029.0],[183.0,1584.0],[434.0,1204.0],[1693.0,1897.0]],[[440.0,1750.0],[1681.0,109.0],[1075.0,253.0],[1193.0,1062.0]],[[1332.0,117.0],[573.0,225.0],[894.0,1294.0],[397.0,671.0]],[[1236.0,1691.0],[587.0,324.0],[2030.0,258.0],[1744.0,1497.0]],[[612.0,1886.0],[1663.0,1342.0],[1155.0,2042.0],[318.0,553.0]],[[73.0,360.0],[840.0,1489.0],[1965.0,607.0],[715.0,543.0]],[[1739.0,186.0],[305.0,1458.0],[1277.0,1028.0],[1476.0,1515.0]],[[1270.0,1665.0],[1632.0,1949.0],[1795.0,1508.0],[1335.0,1690.0]],[[620.0,543.0],[1957.0,1025.0],[1035.0,1749.0],[1931.0,198.0]],[[1204.0,2020.0],[1411.0,2027.0],[538.0,1830.0],[593.0,1942.0]],[[619.0,953.0],[1364.0,287.0],[1495.0,696.0],[1720.0,1678.0]],[[1246.0,1105.0],[904.0,41.0],[1965.0,1464.0],[331.0,1076.0]],[[1990.0,1609.0],[1881.0,186.0],[1749.0,1036.0],[1996.0,612.0]],[[1318.0,607.0],[843.0,1561.0],[498.0,454.0],[1316.0,588.0]],[[1921.0,1839.0],[632.0,2023.0],[638.0,249.0],[775.0,1632.0]],[[1369.0,1059.0],[1999.0,1185.0],[151.0,1785.0],[329.0,878.0]],[[109.0,1310.0],[1657.0,1194.0],[1008.0,1229.0],[1768.0,1433.0]],[[1809.0,1328.0],[1137.0,493.0],[1522.0,139.0],[1182.0,832.0]],[[445.0,1058.0],[69.0,1056.0],[1368.0,452.0],[1632.0,1887.0]],[[1207.0,1145.0],[1526.0,591.0],[2016.0,236.0],[427.0,1551.0]],[[1820.0,1282.0],[28.0,1911.0],[963.0,558.0],[1284.0,614.0]],[[835.0,1987.0],[703.0,1152.0],[1411.0,898.0],[849.0,250.0]],[[1861.0,1129.0],[1898.0,1888.0],[1924.0,1495.0],[1749.0,1732.0]],[[279.0,921.0],[1411.0,227.0],[1331.0,425.0],[1571.0,2047.0]],[[1355.0,583.0],[831.0,280.0],[1098.0,2035.0],[1992.0,1919.0]],[[1305.0,1906.0],[328.0,2017.0],[1147.0,464.0],[539.0,1706.0]],[[381.0,592.0],[1565.0,394.0],[1735.0,893.0],[600.0,15.0]],[[460.0,415.0],[335.0,1353.0],[509.0,1533.0],[1707.0,1616.0]],[[305.0,1695.0],[1847.0,1229.0],[1785.0,1804.0],[1629.0,546.0]],[[1872.0,1958.0],[1585.0,434.0],[1360.0,1136.0],[578.0,1084.0]],[[178.0,387.0],[681.0,145.0],[18.0,1339.0],[1384.0,168.0]],[[662.0,1727.0],[1798.0,70.0],[1766.0,1645.0],[500.0,1618.0]],[[232.0,112.0],[558.0,983.0],[1990.0,1599.0],[1285.0,525.0]],[[1181.0,570.0],[573.0,578.0],[597.0,970.0],[812.0,125.0]],[[870.0,2005.0],[1875.0,1490.0],[1923.0,1753.0],[2.0,1720.0]],[[977.0,1552.0],[1092.0,109.0],[1326.0,445.0],[1005.0,1755.0]],[[1052.0,529.0],[1916.0,1504.0],[1031.0,496.0],[373.0,1433.0]],[[1658.0,1810.0],[1493.0,1727.0],[1798.0,2046.0],[1542.0,115.0]],[[196.0,1998.0],[1321.0,946.0],[422.0,80.0],[1433.0,315.0]],[[735.0,1946.0],[502.0,946.0],[592.0,1882.0],[309.0,686.0]],[[1143.0,1939.0],[1494.0,765.0],[954.0,178.0],[1197.0,1816.0]],[[1856.0,1154.0],[1006.0,45.0],[1245.0,1925.0],[609.0,812.0]],[[806.0,743.0],[1254.0,295.0],[1051.0,690.0],[1604.0,1238.0]],[[740.0,1861.0],[1630.0,383.0],[1463.0,1461.0],[944.0,11.0]],[[673.0,1862.0],[515.0,1156.0],[1583.0,608.0],[1379.0,1404.0]],[[1026.0,13.0],[587.0,770.0],[1053.0,872.0],[25.0,237.0]],[[148.0,1881.0],[1165.0,911.0],[449.0,319.0],[612.0,831.0]],[[729.0,127.0],[1784.0,642.0],[703.0,1802.0],[1313.0,212.0]],[[1829.0,1464.0],[1175.0,522.0],[215.0,996.0],[465.0,1852.0]],[[2013.0,941.0],[1296.0,457.0],[1253.0,618.0],[1091.0,1719.0]],[[1085.0,470.0],[74.0,17.0],[1518.0,753.0],[232.0,1340.0]],[[651.0,173.0],[141.0,120.0],[1070.0,1014.0],[125.0,1193.0]],[[1975.0,2045.0],[1383.0,366.0],[817.0,717.0],[1438.0,365.0]],[[1515.0,591.0],[1474.0,794.0],[1891.0,1551.0],[1835.0,1420.0]],[[320.0,1010.0],[955.0,389.0],[966.0,361.0],[1278.0,1514.0]],[[332.0,499.0],[1476.0,1021.0],[1161.0,1268.0],[510.0,685.0]],[[1665.0,867.0],[1900.0,559.0],[806.0,325.0],[1777.0,297.0]],[[1561.0,630.0],[999.0,1234.0],[1049.0,1961.0],[1582.0,480.0]],[[444.0,1472.0],[1860.0,1974.0],[1616.0,869.0],[1365.0,615.0]],[[1099.0,1331.0],[802.0,184.0],[1648.0,1088.0],[1318.0,440.0]],[[543.0,835.0],[1529.0,1926.0],[1329.0,176.0],[225.0,1877.0]],[[549.0,1860.0],[2043.0,873.0],[1139.0,1054.0],[639.0,1163.0]],[[486.0,1301.0],[915.0,1399.0],[1280.0,804.0],[497.0,1702.0]],[[864.0,1722.0],[544.0,363.0],[1591.0,119.0],[1580.0,570.0]],[[1613.0,1416.0],[1624.0,599.0],[381.0,945.0],[817.0,1992.0]],[[1937.0,1551.0],[1301.0,1034.0],[36.0,1067.0],[99.0,1561.0]],[[1610.0,592.0],[698.0,1063.0],[406.0,1666.0],[467.0,1956.0]],[[1654.0,219.0],[1998.0,334.0],[325.0,911.0],[1381.0,1580.0]],[[1406.0,1456.0],[1027.0,989.0],[1678.0,562.0],[906.0,1642.0]],[[190.0,1008.0],[769.0,604.0],[1388.0,767.0],[445.0,853.0]],[[1722.0,215.0],[1361.0,1480.0],[1430.0,1788.0],[933.0,1435.0]],[[1559.0,292.0],[921.0,1916.0],[525.0,1461.0],[1510.0,378.0]],[[1896.0,1712.0],[1546.0,1076.0],[300.0,1875.0],[1912.0,610.0]],[[1079.0,58.0],[1484.0,1735.0],[968.0,1438.0],[1907.0,1357.0]],[[1805.0,47.0],[592.0,984.0],[405.0,922.0],[1116.0,1495.0]],[[917.0,755.0],[1821.0,1465.0],[1506.0,159.0],[747.0,1592.0]],[[1447.0,1423.0],[1674.0,1237.0],[1066.0,1877.0],[336.0,218.0]],[[424.0,1103.0],[1268.0,870.0],[1749.0,819.0],[1006.0,1142.0]],[[376.0,1708.0],[1606.0,1178.0],[1197.0,1989.0],[378.0,1691.0]],[[919.0,818.0],[1972.0,497.0],[1470.0,341.0],[128.0,334.0]],[[1323.0,1000.0],[1812.0,371.0],[789.0,135.0],[1807.0,543.0]],[[350.0,1928.0],[264.0,204.0],[590.0,2024.0],[513.0,2024.0]],[[1786.0,1963.0],[1205.0,46.0],[1024.0,487.0],[373.0,1496.0]],[[137.0,955.0],[620.0,89.0],[1560.0,171.0],[542.0,1365.0]],[[1569.0,297.0],[1844.0,1261.0],[1342.0,847.0],[1748.0,1982.0]],[[262.0,1559.0],[1032.0,18.0],[431.0,1237.0],[901.0,1548.0]],[[53.0,1603.0],[309.0,368.0],[997.0,256.0],[1716.0,1830.0]],[[1274.0,133.0],[588.0,2023.0],[1987.0,1505.0],[614.0,477.0]],[[845.0,1096.0],[57.0,2035.0],[1854.0,589.0],[604.0,1295.0]],[[1639.0,1808.0],[1116.0,1878.0],[1516.0,1625.0],[343.0,89.0]],[[883.0,912.0],[1009.0,1423.0],[1774.0,393.0],[339.0,119.0]],[[956.0,1357.0],[983.0,822.0],[670.0,344.0],[661.0,251.0]],[[1230.0,1026.0],[1367.0,1425.0],[1735.0,1522.0],[843.0,6.0]],[[2044.0,536.0],[731.0,1561.0],[8.0,746.0],[68.0,1085.0]],[[1498.0,894.0],[1666.0,775.0],[997.0,1712.0],[831.0,1701.0]],[[1554.0,176.0],[700.0,5.0],[851.0,410.0],[235.0,1192.0]],[[603.0,1609.0],[1475.0,753.0],[690.0,669.0],[602.0,2047.0]],[[362.0,1832.0],[748.0,1179.0],[1094.0,1432.0],[1812.0,992.0]],[[178.0,937.0],[891.0,1101.0],[980.0,1762.0],[1146.0,963.0]],[[1473.0,2007.0],[1683.0,827.0],[1878.0,1292.0],[190.0,952.0]],[[911.0,635.0],[1734.0,504.0],[300.0,1706.0],[1744.0,1560.0]],[[1876.0,387.0],[829.0,241.0],[1600.0,854.0],[864.0,1770.0]],[[1931.0,1532.0],[430.0,1541.0],[1204.0,1505.0],[1281.0,720.0]],[[1727.0,1009.0],[779.0,307.0],[929.0,350.0],[1042.0,1720.0]],[[913.0,2016.0],[1048.0,137.0],[1613.0,1411.0],[1644.0,1901.0]],[[1707.0,836.0],[223.0,1169.0],[951.0,618.0],[585.0,1379.0]],[[1215.0,1848.0],[1743.0,1234.0],[1532.0,1282.0],[1062.0,931.0]],[[1857.0,1615.0],[1350.0,156.0],[862.0,390.0],[558.0,1012.0]],[[1440.0,589.0],[1830.0,1634.0],[1026.0,51.0],[1706.0,1293.0]],[[586.0,1304.0],[283.0,847.0],[1582.0,861.0],[1638.0,369.0]],[[1917.0,865.0],[461.0,567.0],[1556.0,457.0],[1581.0,1003.0]],[[1912.0,1978.0],[335.0,662.0],[23.0,1445.0],[230.0,1882.0]],[[559.0,2047.0],[1571.0,826.0],[773.0,251.0],[1393.0,805.0]],[[1960.0,1148.0],[731.0,2037.0],[1709.0,196.0],[635.0,787.0]],[[41.0,1259.0],[1715.0,292.0],[755.0,98.0],[1542.0,1185.0]],[[607.0,1168.0],[564.0,699.0],[1222.0,957.0],[1263.0,1837.0]],[[866.0,796.0],[213.0,843.0],[1541.0,1342.0],[1459.0,1569.0]],[[180.0,1619.0],[361.0,644.0],[2021.0,630.0],[1443.0,1266.0]],[[740.0,164.0],[1974.0,1888.0],[1459.0,1412.0],[1422.0,891.0]],[[547.0,988.0],[1697.0,258.0],[982.0,1053.0],[1226.0,1899.0]],[[1369.0,1813.0],[66.0,2014.0],[1998.0,543.0],[108.0,1741.0]],[[1586.0,766.0],[1170.0,1547.0],[780.0,1635.0],[105.0,996.0]],[[1983.0,1392.0],[1484.0,1823.0],[1267.0,322.0],[237.0,1291.0]],[[644.0,557.0],[216.0,1481.0],[1935.0,990.0],[174.0,142.0]],[[59.0,1955.0],[1878.0,1912.0],[1758.0,994.0],[1662.0,720.0]],[[424.0,994.0],[1166.0,1246.0],[942.0,102.0],[432.0,841.0]],[[1759.0,1169.0],[2030.0,1148.0],[1255.0,449.0],[1910.0,568.0]],[[1151.0,1281.0],[1275.0,1391.0],[145.0,1216.0],[805.0,1045.0]],[[1902.0,912.0],[13.0,1282.0],[1870.0,454.0],[264.0,1746.0]],[[1446.0,1033.0],[172.0,573.0],[1250.0,1534.0],[1406.0,1635.0]],[[733.0,214.0],[1917.0,1336.0],[669.0,939.0],[1966.0,667.0]],[[1747.0,1630.0],[448.0,1279.0],[374.0,1880.0],[665.0,1782.0]],[[2009.0,574.0],[258.0,127.0],[1963.0,542.0],[978.0,182.0]],[[1235.0,1695.0],[1897.0,1053.0],[1428.0,1364.0],[1496.0,138.0]],[[737.0,1749.0],[911.0,2034.0],[1521.0,1036.0],[995.0,270.0]],[[1063.0,1198.0],[138.0,1202.0],[1958.0,1973.0],[670.0,378.0]],[[815.0,205.0],[1390.0,1171.0],[270.0,925.0],[694.0,595.0]],[[1774.0,1285.0],[818.0,1904.0],[758.0,3.0],[828.0,207.0]],[[71.0,313.0],[1584.0,1480.0],[1790.0,768.0],[660.0,20.0]],[[1744.0,129.0],[1007.0,254.0],[1609.0,461.0],[1513.0,1533.0]],[[547.0,1559.0],[384.0,1303.0],[2006.0,15.0],[1915.0,962.0]],[[1653.0,327.0],[1773.0,1540.0],[78.0,1409.0],[1201.0,949.0]],[[1631.0,2033.0],[1655.0,1512.0],[204.0,252.0],[1669.0,1754.0]],[[643.0,594.0],[1354.0,780.0],[577.0,2014.0],[1692.0,1284.0]],[[24.0,1497.0],[1526.0,1484.0],[328.0,1571.0],[539.0,148.0]],[[579.0,202.0],[1264.0,1083.0],[1363.0,166.0],[324.0,522.0]],[[562.0,982.0],[1300.0,863.0],[644.0,1818.0],[815.0,1673.0]],[[1525.0,539.0],[1274.0,751.0],[1884.0,101.0],[730.0,499.0]],[[129.0,577.0],[1696.0,1195.0],[870.0,233.0],[290.0,1737.0]],[[1099.0,1925.0],[1806.0,1906.0],[333.0,1405.0],[435.0,1228.0]],[[938.0,182.0],[973.0,475.0],[842.0,733.0],[1609.0,1659.0]],[[527.0,946.0],[846.0,1998.0],[628.0,477.0],[1569.0,1822.0]],[[1470.0,85.0],[1971.0,1998.0],[240.0,1020.0],[1162.0,710.0]],[[1920.0,279.0],[1302.0,354.0],[1069.0,1236.0],[531.0,243.0]],[[1291.0,324.0],[603.0,2048.0],[533.0,707.0],[100.0,2.0]],[[251.0,283.0],[1478.0,1954.0],[196.0,317.0],[1882.0,1861.0]],[[72.0,1128.0],[1457.0,1837.0],[329.0,1421.0],[263.0,716.0]],[[725.0,17.0],[1616.0,793.0],[917.0,633.0],[1992.0,502.0]],[[1424.0,2010.0],[421.0,724.0],[2048.0,1265.0],[398.0,1228.0]],[[361.0,477.0],[1319.0,1807.0],[1181.0,1801.0],[1182.0,1798.0]],[[1890.0,612.0],[1244.0,1104.0],[317.0,1171.0],[138.0,1623.0]],[[532.0,2005.0],[1324.0,1074.0],[497.0,1185.0],[1784.0,908.0]],[[999.0,1599.0],[298.0,1647.0],[517.0,1039.0],[172.0,1038.0]],[[963.0,1388.0],[330.0,1058.0],[1562.0,427.0],[538.0,990.0]],[[1991.0,1327.0],[1893.0,1666.0],[332.0,1918.0],[147.0,1779.0]],[[813.0,1614.0],[1066.0,524.0],[545.0,1546.0],[226.0,73.0]],[[219.0,1805.0],[15.0,1417.0],[1824.0,1337.0],[760.0,856.0]],[[1805.0,877.0],[819.0,1594.0],[1359.0,1813.0],[1142.0,1123.0]],[[661.0,1229.0],[357.0,288.0],[1964.0,998.0],[391.0,2046.0]],[[491.0,1842.0],[683.0,462.0],[1330.0,1545.0],[918.0,392.0]],[[1846.0,936.0],[458.0,357.0],[1950.0,1031.0],[1238.0,81.0]],[[1366.0,1574.0],[999.0,1766.0],[625.0,1838.0],[278.0,1948.0]],[[1050.0,1677.0],[278.0,500.0],[1573.0,485.0],[1095.0,1549.0]],[[131.0,376.0],[1409.0,305.0],[1605.0,1924.0],[1963.0,643.0]],[[1638.0,173.0],[1070.0,1631.0],[1605.0,1578.0],[1853.0,63.0]],[[994.0,535.0],[300.0,2043.0],[1655.0,858.0],[439.0,1891.0]],[[804.0,1655.0],[1663.0,1780.0],[105.0,1149.0],[89.0,178.0]],[[1992.0,1713.0],[535.0,721.0],[774.0,1541.0],[1162.0,324.0]],[[173.0,1692.0],[598.0,1912.0],[651.0,1216.0],[162.0,1231.0]],[[1198.0,128.0],[1341.0,1203.0],[130.0,1997.0],[355.0,669.0]],[[1569.0,2019.0],[1080.0,1839.0],[92.0,1720.0],[143.0,1208.0]],[[201.0,1421.0],[817.0,1402.0],[740.0,1002.0],[566.0,372.0]],[[166.0,386.0],[632.0,1258.0],[1294.0,2019.0],[1941.0,87.0]],[[1577.0,1553.0],[960.0,582.0],[428.0,1708.0],[1580.0,916.0]],[[34.0,1295.0],[1679.0,1591.0],[894.0,543.0],[1790.0,723.0]],[[448.0,461.0],[1220.0,719.0],[1440.0,1935.0],[2038.0,657.0]],[[1094.0,982.0],[1081.0,288.0],[1332.0,1002.0],[899.0,1076.0]],[[661.0,536.0],[232.0,1895.0],[1460.0,528.0],[1017.0,1247.0]],[[169.0,1738.0],[1829.0,2018.0],[217.0,710.0],[1549.0,2041.0]],[[1280.0,1256.0],[1139.0,648.0],[1465.0,1509.0],[379.0,1758.0]],[[992.0,1488.0],[386.0,1443.0],[994.0,270.0],[1501.0,1272.0]],[[176.0,1450.0],[51.0,1577.0],[393.0,9.0],[9.0,733.0]],[[1892.0,446.0],[886.0,1528.0],[272.0,565.0],[1641.0,754.0]],[[1793.0,388.0],[1115.0,1962.0],[746.0,1533.0],[618.0,495.0]],[[546.0,318.0],[1622.0,660.0],[747.0,919.0],[1176.0,438.0]],[[201.0,891.0],[25.0,881.0],[51.0,1811.0],[785.0,1540.0]],[[1216.0,611.0],[1389.0,1523.0],[1551.0,443.0],[795.0,1389.0]],[[33.0,1730.0],[1828.0,791.0],[677.0,90.0],[1078.0,1918.0]],[[953.0,337.0],[266.0,880.0],[574.0,1018.0],[981.0,1478.0]],[[76.0,810.0],[1842.0,131.0],[404.0,713.0],[1683.0,526.0]],[[1989.0,940.0],[982.0,230.0],[1521.0,692.0],[668.0,1767.0]],[[1007.0,1101.0],[1418.0,831.0],[1209.0,969.0],[419.0,168.0]],[[1350.0,1252.0],[731.0,515.0],[1692.0,1301.0],[1164.0,95.0]],[[1014.0,117.0],[1302.0,1954.0],[207.0,838.0],[1635.0,405.0]],[[1802.0,1658.0],[2023.0,829.0],[1833.0,438.0],[788.0,718.0]],[[1055.0,1075.0],[30.0,275.0],[1582.0,75.0],[783.0,1363.0]],[[1122.0,840.0],[1957.0,606.0],[1852.0,169.0],[1557.0,1617.0]],[[1646.0,226.0],[247.0,1184.0],[1448.0,959.0],[804.0,1882.0]],[[1925.0,1922.0],[154.0,782.0],[482.0,1316.0],[1226.0,1753.0]],[[1019.0,643.0],[1135.0,1091.0],[1534.0,438.0],[1472.0,1740.0]],[[1736.0,1848.0],[1962.0,915.0],[402.0,724.0],[1848.0,1913.0]],[[233.0,419.0],[345.0,1642.0],[1633.0,1089.0],[783.0,2015.0]],[[1464.0,1539.0],[462.0,50.0],[972.0,2001.0],[1578.0,1077.0]],[[1092.0,349.0],[664.0,1613.0],[401.0,210.0],[868.0,1412.0]],[[737.0,712.0],[1190.0,472.0],[959.0,60.0],[1713.0,1180.0]],[[642.0,597.0],[546.0,1342.0],[779.0,503.0],[1369.0,2021.0]],[[827.0,1279.0],[160.0,781.0],[722.0,1352.0],[780.0,787.0]],[[1735.0,453.0],[1336.0,1103.0],[731.0,10.0],[1242.0,1567.0]],[[1580.0,1655.0],[260.0,1298.0],[1376.0,552.0],[1061.0,1096.0]],[[1807.0,654.0],[1888.0,784.0],[1571.0,1922.0],[382.0,1811.0]],[[498.0,765.0],[1052.0,1267.0],[1680.0,1205.0],[1223.0,816.0]],[[1462.0,533.0],[880.0,426.0],[1765.0,887.0],[1073.0,969.0]],[[540.0,278.0],[1381.0,213.0],[1826.0,1026.0],[239.0,235.0]],[[1006.0,150.0],[1450.0,380.0],[692.0,739.0],[784.0,768.0]],[[177.0,47.0],[1673.0,2023.0],[245.0,982.0],[984.0,442.0]],[[1331.0,1746.0],[723.0,131.0],[1582.0,1248.0],[1449.0,126.0]],[[1882.0,1398.0],[1581.0,1793.0],[2043.0,1939.0],[1455.0,670.0]],[[1790.0,249.0],[1346.0,1061.0],[196.0,127.0],[1573.0,630.0]],[[1945.0,804.0],[1597.0,1914.0],[731.0,927.0],[244.0,673.0]],[[1684.0,1856.0],[4.0,811.0],[190.0,1624.0],[301.0,1647.0]],[[23.0,579.0],[921.0,1636.0],[1389.0,852.0],[419.0,1593.0]],[[1297.0,134.0],[313.0,827.0],[1434.0,1441.0],[174.0,1750.0]],[[1252.0,1783.0],[1201.0,658.0],[691.0,549.0],[51.0,2.0]],[[1784.0,881.0],[909.0,1798.0],[393.0,30.0],[1787.0,652.0]],[[1179.0,1892.0],[761.0,1600.0],[786.0,1188.0],[1297.0,470.0]],[[484.0,485.0],[1981.0,210.0],[1573.0,1456.0],[226.0,432.0]],[[1900.0,1094.0],[238.0,1342.0],[1792.0,512.0],[1272.0,1876.0]],[[195.0,519.0],[1180.0,464.0],[500.0,333.0],[1754.0,1763.0]],[[351.0,1732.0],[1457.0,689.0],[2017.0,2045.0],[1698.0,1202.0]],[[1892.0,1913.0],[991.0,855.0],[788.0,540.0],[254.0,1134.0]],[[56.0,1809.0],[1837.0,718.0],[853.0,1184.0],[1239.0,595.0]],[[348.0,755.0],[369.0,682.0],[698.0,1668.0],[689.0,873.0]],[[1727.0,1035.0],[1389.0,628.0],[950.0,1698.0],[1344.0,46.0]],[[945.0,699.0],[1197.0,232.0],[458.0,924.0],[124.0,1746.0]],[[18.0,138.0],[713.0,77.0],[1800.0,1734.0],[600.0,1491.0]],[[1642.0,923.0],[2028.0,1144.0],[499.0,1180.0],[96.0,1201.0]],[[253.0,1172.0],[225.0,377.0],[1516.0,1458.0],[469.0,1844.0]],[[1124.0,140.0],[227.0,1118.0],[1387.0,1428.0],[1004.0,1149.0]],[[2007.0,1082.0],[276.0,939.0],[1672.0,1559.0],[1924.0,664.0]],[[1691.0,1877.0],[1779.0,907.0],[1870.0,335.0],[1616.0,1556.0]],[[772.0,822.0],[545.0,153.0],[585.0,1871.0],[1438.0,1443.0]],[[384.0,1038.0],[987.0,1309.0],[1268.0,72.0],[1045.0,1780.0]],[[1863.0,1087.0],[1395.0,340.0],[50.0,1100.0],[1743.0,633.0]],[[946.0,1521.0],[1577.0,20.0],[967.0,717.0],[909.0,490.0]],[[786.0,385.0],[790.0,945.0],[173.0,1695.0],[1400.0,590.0]],[[51.0,1909.0],[886.0,248.0],[140.0,409.0],[147.0,768.0]],[[1447.0,257.0],[1542.0,888.0],[1304.0,1602.0],[1192.0,702.0]],[[1196.0,1264.0],[1143.0,1484.0],[935.0,901.0],[489.0,1547.0]],[[582.0,1474.0],[1163.0,1440.0],[1459.0,215.0],[232.0,370.0]],[[1778.0,1388.0],[1460.0,986.0],[1249.0,1169.0],[1582.0,1321.0]],[[683.0,609.0],[410.0,621.0],[911.0,1239.0],[1780.0,1728.0]],[[1079.0,1498.0],[1265.0,1812.0],[1832.0,231.0],[1863.0,1680.0]],[[1.0,506.0],[1220.0,742.0],[298.0,461.0],[979.0,1486.0]],[[1835.0,1501.0],[1185.0,615.0],[1136.0,419.0],[528.0,2002.0]],[[1357.0,950.0],[1443.0,154.0],[228.0,481.0],[752.0,2035.0]],[[1863.0,382.0],[1663.0,250.0],[614.0,296.0],[1244.0,45.0]],[[1879.0,993.0],[945.0,884.0],[534.0,1662.0],[541.0,610.0]],[[641.0,1449.0],[229.0,1523.0],[120.0,2032.0],[1851.0,377.0]],[[143.0,1835.0],[562.0,1566.0],[1088.0,247.0],[155.0,155.0]],[[754.0,910.0],[1155.0,486.0],[1248.0,756.0],[146.0,438.0]],[[1959.0,608.0],[1041.0,472.0],[344.0,1347.0],[543.0,944.0]],[[931.0,1377.0],[1803.0,1427.0],[46.0,269.0],[1118.0,757.0]],[[59.0,1514.0],[1205.0,697.0],[46.0,2009.0],[686.0,1485.0]],[[1496.0,1758.0],[2046.0,1933.0],[1411.0,1672.0],[400.0,309.0]],[[404.0,937.0],[1330.0,845.0],[154.0,244.0],[1442.0,906.0]],[[1708.0,1543.0],[211.0,209.0],[1771.0,1137.0],[1535.0,299.0]],[[423.0,1345.0],[735.0,1425.0],[1590.0,1912.0],[650.0,717.0]],[[1717.0,1933.0],[774.0,108.0],[652.0,811.0],[751.0,331.0]],[[107.0,366.0],[781.0,1854.0],[1054.0,249.0],[1632.0,1104.0]],[[1727.0,970.0],[1851.0,508.0],[284.0,868.0],[851.0,891.0]],[[125.0,717.0],[1686.0,2002.0],[1800.0,1356.0],[728.0,784.0]],[[1722.0,380.0],[1561.0,1490.0],[1482.0,353.0],[1883.0,30.0]],[[802.0,1142.0],[1461.0,1471.0],[114.0,768.0],[1007.0,1741.0]],[[1329.0,1511.0],[1804.0,1895.0],[452.0,145.0],[857.0,1402.0]],[[316.0,1766.0],[1034.0,997.0],[237.0,1274.0],[385.0,1274.0]],[[604.0,1762.0],[1479.0,340.0],[1254.0,295.0],[1812.0,1958.0]],[[671.0,980.0],[4.0,121.0],[813.0,863.0],[1343.0,375.0]],[[1596.0,1680.0],[1549.0,1401.0],[1551.0,1793.0],[1153.0,1460.0]],[[779.0,573.0],[1220.0,388.0],[1996.0,1434.0],[1624.0,756.0]],[[135.0,347.0],[2016.0,245.0],[1608.0,633.0],[561.0,751.0]],[[282.0,1611.0],[791.0,1606.0],[1454.0,1696.0],[713.0,1666.0]],[[1124.0,956.0],[1535.0,33.0],[1199.0,1178.0],[1096.0,597.0]],[[843.0,1907.0],[159.0,1109.0],[470.0,359.0],[210.0,272.0]],[[2039.0,554.0],[1993.0,1892.0],[1595.0,925.0],[217.0,1700.0]],[[694.0,215.0],[1190.0,1860.0],[183.0,636.0],[781.0,852.0]],[[660.0,875.0],[517.0,77.0],[1539.0,70.0],[1659.0,1506.0]],[[1477.0,1366.0],[619.0,1319.0],[1321.0,1390.0],[240.0,1326.0]],[[1856.0,664.0],[380.0,61.0],[109.0,1996.0],[424.0,771.0]],[[1596.0,1686.0],[449.0,1074.0],[1787.0,179.0],[1880.0,886.0]],[[178.0,11.0],[1201.0,1722.0],[260.0,1371.0],[1414.0,1644.0]],[[252.0,1102.0],[539.0,838.0],[53.0,657.0],[1828.0,217.0]],[[1685.0,447.0],[133.0,689.0],[1887.0,266.0],[47.0,35.0]],[[577.0,924.0],[11.0,128.0],[962.0,1783.0],[441.0,1995.0]],[[1527.0,1367.0],[671.0,871.0],[1000.0,343.0],[891.0,676.0]],[[1233.0,379.0],[322.0,853.0],[529.0,308.0],[1842.0,1056.0]],[[1479.0,1012.0],[577.0,705.0],[979.0,1739.0],[305.0,1523.0]],[[711.0,246.0],[177.0,1143.0],[26.0,1955.0],[1704.0,786.0]],[[460.0,1444.0],[1372.0,1058.0],[674.0,1165.0],[1215.0,1996.0]],[[1327.0,1752.0],[455.0,196.0],[266.0,260.0],[1277.0,1724.0]],[[1582.0,1524.0],[1344.0,367.0],[601.0,1804.0],[1710.0,921.0]],[[1036.0,1697.0],[1377.0,1277.0],[708.0,171.0],[462.0,411.0]],[[1793.0,498.0],[1947.0,1429.0],[1128.0,489.0],[1287.0,1564.0]],[[498.0,673.0],[1836.0,363.0],[2040.0,1402.0],[508.0,1317.0]],[[400.0,404.0],[998.0,2028.0],[993.0,1990.0],[1380.0,181.0]],[[682.0,252.0],[63.0,1755.0],[1358.0,1248.0],[1007.0,1455.0]],[[598.0,289.0],[771.0,142.0],[721.0,1464.0],[1785.0,1079.0]],[[860.0,659.0],[858.0,1660.0],[758.0,716.0],[979.0,1903.0]],[[586.0,1154.0],[1327.0,659.0],[1308.0,1154.0],[986.0,799.0]],[[683.0,1154.0],[131.0,582.0],[533.0,105.0],[1787.0,213.0]],[[1434.0,1838.0],[593.0,1752.0],[884.0,642.0],[1467.0,713.0]],[[280.0,1073.0],[1521.0,1374.0],[198.0,1249.0],[637.0,1968.0]],[[212.0,1906.0],[1708.0,359.0],[74.0,33.0],[177.0,66.0]],[[118.0,881.0],[1512.0,834.0],[1845.0,181.0],[203.0,907.0]],[[1221.0,821.0],[732.0,790.0],[813.0,1521.0],[880.0,554.0]],[[409.0,1286.0],[656.0,1903.0],[2044.0,94.0],[1959.0,277.0]],[[53.0,1860.0],[1309.0,1470.0],[1416.0,1685.0],[586.0,508.0]],[[446.0,1895.0],[1332.0,681.0],[287.0,1577.0],[85.0,1818.0]],[[0.0,552.0],[1214.0,1151.0],[1244.0,244.0],[1949.0,1945.0]],[[251.0,611.0],[3.0,2036.0],[1686.0,151.0],[997.0,477.0]],[[1591.0,114.0],[1260.0,27.0],[92.0,987.0],[1577.0,1140.0]],[[1490.0,22.0],[873.0,1756.0],[874.0,1853.0],[202.0,529.0]],[[1107.0,139.0],[397.0,1623.0],[391.0,197.0],[2026.0,2006.0]],[[612.0,456.0],[2001.0,1026.0],[746.0,246.0],[443.0,1902.0]],[[293.0,750.0],[1381.0,1908.0],[643.0,1997.0],[1533.0,1053.0]],[[1218.0,1982.0],[705.0,1236.0],[119.0,722.0],[996.0,428.0]],[[1593.0,315.0],[496.0,1209.0],[404.0,1039.0],[904.0,710.0]],[[1480.0,438.0],[1841.0,407.0],[1322.0,271.0],[1828.0,1779.0]],[[357.0,458.0],[1916.0,1935.0],[1050.0,1561.0],[1905.0,1937.0]],[[1552.0,1571.0],[399.0,1540.0],[1367.0,42.0],[283.0,711.0]],[[2016.0,1763.0],[122.0,350.0],[1590.0,1352.0],[162.0,110.0]],[[3.0,1847.0],[717.0,1853.0],[1239.0,1664.0],[316.0,837.0]],[[1749.0,1303.0],[2015.0,1759.0],[1603.0,126.0],[418.0,700.0]],[[1659.0,634.0],[1043.0,881.0],[2026.0,563.0],[1407.0,422.0]],[[647.0,2018.0],[79.0,1274.0],[154.0,399.0],[1102.0,2018.0]],[[643.0,1683.0],[1029.0,1441.0],[1206.0,1326.0],[1037.0,760.0]],[[876.0,445.0],[640.0,1032.0],[495.0,828.0],[181.0,562.0]],[[1857.0,471.0],[1012.0,727.0],[1710.0,1727.0],[679.0,163.0]],[[1344.0,51.0],[1313.0,697.0],[604.0,1509.0],[1011.0,172.0]],[[1715.0,640.0],[1337.0,877.0],[997.0,1970.0],[1153.0,1105.0]],[[1975.0,916.0],[663.0,269.0],[1021.0,1862.0],[568.0,1540.0]],[[688.0,84.0],[385.0,233.0],[139.0,225.0],[2040.0,1901.0]],[[99.0,2016.0],[1074.0,1160.0],[1129.0,1561.0],[1960.0,487.0]],[[359.0,1272.0],[1545.0,1783.0],[806.0,963.0],[629.0,282.0]],[[1184.0,845.0],[414.0,1432.0],[1102.0,560.0],[905.0,1466.0]],[[1439.0,652.0],[424.0,429.0],[805.0,1243.0],[964.0,268.0]],[[168.0,1361.0],[1545.0,874.0],[1495.0,314.0],[1880.0,1915.0]],[[1359.0,1198.0],[1361.0,78.0],[352.0,527.0],[677.0,82.0]],[[108.0,1015.0],[836.0,1769.0],[1268.0,758.0],[437.0,1538.0]],[[36.0,807.0],[273.0,894.0],[654.0,1282.0],[834.0,1297.0]],[[938.0,1073.0],[1422.0,1605.0],[1300.0,200.0],[1864.0,819.0]],[[449.0,1889.0],[636.0,1596.0],[1544.0,849.0],[1285.0,1389.0]],[[975.0,2030.0],[1912.0,1633.0],[17.0,1908.0],[1440.0,1971.0]],[[1381.0,224.0],[1813.0,1607.0],[1948.0,1998.0],[1178.0,246.0]],[[1894.0,949.0],[383.0,1239.0],[667.0,1847.0],[864.0,1095.0]],[[666.0,1239.0],[1606.0,27.0],[771.0,1966.0],[56.0,1199.0]],[[1087.0,1063.0],[315.0,1598.0],[190.0,157.0],[803.0,1937.0]],[[885.0,1616.0],[1325.0,1224.0],[1994.0,1049.0],[1079.0,658.0]],[[1622.0,1655.0],[2047.0,924.0],[964.0,1665.0],[1830.0,307.0]],[[1586.0,230.0],[5.0,777.0],[1442.0,807.0],[432.0,154.0]],[[1932.0,782.0],[222.0,731.0],[1293.0,175.0],[1046.0,844.0]],[[782.0,1670.0],[265.0,735.0],[1047.0,184.0],[595.0,56.0]],[[242.0,883.0],[717.0,1342.0],[573.0,706.0],[1654.0,4.0]],[[11.0,1364.0],[109.0,881.0],[326.0,1974.0],[1900.0,303.0]],[[699.0,1105.0],[75.0,1695.0],[1193.0,820.0],[742.0,1309.0]],[[402.0,1192.0],[1242.0,820.0],[456.0,50.0],[1148.0,850.0]],[[886.0,963.0],[813.0,2041.0],[1300.0,1634.0],[1974.0,428.0]],[[1521.0,307.0],[1907.0,1033.0],[1948.0,244.0],[82.0,1361.0]],[[1959.0,43.0],[1009.0,514.0],[201.0,1616.0],[393.0,948.0]],[[216.0,1228.0],[1515.0,420.0],[394.0,1129.0],[1149.0,627.0]],[[964.0,639.0],[1955.0,228.0],[1686.0,1802.0],[123.0,50.0]],[[1356.0,1779.0],[1221.0,1998.0],[211.0,1807.0],[1303.0,1740.0]],[[104.0,690.0],[1150.0,1485.0],[985.0,277.0],[593.0,1308.0]],[[1618.0,1267.0],[1320.0,1503.0],[1998.0,577.0],[858.0,1704.0]],[[881.0,424.0],[332.0,703.0],[60.0,87.0],[339.0,1860.0]],[[300.0,760.0],[1470.0,709.0],[751.0,1706.0],[310.0,1892.0]],[[444.0,10.0],[1973.0,1494.0],[1653.0,589.0],[1490.0,569.0]],[[969.0,1595.0],[1337.0,773.0],[408.0,1264.0],[1803.0,846.0]],[[1859.0,622.0],[1049.0,219.0],[669.0,804.0],[2011.0,807.0]],[[174.0,1636.0],[1411.0,1956.0],[1701.0,906.0],[796.0,329.0]],[[63.0,637.0],[2027.0,883.0],[2035.0,1101.0],[1612.0,506.0]],[[286.0,401.0],[1420.0,690.0],[814.0,1751.0],[1268.0,1676.0]],[[1055.0,1004.0],[1544.0,1670.0],[139.0,163.0],[788.0,1524.0]],[[1661.0,1318.0],[1678.0,1132.0],[1851.0,1604.0],[719.0,527.0]],[[1762.0,1430.0],[1498.0,1528.0],[1781.0,140.0],[1507.0,506.0]],[[1368.0,219.0],[871.0,1467.0],[732.0,1173.0],[2044.0,712.0]],[[1732.0,1509.0],[1520.0,124.0],[682.0,749.0],[1470.0,1925.0]],[[1827.0,786.0],[501.0,1314.0],[67.0,1755.0],[911.0,1798.0]],[[1264.0,1721.0],[1129.0,325.0],[813.0,1854.0],[457.0,156.0]],[[19.0,918.0],[981.0,130.0],[2041.0,1358.0],[838.0,780.0]],[[455.0,116.0],[1226.0,1149.0],[387.0,546.0],[1518.0,1843.0]],[[1735.0,1348.0],[254.0,1244.0],[343.0,1899.0],[911.0,652.0]],[[859.0,1198.0],[1924.0,1174.0],[1148.0,405.0],[1039.0,660.0]],[[727.0,681.0],[1757.0,1236.0],[1626.0,1630.0],[949.0,686.0]],[[1687.0,702.0],[1352.0,999.0],[891.0,609.0],[1178.0,306.0]],[[308.0,980.0],[472.0,1248.0],[43.0,540.0],[1662.0,1620.0]],[[230.0,510.0],[215.0,539.0],[1906.0,1617.0],[1196.0,976.0]],[[651.0,1927.0],[958.0,922.0],[1029.0,1711.0],[1886.0,1152.0]],[[541.0,528.0],[132.0,535.0],[630.0,38.0],[1035.0,712.0]],[[140.0,1420.0],[1969.0,763.0],[949.0,1332.0],[755.0,864.0]],[[706.0,2019.0],[1013.0,513.0],[855.0,191.0],[1370.0,1010.0]],[[1656.0,1872.0],[1085.0,746.0],[479.0,180.0],[356.0,148.0]],[[1172.0,2040.0],[206.0,492.0],[1278.0,1519.0],[1416.0,979.0]],[[867.0,1050.0],[778.0,664.0],[1391.0,1142.0],[1323.0,1484.0]],[[151.0,319.0],[873.0,710.0],[1613.0,1875.0],[859.0,50.0]],[[1897.0,1624.0],[1548.0,1427.0],[1519.0,2017.0],[34.0,1033.0]],[[316.0,238.0],[1656.0,1666.0],[1530.0,1642.0],[757.0,1247.0]],[[800.0,367.0],[1244.0,121.0],[1739.0,158.0],[1875.0,23.0]],[[1653.0,831.0],[1949.0,1049.0],[97.0,857.0],[914.0,569.0]],[[1357.0,980.0],[1078.0,1356.0],[146.0,1577.0],[1187.0,71.0]],[[1916.0,105.0],[1741.0,64.0],[447.0,709.0],[1016.0,76.0]],[[1657.0,1822.0],[629.0,315.0],[879.0,782.0],[1707.0,1466.0]],[[1504.0,256.0],[902.0,804.0],[385.0,522.0],[1133.0,1420.0]],[[1341.0,1738.0],[1080.0,1689.0],[1455.0,933.0],[306.0,1861.0]],[[896.0,1132.0],[42.0,1304.0],[774.0,1471.0],[1246.0,1532.0]],[[807.0,1437.0],[1321.0,1097.0],[1666.0,367.0],[1544.0,780.0]],[[1838.0,806.0],[886.0,1333.0],[1962.0,1799.0],[20.0,197.0]],[[1227.0,517.0],[1594.0,290.0],[1202.0,525.0],[1321.0,1319.0]],[[1581.0,1948.0],[598.0,1052.0],[900.0,1009.0],[1544.0,1509.0]],[[204.0,1234.0],[1896.0,1409.0],[21.0,611.0],[1950.0,2040.0]],[[78.0,133.0],[1601.0,1784.0],[440.0,839.0],[1740.0,1523.0]],[[1764.0,1845.0],[1574.0,1740.0],[257.0,2044.0],[747.0,876.0]],[[761.0,408.0],[1739.0,1479.0],[900.0,736.0],[1577.0,1455.0]],[[1515.0,578.0],[652.0,956.0],[1051.0,1013.0],[292.0,324.0]],[[829.0,1789.0],[2015.0,156.0],[1316.0,901.0],[307.0,1759.0]],[[345.0,1307.0],[746.0,1956.0],[965.0,1449.0],[815.0,1160.0]],[[685.0,1855.0],[460.0,115.0],[418.0,1039.0],[1250.0,423.0]],[[1163.0,829.0],[1895.0,920.0],[116.0,1701.0],[303.0,1392.0]],[[1906.0,1850.0],[1850.0,915.0],[1771.0,1131.0],[1554.0,356.0]],[[1635.0,1624.0],[409.0,427.0],[1493.0,1446.0],[1438.0,1622.0]],[[1952.0,1994.0],[843.0,882.0],[577.0,847.0],[474.0,56.0]],[[754.0,1994.0],[1407.0,1031.0],[1273.0,366.0],[860.0,577.0]],[[1035.0,976.0],[392.0,1224.0],[1705.0,1318.0],[1460.0,635.0]],[[1880.0,932.0],[1281.0,849.0],[1346.0,525.0],[563.0,1618.0]],[[1154.0,151.0],[120.0,140.0],[1666.0,1503.0],[174.0,1889.0]],[[1866.0,1212.0],[1635.0,1655.0],[1353.0,1037.0],[1994.0,327.0]],[[82.0,612.0],[1294.0,264.0],[1474.0,223.0],[248.0,1570.0]],[[1318.0,884.0],[568.0,1755.0],[957.0,1160.0],[1395.0,19.0]],[[921.0,871.0],[1085.0,1656.0],[1231.0,1927.0],[1660.0,1997.0]],[[1909.0,1080.0],[69.0,1910.0],[1836.0,1863.0],[1744.0,892.0]],[[1499.0,1791.0],[696.0,714.0],[1872.0,1449.0],[94.0,881.0]],[[454.0,1530.0],[164.0,1837.0],[266.0,1332.0],[397.0,372.0]],[[1496.0,936.0],[371.0,1362.0],[528.0,996.0],[1439.0,960.0]],[[1095.0,1004.0],[1600.0,149.0],[59.0,1065.0],[1147.0,2041.0]],[[476.0,1992.0],[1687.0,1146.0],[396.0,252.0],[1408.0,1998.0]],[[1504.0,1082.0],[821.0,1755.0],[1567.0,695.0],[1510.0,286.0]],[[1823.0,1154.0],[1924.0,1508.0],[827.0,1351.0],[1575.0,1511.0]],[[697.0,1823.0],[1483.0,1026.0],[523.0,906.0],[653.0,1361.0]],[[9.0,508.0],[568.0,823.0],[538.0,204.0],[1564.0,575.0]],[[530.0,1391.0],[1935.0,1903.0],[393.0,64.0],[1592.0,599.0]],[[1552.0,353.0],[546.0,349.0],[1065.0,1214.0],[619.0,1367.0]],[[411.0,1814.0],[350.0,50.0],[791.0,1473.0],[1799.0,468.0]],[[773.0,70.0],[100.0,427.0],[1248.0,87.0],[1707.0,706.0]],[[1071.0,720.0],[191.0,94.0],[774.0,770.0],[708.0,495.0]],[[1472.0,197.0],[1264.0,452.0],[685.0,886.0],[0.0,206.0]],[[956.0,702.0],[1727.0,958.0],[578.0,20.0],[1913.0,2014.0]],[[129.0,604.0],[2041.0,1044.0],[1902.0,1457.0],[1385.0,1897.0]],[[1705.0,967.0],[1528.0,1908.0],[1963.0,2024.0],[26.0,768.0]],[[1361.0,784.0],[409.0,1371.0],[1909.0,184.0],[764.0,1070.0]],[[995.0,1834.0],[1771.0,524.0],[704.0,162.0],[1281.0,1979.0]],[[1220.0,1043.0],[1136.0,1543.0],[1002.0,917.0],[1406.0,1263.0]],[[1132.0,1305.0],[1559.0,1070.0],[995.0,340.0],[2031.0,678.0]],[[1526.0,652.0],[334.0,1809.0],[1953.0,1491.0],[1301.0,306.0]],[[885.0,909.0],[599.0,1454.0],[1404.0,702.0],[679.0,784.0]],[[837.0,845.0],[129.0,1698.0],[770.0,1882.0],[1680.0,1355.0]],[[118.0,1293.0],[733.0,516.0],[947.0,1713.0],[1994.0,709.0]],[[484.0,510.0],[401.0,65.0],[1908.0,730.0],[342.0,361.0]],[[572.0,52.0],[1671.0,1138.0],[780.0,1414.0],[1796.0,2002.0]],[[1898.0,2022.0],[1915.0,1071.0],[418.0,1378.0],[1394.0,260.0]],[[1870.0,1588.0],[1148.0,1615.0],[1358.0,278.0],[1238.0,560.0]],[[1063.0,81.0],[601.0,738.0],[767.0,1566.0],[511.0,1013.0]],[[1653.0,699.0],[1195.0,1417.0],[525.0,1630.0],[471.0,1557.0]],[[1484.0,935.0],[53.0,417.0],[870.0,1562.0],[320.0,119.0]],[[1503.0,1451.0],[1582.0,476.0],[567.0,678.0],[888.0,859.0]],[[1907.0,901.0],[742.0,1889.0],[1054.0,1982.0],[920.0,1603.0]],[[77.0,50.0],[507.0,711.0],[206.0,958.0],[1304.0,1292.0]],[[437.0,1710.0],[1935.0,568.0],[162.0,1116.0],[1463.0,859.0]],[[1404.0,2031.0],[560.0,1158.0],[1190.0,91.0],[1488.0,2027.0]]]
\ No newline at end of file
diff --git a/Tests/cu2qu/ufo_test.py b/Tests/cu2qu/ufo_test.py
new file mode 100644
index 0000000..b678ae3
--- /dev/null
+++ b/Tests/cu2qu/ufo_test.py
@@ -0,0 +1,285 @@
+import os
+
+from fontTools.misc.loggingTools import CapturingLogHandler
+from fontTools.cu2qu.ufo import (
+    fonts_to_quadratic,
+    font_to_quadratic,
+    glyphs_to_quadratic,
+    glyph_to_quadratic,
+    logger,
+    CURVE_TYPE_LIB_KEY,
+)
+from fontTools.cu2qu.errors import (
+    IncompatibleSegmentNumberError,
+    IncompatibleSegmentTypesError,
+    IncompatibleFontsError,
+)
+
+import pytest
+
+
+ufoLib2 = pytest.importorskip("ufoLib2")
+
+DATADIR = os.path.join(os.path.dirname(__file__), 'data')
+
+TEST_UFOS = [
+    os.path.join(DATADIR, "RobotoSubset-Regular.ufo"),
+    os.path.join(DATADIR, "RobotoSubset-Bold.ufo"),
+]
+
+
+@pytest.fixture
+def fonts():
+    return [ufoLib2.Font.open(ufo) for ufo in TEST_UFOS]
+
+
+class FontsToQuadraticTest(object):
+
+    def test_modified(self, fonts):
+        modified = fonts_to_quadratic(fonts)
+        assert modified
+
+    def test_stats(self, fonts):
+        stats = {}
+        fonts_to_quadratic(fonts, stats=stats)
+        assert stats == {'1': 1, '2': 79, '3': 130, '4': 2}
+
+    def test_dump_stats(self, fonts):
+        with CapturingLogHandler(logger, "INFO") as captor:
+            fonts_to_quadratic(fonts, dump_stats=True)
+        assert captor.assertRegex("New spline lengths:")
+
+    def test_remember_curve_type(self, fonts):
+        fonts_to_quadratic(fonts, remember_curve_type=True)
+        assert fonts[0].lib[CURVE_TYPE_LIB_KEY] == "quadratic"
+        with CapturingLogHandler(logger, "INFO") as captor:
+            fonts_to_quadratic(fonts, remember_curve_type=True)
+        assert captor.assertRegex("already converted")
+
+    def test_no_remember_curve_type(self, fonts):
+        assert CURVE_TYPE_LIB_KEY not in fonts[0].lib
+        fonts_to_quadratic(fonts, remember_curve_type=False)
+        assert CURVE_TYPE_LIB_KEY not in fonts[0].lib
+
+    def test_different_glyphsets(self, fonts):
+        del fonts[0]['a']
+        assert 'a' not in fonts[0]
+        assert 'a' in fonts[1]
+        assert fonts_to_quadratic(fonts)
+
+    def test_max_err_em_float(self, fonts):
+        stats = {}
+        fonts_to_quadratic(fonts, max_err_em=0.002, stats=stats)
+        assert stats == {'1': 5, '2': 193, '3': 14}
+
+    def test_max_err_em_list(self, fonts):
+        stats = {}
+        fonts_to_quadratic(fonts, max_err_em=[0.002, 0.002], stats=stats)
+        assert stats == {'1': 5, '2': 193, '3': 14}
+
+    def test_max_err_float(self, fonts):
+        stats = {}
+        fonts_to_quadratic(fonts, max_err=4.096, stats=stats)
+        assert stats == {'1': 5, '2': 193, '3': 14}
+
+    def test_max_err_list(self, fonts):
+        stats = {}
+        fonts_to_quadratic(fonts, max_err=[4.096, 4.096], stats=stats)
+        assert stats == {'1': 5, '2': 193, '3': 14}
+
+    def test_both_max_err_and_max_err_em(self, fonts):
+        with pytest.raises(TypeError, match="Only one .* can be specified"):
+            fonts_to_quadratic(fonts, max_err=1.000, max_err_em=0.001)
+
+    def test_single_font(self, fonts):
+        assert font_to_quadratic(fonts[0], max_err_em=0.002,
+                                 reverse_direction=True)
+
+
+class GlyphsToQuadraticTest(object):
+
+    @pytest.mark.parametrize(
+        ["glyph", "expected"],
+        [('A', False),  # contains no curves, it is not modified
+         ('a', True)],
+        ids=['lines-only', 'has-curves']
+    )
+    def test_modified(self, fonts, glyph, expected):
+        glyphs = [f[glyph] for f in fonts]
+        assert glyphs_to_quadratic(glyphs) == expected
+
+    def test_stats(self, fonts):
+        stats = {}
+        glyphs_to_quadratic([f['a'] for f in fonts], stats=stats)
+        assert stats == {'2': 1, '3': 7, '4': 3, '5': 1}
+
+    def test_max_err_float(self, fonts):
+        glyphs = [f['a'] for f in fonts]
+        stats = {}
+        glyphs_to_quadratic(glyphs, max_err=4.096, stats=stats)
+        assert stats == {'2': 11, '3': 1}
+
+    def test_max_err_list(self, fonts):
+        glyphs = [f['a'] for f in fonts]
+        stats = {}
+        glyphs_to_quadratic(glyphs, max_err=[4.096, 4.096], stats=stats)
+        assert stats == {'2': 11, '3': 1}
+
+    def test_reverse_direction(self, fonts):
+        glyphs = [f['A'] for f in fonts]
+        assert glyphs_to_quadratic(glyphs, reverse_direction=True)
+
+    def test_single_glyph(self, fonts):
+        assert glyph_to_quadratic(fonts[0]['a'], max_err=4.096,
+                                  reverse_direction=True)
+
+    @pytest.mark.parametrize(
+        ["outlines", "exception", "message"],
+        [
+            [
+                [
+                    [
+                        ('moveTo', ((0, 0),)),
+                        ('curveTo', ((1, 1), (2, 2), (3, 3))),
+                        ('curveTo', ((4, 4), (5, 5), (6, 6))),
+                        ('closePath', ()),
+                    ],
+                    [
+                        ('moveTo', ((7, 7),)),
+                        ('curveTo', ((8, 8), (9, 9), (10, 10))),
+                        ('closePath', ()),
+                    ]
+                ],
+                IncompatibleSegmentNumberError,
+                "have different number of segments",
+            ],
+            [
+                [
+
+                    [
+                        ('moveTo', ((0, 0),)),
+                        ('curveTo', ((1, 1), (2, 2), (3, 3))),
+                        ('closePath', ()),
+                    ],
+                    [
+                        ('moveTo', ((4, 4),)),
+                        ('lineTo', ((5, 5),)),
+                        ('closePath', ()),
+                    ],
+                ],
+                IncompatibleSegmentTypesError,
+                "have incompatible segment types",
+            ],
+        ],
+        ids=[
+            "unequal-length",
+            "different-segment-types",
+        ]
+    )
+    def test_incompatible_glyphs(self, outlines, exception, message):
+        glyphs = []
+        for i, outline in enumerate(outlines):
+            glyph = ufoLib2.objects.Glyph("glyph%d" % i)
+            pen = glyph.getPen()
+            for operator, args in outline:
+                getattr(pen, operator)(*args)
+            glyphs.append(glyph)
+        with pytest.raises(exception) as excinfo:
+            glyphs_to_quadratic(glyphs)
+        assert excinfo.match(message)
+
+    def test_incompatible_fonts(self):
+        font1 = ufoLib2.Font()
+        font1.info.unitsPerEm = 1000
+        glyph1 = font1.newGlyph("a")
+        pen1 = glyph1.getPen()
+        for operator, args in [("moveTo", ((0, 0),)),
+                               ("lineTo", ((1, 1),)),
+                               ("endPath", ())]:
+            getattr(pen1, operator)(*args)
+
+        font2 = ufoLib2.Font()
+        font2.info.unitsPerEm = 1000
+        glyph2 = font2.newGlyph("a")
+        pen2 = glyph2.getPen()
+        for operator, args in [("moveTo", ((0, 0),)),
+                               ("curveTo", ((1, 1), (2, 2), (3, 3))),
+                               ("endPath", ())]:
+            getattr(pen2, operator)(*args)
+
+        with pytest.raises(IncompatibleFontsError) as excinfo:
+            fonts_to_quadratic([font1, font2])
+        assert excinfo.match("fonts contains incompatible glyphs: 'a'")
+
+        assert hasattr(excinfo.value, "glyph_errors")
+        error = excinfo.value.glyph_errors['a']
+        assert isinstance(error, IncompatibleSegmentTypesError)
+        assert error.segments == {1: ["line", "curve"]}
+
+    def test_already_quadratic(self):
+        glyph = ufoLib2.objects.Glyph()
+        pen = glyph.getPen()
+        pen.moveTo((0, 0))
+        pen.qCurveTo((1, 1), (2, 2))
+        pen.closePath()
+        assert not glyph_to_quadratic(glyph)
+
+    def test_open_paths(self):
+        glyph = ufoLib2.objects.Glyph()
+        pen = glyph.getPen()
+        pen.moveTo((0, 0))
+        pen.lineTo((1, 1))
+        pen.curveTo((2, 2), (3, 3), (4, 4))
+        pen.endPath()
+        assert glyph_to_quadratic(glyph)
+        # open contour is still open
+        assert glyph[-1][0].segmentType == "move"
+
+    def test_ignore_components(self):
+        glyph = ufoLib2.objects.Glyph()
+        pen = glyph.getPen()
+        pen.addComponent('a', (1, 0, 0, 1, 0, 0))
+        pen.moveTo((0, 0))
+        pen.curveTo((1, 1), (2, 2), (3, 3))
+        pen.closePath()
+        assert glyph_to_quadratic(glyph)
+        assert len(glyph.components) == 1
+
+    def test_overlapping_start_end_points(self):
+        # https://github.com/googlefonts/fontmake/issues/572
+        glyph1 = ufoLib2.objects.Glyph()
+        pen = glyph1.getPointPen()
+        pen.beginPath()
+        pen.addPoint((0, 651), segmentType="line")
+        pen.addPoint((0, 101), segmentType="line")
+        pen.addPoint((0, 101), segmentType="line")
+        pen.addPoint((0, 651), segmentType="line")
+        pen.endPath()
+
+        glyph2 = ufoLib2.objects.Glyph()
+        pen = glyph2.getPointPen()
+        pen.beginPath()
+        pen.addPoint((1, 651), segmentType="line")
+        pen.addPoint((2, 101), segmentType="line")
+        pen.addPoint((3, 101), segmentType="line")
+        pen.addPoint((4, 651), segmentType="line")
+        pen.endPath()
+
+        glyphs = [glyph1, glyph2]
+
+        assert glyphs_to_quadratic(glyphs, reverse_direction=True)
+
+        assert [[(p.x, p.y) for p in glyph[0]] for glyph in glyphs] == [
+            [
+                (0, 651),
+                (0, 651),
+                (0, 101),
+                (0, 101),
+            ],
+            [
+                (1, 651),
+                (4, 651),
+                (3, 101),
+                (2, 101)
+            ],
+        ]
diff --git a/Tests/designspaceLib/data/test.designspace b/Tests/designspaceLib/data/test.designspace
new file mode 100644
index 0000000..e12f156
--- /dev/null
+++ b/Tests/designspaceLib/data/test.designspace
@@ -0,0 +1,112 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<designspace format="4.1">
+  <axes>
+    <axis tag="wght" name="weight" minimum="0" maximum="1000" default="0">
+      <labelname xml:lang="en">Wéíght</labelname>
+      <labelname xml:lang="fa-IR">قطر</labelname>
+    </axis>
+    <axis tag="wdth" name="width" minimum="0" maximum="1000" default="15" hidden="1">
+      <labelname xml:lang="fr">Chasse</labelname>
+      <map input="0" output="10"/>
+      <map input="15" output="20"/>
+      <map input="401" output="66"/>
+      <map input="1000" output="990"/>
+    </axis>
+  </axes>
+  <rules processing="last">
+    <rule name="named.rule.1">
+      <conditionset>
+        <condition name="axisName_a" minimum="0" maximum="1"/>
+        <condition name="axisName_b" minimum="2" maximum="3"/>
+      </conditionset>
+      <sub name="a" with="a.alt"/>
+    </rule>
+  </rules>
+  <sources>
+    <source filename="masters/masterTest1.ufo" name="master.ufo1" familyname="MasterFamilyName" stylename="MasterStyleNameOne">
+      <lib copy="1"/>
+      <features copy="1"/>
+      <info copy="1"/>
+      <glyph name="A" mute="1"/>
+      <glyph name="Z" mute="1"/>
+      <location>
+        <dimension name="weight" xvalue="0"/>
+        <dimension name="width" xvalue="20"/>
+      </location>
+    </source>
+    <source filename="masters/masterTest2.ufo" name="master.ufo2" familyname="MasterFamilyName" stylename="MasterStyleNameTwo">
+      <kerning mute="1"/>
+      <location>
+        <dimension name="weight" xvalue="1000"/>
+        <dimension name="width" xvalue="20"/>
+      </location>
+    </source>
+    <source filename="masters/masterTest2.ufo" name="master.ufo2" familyname="MasterFamilyName" stylename="Supports" layer="supports">
+      <location>
+        <dimension name="weight" xvalue="1000"/>
+        <dimension name="width" xvalue="20"/>
+      </location>
+    </source>
+  </sources>
+  <instances>
+    <instance name="instance.ufo1" familyname="InstanceFamilyName" stylename="InstanceStyleName" filename="instances/instanceTest1.ufo" postscriptfontname="InstancePostscriptName" stylemapfamilyname="InstanceStyleMapFamilyName" stylemapstylename="InstanceStyleMapStyleName">
+      <location>
+        <dimension name="weight" xvalue="500"/>
+        <dimension name="width" xvalue="20"/>
+      </location>
+      <glyphs>
+        <glyph mute="1" unicode="0x123 0x124 0x125" name="arrow"/>
+      </glyphs>
+      <kerning/>
+      <info/>
+      <lib>
+        <dict>
+          <key>com.coolDesignspaceApp.binaryData</key>
+          <data>
+          PGJpbmFyeSBndW5rPg==
+          </data>
+          <key>com.coolDesignspaceApp.specimenText</key>
+          <string>Hamburgerwhatever</string>
+        </dict>
+      </lib>
+    </instance>
+    <instance name="instance.ufo2" familyname="InstanceFamilyName" stylename="InstanceStyleName" filename="instances/instanceTest2.ufo" postscriptfontname="InstancePostscriptName" stylemapfamilyname="InstanceStyleMapFamilyName" stylemapstylename="InstanceStyleMapStyleName">
+      <location>
+        <dimension name="weight" xvalue="500"/>
+        <dimension name="width" xvalue="400" yvalue="300"/>
+      </location>
+      <glyphs>
+        <glyph unicode="0x65 0xc9 0x12d" name="arrow">
+          <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..8daf741
--- /dev/null
+++ b/Tests/designspaceLib/designspace_test.py
@@ -0,0 +1,1024 @@
+# coding=utf-8
+
+import os
+import sys
+import pytest
+import warnings
+
+from fontTools.misc import plistlib
+from fontTools.designspaceLib import (
+    DesignSpaceDocument, SourceDescriptor, AxisDescriptor, RuleDescriptor,
+    InstanceDescriptor, evaluateRule, processRules, posix, DesignSpaceDocumentError)
+from fontTools import ttLib
+
+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()
+    doc.rulesProcessingLast = True
+
+    # 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 = 15
+    a2.name = "width"
+    a2.tag = "wdth"
+    a2.map = [(0.0, 10.0), (15.0, 20.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.binaryData'] = plistlib.Data(b'<binary gunk>')
+    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
+    with open(testDocPath, 'r', encoding='utf-8') as f1:
+        t1 = f1.read()
+    with open(testDocPath2, 'r', encoding='utf-8') as f2:
+        t2 = f2.read()
+    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)
+    with open(testDocPath, 'r', encoding='utf-8') as f1:
+        t1 = f1.read()
+    with open(testDocPath2, 'r', encoding='utf-8') as f2:
+        t2 = f2.read()
+    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.0), (300, 0.5), (600, 0.5), (1000, 1.0)])]
+
+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.0), (300, 0.5), (600, 0.5), (1000, 1.0)])]
+
+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()
+    doc.rulesProcessingLast = True
+    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 doc2.rulesProcessingLast
+    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.
+    with open(path, 'r', encoding='utf-8') as f:
+        d = f.read()
+    print(d)
+    d = d.replace('<rule name="named.rule.1">', '<rule name="named.rule.1">\n\t<condition maximum="22" minimum="33" name="axisName_a" />')
+    with open(path, 'w', encoding='utf-8') as f:
+        f.write(d)
+
+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
+
+
+def test_updatePaths(tmpdir):
+    doc = DesignSpaceDocument()
+    doc.path = str(tmpdir / "foo" / "bar" / "MyDesignspace.designspace")
+
+    s1 = SourceDescriptor()
+    doc.addSource(s1)
+
+    doc.updatePaths()
+
+    # expect no changes
+    assert s1.path is None
+    assert s1.filename is None
+
+    name1 = "../masters/Source1.ufo"
+    path1 = posix(str(tmpdir / "foo" / "masters" / "Source1.ufo"))
+
+    s1.path = path1
+    s1.filename = None
+
+    doc.updatePaths()
+
+    assert s1.path == path1
+    assert s1.filename == name1  # empty filename updated
+
+    name2 = "../masters/Source2.ufo"
+    s1.filename = name2
+
+    doc.updatePaths()
+
+    # conflicting filename discarded, path always gets precedence
+    assert s1.path == path1
+    assert s1.filename == "../masters/Source1.ufo"
+
+    s1.path = None
+    s1.filename = name2
+
+    doc.updatePaths()
+
+    # expect no changes
+    assert s1.path is None
+    assert s1.filename == name2
+
+
+@pytest.mark.skipif(sys.version_info[:2] < (3, 6), reason="pathlib is only tested on 3.6 and up")
+def test_read_with_path_object():
+    import pathlib
+    source = (pathlib.Path(__file__) / "../data/test.designspace").resolve()
+    assert source.exists()
+    doc = DesignSpaceDocument()
+    doc.read(source)
+
+
+@pytest.mark.skipif(sys.version_info[:2] < (3, 6), reason="pathlib is only tested on 3.6 and up")
+def test_with_with_path_object(tmpdir):
+    import pathlib
+    tmpdir = str(tmpdir)
+    dest = pathlib.Path(tmpdir) / "test.designspace"
+    doc = DesignSpaceDocument()
+    doc.write(dest)
+    assert dest.exists()
+
+
+def test_findDefault_axis_mapping():
+    designspace_string = """\
+<?xml version='1.0' encoding='UTF-8'?>
+<designspace format="4.0">
+  <axes>
+    <axis tag="wght" name="Weight" minimum="100" maximum="800" default="400">
+      <map input="100" output="20"/>
+      <map input="300" output="40"/>
+      <map input="400" output="80"/>
+      <map input="700" output="126"/>
+      <map input="800" output="170"/>
+    </axis>
+    <axis tag="ital" name="Italic" minimum="0" maximum="1" default="1"/>
+  </axes>
+  <sources>
+    <source filename="Font-Light.ufo">
+      <location>
+        <dimension name="Weight" xvalue="20"/>
+        <dimension name="Italic" xvalue="0"/>
+      </location>
+    </source>
+    <source filename="Font-Regular.ufo">
+      <location>
+        <dimension name="Weight" xvalue="80"/>
+        <dimension name="Italic" xvalue="0"/>
+      </location>
+    </source>
+    <source filename="Font-Bold.ufo">
+      <location>
+        <dimension name="Weight" xvalue="170"/>
+        <dimension name="Italic" xvalue="0"/>
+      </location>
+    </source>
+    <source filename="Font-LightItalic.ufo">
+      <location>
+        <dimension name="Weight" xvalue="20"/>
+        <dimension name="Italic" xvalue="1"/>
+      </location>
+    </source>
+    <source filename="Font-Italic.ufo">
+      <location>
+        <dimension name="Weight" xvalue="80"/>
+        <dimension name="Italic" xvalue="1"/>
+      </location>
+    </source>
+    <source filename="Font-BoldItalic.ufo">
+      <location>
+        <dimension name="Weight" xvalue="170"/>
+        <dimension name="Italic" xvalue="1"/>
+      </location>
+    </source>
+  </sources>
+</designspace>
+    """
+    designspace = DesignSpaceDocument.fromstring(designspace_string)
+    assert designspace.findDefault().filename == "Font-Italic.ufo"
+
+    designspace.axes[1].default = 0
+
+    assert designspace.findDefault().filename == "Font-Regular.ufo"
+
+
+def test_loadSourceFonts():
+
+    def opener(path):
+        font = ttLib.TTFont()
+        font.importXML(path)
+        return font
+
+    # this designspace file contains .TTX source paths
+    path = os.path.join(
+        os.path.dirname(os.path.dirname(__file__)),
+        "varLib",
+        "data",
+        "SparseMasters.designspace"
+    )
+    designspace = DesignSpaceDocument.fromfile(path)
+
+    # force two source descriptors to have the same path
+    designspace.sources[1].path = designspace.sources[0].path
+
+    fonts = designspace.loadSourceFonts(opener)
+
+    assert len(fonts) == 3
+    assert all(isinstance(font, ttLib.TTFont) for font in fonts)
+    assert fonts[0] is fonts[1]  # same path, identical font object
+
+    fonts2 = designspace.loadSourceFonts(opener)
+
+    for font1, font2 in zip(fonts, fonts2):
+        assert font1 is font2
+
+
+def test_loadSourceFonts_no_required_path():
+    designspace = DesignSpaceDocument()
+    designspace.sources.append(SourceDescriptor())
+
+    with pytest.raises(DesignSpaceDocumentError, match="no 'path' attribute"):
+        designspace.loadSourceFonts(lambda p: p)
+
+
+def test_addAxisDescriptor():
+    ds = DesignSpaceDocument()
+
+    axis = ds.addAxisDescriptor(
+      name="Weight", tag="wght", minimum=100, default=400, maximum=900
+    )
+
+    assert ds.axes[0] is axis
+    assert isinstance(axis, AxisDescriptor)
+    assert axis.name == "Weight"
+    assert axis.tag == "wght"
+    assert axis.minimum == 100
+    assert axis.default == 400
+    assert axis.maximum == 900
+
+
+def test_addSourceDescriptor():
+    ds = DesignSpaceDocument()
+
+    source = ds.addSourceDescriptor(name="TestSource", location={"Weight": 400})
+
+    assert ds.sources[0] is source
+    assert isinstance(source, SourceDescriptor)
+    assert source.name == "TestSource"
+    assert source.location == {"Weight": 400}
+
+
+def test_addInstanceDescriptor():
+    ds = DesignSpaceDocument()
+
+    instance = ds.addInstanceDescriptor(
+      name="TestInstance",
+      location={"Weight": 400},
+      styleName="Regular",
+      styleMapStyleName="regular",
+    )
+
+    assert ds.instances[0] is instance
+    assert isinstance(instance, InstanceDescriptor)
+    assert instance.name == "TestInstance"
+    assert instance.location == {"Weight": 400}
+    assert instance.styleName == "Regular"
+    assert instance.styleMapStyleName == "regular"
+
+
+def test_addRuleDescriptor(tmp_path):
+    ds = DesignSpaceDocument()
+
+    rule = ds.addRuleDescriptor(
+        name="TestRule",
+        conditionSets=[
+            [
+                dict(name="Weight", minimum=100, maximum=200),
+                dict(name="Weight", minimum=700, maximum=900),
+            ]
+        ],
+        subs=[("a", "a.alt")],
+    )
+
+    assert ds.rules[0] is rule
+    assert isinstance(rule, RuleDescriptor)
+    assert rule.name == "TestRule"
+    assert rule.conditionSets == [
+        [
+            dict(name="Weight", minimum=100, maximum=200),
+            dict(name="Weight", minimum=700, maximum=900),
+        ]
+    ]
+    assert rule.subs == [("a", "a.alt")]
+
+    # Test it doesn't crash.
+    ds.write(tmp_path / "test.designspace")
diff --git a/Tests/encodings/codecs_test.py b/Tests/encodings/codecs_test.py
index 29e3a0d..9dac416 100644
--- a/Tests/encodings/codecs_test.py
+++ b/Tests/encodings/codecs_test.py
@@ -1,5 +1,3 @@
-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"
 
@@ -7,19 +5,19 @@
 
 	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))
+				 chr(0x78)+chr(0x2122)+chr(0x00A9)+chr(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"))
+				 (chr(0x78)+chr(0x2122)+chr(0x00A9)+chr(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))
+				 chr(0x5C))
 
 	def test_decode_mac_romanian(self):
 		self.assertEqual(b'x\xfb'.decode("mac_romanian"),
-				 unichr(0x78)+unichr(0x02DA))
+				 chr(0x78)+chr(0x02DA))
 
 if __name__ == '__main__':
 	import sys
diff --git a/Tests/feaLib/STAT2.fea b/Tests/feaLib/STAT2.fea
new file mode 100644
index 0000000..2595a9a
--- /dev/null
+++ b/Tests/feaLib/STAT2.fea
@@ -0,0 +1,4 @@
+table STAT {
+    ElidedFallbackName { name "Roman"; };
+    DesignAxis zonk 0 { name "Zonkey"; };'
+} STAT;
diff --git a/Tests/feaLib/ast_test.py b/Tests/feaLib/ast_test.py
new file mode 100644
index 0000000..4462f05
--- /dev/null
+++ b/Tests/feaLib/ast_test.py
@@ -0,0 +1,25 @@
+from fontTools.feaLib import ast
+import unittest
+
+
+class AstTest(unittest.TestCase):
+    def test_glyphname_escape(self):
+        statement = ast.GlyphClass()
+        for name in ("BASE", "NULL", "foo", "a"):
+            statement.append(ast.GlyphName(name))
+        self.assertEqual(statement.asFea(), r"[\BASE \NULL foo a]")
+
+    def test_valuerecord_none(self):
+        statement = ast.ValueRecord(xPlacement=10, xAdvance=20)
+        self.assertEqual(statement.asFea(), "<10 0 20 0>")
+
+    def test_non_object_location(self):
+        el = ast.Element(location=("file.fea", 1, 2))
+        self.assertEqual(el.location.file, "file.fea")
+        self.assertEqual(el.location.line, 1)
+        self.assertEqual(el.location.column, 2)
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/feaLib/builder_test.py b/Tests/feaLib/builder_test.py
index ff82b2f..97fc0f0 100644
--- a/Tests/feaLib/builder_test.py
+++ b/Tests/feaLib/builder_test.py
@@ -1,6 +1,4 @@
-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
@@ -9,10 +7,13 @@
 from fontTools.feaLib import ast
 from fontTools.feaLib.lexer import Lexer
 import difflib
+from io import StringIO
 import os
+import re
 import shutil
 import sys
 import tempfile
+import logging
 import unittest
 
 
@@ -41,8 +42,9 @@
         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
+        by feature lookup sub table uni0327 uni0328 e.fina
     """.split()
+    glyphs.extend("cid{:05d}".format(cid) for cid in range(800, 1001 + 1))
     font = TTFont()
     font.setGlyphOrder(glyphs)
     return font
@@ -51,7 +53,7 @@
 class BuilderTest(unittest.TestCase):
     # Feature files in data/*.fea; output gets compared to data/*.ttx.
     TEST_FEATURE_FILES = """
-        Attach enum markClass language_required
+        Attach cid_range 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
@@ -59,15 +61,20 @@
         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
+        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
+        bug512 bug514 bug568 bug633 bug1307 bug1459 bug2276
         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 ChainSubstSubtable SubstSubtable ChainPosSubtable 
+        LigatureSubtable AlternateSubtable MultipleSubstSubtable 
+        SingleSubstSubtable aalt_chain_contextual_subst AlternateChained 
+        MultipleLookupsPerGlyph MultipleLookupsPerGlyph2 GSUB_6_formats
+        GSUB_5_formats delete_glyph STAT_test STAT_test_elidedFallbackNameID
     """.split()
 
     def __init__(self, methodName):
@@ -101,39 +108,52 @@
         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.
+                # Elide ttFont attributes because ttLibVersion may change.
                 if line.startswith("<ttFont "):
-                    lines.append("<ttFont>" + os.linesep)
+                    lines.append("<ttFont>\n")
                 else:
-                    lines.append(line.rstrip() + os.linesep)
+                    lines.append(line.rstrip() + "\n")
         return lines
 
-    def expect_ttx(self, font, expected_ttx):
+    def expect_ttx(self, font, expected_ttx, replace=None):
         path = self.temp_path(suffix=".ttx")
         font.saveXML(path, tables=['head', 'name', 'BASE', 'GDEF', 'GSUB',
-                                   'GPOS', 'OS/2', 'hhea', 'vhea'])
+                                   'GPOS', 'OS/2', 'STAT', 'hhea', 'vhea'])
         actual = self.read_ttx(path)
         expected = self.read_ttx(expected_ttx)
+        if replace:
+            for i in range(len(expected)):
+                for k, v in replace.items():
+                    expected[i] = expected[i].replace(k, v)
         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):
+    def build(self, featureFile, tables=None):
         font = makeTTFont()
-        addOpenTypeFeaturesFromString(font, featureFile)
+        addOpenTypeFeaturesFromString(font, featureFile, tables=tables)
         return font
 
     def check_feature_file(self, name):
         font = makeTTFont()
-        addOpenTypeFeatures(font, self.getpath("%s.fea" % name))
+        feapath = self.getpath("%s.fea" % name)
+        addOpenTypeFeatures(font, feapath)
         self.expect_ttx(font, self.getpath("%s.ttx" % name))
-        # Make sure we can produce binary OpenType tables, not just XML.
+        # Check that:
+        # 1) tables do compile (only G* tables as long as we have a mock font)
+        # 2) dumping after save-reload yields the same TTX dump as before
         for tag in ('GDEF', 'GSUB', 'GPOS'):
             if tag in font:
-                font[tag].compile(font)
+                data = font[tag].compile(font)
+                font[tag].decompile(data, font)
+        self.expect_ttx(font, self.getpath("%s.ttx" % name))
+        # Optionally check a debug dump.
+        debugttx = self.getpath("%s-debug.ttx" % name)
+        if os.path.exists(debugttx):
+            addOpenTypeFeatures(font, feapath, debug=True)
+            self.expect_ttx(font, debugttx, replace = {"__PATH__": feapath})
 
     def check_fea2fea_file(self, name, base=None, parser=Parser):
         font = makeTTFont()
@@ -185,6 +205,17 @@
             "    sub A from [A.alt1 A.alt2];"
             "} test;")
 
+    def test_singleSubst_multipleIdenticalSubstitutionsForSameGlyph_info(self):
+        logger = logging.getLogger("fontTools.feaLib.builder")
+        with CapturingLogHandler(logger, "INFO") as captor:
+            self.build(
+                "feature test {"
+                "    sub A by A.sc;"
+                "    sub B by B.sc;"
+                "    sub A by A.sc;"
+                "} test;")
+        captor.assertRegex('Removing duplicate single substitution from glyph "A" to "A.sc"')
+
     def test_multipleSubst_multipleSubstitutionsForSameGlyph(self):
         self.assertRaisesRegex(
             FeatureLibError,
@@ -193,19 +224,43 @@
             "feature test {"
             "    sub f_f_i by f f i;"
             "    sub c_t by c t;"
-            "    sub f_f_i by f f i;"
+            "    sub f_f_i by f_f i;"
             "} test;")
 
-    def test_pairPos_redefinition(self):
-        self.assertRaisesRegex(
-            FeatureLibError,
-            r"Already defined position for pair A B "
-            "at .*:2:[0-9]+",  # :2: = line 2
-            self.build,
-            "feature test {\n"
-            "    pos A B 123;\n"  # line 2
-            "    pos A B 456;\n"
-            "} test;\n")
+    def test_multipleSubst_multipleIdenticalSubstitutionsForSameGlyph_info(self):
+        logger = logging.getLogger("fontTools.feaLib.builder")
+        with CapturingLogHandler(logger, "INFO") as captor:
+            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;")
+        captor.assertRegex(r"Removing duplicate multiple substitution from glyph \"f_f_i\" to \('f', 'f', 'i'\)")
+
+    def test_pairPos_redefinition_warning(self):
+        # https://github.com/fonttools/fonttools/issues/1147
+        logger = logging.getLogger("fontTools.otlLib.builder")
+        with CapturingLogHandler(logger, "DEBUG") 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(
@@ -269,6 +324,17 @@
             "it must be the first of the languagesystem statements",
             self.build, "languagesystem latn TRK; languagesystem DFLT dflt;")
 
+    def test_languagesystem_DFLT_not_preceding(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "languagesystems using the \"DFLT\" script tag must "
+            "precede all other languagesystems",
+            self.build,
+            "languagesystem DFLT dflt; "
+            "languagesystem latn dflt; "
+            "languagesystem DFLT fooo; "
+        )
+
     def test_script(self):
         builder = Builder(makeTTFont(), (None, None))
         builder.start_feature(location=None, name='test')
@@ -287,6 +353,12 @@
             "Script statements are not allowed within \"feature size\"",
             self.build, "feature size { script latn; } size;")
 
+    def test_script_in_standalone_lookup(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Script statements are not allowed within standalone lookup blocks",
+            self.build, "lookup test { script latn; } test;")
+
     def test_language(self):
         builder = Builder(makeTTFont(), (None, None))
         builder.add_language_system(None, 'latn', 'FRA ')
@@ -300,11 +372,7 @@
         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)
+        self.assertEqual(builder.language_systems, {('latn', 'FRA ')})
 
     def test_language_in_aalt_feature(self):
         self.assertRaisesRegex(
@@ -318,6 +386,12 @@
             "Language statements are not allowed within \"feature size\"",
             self.build, "feature size { language FRA; } size;")
 
+    def test_language_in_standalone_lookup(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Language statements are not allowed within standalone lookup blocks",
+            self.build, "lookup test { language FRA; } test;")
+
     def test_language_required_duplicate(self):
         self.assertRaisesRegex(
             FeatureLibError,
@@ -372,6 +446,223 @@
             "Lookup blocks cannot be placed inside 'aalt' features",
             self.build, "feature aalt {lookup L {} L;} aalt;")
 
+    def test_chain_subst_refrences_GPOS_looup(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Missing index of the specified lookup, might be a positioning lookup",
+            self.build,
+            "lookup dummy { pos a 50; } dummy;"
+            "feature test {"
+            "    sub a' lookup dummy b;"
+            "} test;"
+        )
+
+    def test_chain_pos_refrences_GSUB_looup(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Missing index of the specified lookup, might be a substitution lookup",
+            self.build,
+            "lookup dummy { sub a by A; } dummy;"
+            "feature test {"
+            "    pos a' lookup dummy b;"
+            "} test;"
+        )
+
+    def test_STAT_elidedfallbackname_already_defined(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'ElidedFallbackName is already set.',
+            self.build,
+            'table name {'
+            '   nameid 256 "Roman"; '
+            '} name;'
+            'table STAT {'
+            '    ElidedFallbackName { name "Roman"; };'
+            '    ElidedFallbackNameID 256;'
+            '} STAT;')
+
+    def test_STAT_elidedfallbackname_set_twice(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'ElidedFallbackName is already set.',
+            self.build,
+            'table name {'
+            '   nameid 256 "Roman"; '
+            '} name;'
+            'table STAT {'
+            '    ElidedFallbackName { name "Roman"; };'
+            '    ElidedFallbackName { name "Italic"; };'
+            '} STAT;')
+
+    def test_STAT_elidedfallbacknameID_already_defined(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'ElidedFallbackNameID is already set.',
+            self.build,
+            'table name {'
+            '   nameid 256 "Roman"; '
+            '} name;'
+            'table STAT {'
+            '    ElidedFallbackNameID 256;'
+            '    ElidedFallbackName { name "Roman"; };'
+            '} STAT;')
+
+    def test_STAT_elidedfallbacknameID_not_in_name_table(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'ElidedFallbackNameID 256 points to a nameID that does not '
+            'exist in the "name" table',
+            self.build,
+            'table name {'
+            '   nameid 257 "Roman"; '
+            '} name;'
+            'table STAT {'
+            '    ElidedFallbackNameID 256;'
+            '    DesignAxis opsz 1 { name "Optical Size"; };'
+            '} STAT;')
+
+    def test_STAT_design_axis_name(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Expected "name"',
+            self.build,
+            'table name {'
+            '   nameid 256 "Roman"; '
+            '} name;'
+            'table STAT {'
+            '    ElidedFallbackName { name "Roman"; };'
+            '    DesignAxis opsz 0 { badtag "Optical Size"; };'
+            '} STAT;')
+
+    def test_STAT_duplicate_design_axis_name(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'DesignAxis already defined for tag "opsz".',
+            self.build,
+            'table name {'
+            '   nameid 256 "Roman"; '
+            '} name;'
+            'table STAT {'
+            '    ElidedFallbackName { name "Roman"; };'
+            '    DesignAxis opsz 0 { name "Optical Size"; };'
+            '    DesignAxis opsz 1 { name "Optical Size"; };'
+            '} STAT;')
+
+    def test_STAT_design_axis_duplicate_order(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "DesignAxis already defined for axis number 0.",
+            self.build,
+            'table name {'
+            '   nameid 256 "Roman"; '
+            '} name;'
+            'table STAT {'
+            '    ElidedFallbackName { name "Roman"; };'
+            '    DesignAxis opsz 0 { name "Optical Size"; };'
+            '    DesignAxis wdth 0 { name "Width"; };'
+            '    AxisValue {'
+            '         location opsz 8;'
+            '         location wdth 400;'
+            '         name "Caption";'
+            '     };'
+            '} STAT;')
+
+    def test_STAT_undefined_tag(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'DesignAxis not defined for wdth.',
+            self.build,
+            'table name {'
+            '   nameid 256 "Roman"; '
+            '} name;'
+            'table STAT {'
+            '    ElidedFallbackName { name "Roman"; };'
+            '    DesignAxis opsz 0 { name "Optical Size"; };'
+            '    AxisValue { '
+            '        location wdth 125; '
+            '        name "Wide"; '
+            '    };'
+            '} STAT;')
+
+    def test_STAT_axis_value_format4(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Axis tag wdth already defined.',
+            self.build,
+            'table name {'
+            '   nameid 256 "Roman"; '
+            '} name;'
+            'table STAT {'
+            '    ElidedFallbackName { name "Roman"; };'
+            '    DesignAxis opsz 0 { name "Optical Size"; };'
+            '    DesignAxis wdth 1 { name "Width"; };'
+            '    DesignAxis wght 2 { name "Weight"; };'
+            '    AxisValue { '
+            '        location opsz 8; '
+            '        location wdth 125; '
+            '        location wdth 125; '
+            '        location wght 500; '
+            '        name "Caption Medium Wide"; '
+            '    };'
+            '} STAT;')
+
+    def test_STAT_duplicate_axis_value_record(self):
+        # Test for Duplicate AxisValueRecords even when the definition order
+        # is different.
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'An AxisValueRecord with these values is already defined.',
+            self.build,
+            'table name {'
+            '   nameid 256 "Roman"; '
+            '} name;'
+            'table STAT {'
+            '    ElidedFallbackName { name "Roman"; };'
+            '    DesignAxis opsz 0 { name "Optical Size"; };'
+            '    DesignAxis wdth 1 { name "Width"; };'
+            '    AxisValue {'
+            '         location opsz 8;'
+            '         location wdth 400;'
+            '         name "Caption";'
+            '     };'
+            '    AxisValue {'
+            '         location wdth 400;'
+            '         location opsz 8;'
+            '         name "Caption";'
+            '     };'
+            '} STAT;')
+
+    def test_STAT_axis_value_missing_location(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Expected "Axis location"',
+            self.build,
+            'table name {'
+            '   nameid 256 "Roman"; '
+            '} name;'
+            'table STAT {'
+            '    ElidedFallbackName {   name "Roman"; '
+            '};'
+            '    DesignAxis opsz 0 { name "Optical Size"; };'
+            '    AxisValue { '
+            '        name "Wide"; '
+            '    };'
+            '} STAT;')
+
+    def test_STAT_invalid_location_tag(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Tags cannot be longer than 4 characters',
+            self.build,
+            'table name {'
+            '   nameid 256 "Roman"; '
+            '} name;'
+            'table STAT {'
+            '    ElidedFallbackName { name "Roman"; '
+            '                         name 3 1 0x0411 "ローマン"; }; '
+            '    DesignAxis width 0 { name "Width"; };'
+            '} STAT;')
+
     def test_extensions(self):
         class ast_BaseClass(ast.MarkClass):
             def asFea(self, indent=""):
@@ -423,7 +714,8 @@
                         m = self.expect_markClass_reference_()
                         marks.append(m)
                 self.expect_symbol_(";")
-                return self.ast.MarkBasePosStatement(location, base, marks)
+                return self.ast.MarkBasePosStatement(base, marks,
+                                                     location=location)
 
             def parseBaseClass(self):
                 if not hasattr(self.doc_, 'baseClasses'):
@@ -438,7 +730,8 @@
                     baseClass = ast_BaseClass(name)
                     self.doc_.baseClasses[name] = baseClass
                     self.glyphclasses_.define(name, baseClass)
-                bcdef = ast_BaseClassDefinition(location, baseClass, anchor, glyphs)
+                bcdef = ast_BaseClassDefinition(baseClass, anchor, glyphs,
+                                                location=location)
                 baseClass.addDefinition(bcdef)
                 return bcdef
 
@@ -469,6 +762,81 @@
             "    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(NotImplementedError, self.build, "", tables={"FOO"})
+
+    def test_build_pre_parsed_ast_featurefile(self):
+        f = StringIO("feature liga {sub f i by f_i;} liga;")
+        tree = Parser(f).parse()
+        font = makeTTFont()
+        addOpenTypeFeatures(font, tree)
+        assert "GSUB" in font
+
+    def test_unsupported_subtable_break(self):
+        logger = logging.getLogger("fontTools.otlLib.builder")
+        with CapturingLogHandler(logger, level='WARNING') as captor:
+            self.build(
+                "feature test {"
+                "    pos a 10;"
+                "    subtable;"
+                "    pos b 10;"
+                "} test;"
+            )
+
+        captor.assertRegex(
+            '<features>:1:32: unsupported "subtable" statement for lookup type'
+        )
+
+    def test_skip_featureNames_if_no_name_table(self):
+        features = (
+            "feature ss01 {"
+            "    featureNames {"
+            '        name "ignored as we request to skip name table";'
+            "    };"
+            "    sub A by A.alt1;"
+            "} ss01;"
+        )
+        font = self.build(features, tables=["GSUB"])
+        self.assertIn("GSUB", font)
+        self.assertNotIn("name", font)
+
+    def test_singlePos_multiplePositionsForSameGlyph(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Already defined different position for glyph",
+            self.build,
+            "lookup foo {"
+            "    pos A -45; "
+            "    pos A 45; "
+            "} foo;")
+
+    def test_pairPos_enumRuleOverridenBySinglePair_DEBUG(self):
+        logger = logging.getLogger("fontTools.otlLib.builder")
+        with CapturingLogHandler(logger, "DEBUG") as captor:
+            self.build(
+                "feature test {"
+                "    enum pos A [V Y] -80;"
+                "    pos A V -75;"
+                "} test;")
+        captor.assertRegex('Already defined position for pair A V at')
+
+    def test_ignore_empty_lookup_block(self):
+        # https://github.com/fonttools/fonttools/pull/2277
+        font = self.build(
+            "lookup EMPTY { ; } EMPTY;"
+            "feature ss01 { lookup EMPTY; } ss01;"
+        )
+        assert "GPOS" not in font
+        assert "GSUB" not in font
+
 
 def generate_feature_file_test(name):
     return lambda self: self.check_feature_file(name)
diff --git a/Tests/feaLib/data/AlternateChained.fea b/Tests/feaLib/data/AlternateChained.fea
new file mode 100644
index 0000000..4517769
--- /dev/null
+++ b/Tests/feaLib/data/AlternateChained.fea
@@ -0,0 +1,3 @@
+feature test {
+  sub A B a' [Y y] Z from [a.alt1 a.alt2 a.alt3];
+} test;
diff --git a/Tests/feaLib/data/AlternateChained.ttx b/Tests/feaLib/data/AlternateChained.ttx
new file mode 100644
index 0000000..e02bb7d
--- /dev/null
+++ b/Tests/feaLib/data/AlternateChained.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=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=2 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="B"/>
+          </BacktrackCoverage>
+          <BacktrackCoverage index="1">
+            <Glyph value="A"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="a"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=2 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="Y"/>
+            <Glyph value="y"/>
+          </LookAheadCoverage>
+          <LookAheadCoverage index="1">
+            <Glyph value="Z"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </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"/>
+            <Alternate glyph="a.alt3"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/AlternateSubtable.fea b/Tests/feaLib/data/AlternateSubtable.fea
new file mode 100644
index 0000000..c4bce3c
--- /dev/null
+++ b/Tests/feaLib/data/AlternateSubtable.fea
@@ -0,0 +1,5 @@
+feature test {
+    sub A from [A.alt1 A.alt2];
+    subtable;
+    sub B from [B.alt1 B.alt2];
+} test;
diff --git a/Tests/feaLib/data/AlternateSubtable.ttx b/Tests/feaLib/data/AlternateSubtable.ttx
new file mode 100644
index 0000000..e8119ba
--- /dev/null
+++ b/Tests/feaLib/data/AlternateSubtable.ttx
@@ -0,0 +1,52 @@
+<?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="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <AlternateSubst index="0">
+          <AlternateSet glyph="A">
+            <Alternate glyph="A.alt1"/>
+            <Alternate glyph="A.alt2"/>
+          </AlternateSet>
+        </AlternateSubst>
+        <AlternateSubst index="1">
+          <AlternateSet glyph="B">
+            <Alternate glyph="B.alt1"/>
+            <Alternate glyph="B.alt2"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ChainPosSubtable.fea b/Tests/feaLib/data/ChainPosSubtable.fea
new file mode 100644
index 0000000..e64f886
--- /dev/null
+++ b/Tests/feaLib/data/ChainPosSubtable.fea
@@ -0,0 +1,7 @@
+feature test {
+    pos X [A - B]' -40 B' -40 A' -40 Y;
+    subtable;
+    pos X A' -111 Y;
+    subtable;
+    pos X B' -40 A' -111 [A - C]' -40 Y;
+} test;
diff --git a/Tests/feaLib/data/ChainPosSubtable.ttx b/Tests/feaLib/data/ChainPosSubtable.ttx
new file mode 100644
index 0000000..695dd86
--- /dev/null
+++ b/Tests/feaLib/data/ChainPosSubtable.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=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=5 -->
+      <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="1">
+          <Coverage>
+            <Glyph value="A"/>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=1 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="X"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="Y"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="2"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
+        </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="3"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="3"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="2">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="4"/>
+          </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"/>
+          </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>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <!-- ValueCount=2 -->
+          <Value index="0" XAdvance="-111"/>
+          <Value index="1" XAdvance="-40"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="4">
+        <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>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ChainSubstSubtable.fea b/Tests/feaLib/data/ChainSubstSubtable.fea
new file mode 100644
index 0000000..ec24880
--- /dev/null
+++ b/Tests/feaLib/data/ChainSubstSubtable.fea
@@ -0,0 +1,9 @@
+feature test {
+    sub A G' by G.swash;
+    subtable;
+    sub A H' by H.swash;
+    subtable;
+    sub A G' by g;
+    subtable;
+    sub A H' by H.swash;
+} test;
diff --git a/Tests/feaLib/data/ChainSubstSubtable.ttx b/Tests/feaLib/data/ChainSubstSubtable.ttx
new file mode 100644
index 0000000..75348dc
--- /dev/null
+++ b/Tests/feaLib/data/ChainSubstSubtable.ttx
@@ -0,0 +1,136 @@
+<?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=5 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=4 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="A"/>
+          </BacktrackCoverage>
+          <!-- 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=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="A"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="H"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="2"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="2" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="A"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="G"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="3"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="3" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="A"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="H"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="4"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="G" out="G.swash"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="H" out="H.swash"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="G" out="g"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="H" out="H.swash"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GPOS_1.ttx b/Tests/feaLib/data/GPOS_1.ttx
index bd32013..1a24f0b 100644
--- a/Tests/feaLib/data/GPOS_1.ttx
+++ b/Tests/feaLib/data/GPOS_1.ttx
@@ -40,8 +40,26 @@
       <Lookup index="0">
         <LookupType value="1"/>
         <LookupFlag value="0"/>
-        <!-- SubTableCount=8 -->
-        <SinglePos index="0" Format="1">
+        <!-- SubTableCount=7 -->
+        <SinglePos index="0" Format="2">
+          <Coverage>
+            <Glyph value="four"/>
+            <Glyph value="six"/>
+            <Glyph value="seven"/>
+            <Glyph value="eight"/>
+            <Glyph value="nine"/>
+            <Glyph value="four.oldstyle"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <!-- ValueCount=6 -->
+          <Value index="0" XAdvance="400"/>
+          <Value index="1" XAdvance="-200"/>
+          <Value index="2" XAdvance="-100"/>
+          <Value index="3" XAdvance="-100"/>
+          <Value index="4" XAdvance="-100"/>
+          <Value index="5" XAdvance="401"/>
+        </SinglePos>
+        <SinglePos index="1" Format="1">
           <Coverage>
             <Glyph value="one"/>
             <Glyph value="two"/>
@@ -51,28 +69,7 @@
           <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">
+        <SinglePos index="2" Format="2">
           <Coverage>
             <Glyph value="P"/>
             <Glyph value="Q"/>
@@ -84,7 +81,7 @@
           <Value index="1" XPlacement="1" XAdvance="801"/>
           <Value index="2" XPlacement="1" XAdvance="802"/>
         </SinglePos>
-        <SinglePos index="4" Format="2">
+        <SinglePos index="3" Format="2">
           <Coverage>
             <Glyph value="S"/>
             <Glyph value="T"/>
@@ -96,7 +93,7 @@
           <Value index="1" XPlacement="1" YPlacement="1" XAdvance="804"/>
           <Value index="2" XPlacement="1" YPlacement="1" XAdvance="805"/>
         </SinglePos>
-        <SinglePos index="5" Format="1">
+        <SinglePos index="4" Format="1">
           <Coverage>
             <Glyph value="A"/>
             <Glyph value="B"/>
@@ -123,13 +120,13 @@
             </XAdvDevice>
           </Value>
         </SinglePos>
-        <SinglePos index="6" Format="1">
+        <SinglePos index="5" Format="1">
           <Coverage>
             <Glyph value="zero"/>
           </Coverage>
           <ValueFormat value="0"/>
         </SinglePos>
-        <SinglePos index="7" Format="1">
+        <SinglePos index="6" Format="1">
           <Coverage>
             <Glyph value="C"/>
           </Coverage>
diff --git a/Tests/feaLib/data/GPOS_1_zero.fea b/Tests/feaLib/data/GPOS_1_zero.fea
index bfb51db..2e738e2 100644
--- a/Tests/feaLib/data/GPOS_1_zero.fea
+++ b/Tests/feaLib/data/GPOS_1_zero.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/471
+# https://github.com/fonttools/fonttools/issues/471
 feature test {
     pos zero 0;
     pos four 500;
diff --git a/Tests/feaLib/data/GPOS_2.fea b/Tests/feaLib/data/GPOS_2.fea
index b1f26d1..2948148 100644
--- a/Tests/feaLib/data/GPOS_2.fea
+++ b/Tests/feaLib/data/GPOS_2.fea
@@ -1,7 +1,7 @@
 languagesystem DFLT dflt;
 
 # Mixes kerning between single glyphs, and class-based kerning.
-# https://github.com/behdad/fonttools/issues/456
+# https://github.com/fonttools/fonttools/issues/456
 lookup MixedKerning {
     pos v v 14;
     pos [D O Q] [T V W] -26;
diff --git a/Tests/feaLib/data/GPOS_2.ttx b/Tests/feaLib/data/GPOS_2.ttx
index 84dc819..c9a6c14 100644
--- a/Tests/feaLib/data/GPOS_2.ttx
+++ b/Tests/feaLib/data/GPOS_2.ttx
@@ -76,6 +76,7 @@
           <!-- Class2Count=2 -->
           <Class1Record index="0">
             <Class2Record index="0">
+              <Value1 XAdvance="0"/>
             </Class2Record>
             <Class2Record index="1">
               <Value1 XAdvance="-26"/>
diff --git a/Tests/feaLib/data/GPOS_2b.ttx b/Tests/feaLib/data/GPOS_2b.ttx
index 40f458f..8a892c1 100644
--- a/Tests/feaLib/data/GPOS_2b.ttx
+++ b/Tests/feaLib/data/GPOS_2b.ttx
@@ -50,6 +50,7 @@
           <!-- Class2Count=2 -->
           <Class1Record index="0">
             <Class2Record index="0">
+              <Value1 XAdvance="0"/>
             </Class2Record>
             <Class2Record index="1">
               <Value1 XAdvance="1"/>
@@ -79,6 +80,7 @@
           <!-- Class2Count=3 -->
           <Class1Record index="0">
             <Class2Record index="0">
+              <Value1 XAdvance="0"/>
             </Class2Record>
             <Class2Record index="1">
               <Value1 XAdvance="4"/>
@@ -89,8 +91,10 @@
           </Class1Record>
           <Class1Record index="1">
             <Class2Record index="0">
+              <Value1 XAdvance="0"/>
             </Class2Record>
             <Class2Record index="1">
+              <Value1 XAdvance="0"/>
             </Class2Record>
             <Class2Record index="2">
               <Value1 XAdvance="2"/>
@@ -114,6 +118,7 @@
           <!-- Class2Count=2 -->
           <Class1Record index="0">
             <Class2Record index="0">
+              <Value1 XPlacement="0" YPlacement="0" XAdvance="0" YAdvance="0"/>
             </Class2Record>
             <Class2Record index="1">
               <Value1 XPlacement="5" YPlacement="5" XAdvance="5" YAdvance="5"/>
diff --git a/Tests/feaLib/data/GPOS_4.fea b/Tests/feaLib/data/GPOS_4.fea
index cfd2d75..7c90ab6 100644
--- a/Tests/feaLib/data/GPOS_4.fea
+++ b/Tests/feaLib/data/GPOS_4.fea
@@ -6,7 +6,11 @@
 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;
+    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_5.fea b/Tests/feaLib/data/GPOS_5.fea
index b116539..a8f8536 100644
--- a/Tests/feaLib/data/GPOS_5.fea
+++ b/Tests/feaLib/data/GPOS_5.fea
@@ -5,14 +5,29 @@
 
 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 [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_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;
+    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_6.fea b/Tests/feaLib/data/GPOS_6.fea
index 37b2936..e54ff6e 100644
--- a/Tests/feaLib/data/GPOS_6.fea
+++ b/Tests/feaLib/data/GPOS_6.fea
@@ -5,6 +5,9 @@
 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;
+    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_8.ttx b/Tests/feaLib/data/GPOS_8.ttx
index 86f9756..4d79071 100644
--- a/Tests/feaLib/data/GPOS_8.ttx
+++ b/Tests/feaLib/data/GPOS_8.ttx
@@ -34,42 +34,40 @@
         <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">
+        <ChainContextPos index="0" Format="1">
+          <Coverage>
             <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>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=1 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="A"/>
+              <!-- InputGlyphCount=4 -->
+              <Input index="0" value="two"/>
+              <Input index="1" value="one"/>
+              <Input index="2" value="two"/>
+              <!-- 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>
+            </ChainPosRule>
+          </ChainPosRuleSet>
         </ChainContextPos>
       </Lookup>
       <Lookup index="1">
diff --git a/Tests/feaLib/data/GSUB_5_formats.fea b/Tests/feaLib/data/GSUB_5_formats.fea
new file mode 100644
index 0000000..3acb7ed
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_5_formats.fea
@@ -0,0 +1,20 @@
+lookup GSUB5f1 {
+    ignore sub three four;
+    ignore sub four five;
+} GSUB5f1;
+
+lookup GSUB5f2 {
+    ignore sub [a - z] [A - H] [I - Z];
+    ignore sub [a - z] [A - H] [I - Z];
+    ignore sub [a - z] [I - Z] [A - H];
+} GSUB5f2;
+
+lookup GSUB5f3 {
+    ignore sub e;
+} GSUB5f3;
+
+feature test {
+    lookup GSUB5f1;
+    lookup GSUB5f2;
+    lookup GSUB5f3;
+} test;
diff --git a/Tests/feaLib/data/GSUB_5_formats.ttx b/Tests/feaLib/data/GSUB_5_formats.ttx
new file mode 100644
index 0000000..80196aa
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_5_formats.ttx
@@ -0,0 +1,197 @@
+<?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=3 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+          <LookupListIndex index="2" value="2"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage>
+            <Glyph value="three"/>
+            <Glyph value="four"/>
+          </Coverage>
+          <!-- SubRuleSetCount=2 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=1 -->
+            <SubRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- SubstCount=0 -->
+              <Input index="0" value="four"/>
+            </SubRule>
+          </SubRuleSet>
+          <SubRuleSet index="1">
+            <!-- SubRuleCount=1 -->
+            <SubRule index="0">
+              <!-- GlyphCount=2 -->
+              <!-- SubstCount=0 -->
+              <Input index="0" value="five"/>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="2">
+          <Coverage>
+            <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"/>
+          </Coverage>
+          <ClassDef>
+            <ClassDef glyph="A" class="3"/>
+            <ClassDef glyph="B" class="3"/>
+            <ClassDef glyph="C" class="3"/>
+            <ClassDef glyph="D" class="3"/>
+            <ClassDef glyph="E" class="3"/>
+            <ClassDef glyph="F" class="3"/>
+            <ClassDef glyph="G" class="3"/>
+            <ClassDef glyph="H" class="3"/>
+            <ClassDef glyph="I" class="2"/>
+            <ClassDef glyph="J" class="2"/>
+            <ClassDef glyph="K" class="2"/>
+            <ClassDef glyph="L" class="2"/>
+            <ClassDef glyph="M" class="2"/>
+            <ClassDef glyph="N" class="2"/>
+            <ClassDef glyph="O" class="2"/>
+            <ClassDef glyph="P" class="2"/>
+            <ClassDef glyph="Q" class="2"/>
+            <ClassDef glyph="R" class="2"/>
+            <ClassDef glyph="S" class="2"/>
+            <ClassDef glyph="T" class="2"/>
+            <ClassDef glyph="U" class="2"/>
+            <ClassDef glyph="V" class="2"/>
+            <ClassDef glyph="W" class="2"/>
+            <ClassDef glyph="X" class="2"/>
+            <ClassDef glyph="Y" class="2"/>
+            <ClassDef glyph="Z" class="2"/>
+            <ClassDef glyph="a" class="1"/>
+            <ClassDef glyph="b" class="1"/>
+            <ClassDef glyph="c" class="1"/>
+            <ClassDef glyph="d" class="1"/>
+            <ClassDef glyph="e" class="1"/>
+            <ClassDef glyph="f" class="1"/>
+            <ClassDef glyph="g" class="1"/>
+            <ClassDef glyph="h" class="1"/>
+            <ClassDef glyph="i" class="1"/>
+            <ClassDef glyph="j" class="1"/>
+            <ClassDef glyph="k" class="1"/>
+            <ClassDef glyph="l" class="1"/>
+            <ClassDef glyph="m" class="1"/>
+            <ClassDef glyph="n" class="1"/>
+            <ClassDef glyph="o" class="1"/>
+            <ClassDef glyph="p" class="1"/>
+            <ClassDef glyph="q" class="1"/>
+            <ClassDef glyph="r" class="1"/>
+            <ClassDef glyph="s" class="1"/>
+            <ClassDef glyph="t" class="1"/>
+            <ClassDef glyph="u" class="1"/>
+            <ClassDef glyph="v" class="1"/>
+            <ClassDef glyph="w" class="1"/>
+            <ClassDef glyph="x" class="1"/>
+            <ClassDef glyph="y" class="1"/>
+            <ClassDef glyph="z" class="1"/>
+          </ClassDef>
+          <!-- SubClassSetCount=4 -->
+          <SubClassSet index="0">
+            <!-- SubClassRuleCount=0 -->
+          </SubClassSet>
+          <SubClassSet index="1">
+            <!-- SubClassRuleCount=3 -->
+            <SubClassRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=0 -->
+              <Class index="0" value="3"/>
+              <Class index="1" value="2"/>
+            </SubClassRule>
+            <SubClassRule index="1">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=0 -->
+              <Class index="0" value="3"/>
+              <Class index="1" value="2"/>
+            </SubClassRule>
+            <SubClassRule index="2">
+              <!-- GlyphCount=3 -->
+              <!-- SubstCount=0 -->
+              <Class index="0" value="2"/>
+              <Class index="1" value="3"/>
+            </SubClassRule>
+          </SubClassSet>
+          <SubClassSet index="2">
+            <!-- SubClassRuleCount=0 -->
+          </SubClassSet>
+          <SubClassSet index="3">
+            <!-- SubClassRuleCount=0 -->
+          </SubClassSet>
+        </ContextSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="3">
+          <!-- GlyphCount=1 -->
+          <!-- SubstCount=0 -->
+          <Coverage index="0">
+            <Glyph value="e"/>
+          </Coverage>
+        </ContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GSUB_6.fea b/Tests/feaLib/data/GSUB_6.fea
index 82fdac2..2230670 100644
--- a/Tests/feaLib/data/GSUB_6.fea
+++ b/Tests/feaLib/data/GSUB_6.fea
@@ -1,10 +1,10 @@
 lookup ChainedSingleSubst {
     sub [one two] three A' by A.sc;
-    sub [B-D]' seven [eight nine] by [B.sc-D.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;
+    sub [A - C a - c] [D d] E c_t' V [W w] [X - Z x - z] by c t;
 } ChainedMultipleSubst;
 
 lookup ChainedAlternateSubst {
diff --git a/Tests/feaLib/data/GSUB_6.ttx b/Tests/feaLib/data/GSUB_6.ttx
index f32e47d..18405d2 100644
--- a/Tests/feaLib/data/GSUB_6.ttx
+++ b/Tests/feaLib/data/GSUB_6.ttx
@@ -229,36 +229,30 @@
         <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">
+        <ChainContextSubst index="0" Format="1">
+          <Coverage>
             <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>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=1 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=1 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=3 -->
+              <Backtrack index="0" value="E"/>
+              <Backtrack index="1" value="D"/>
+              <Backtrack index="2" value="A"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=3 -->
+              <LookAhead index="0" value="V"/>
+              <LookAhead index="1" value="W"/>
+              <LookAhead index="2" value="X"/>
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </ChainSubRule>
+          </ChainSubRuleSet>
         </ChainContextSubst>
       </Lookup>
     </LookupList>
diff --git a/Tests/feaLib/data/GSUB_6_formats.fea b/Tests/feaLib/data/GSUB_6_formats.fea
new file mode 100644
index 0000000..44135d2
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_6_formats.fea
@@ -0,0 +1,20 @@
+lookup GSUB6f1 {
+    ignore sub one two three' four' five six seven;
+    ignore sub two one three' four' six five seven;
+} GSUB6f1;
+
+lookup GSUB6f2 {
+    ignore sub [A - H] [I - Z] [a - z]' [A - H]' [I - Z]';
+    ignore sub [I - Z] [A - H] [a - z]' [A - H]' [I - Z]';
+    ignore sub [A - H] [I - Z] [a - z]' [I - Z]' [A - H]';
+} GSUB6f2;
+
+lookup GSUB6f3 {
+    ignore sub [space comma semicolon] e';
+} GSUB6f3;
+
+feature test {
+    lookup GSUB6f1;
+    lookup GSUB6f2;
+    lookup GSUB6f3;
+} test;
diff --git a/Tests/feaLib/data/GSUB_6_formats.ttx b/Tests/feaLib/data/GSUB_6_formats.ttx
new file mode 100644
index 0000000..ad2a1c5
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_6_formats.ttx
@@ -0,0 +1,256 @@
+<?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=3 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+          <LookupListIndex index="2" value="2"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="1">
+          <Coverage>
+            <Glyph value="three"/>
+          </Coverage>
+          <!-- ChainSubRuleSetCount=1 -->
+          <ChainSubRuleSet index="0">
+            <!-- ChainSubRuleCount=2 -->
+            <ChainSubRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="two"/>
+              <Backtrack index="1" value="one"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="four"/>
+              <!-- LookAheadGlyphCount=3 -->
+              <LookAhead index="0" value="five"/>
+              <LookAhead index="1" value="six"/>
+              <LookAhead index="2" value="seven"/>
+              <!-- SubstCount=0 -->
+            </ChainSubRule>
+            <ChainSubRule index="1">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="one"/>
+              <Backtrack index="1" value="two"/>
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="four"/>
+              <!-- LookAheadGlyphCount=3 -->
+              <LookAhead index="0" value="six"/>
+              <LookAhead index="1" value="five"/>
+              <LookAhead index="2" value="seven"/>
+              <!-- SubstCount=0 -->
+            </ChainSubRule>
+          </ChainSubRuleSet>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage>
+            <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"/>
+          </Coverage>
+          <BacktrackClassDef>
+            <ClassDef glyph="A" class="2"/>
+            <ClassDef glyph="B" class="2"/>
+            <ClassDef glyph="C" class="2"/>
+            <ClassDef glyph="D" class="2"/>
+            <ClassDef glyph="E" class="2"/>
+            <ClassDef glyph="F" class="2"/>
+            <ClassDef glyph="G" class="2"/>
+            <ClassDef glyph="H" class="2"/>
+            <ClassDef glyph="I" class="1"/>
+            <ClassDef glyph="J" class="1"/>
+            <ClassDef glyph="K" class="1"/>
+            <ClassDef glyph="L" class="1"/>
+            <ClassDef glyph="M" class="1"/>
+            <ClassDef glyph="N" class="1"/>
+            <ClassDef glyph="O" class="1"/>
+            <ClassDef glyph="P" class="1"/>
+            <ClassDef glyph="Q" class="1"/>
+            <ClassDef glyph="R" class="1"/>
+            <ClassDef glyph="S" class="1"/>
+            <ClassDef glyph="T" class="1"/>
+            <ClassDef glyph="U" class="1"/>
+            <ClassDef glyph="V" class="1"/>
+            <ClassDef glyph="W" class="1"/>
+            <ClassDef glyph="X" class="1"/>
+            <ClassDef glyph="Y" class="1"/>
+            <ClassDef glyph="Z" class="1"/>
+          </BacktrackClassDef>
+          <InputClassDef>
+            <ClassDef glyph="A" class="3"/>
+            <ClassDef glyph="B" class="3"/>
+            <ClassDef glyph="C" class="3"/>
+            <ClassDef glyph="D" class="3"/>
+            <ClassDef glyph="E" class="3"/>
+            <ClassDef glyph="F" class="3"/>
+            <ClassDef glyph="G" class="3"/>
+            <ClassDef glyph="H" class="3"/>
+            <ClassDef glyph="I" class="2"/>
+            <ClassDef glyph="J" class="2"/>
+            <ClassDef glyph="K" class="2"/>
+            <ClassDef glyph="L" class="2"/>
+            <ClassDef glyph="M" class="2"/>
+            <ClassDef glyph="N" class="2"/>
+            <ClassDef glyph="O" class="2"/>
+            <ClassDef glyph="P" class="2"/>
+            <ClassDef glyph="Q" class="2"/>
+            <ClassDef glyph="R" class="2"/>
+            <ClassDef glyph="S" class="2"/>
+            <ClassDef glyph="T" class="2"/>
+            <ClassDef glyph="U" class="2"/>
+            <ClassDef glyph="V" class="2"/>
+            <ClassDef glyph="W" class="2"/>
+            <ClassDef glyph="X" class="2"/>
+            <ClassDef glyph="Y" class="2"/>
+            <ClassDef glyph="Z" class="2"/>
+            <ClassDef glyph="a" class="1"/>
+            <ClassDef glyph="b" class="1"/>
+            <ClassDef glyph="c" class="1"/>
+            <ClassDef glyph="d" class="1"/>
+            <ClassDef glyph="e" class="1"/>
+            <ClassDef glyph="f" class="1"/>
+            <ClassDef glyph="g" class="1"/>
+            <ClassDef glyph="h" class="1"/>
+            <ClassDef glyph="i" class="1"/>
+            <ClassDef glyph="j" class="1"/>
+            <ClassDef glyph="k" class="1"/>
+            <ClassDef glyph="l" class="1"/>
+            <ClassDef glyph="m" class="1"/>
+            <ClassDef glyph="n" class="1"/>
+            <ClassDef glyph="o" class="1"/>
+            <ClassDef glyph="p" class="1"/>
+            <ClassDef glyph="q" class="1"/>
+            <ClassDef glyph="r" class="1"/>
+            <ClassDef glyph="s" class="1"/>
+            <ClassDef glyph="t" class="1"/>
+            <ClassDef glyph="u" class="1"/>
+            <ClassDef glyph="v" class="1"/>
+            <ClassDef glyph="w" class="1"/>
+            <ClassDef glyph="x" class="1"/>
+            <ClassDef glyph="y" class="1"/>
+            <ClassDef glyph="z" class="1"/>
+          </InputClassDef>
+          <LookAheadClassDef>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=4 -->
+          <ChainSubClassSet index="0">
+            <!-- ChainSubClassRuleCount=0 -->
+          </ChainSubClassSet>
+          <ChainSubClassSet index="1">
+            <!-- ChainSubClassRuleCount=3 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="1"/>
+              <Backtrack index="1" value="2"/>
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="3"/>
+              <Input index="1" value="2"/>
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=0 -->
+            </ChainSubClassRule>
+            <ChainSubClassRule index="1">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="2"/>
+              <Backtrack index="1" value="1"/>
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="3"/>
+              <Input index="1" value="2"/>
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=0 -->
+            </ChainSubClassRule>
+            <ChainSubClassRule index="2">
+              <!-- BacktrackGlyphCount=2 -->
+              <Backtrack index="0" value="1"/>
+              <Backtrack index="1" value="2"/>
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="2"/>
+              <Input index="1" value="3"/>
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=0 -->
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+          <ChainSubClassSet index="2">
+            <!-- ChainSubClassRuleCount=0 -->
+          </ChainSubClassSet>
+          <ChainSubClassSet index="3">
+            <!-- ChainSubClassRuleCount=0 -->
+          </ChainSubClassSet>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="2">
+        <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=0 -->
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GSUB_8.fea b/Tests/feaLib/data/GSUB_8.fea
index 62c7bee..8f41749 100644
--- a/Tests/feaLib/data/GSUB_8.fea
+++ b/Tests/feaLib/data/GSUB_8.fea
@@ -2,7 +2,7 @@
 
 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];
+    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
diff --git a/Tests/feaLib/data/GSUB_error.fea b/Tests/feaLib/data/GSUB_error.fea
new file mode 100644
index 0000000..ae175c5
--- /dev/null
+++ b/Tests/feaLib/data/GSUB_error.fea
@@ -0,0 +1,13 @@
+# Trigger a parser error in the function parse_substitute_ in order to improve the error message.
+# Note that this is not a valid substitution, this test is made to trigger an error.
+
+languagesystem latn dflt;
+
+@base = [a e];
+@accents = [acute grave];
+
+feature abvs {
+    lookup lookup1 {
+        sub @base @accents by @base;
+    } lookup1;
+} abvs;
diff --git a/Tests/feaLib/data/LigatureCaretByIndex.fea b/Tests/feaLib/data/LigatureCaretByIndex.fea
index 1968172..5f74fc6 100644
--- a/Tests/feaLib/data/LigatureCaretByIndex.fea
+++ b/Tests/feaLib/data/LigatureCaretByIndex.fea
@@ -1,10 +1,6 @@
 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
index a758077..f3a378c 100644
--- a/Tests/feaLib/data/LigatureCaretByIndex.ttx
+++ b/Tests/feaLib/data/LigatureCaretByIndex.ttx
@@ -17,14 +17,11 @@
         </CaretValue>
       </LigGlyph>
       <LigGlyph index="1">
-        <!-- CaretCount=3 -->
+        <!-- CaretCount=2 -->
         <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>
diff --git a/Tests/feaLib/data/LigatureCaretByPos.fea b/Tests/feaLib/data/LigatureCaretByPos.fea
index a8ccf6f..e799c3c 100644
--- a/Tests/feaLib/data/LigatureCaretByPos.fea
+++ b/Tests/feaLib/data/LigatureCaretByPos.fea
@@ -1,10 +1,6 @@
 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
index 911db6b..8c0bf8c 100644
--- a/Tests/feaLib/data/LigatureCaretByPos.ttx
+++ b/Tests/feaLib/data/LigatureCaretByPos.ttx
@@ -23,16 +23,13 @@
         </CaretValue>
       </LigGlyph>
       <LigGlyph index="2">
-        <!-- CaretCount=3 -->
+        <!-- CaretCount=2 -->
         <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>
diff --git a/Tests/feaLib/data/LigatureSubtable.fea b/Tests/feaLib/data/LigatureSubtable.fea
new file mode 100644
index 0000000..aa4ec26
--- /dev/null
+++ b/Tests/feaLib/data/LigatureSubtable.fea
@@ -0,0 +1,5 @@
+feature test {
+    sub f f by f_f;
+    subtable;
+    sub f i by f_i;
+} test;
diff --git a/Tests/feaLib/data/LigatureSubtable.ttx b/Tests/feaLib/data/LigatureSubtable.ttx
new file mode 100644
index 0000000..e13451a
--- /dev/null
+++ b/Tests/feaLib/data/LigatureSubtable.ttx
@@ -0,0 +1,50 @@
+<?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="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="f" glyph="f_f"/>
+          </LigatureSet>
+        </LigatureSubst>
+        <LigatureSubst index="1">
+          <LigatureSet glyph="f">
+            <Ligature components="i" glyph="f_i"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/MultipleLookupsPerGlyph.fea b/Tests/feaLib/data/MultipleLookupsPerGlyph.fea
new file mode 100644
index 0000000..e0c2222
--- /dev/null
+++ b/Tests/feaLib/data/MultipleLookupsPerGlyph.fea
@@ -0,0 +1,11 @@
+lookup a_to_bc {
+	sub a by b c;
+} a_to_bc;
+
+lookup b_to_d {
+	sub b by d;
+} b_to_d;
+
+feature test {
+	sub a' lookup a_to_bc lookup b_to_d b;
+} test;
\ No newline at end of file
diff --git a/Tests/feaLib/data/MultipleLookupsPerGlyph.ttx b/Tests/feaLib/data/MultipleLookupsPerGlyph.ttx
new file mode 100644
index 0000000..927694c
--- /dev/null
+++ b/Tests/feaLib/data/MultipleLookupsPerGlyph.ttx
@@ -0,0 +1,76 @@
+<?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="2"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0">
+          <Substitution in="a" out="b,c"/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="b" out="d"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="a"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="b"/>
+          </LookAheadCoverage>
+          <!-- SubstCount=2 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="1">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/MultipleLookupsPerGlyph2.fea b/Tests/feaLib/data/MultipleLookupsPerGlyph2.fea
new file mode 100644
index 0000000..5a9d19b
--- /dev/null
+++ b/Tests/feaLib/data/MultipleLookupsPerGlyph2.fea
@@ -0,0 +1,11 @@
+lookup a_reduce_sb {
+       pos a <-80 0 -160 0>;
+} a_reduce_sb;
+
+lookup a_raise {
+       pos a <0 100 0 0>;
+} a_raise;
+
+feature test {
+       pos a' lookup a_reduce_sb lookup a_raise b;
+} test;
\ No newline at end of file
diff --git a/Tests/feaLib/data/MultipleLookupsPerGlyph2.ttx b/Tests/feaLib/data/MultipleLookupsPerGlyph2.ttx
new file mode 100644
index 0000000..008d95b
--- /dev/null
+++ b/Tests/feaLib/data/MultipleLookupsPerGlyph2.ttx
@@ -0,0 +1,84 @@
+<?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="2"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="a"/>
+          </Coverage>
+          <ValueFormat value="5"/>
+          <Value XPlacement="-80" XAdvance="-160"/>
+        </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="2"/>
+          <Value YPlacement="100"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="a"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="b"/>
+          </LookAheadCoverage>
+          <!-- PosCount=2 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="0"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/MultipleSubstSubtable.fea b/Tests/feaLib/data/MultipleSubstSubtable.fea
new file mode 100644
index 0000000..4964037
--- /dev/null
+++ b/Tests/feaLib/data/MultipleSubstSubtable.fea
@@ -0,0 +1,5 @@
+feature test {
+    sub c_t by c t;
+    subtable;
+    sub f_i by f i;
+} test;
diff --git a/Tests/feaLib/data/MultipleSubstSubtable.ttx b/Tests/feaLib/data/MultipleSubstSubtable.ttx
new file mode 100644
index 0000000..6b3afd4
--- /dev/null
+++ b/Tests/feaLib/data/MultipleSubstSubtable.ttx
@@ -0,0 +1,46 @@
+<?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="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <MultipleSubst index="0">
+          <Substitution in="c_t" out="c,t"/>
+        </MultipleSubst>
+        <MultipleSubst index="1">
+          <Substitution in="f_i" out="f,i"/>
+        </MultipleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/PairPosSubtable.fea b/Tests/feaLib/data/PairPosSubtable.fea
new file mode 100644
index 0000000..cb78801
--- /dev/null
+++ b/Tests/feaLib/data/PairPosSubtable.fea
@@ -0,0 +1,29 @@
+languagesystem DFLT dflt;
+languagesystem latn dflt;
+
+@group1 = [b o];
+@group2 = [c d];
+@group3 = [v w];
+@group4 = [];
+
+lookup kernlookup {
+    pos A V -34;
+    subtable;
+    pos @group1 @group2 -12;
+    subtable;
+    pos @group1 @group3 -10;
+    pos @group3 @group2 -20;
+    subtable;
+    pos @group4 @group1 -10;
+    pos @group4 @group4 -10;
+} 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..2d78f64
--- /dev/null
+++ b/Tests/feaLib/data/PairPosSubtable.ttx
@@ -0,0 +1,134 @@
+<?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="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="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">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-12"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+        <PairPos index="2" Format="2">
+          <Coverage>
+            <Glyph value="b"/>
+            <Glyph value="o"/>
+            <Glyph value="v"/>
+            <Glyph value="w"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+            <ClassDef glyph="b" class="1"/>
+            <ClassDef glyph="o" class="1"/>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="c" class="2"/>
+            <ClassDef glyph="d" class="2"/>
+            <ClassDef glyph="v" class="1"/>
+            <ClassDef glyph="w" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=3 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-20"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-10"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/STAT_bad.fea b/Tests/feaLib/data/STAT_bad.fea
new file mode 100644
index 0000000..8ec887f
--- /dev/null
+++ b/Tests/feaLib/data/STAT_bad.fea
@@ -0,0 +1,96 @@
+# bad fea file: Testing DesignAxis tag with incorrect label
+table name {
+	nameid 25 "TestFont";
+} name;
+
+
+table STAT {
+
+   ElidedFallbackName { name "Roman"; };
+
+   DesignAxis opsz 0 { badtag "Optical Size"; };  #'badtag' instead of 'name' is incorrect
+   DesignAxis wdth 1 { name "Width"; };
+   DesignAxis wght 2 { name "Weight"; };
+   DesignAxis ital 3 { name "Italic"; };
+
+   AxisValue {
+      location opsz 8 5 9;
+      location wdth 300 350 450;
+      name "Caption";
+   };
+
+   AxisValue {
+      location opsz 11 9 12;
+      name "Text";
+      flag OlderSiblingFontAttribute ElidableAxisValueName ;
+   };
+
+   AxisValue {
+      location opsz 16.7 12 24;
+      name "Subhead";
+   };
+
+   AxisValue {
+      location opsz 72 24 72;
+      name "Display";
+   };
+
+   AxisValue {
+      location wdth 80 80 89;
+      name "Condensed";
+   };
+
+   AxisValue {
+      location wdth 90 90 96;
+      name "Semicondensed";
+   };
+
+   AxisValue {
+      location wdth 100 97 101;
+      name "Normal";
+      flag ElidableAxisValueName;
+   };
+
+   AxisValue {
+      location wdth 125 102 125;
+      name "Extended";
+   };
+
+   AxisValue {
+      location wght 300 300 349;
+      name "Light";
+   };
+
+   AxisValue {
+      location wght 400 350 449;
+      name "Regular";
+      flag ElidableAxisValueName;
+   };
+
+   AxisValue {
+      location wght 500 450 549;
+      name "Medium";
+   };
+
+   AxisValue {
+      location wght 600 550 649;
+      name "Semibold";
+   };
+
+   AxisValue {
+      location wght 700 650 749;
+      name "Bold";
+   };
+
+   AxisValue {
+      location wght 900 750 900;
+      name "Black";
+   };
+
+   AxisValue {
+      location ital 0;
+      name "Roman";
+      flag ElidableAxisValueName;
+   };
+
+} STAT;
diff --git a/Tests/feaLib/data/STAT_test.fea b/Tests/feaLib/data/STAT_test.fea
new file mode 100644
index 0000000..0103637
--- /dev/null
+++ b/Tests/feaLib/data/STAT_test.fea
@@ -0,0 +1,109 @@
+table name {
+	nameid 25 "TestFont";
+} name;
+
+
+table STAT {
+
+    ElidedFallbackName {
+        name "Roman";
+        name 3 1 1041 "ローマン";
+    };
+
+    DesignAxis opsz 0 {
+        name "Optical Size";
+    };
+
+    DesignAxis wdth 1 {
+        name "Width";
+    };
+
+    DesignAxis wght 2 {
+        name "Weight";
+    };
+
+    DesignAxis ital 3 {
+        name "Italic";
+    };  # here comment
+
+    AxisValue {
+        location opsz 8;  # comment here
+        location wdth 400; # another comment
+        name "Caption"; # more comments
+    };
+
+    AxisValue {
+        location opsz 11 9 12;
+        name "Text";
+        flag OlderSiblingFontAttribute ElidableAxisValueName;
+    };
+
+    AxisValue {
+        location opsz 16.7 12 24;
+        name "Subhead";
+    };
+
+    AxisValue {
+        location opsz 72 24 72;
+        name "Display";
+    };
+
+    AxisValue {
+        location wdth 80 80 89;
+        name "Condensed";
+    };
+
+    AxisValue {
+        location wdth 90 90 96;
+        name "Semicondensed";
+    };
+
+    AxisValue {
+        location wdth 100 97 101;
+        name "Normal";
+        flag ElidableAxisValueName;
+    };
+
+    AxisValue {
+        location wdth 125 102 125;
+        name "Extended";
+    };
+
+    AxisValue {
+        location wght 300 300 349;
+        name "Light";
+    };
+
+    AxisValue {
+        location wght 400 350 449;
+        name "Regular";
+        flag ElidableAxisValueName;
+    };
+
+    AxisValue {
+        location wght 500 450 549;
+        name "Medium";
+    };
+
+    AxisValue {
+        location wght 600 550 649;
+        name "Semibold";
+    };
+
+    AxisValue {
+        location wght 700 650 749;
+        name "Bold";
+    };
+
+    AxisValue {
+        location wght 900 750 900;
+        name "Black";
+    };
+
+    AxisValue {
+        location ital 0;
+        name "Roman";
+        flag ElidableAxisValueName; # flag comment
+    };
+
+} STAT;
diff --git a/Tests/feaLib/data/STAT_test.ttx b/Tests/feaLib/data/STAT_test.ttx
new file mode 100644
index 0000000..d1b2b69
--- /dev/null
+++ b/Tests/feaLib/data/STAT_test.ttx
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.20">
+
+  <name>
+    <namerecord nameID="25" platformID="3" platEncID="1" langID="0x409">
+      TestFont
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Roman
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x411">
+      ローマン
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Optical Size
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      Text
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      Subhead
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      Display
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
+      Condensed
+    </namerecord>
+    <namerecord nameID="263" platformID="3" platEncID="1" langID="0x409">
+      Semicondensed
+    </namerecord>
+    <namerecord nameID="264" platformID="3" platEncID="1" langID="0x409">
+      Normal
+    </namerecord>
+    <namerecord nameID="265" platformID="3" platEncID="1" langID="0x409">
+      Extended
+    </namerecord>
+    <namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="267" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+    <namerecord nameID="268" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="269" platformID="3" platEncID="1" langID="0x409">
+      Medium
+    </namerecord>
+    <namerecord nameID="270" platformID="3" platEncID="1" langID="0x409">
+      Semibold
+    </namerecord>
+    <namerecord nameID="271" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="272" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+    <namerecord nameID="273" platformID="3" platEncID="1" langID="0x409">
+      Italic
+    </namerecord>
+    <namerecord nameID="274" platformID="3" platEncID="1" langID="0x409">
+      Roman
+    </namerecord>
+    <namerecord nameID="275" platformID="3" platEncID="1" langID="0x409">
+      Caption
+    </namerecord>
+  </name>
+
+  <STAT>
+    <Version value="0x00010002"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=4 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="opsz"/>
+        <AxisNameID value="257"/>  <!-- Optical Size -->
+        <AxisOrdering value="0"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="wdth"/>
+        <AxisNameID value="261"/>  <!-- Width -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="2">
+        <AxisTag value="wght"/>
+        <AxisNameID value="266"/>  <!-- Weight -->
+        <AxisOrdering value="2"/>
+      </Axis>
+      <Axis index="3">
+        <AxisTag value="ital"/>
+        <AxisNameID value="273"/>  <!-- Italic -->
+        <AxisOrdering value="3"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=15 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="4">
+        <!-- AxisCount=2 -->
+        <Flags value="0"/>
+        <ValueNameID value="275"/>  <!-- Caption -->
+        <AxisValueRecord index="0">
+          <AxisIndex value="0"/>
+          <Value value="8.0"/>
+        </AxisValueRecord>
+        <AxisValueRecord index="1">
+          <AxisIndex value="1"/>
+          <Value value="400.0"/>
+        </AxisValueRecord>
+      </AxisValue>
+      <AxisValue index="1" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="3"/>  <!-- OlderSiblingFontAttribute ElidableAxisValueName -->
+        <ValueNameID value="258"/>  <!-- Text -->
+        <NominalValue value="11.0"/>
+        <RangeMinValue value="9.0"/>
+        <RangeMaxValue value="12.0"/>
+      </AxisValue>
+      <AxisValue index="2" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="259"/>  <!-- Subhead -->
+        <NominalValue value="16.7"/>
+        <RangeMinValue value="12.0"/>
+        <RangeMaxValue value="24.0"/>
+      </AxisValue>
+      <AxisValue index="3" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="260"/>  <!-- Display -->
+        <NominalValue value="72.0"/>
+        <RangeMinValue value="24.0"/>
+        <RangeMaxValue value="72.0"/>
+      </AxisValue>
+      <AxisValue index="4" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="262"/>  <!-- Condensed -->
+        <NominalValue value="80.0"/>
+        <RangeMinValue value="80.0"/>
+        <RangeMaxValue value="89.0"/>
+      </AxisValue>
+      <AxisValue index="5" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="263"/>  <!-- Semicondensed -->
+        <NominalValue value="90.0"/>
+        <RangeMinValue value="90.0"/>
+        <RangeMaxValue value="96.0"/>
+      </AxisValue>
+      <AxisValue index="6" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="264"/>  <!-- Normal -->
+        <NominalValue value="100.0"/>
+        <RangeMinValue value="97.0"/>
+        <RangeMaxValue value="101.0"/>
+      </AxisValue>
+      <AxisValue index="7" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="265"/>  <!-- Extended -->
+        <NominalValue value="125.0"/>
+        <RangeMinValue value="102.0"/>
+        <RangeMaxValue value="125.0"/>
+      </AxisValue>
+      <AxisValue index="8" Format="2">
+        <AxisIndex value="2"/>
+        <Flags value="0"/>
+        <ValueNameID value="267"/>  <!-- Light -->
+        <NominalValue value="300.0"/>
+        <RangeMinValue value="300.0"/>
+        <RangeMaxValue value="349.0"/>
+      </AxisValue>
+      <AxisValue index="9" Format="2">
+        <AxisIndex value="2"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="268"/>  <!-- Regular -->
+        <NominalValue value="400.0"/>
+        <RangeMinValue value="350.0"/>
+        <RangeMaxValue value="449.0"/>
+      </AxisValue>
+      <AxisValue index="10" Format="2">
+        <AxisIndex value="2"/>
+        <Flags value="0"/>
+        <ValueNameID value="269"/>  <!-- Medium -->
+        <NominalValue value="500.0"/>
+        <RangeMinValue value="450.0"/>
+        <RangeMaxValue value="549.0"/>
+      </AxisValue>
+      <AxisValue index="11" Format="2">
+        <AxisIndex value="2"/>
+        <Flags value="0"/>
+        <ValueNameID value="270"/>  <!-- Semibold -->
+        <NominalValue value="600.0"/>
+        <RangeMinValue value="550.0"/>
+        <RangeMaxValue value="649.0"/>
+      </AxisValue>
+      <AxisValue index="12" Format="2">
+        <AxisIndex value="2"/>
+        <Flags value="0"/>
+        <ValueNameID value="271"/>  <!-- Bold -->
+        <NominalValue value="700.0"/>
+        <RangeMinValue value="650.0"/>
+        <RangeMaxValue value="749.0"/>
+      </AxisValue>
+      <AxisValue index="13" Format="2">
+        <AxisIndex value="2"/>
+        <Flags value="0"/>
+        <ValueNameID value="272"/>  <!-- Black -->
+        <NominalValue value="900.0"/>
+        <RangeMinValue value="750.0"/>
+        <RangeMaxValue value="900.0"/>
+      </AxisValue>
+      <AxisValue index="14" Format="1">
+        <AxisIndex value="3"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="274"/>  <!-- Roman -->
+        <Value value="0.0"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="256"/>  <!-- Roman -->
+  </STAT>
+
+</ttFont>
diff --git a/Tests/feaLib/data/STAT_test_elidedFallbackNameID.fea b/Tests/feaLib/data/STAT_test_elidedFallbackNameID.fea
new file mode 100644
index 0000000..5a14180
--- /dev/null
+++ b/Tests/feaLib/data/STAT_test_elidedFallbackNameID.fea
@@ -0,0 +1,84 @@
+table name {
+    nameid 25 "TestFont";
+    nameid 256 "Roman";
+} name;
+table STAT {
+    ElidedFallbackNameID 256;
+    DesignAxis opsz 0 {
+        name "Optical Size";
+    };
+    DesignAxis wdth 1 {
+        name "Width";
+    };
+    DesignAxis wght 2 {
+        name "Weight";
+    };
+    DesignAxis ital 3 {
+        name "Italic";
+    };  # here comment
+    AxisValue {
+        location opsz 8;  # comment here
+        location wdth 400; # another comment
+        name "Caption"; # more comments
+    };
+    AxisValue {
+        location opsz 11 9 12;
+        name "Text";
+        flag OlderSiblingFontAttribute ElidableAxisValueName;
+    };
+    AxisValue {
+        location opsz 16.7 12 24;
+        name "Subhead";
+    };
+    AxisValue {
+        location opsz 72 24 72;
+        name "Display";
+    };
+    AxisValue {
+        location wdth 80 80 89;
+        name "Condensed";
+    };
+    AxisValue {
+        location wdth 90 90 96;
+        name "Semicondensed";
+    };
+    AxisValue {
+        location wdth 100 97 101;
+        name "Normal";
+        flag ElidableAxisValueName;
+    };
+    AxisValue {
+        location wdth 125 102 125;
+        name "Extended";
+    };
+    AxisValue {
+        location wght 300 300 349;
+        name "Light";
+    };
+    AxisValue {
+        location wght 400 350 449;
+        name "Regular";
+        flag ElidableAxisValueName;
+    };
+    AxisValue {
+        location wght 500 450 549;
+        name "Medium";
+    };
+    AxisValue {
+        location wght 600 550 649;
+        name "Semibold";
+    };
+    AxisValue {
+        location wght 700 650 749;
+        name "Bold";
+    };
+    AxisValue {
+        location wght 900 750 900;
+        name "Black";
+    };
+    AxisValue {
+        location ital 0;
+        name "Roman";
+        flag ElidableAxisValueName; # flag comment
+    };
+} STAT;
\ No newline at end of file
diff --git a/Tests/feaLib/data/STAT_test_elidedFallbackNameID.ttx b/Tests/feaLib/data/STAT_test_elidedFallbackNameID.ttx
new file mode 100644
index 0000000..32802e0
--- /dev/null
+++ b/Tests/feaLib/data/STAT_test_elidedFallbackNameID.ttx
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.20">
+
+  <name>
+    <namerecord nameID="25" platformID="3" platEncID="1" langID="0x409">
+      TestFont
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Roman
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Optical Size
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      Text
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      Subhead
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      Display
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
+      Condensed
+    </namerecord>
+    <namerecord nameID="263" platformID="3" platEncID="1" langID="0x409">
+      Semicondensed
+    </namerecord>
+    <namerecord nameID="264" platformID="3" platEncID="1" langID="0x409">
+      Normal
+    </namerecord>
+    <namerecord nameID="265" platformID="3" platEncID="1" langID="0x409">
+      Extended
+    </namerecord>
+    <namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="267" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+    <namerecord nameID="268" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="269" platformID="3" platEncID="1" langID="0x409">
+      Medium
+    </namerecord>
+    <namerecord nameID="270" platformID="3" platEncID="1" langID="0x409">
+      Semibold
+    </namerecord>
+    <namerecord nameID="271" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="272" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+    <namerecord nameID="273" platformID="3" platEncID="1" langID="0x409">
+      Italic
+    </namerecord>
+    <namerecord nameID="274" platformID="3" platEncID="1" langID="0x409">
+      Roman
+    </namerecord>
+    <namerecord nameID="275" platformID="3" platEncID="1" langID="0x409">
+      Caption
+    </namerecord>
+  </name>
+
+  <STAT>
+    <Version value="0x00010002"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=4 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="opsz"/>
+        <AxisNameID value="257"/>  <!-- Optical Size -->
+        <AxisOrdering value="0"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="wdth"/>
+        <AxisNameID value="261"/>  <!-- Width -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="2">
+        <AxisTag value="wght"/>
+        <AxisNameID value="266"/>  <!-- Weight -->
+        <AxisOrdering value="2"/>
+      </Axis>
+      <Axis index="3">
+        <AxisTag value="ital"/>
+        <AxisNameID value="273"/>  <!-- Italic -->
+        <AxisOrdering value="3"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=15 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="4">
+        <!-- AxisCount=2 -->
+        <Flags value="0"/>
+        <ValueNameID value="275"/>  <!-- Caption -->
+        <AxisValueRecord index="0">
+          <AxisIndex value="0"/>
+          <Value value="8.0"/>
+        </AxisValueRecord>
+        <AxisValueRecord index="1">
+          <AxisIndex value="1"/>
+          <Value value="400.0"/>
+        </AxisValueRecord>
+      </AxisValue>
+      <AxisValue index="1" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="3"/>  <!-- OlderSiblingFontAttribute ElidableAxisValueName -->
+        <ValueNameID value="258"/>  <!-- Text -->
+        <NominalValue value="11.0"/>
+        <RangeMinValue value="9.0"/>
+        <RangeMaxValue value="12.0"/>
+      </AxisValue>
+      <AxisValue index="2" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="259"/>  <!-- Subhead -->
+        <NominalValue value="16.7"/>
+        <RangeMinValue value="12.0"/>
+        <RangeMaxValue value="24.0"/>
+      </AxisValue>
+      <AxisValue index="3" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="260"/>  <!-- Display -->
+        <NominalValue value="72.0"/>
+        <RangeMinValue value="24.0"/>
+        <RangeMaxValue value="72.0"/>
+      </AxisValue>
+      <AxisValue index="4" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="262"/>  <!-- Condensed -->
+        <NominalValue value="80.0"/>
+        <RangeMinValue value="80.0"/>
+        <RangeMaxValue value="89.0"/>
+      </AxisValue>
+      <AxisValue index="5" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="263"/>  <!-- Semicondensed -->
+        <NominalValue value="90.0"/>
+        <RangeMinValue value="90.0"/>
+        <RangeMaxValue value="96.0"/>
+      </AxisValue>
+      <AxisValue index="6" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="264"/>  <!-- Normal -->
+        <NominalValue value="100.0"/>
+        <RangeMinValue value="97.0"/>
+        <RangeMaxValue value="101.0"/>
+      </AxisValue>
+      <AxisValue index="7" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="265"/>  <!-- Extended -->
+        <NominalValue value="125.0"/>
+        <RangeMinValue value="102.0"/>
+        <RangeMaxValue value="125.0"/>
+      </AxisValue>
+      <AxisValue index="8" Format="2">
+        <AxisIndex value="2"/>
+        <Flags value="0"/>
+        <ValueNameID value="267"/>  <!-- Light -->
+        <NominalValue value="300.0"/>
+        <RangeMinValue value="300.0"/>
+        <RangeMaxValue value="349.0"/>
+      </AxisValue>
+      <AxisValue index="9" Format="2">
+        <AxisIndex value="2"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="268"/>  <!-- Regular -->
+        <NominalValue value="400.0"/>
+        <RangeMinValue value="350.0"/>
+        <RangeMaxValue value="449.0"/>
+      </AxisValue>
+      <AxisValue index="10" Format="2">
+        <AxisIndex value="2"/>
+        <Flags value="0"/>
+        <ValueNameID value="269"/>  <!-- Medium -->
+        <NominalValue value="500.0"/>
+        <RangeMinValue value="450.0"/>
+        <RangeMaxValue value="549.0"/>
+      </AxisValue>
+      <AxisValue index="11" Format="2">
+        <AxisIndex value="2"/>
+        <Flags value="0"/>
+        <ValueNameID value="270"/>  <!-- Semibold -->
+        <NominalValue value="600.0"/>
+        <RangeMinValue value="550.0"/>
+        <RangeMaxValue value="649.0"/>
+      </AxisValue>
+      <AxisValue index="12" Format="2">
+        <AxisIndex value="2"/>
+        <Flags value="0"/>
+        <ValueNameID value="271"/>  <!-- Bold -->
+        <NominalValue value="700.0"/>
+        <RangeMinValue value="650.0"/>
+        <RangeMaxValue value="749.0"/>
+      </AxisValue>
+      <AxisValue index="13" Format="2">
+        <AxisIndex value="2"/>
+        <Flags value="0"/>
+        <ValueNameID value="272"/>  <!-- Black -->
+        <NominalValue value="900.0"/>
+        <RangeMinValue value="750.0"/>
+        <RangeMaxValue value="900.0"/>
+      </AxisValue>
+      <AxisValue index="14" Format="1">
+        <AxisIndex value="3"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="274"/>  <!-- Roman -->
+        <Value value="0.0"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="256"/>  <!-- Roman -->
+  </STAT>
+
+</ttFont>
diff --git a/Tests/feaLib/data/SingleSubstSubtable.fea b/Tests/feaLib/data/SingleSubstSubtable.fea
new file mode 100644
index 0000000..f5a6da3
--- /dev/null
+++ b/Tests/feaLib/data/SingleSubstSubtable.fea
@@ -0,0 +1,5 @@
+feature test {
+    sub a by b;
+    subtable;
+    sub c by d;
+} test;
diff --git a/Tests/feaLib/data/SingleSubstSubtable.ttx b/Tests/feaLib/data/SingleSubstSubtable.ttx
new file mode 100644
index 0000000..5030b73
--- /dev/null
+++ b/Tests/feaLib/data/SingleSubstSubtable.ttx
@@ -0,0 +1,46 @@
+<?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="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <SingleSubst index="0">
+          <Substitution in="a" out="b"/>
+        </SingleSubst>
+        <SingleSubst index="1">
+          <Substitution in="c" out="d"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/SubstSubtable.fea b/Tests/feaLib/data/SubstSubtable.fea
new file mode 100644
index 0000000..b6e959a
--- /dev/null
+++ b/Tests/feaLib/data/SubstSubtable.fea
@@ -0,0 +1,9 @@
+feature test {
+    sub G' by G.swash;
+    subtable;
+    sub H' by H.swash;
+    subtable;
+    sub G' by g;
+    subtable;
+    sub H' by H.swash;
+} test;
diff --git a/Tests/feaLib/data/SubstSubtable.ttx b/Tests/feaLib/data/SubstSubtable.ttx
new file mode 100644
index 0000000..8718f46
--- /dev/null
+++ b/Tests/feaLib/data/SubstSubtable.ttx
@@ -0,0 +1,116 @@
+<?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=5 -->
+      <Lookup index="0">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=4 -->
+        <ContextSubst index="0" Format="3">
+          <!-- GlyphCount=1 -->
+          <!-- SubstCount=1 -->
+          <Coverage index="0">
+            <Glyph value="G"/>
+          </Coverage>
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ContextSubst>
+        <ContextSubst index="1" Format="3">
+          <!-- GlyphCount=1 -->
+          <!-- SubstCount=1 -->
+          <Coverage index="0">
+            <Glyph value="H"/>
+          </Coverage>
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="2"/>
+          </SubstLookupRecord>
+        </ContextSubst>
+        <ContextSubst index="2" Format="3">
+          <!-- GlyphCount=1 -->
+          <!-- SubstCount=1 -->
+          <Coverage index="0">
+            <Glyph value="G"/>
+          </Coverage>
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="3"/>
+          </SubstLookupRecord>
+        </ContextSubst>
+        <ContextSubst index="3" Format="3">
+          <!-- GlyphCount=1 -->
+          <!-- SubstCount=1 -->
+          <Coverage index="0">
+            <Glyph value="H"/>
+          </Coverage>
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="4"/>
+          </SubstLookupRecord>
+        </ContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="G" out="G.swash"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="H" out="H.swash"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="G" out="g"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="H" out="H.swash"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.ttx b/Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.ttx
index ce76370..98f7aa9 100644
--- a/Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.ttx
+++ b/Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.ttx
@@ -32,44 +32,39 @@
       <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">
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage>
             <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>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=2 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="A"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="A"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="1"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+            <ChainPosRule index="1">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="B"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="B"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="1"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
         </ChainContextPos>
       </Lookup>
       <Lookup index="1">
diff --git a/Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.ttx b/Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.ttx
index e1cb5d6..973cb4f 100644
--- a/Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.ttx
+++ b/Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.ttx
@@ -32,44 +32,39 @@
       <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">
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="1">
+          <Coverage>
             <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>
+          </Coverage>
+          <!-- ChainPosRuleSetCount=1 -->
+          <ChainPosRuleSet index="0">
+            <!-- ChainPosRuleCount=2 -->
+            <ChainPosRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="A"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="A"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="1"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+            <ChainPosRule index="1">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="B"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="B"/>
+              <!-- PosCount=1 -->
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="1"/>
+              </PosLookupRecord>
+            </ChainPosRule>
+          </ChainPosRuleSet>
         </ChainContextPos>
       </Lookup>
       <Lookup index="1">
diff --git a/Tests/feaLib/data/aalt_chain_contextual_subst.fea b/Tests/feaLib/data/aalt_chain_contextual_subst.fea
new file mode 100644
index 0000000..677c230
--- /dev/null
+++ b/Tests/feaLib/data/aalt_chain_contextual_subst.fea
@@ -0,0 +1,20 @@
+# https://github.com/googlefonts/fontmake/issues/648
+
+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 calt {
+    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;
+} calt;
+
+feature aalt {
+    feature calt;
+} aalt;
diff --git a/Tests/feaLib/data/aalt_chain_contextual_subst.ttx b/Tests/feaLib/data/aalt_chain_contextual_subst.ttx
new file mode 100644
index 0000000..256a9c7
--- /dev/null
+++ b/Tests/feaLib/data/aalt_chain_contextual_subst.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="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="aalt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="calt"/>
+        <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">
+          <Substitution in="n" out="n.end"/>
+          <Substitution in="s" out="s.end"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <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="2">
+        <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="3">
+        <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="1"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="1">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="2"/>
+          </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="1"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="1">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="2"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug1307.fea b/Tests/feaLib/data/bug1307.fea
new file mode 100644
index 0000000..dbc4ae3
--- /dev/null
+++ b/Tests/feaLib/data/bug1307.fea
@@ -0,0 +1,65 @@
+# Test of features and languagesystems
+
+lookup a {
+    sub a by A;
+} a;
+
+lookup b {
+    sub b by B;
+} b;
+
+lookup c {
+    sub c by C;
+} c;
+
+lookup d {
+    sub d by D;
+} d;
+
+lookup e {
+    sub e by E;
+} e;
+
+lookup f {
+    sub f by F;
+} f;
+
+lookup g {
+    sub g by G;
+} g;
+
+lookup h {
+    sub h by H;
+} h;
+
+lookup i {
+    sub i by I;
+} i;
+
+languagesystem DFLT dflt;
+languagesystem DFLT FRE;
+languagesystem DFLT ABC;
+languagesystem latn dflt;
+languagesystem latn ABC;
+
+feature smcp {
+    lookup a;
+} smcp;
+
+feature liga {
+    lookup b;
+    script DFLT;
+        lookup c;
+        language dflt;
+            lookup d;
+        language FRE;
+            lookup e;
+    script latn;
+        lookup f;
+        language dflt;
+            lookup g;
+        language FRE;
+            lookup h;
+        language DEF exclude_dflt;
+            lookup i;
+} liga;
diff --git a/Tests/feaLib/data/bug1307.ttx b/Tests/feaLib/data/bug1307.ttx
new file mode 100644
index 0000000..1ecbf03
--- /dev/null
+++ b/Tests/feaLib/data/bug1307.ttx
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=2 -->
+            <FeatureIndex index="0" value="4"/>
+            <FeatureIndex index="1" value="6"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=2 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="ABC "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=2 -->
+              <FeatureIndex index="0" value="0"/>
+              <FeatureIndex index="1" value="6"/>
+            </LangSys>
+          </LangSysRecord>
+          <LangSysRecord index="1">
+            <LangSysTag value="FRE "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=2 -->
+              <FeatureIndex index="0" value="2"/>
+              <FeatureIndex index="1" value="6"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=2 -->
+            <FeatureIndex index="0" value="5"/>
+            <FeatureIndex index="1" value="6"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=3 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="ABC "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=2 -->
+              <FeatureIndex index="0" value="0"/>
+              <FeatureIndex index="1" value="6"/>
+            </LangSys>
+          </LangSysRecord>
+          <LangSysRecord index="1">
+            <LangSysTag value="DEF "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=1 -->
+              <FeatureIndex index="0" value="1"/>
+            </LangSys>
+          </LangSysRecord>
+          <LangSysRecord index="2">
+            <LangSysTag value="FRE "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=1 -->
+              <FeatureIndex index="0" value="3"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=7 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="8"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=4 -->
+          <LookupListIndex index="0" value="1"/>
+          <LookupListIndex index="1" value="2"/>
+          <LookupListIndex index="2" value="3"/>
+          <LookupListIndex index="3" value="4"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=4 -->
+          <LookupListIndex index="0" value="1"/>
+          <LookupListIndex index="1" value="5"/>
+          <LookupListIndex index="2" value="6"/>
+          <LookupListIndex index="3" value="7"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="4">
+        <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="5">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=3 -->
+          <LookupListIndex index="0" value="1"/>
+          <LookupListIndex index="1" value="5"/>
+          <LookupListIndex index="2" value="6"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="6">
+        <FeatureTag value="smcp"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=9 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="a" out="A"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="b" out="B"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="c" out="C"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="d" out="D"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="e" out="E"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="5">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="f" out="F"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="6">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="g" out="G"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="7">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="h" out="H"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="8">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="i" out="I"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug1459.fea b/Tests/feaLib/data/bug1459.fea
new file mode 100644
index 0000000..1ad688e
--- /dev/null
+++ b/Tests/feaLib/data/bug1459.fea
@@ -0,0 +1,7 @@
+# A pair position lookup where only the second glyph has a non-empty valuerecord
+# while the first glyph has a NULL valuerecord. The ValueFormat1 for the first
+# glyph is expected to be 0.
+# https://github.com/fonttools/fonttools/issues/1459
+feature kern {
+    pos A <NULL> V <-180 0 -90 0>;
+} kern;
diff --git a/Tests/feaLib/data/bug1459.ttx b/Tests/feaLib/data/bug1459.ttx
new file mode 100644
index 0000000..8a7c096
--- /dev/null
+++ b/Tests/feaLib/data/bug1459.ttx
@@ -0,0 +1,55 @@
+<?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="A"/>
+          </Coverage>
+          <ValueFormat1 value="0"/>
+          <ValueFormat2 value="5"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value2 XPlacement="-180" XAdvance="-90"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug2276.fea b/Tests/feaLib/data/bug2276.fea
new file mode 100644
index 0000000..96f9885
--- /dev/null
+++ b/Tests/feaLib/data/bug2276.fea
@@ -0,0 +1,11 @@
+# https://github.com/fonttools/fonttools/issues/2276
+lookup EMPTY {
+    # pass
+} EMPTY;
+feature ss01 {
+    sub a by a.alt1;
+    lookup EMPTY;
+} ss01;
+feature aalt {
+    feature ss01;
+} aalt;
diff --git a/Tests/feaLib/data/bug2276.ttx b/Tests/feaLib/data/bug2276.ttx
new file mode 100644
index 0000000..57902da
--- /dev/null
+++ b/Tests/feaLib/data/bug2276.ttx
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.22">
+
+  <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="aalt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="ss01"/>
+        <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">
+          <Substitution in="a" out="a.alt1"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="a" out="a.alt1"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug453.fea b/Tests/feaLib/data/bug453.fea
index fc5072e..ed0e6f9 100644
--- a/Tests/feaLib/data/bug453.fea
+++ b/Tests/feaLib/data/bug453.fea
@@ -1,11 +1,13 @@
-# https://github.com/behdad/fonttools/issues/453
+# https://github.com/fonttools/fonttools/issues/453
 feature mark {
     lookup mark1 {
         markClass [acute] <anchor 150 -10> @TOP_MARKS;
-        pos base [e] <anchor 250 450> mark @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;
+        pos base [e]
+            <anchor 250 450> mark @TOP_MARKS_2;
     } mark2;
 } mark;
diff --git a/Tests/feaLib/data/bug463.fea b/Tests/feaLib/data/bug463.fea
index e7e21af..4b1910c 100644
--- a/Tests/feaLib/data/bug463.fea
+++ b/Tests/feaLib/data/bug463.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/463
+# https://github.com/fonttools/fonttools/issues/463
 feature ordn {
     @DIGIT = [zero one two three four five six seven eight nine];
     sub @DIGIT [A a]' by ordfeminine;
diff --git a/Tests/feaLib/data/bug501.fea b/Tests/feaLib/data/bug501.fea
index 58a4081..06d91dd 100644
--- a/Tests/feaLib/data/bug501.fea
+++ b/Tests/feaLib/data/bug501.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/501
+# https://github.com/fonttools/fonttools/issues/501
 languagesystem DFLT dflt;
 feature test {
     lookup L {
diff --git a/Tests/feaLib/data/bug502.fea b/Tests/feaLib/data/bug502.fea
index b8130c4..3fcb94e 100644
--- a/Tests/feaLib/data/bug502.fea
+++ b/Tests/feaLib/data/bug502.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/502
+# https://github.com/fonttools/fonttools/issues/502
 feature aalt {
     sub A by A.alt1;
     sub Eng by Eng.alt1;
diff --git a/Tests/feaLib/data/bug504.fea b/Tests/feaLib/data/bug504.fea
index 64d05ba..e6920b2 100644
--- a/Tests/feaLib/data/bug504.fea
+++ b/Tests/feaLib/data/bug504.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/504
+# https://github.com/fonttools/fonttools/issues/504
 
 feature test {
     sub [a b c d] by [T E S T];
diff --git a/Tests/feaLib/data/bug505.fea b/Tests/feaLib/data/bug505.fea
index 016ed1f..742ea11 100644
--- a/Tests/feaLib/data/bug505.fea
+++ b/Tests/feaLib/data/bug505.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/505
+# https://github.com/fonttools/fonttools/issues/505
 
 languagesystem armn dflt;
 languagesystem avst dflt;
diff --git a/Tests/feaLib/data/bug506.fea b/Tests/feaLib/data/bug506.fea
index 62e4bbb..2e1e97d 100644
--- a/Tests/feaLib/data/bug506.fea
+++ b/Tests/feaLib/data/bug506.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/506
+# https://github.com/fonttools/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
index 9aff913..4daba45 100644
--- a/Tests/feaLib/data/bug506.ttx
+++ b/Tests/feaLib/data/bug506.ttx
@@ -30,25 +30,23 @@
     <LookupList>
       <!-- LookupCount=2 -->
       <Lookup index="0">
-        <LookupType value="6"/>
+        <LookupType value="5"/>
         <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 -->
+        <ContextSubst index="0" Format="3">
+          <!-- GlyphCount=2 -->
           <!-- SubstCount=1 -->
+          <Coverage index="0">
+            <Glyph value="f"/>
+          </Coverage>
+          <Coverage index="1">
+            <Glyph value="i"/>
+          </Coverage>
           <SubstLookupRecord index="0">
             <SequenceIndex value="0"/>
             <LookupListIndex value="1"/>
           </SubstLookupRecord>
-        </ChainContextSubst>
+        </ContextSubst>
       </Lookup>
       <Lookup index="1">
         <LookupType value="4"/>
diff --git a/Tests/feaLib/data/bug509.ttx b/Tests/feaLib/data/bug509.ttx
index e5a36af..52fba20 100644
--- a/Tests/feaLib/data/bug509.ttx
+++ b/Tests/feaLib/data/bug509.ttx
@@ -30,33 +30,29 @@
     <LookupList>
       <!-- LookupCount=2 -->
       <Lookup index="0">
-        <LookupType value="6"/>
+        <LookupType value="5"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=2 -->
-        <ChainContextSubst index="0" Format="3">
-          <!-- BacktrackGlyphCount=0 -->
-          <!-- InputGlyphCount=1 -->
-          <InputCoverage index="0">
-            <Glyph value="A"/>
-          </InputCoverage>
-          <!-- LookAheadGlyphCount=0 -->
+        <ContextSubst index="0" Format="3">
+          <!-- GlyphCount=1 -->
           <!-- SubstCount=0 -->
-        </ChainContextSubst>
-        <ChainContextSubst index="1" Format="3">
-          <!-- BacktrackGlyphCount=0 -->
-          <!-- InputGlyphCount=1 -->
-          <InputCoverage index="0">
+          <Coverage index="0">
+            <Glyph value="A"/>
+          </Coverage>
+        </ContextSubst>
+        <ContextSubst index="1" Format="3">
+          <!-- GlyphCount=1 -->
+          <!-- SubstCount=1 -->
+          <Coverage index="0">
             <Glyph value="A"/>
             <Glyph value="A.sc"/>
             <Glyph value="A.alt1"/>
-          </InputCoverage>
-          <!-- LookAheadGlyphCount=0 -->
-          <!-- SubstCount=1 -->
+          </Coverage>
           <SubstLookupRecord index="0">
             <SequenceIndex value="0"/>
             <LookupListIndex value="1"/>
           </SubstLookupRecord>
-        </ChainContextSubst>
+        </ContextSubst>
       </Lookup>
       <Lookup index="1">
         <LookupType value="1"/>
diff --git a/Tests/feaLib/data/bug512.ttx b/Tests/feaLib/data/bug512.ttx
index 1dfe63f..693ebeb 100644
--- a/Tests/feaLib/data/bug512.ttx
+++ b/Tests/feaLib/data/bug512.ttx
@@ -30,61 +30,54 @@
     <LookupList>
       <!-- LookupCount=3 -->
       <Lookup index="0">
-        <LookupType value="6"/>
+        <LookupType value="5"/>
         <LookupFlag value="0"/>
-        <!-- SubTableCount=4 -->
-        <ChainContextSubst index="0" Format="3">
-          <!-- BacktrackGlyphCount=0 -->
-          <!-- InputGlyphCount=1 -->
-          <InputCoverage index="0">
+        <!-- SubTableCount=1 -->
+        <ContextSubst index="0" Format="1">
+          <Coverage>
             <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>
+          </Coverage>
+          <!-- SubRuleSetCount=2 -->
+          <SubRuleSet index="0">
+            <!-- SubRuleCount=2 -->
+            <SubRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="1"/>
+              </SubstLookupRecord>
+            </SubRule>
+            <SubRule index="1">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </SubRule>
+          </SubRuleSet>
+          <SubRuleSet index="1">
+            <!-- SubRuleCount=2 -->
+            <SubRule index="0">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="1"/>
+              </SubstLookupRecord>
+            </SubRule>
+            <SubRule index="1">
+              <!-- GlyphCount=1 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </SubRule>
+          </SubRuleSet>
+        </ContextSubst>
       </Lookup>
       <Lookup index="1">
         <LookupType value="1"/>
diff --git a/Tests/feaLib/data/bug514.fea b/Tests/feaLib/data/bug514.fea
index 26da865..1ef5af6 100644
--- a/Tests/feaLib/data/bug514.fea
+++ b/Tests/feaLib/data/bug514.fea
@@ -5,7 +5,7 @@
 # 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 - B]' -40 B' -40 A' -40 Y;
     pos X A' -111 Y;
-    pos X B' -40 A' -111 [A-C]' -40 Y;
+    pos X B' -40 A' -111 [A - C]' -40 Y;
 } test;
diff --git a/Tests/feaLib/data/bug568.fea b/Tests/feaLib/data/bug568.fea
index 3e879b1..d9f56f9 100644
--- a/Tests/feaLib/data/bug568.fea
+++ b/Tests/feaLib/data/bug568.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/568
+# https://github.com/fonttools/fonttools/issues/568
 
 feature tst1 {
   script latn;
diff --git a/Tests/feaLib/data/bug633.ttx b/Tests/feaLib/data/bug633.ttx
index b119ebb..075c177 100644
--- a/Tests/feaLib/data/bug633.ttx
+++ b/Tests/feaLib/data/bug633.ttx
@@ -52,6 +52,7 @@
           <!-- Class2Count=3 -->
           <Class1Record index="0">
             <Class2Record index="0">
+              <Value1 XAdvance="0"/>
             </Class2Record>
             <Class2Record index="1">
               <Value1 XAdvance="0"/>
diff --git a/Tests/feaLib/data/cid_range.fea b/Tests/feaLib/data/cid_range.fea
new file mode 100644
index 0000000..7a17aed
--- /dev/null
+++ b/Tests/feaLib/data/cid_range.fea
@@ -0,0 +1,6 @@
+# A CID range can be valid even if it is invalid as a glyph name range.
+# For example, [cid00800 - cid01001] is invalid.
+
+feature zero {
+  sub [\800 - \1001] by zero;
+} zero;
diff --git a/Tests/feaLib/data/cid_range.ttx b/Tests/feaLib/data/cid_range.ttx
new file mode 100644
index 0000000..48b502b
--- /dev/null
+++ b/Tests/feaLib/data/cid_range.ttx
@@ -0,0 +1,244 @@
+<?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="zero"/>
+        <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="cid00800" out="zero"/>
+          <Substitution in="cid00801" out="zero"/>
+          <Substitution in="cid00802" out="zero"/>
+          <Substitution in="cid00803" out="zero"/>
+          <Substitution in="cid00804" out="zero"/>
+          <Substitution in="cid00805" out="zero"/>
+          <Substitution in="cid00806" out="zero"/>
+          <Substitution in="cid00807" out="zero"/>
+          <Substitution in="cid00808" out="zero"/>
+          <Substitution in="cid00809" out="zero"/>
+          <Substitution in="cid00810" out="zero"/>
+          <Substitution in="cid00811" out="zero"/>
+          <Substitution in="cid00812" out="zero"/>
+          <Substitution in="cid00813" out="zero"/>
+          <Substitution in="cid00814" out="zero"/>
+          <Substitution in="cid00815" out="zero"/>
+          <Substitution in="cid00816" out="zero"/>
+          <Substitution in="cid00817" out="zero"/>
+          <Substitution in="cid00818" out="zero"/>
+          <Substitution in="cid00819" out="zero"/>
+          <Substitution in="cid00820" out="zero"/>
+          <Substitution in="cid00821" out="zero"/>
+          <Substitution in="cid00822" out="zero"/>
+          <Substitution in="cid00823" out="zero"/>
+          <Substitution in="cid00824" out="zero"/>
+          <Substitution in="cid00825" out="zero"/>
+          <Substitution in="cid00826" out="zero"/>
+          <Substitution in="cid00827" out="zero"/>
+          <Substitution in="cid00828" out="zero"/>
+          <Substitution in="cid00829" out="zero"/>
+          <Substitution in="cid00830" out="zero"/>
+          <Substitution in="cid00831" out="zero"/>
+          <Substitution in="cid00832" out="zero"/>
+          <Substitution in="cid00833" out="zero"/>
+          <Substitution in="cid00834" out="zero"/>
+          <Substitution in="cid00835" out="zero"/>
+          <Substitution in="cid00836" out="zero"/>
+          <Substitution in="cid00837" out="zero"/>
+          <Substitution in="cid00838" out="zero"/>
+          <Substitution in="cid00839" out="zero"/>
+          <Substitution in="cid00840" out="zero"/>
+          <Substitution in="cid00841" out="zero"/>
+          <Substitution in="cid00842" out="zero"/>
+          <Substitution in="cid00843" out="zero"/>
+          <Substitution in="cid00844" out="zero"/>
+          <Substitution in="cid00845" out="zero"/>
+          <Substitution in="cid00846" out="zero"/>
+          <Substitution in="cid00847" out="zero"/>
+          <Substitution in="cid00848" out="zero"/>
+          <Substitution in="cid00849" out="zero"/>
+          <Substitution in="cid00850" out="zero"/>
+          <Substitution in="cid00851" out="zero"/>
+          <Substitution in="cid00852" out="zero"/>
+          <Substitution in="cid00853" out="zero"/>
+          <Substitution in="cid00854" out="zero"/>
+          <Substitution in="cid00855" out="zero"/>
+          <Substitution in="cid00856" out="zero"/>
+          <Substitution in="cid00857" out="zero"/>
+          <Substitution in="cid00858" out="zero"/>
+          <Substitution in="cid00859" out="zero"/>
+          <Substitution in="cid00860" out="zero"/>
+          <Substitution in="cid00861" out="zero"/>
+          <Substitution in="cid00862" out="zero"/>
+          <Substitution in="cid00863" out="zero"/>
+          <Substitution in="cid00864" out="zero"/>
+          <Substitution in="cid00865" out="zero"/>
+          <Substitution in="cid00866" out="zero"/>
+          <Substitution in="cid00867" out="zero"/>
+          <Substitution in="cid00868" out="zero"/>
+          <Substitution in="cid00869" out="zero"/>
+          <Substitution in="cid00870" out="zero"/>
+          <Substitution in="cid00871" out="zero"/>
+          <Substitution in="cid00872" out="zero"/>
+          <Substitution in="cid00873" out="zero"/>
+          <Substitution in="cid00874" out="zero"/>
+          <Substitution in="cid00875" out="zero"/>
+          <Substitution in="cid00876" out="zero"/>
+          <Substitution in="cid00877" out="zero"/>
+          <Substitution in="cid00878" out="zero"/>
+          <Substitution in="cid00879" out="zero"/>
+          <Substitution in="cid00880" out="zero"/>
+          <Substitution in="cid00881" out="zero"/>
+          <Substitution in="cid00882" out="zero"/>
+          <Substitution in="cid00883" out="zero"/>
+          <Substitution in="cid00884" out="zero"/>
+          <Substitution in="cid00885" out="zero"/>
+          <Substitution in="cid00886" out="zero"/>
+          <Substitution in="cid00887" out="zero"/>
+          <Substitution in="cid00888" out="zero"/>
+          <Substitution in="cid00889" out="zero"/>
+          <Substitution in="cid00890" out="zero"/>
+          <Substitution in="cid00891" out="zero"/>
+          <Substitution in="cid00892" out="zero"/>
+          <Substitution in="cid00893" out="zero"/>
+          <Substitution in="cid00894" out="zero"/>
+          <Substitution in="cid00895" out="zero"/>
+          <Substitution in="cid00896" out="zero"/>
+          <Substitution in="cid00897" out="zero"/>
+          <Substitution in="cid00898" out="zero"/>
+          <Substitution in="cid00899" out="zero"/>
+          <Substitution in="cid00900" out="zero"/>
+          <Substitution in="cid00901" out="zero"/>
+          <Substitution in="cid00902" out="zero"/>
+          <Substitution in="cid00903" out="zero"/>
+          <Substitution in="cid00904" out="zero"/>
+          <Substitution in="cid00905" out="zero"/>
+          <Substitution in="cid00906" out="zero"/>
+          <Substitution in="cid00907" out="zero"/>
+          <Substitution in="cid00908" out="zero"/>
+          <Substitution in="cid00909" out="zero"/>
+          <Substitution in="cid00910" out="zero"/>
+          <Substitution in="cid00911" out="zero"/>
+          <Substitution in="cid00912" out="zero"/>
+          <Substitution in="cid00913" out="zero"/>
+          <Substitution in="cid00914" out="zero"/>
+          <Substitution in="cid00915" out="zero"/>
+          <Substitution in="cid00916" out="zero"/>
+          <Substitution in="cid00917" out="zero"/>
+          <Substitution in="cid00918" out="zero"/>
+          <Substitution in="cid00919" out="zero"/>
+          <Substitution in="cid00920" out="zero"/>
+          <Substitution in="cid00921" out="zero"/>
+          <Substitution in="cid00922" out="zero"/>
+          <Substitution in="cid00923" out="zero"/>
+          <Substitution in="cid00924" out="zero"/>
+          <Substitution in="cid00925" out="zero"/>
+          <Substitution in="cid00926" out="zero"/>
+          <Substitution in="cid00927" out="zero"/>
+          <Substitution in="cid00928" out="zero"/>
+          <Substitution in="cid00929" out="zero"/>
+          <Substitution in="cid00930" out="zero"/>
+          <Substitution in="cid00931" out="zero"/>
+          <Substitution in="cid00932" out="zero"/>
+          <Substitution in="cid00933" out="zero"/>
+          <Substitution in="cid00934" out="zero"/>
+          <Substitution in="cid00935" out="zero"/>
+          <Substitution in="cid00936" out="zero"/>
+          <Substitution in="cid00937" out="zero"/>
+          <Substitution in="cid00938" out="zero"/>
+          <Substitution in="cid00939" out="zero"/>
+          <Substitution in="cid00940" out="zero"/>
+          <Substitution in="cid00941" out="zero"/>
+          <Substitution in="cid00942" out="zero"/>
+          <Substitution in="cid00943" out="zero"/>
+          <Substitution in="cid00944" out="zero"/>
+          <Substitution in="cid00945" out="zero"/>
+          <Substitution in="cid00946" out="zero"/>
+          <Substitution in="cid00947" out="zero"/>
+          <Substitution in="cid00948" out="zero"/>
+          <Substitution in="cid00949" out="zero"/>
+          <Substitution in="cid00950" out="zero"/>
+          <Substitution in="cid00951" out="zero"/>
+          <Substitution in="cid00952" out="zero"/>
+          <Substitution in="cid00953" out="zero"/>
+          <Substitution in="cid00954" out="zero"/>
+          <Substitution in="cid00955" out="zero"/>
+          <Substitution in="cid00956" out="zero"/>
+          <Substitution in="cid00957" out="zero"/>
+          <Substitution in="cid00958" out="zero"/>
+          <Substitution in="cid00959" out="zero"/>
+          <Substitution in="cid00960" out="zero"/>
+          <Substitution in="cid00961" out="zero"/>
+          <Substitution in="cid00962" out="zero"/>
+          <Substitution in="cid00963" out="zero"/>
+          <Substitution in="cid00964" out="zero"/>
+          <Substitution in="cid00965" out="zero"/>
+          <Substitution in="cid00966" out="zero"/>
+          <Substitution in="cid00967" out="zero"/>
+          <Substitution in="cid00968" out="zero"/>
+          <Substitution in="cid00969" out="zero"/>
+          <Substitution in="cid00970" out="zero"/>
+          <Substitution in="cid00971" out="zero"/>
+          <Substitution in="cid00972" out="zero"/>
+          <Substitution in="cid00973" out="zero"/>
+          <Substitution in="cid00974" out="zero"/>
+          <Substitution in="cid00975" out="zero"/>
+          <Substitution in="cid00976" out="zero"/>
+          <Substitution in="cid00977" out="zero"/>
+          <Substitution in="cid00978" out="zero"/>
+          <Substitution in="cid00979" out="zero"/>
+          <Substitution in="cid00980" out="zero"/>
+          <Substitution in="cid00981" out="zero"/>
+          <Substitution in="cid00982" out="zero"/>
+          <Substitution in="cid00983" out="zero"/>
+          <Substitution in="cid00984" out="zero"/>
+          <Substitution in="cid00985" out="zero"/>
+          <Substitution in="cid00986" out="zero"/>
+          <Substitution in="cid00987" out="zero"/>
+          <Substitution in="cid00988" out="zero"/>
+          <Substitution in="cid00989" out="zero"/>
+          <Substitution in="cid00990" out="zero"/>
+          <Substitution in="cid00991" out="zero"/>
+          <Substitution in="cid00992" out="zero"/>
+          <Substitution in="cid00993" out="zero"/>
+          <Substitution in="cid00994" out="zero"/>
+          <Substitution in="cid00995" out="zero"/>
+          <Substitution in="cid00996" out="zero"/>
+          <Substitution in="cid00997" out="zero"/>
+          <Substitution in="cid00998" out="zero"/>
+          <Substitution in="cid00999" out="zero"/>
+          <Substitution in="cid01000" out="zero"/>
+          <Substitution in="cid01001" out="zero"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/delete_glyph.fea b/Tests/feaLib/data/delete_glyph.fea
new file mode 100644
index 0000000..ab4b93f
--- /dev/null
+++ b/Tests/feaLib/data/delete_glyph.fea
@@ -0,0 +1,7 @@
+feature test {
+	sub a by NULL;
+} test;
+
+feature test {
+	sub [a b c] by NULL;
+} test;
diff --git a/Tests/feaLib/data/delete_glyph.ttx b/Tests/feaLib/data/delete_glyph.ttx
new file mode 100644
index 0000000..b28259f
--- /dev/null
+++ b/Tests/feaLib/data/delete_glyph.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=2 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0">
+          <Substitution in="a" out=""/>
+        </MultipleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MultipleSubst index="0">
+          <Substitution in="a" out=""/>
+          <Substitution in="b" out=""/>
+          <Substitution in="c" out=""/>
+        </MultipleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/include/test.fea b/Tests/feaLib/data/include/test.fea
new file mode 100644
index 0000000..5eb5f68
--- /dev/null
+++ b/Tests/feaLib/data/include/test.fea
@@ -0,0 +1 @@
+include(../test.fea)
\ No newline at end of file
diff --git a/Tests/feaLib/data/include/test.ufo/features.fea b/Tests/feaLib/data/include/test.ufo/features.fea
new file mode 100644
index 0000000..41ccce3
--- /dev/null
+++ b/Tests/feaLib/data/include/test.ufo/features.fea
@@ -0,0 +1 @@
+include(test.fea)
diff --git a/Tests/feaLib/data/language_required.fea b/Tests/feaLib/data/language_required.fea
index 4005a78..687c48a 100644
--- a/Tests/feaLib/data/language_required.fea
+++ b/Tests/feaLib/data/language_required.fea
@@ -18,5 +18,5 @@
 } liga;
 
 feature scmp {
-  sub [a-z] by [A.sc-Z.sc];
+  sub [a - z] by [A.sc - Z.sc];
 } scmp;
diff --git a/Tests/feaLib/data/lookup-debug.ttx b/Tests/feaLib/data/lookup-debug.ttx
new file mode 100644
index 0000000..f869617
--- /dev/null
+++ b/Tests/feaLib/data/lookup-debug.ttx
@@ -0,0 +1,80 @@
+<?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 -->
+      <!-- SomeLookup: __PATH__:4:5 in tst2 (DFLT/dflt) -->
+      <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>
+      <!-- EmbeddedLookup: __PATH__:18:9 in tst4 (DFLT/dflt) -->
+      <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
index 651dcd0..0210ab4 100644
--- a/Tests/feaLib/data/lookupflag.fea
+++ b/Tests/feaLib/data/lookupflag.fea
@@ -95,3 +95,65 @@
     lookup M;
     lookup N;
 } test;
+
+feature test {
+    lookupflag IgnoreMarks;
+    lookup O {
+        pos one 1;
+    } O;
+    lookup P {
+       pos one 1;
+    } P;
+} test;
+
+feature test {
+    lookup Q {
+         pos one 1;
+    } Q;
+    lookup R {
+         pos one 1;
+    } R;
+} test;
+
+feature test {
+    lookup S {
+        lookupflag IgnoreMarks;
+        pos one 1;
+    } S;
+    lookup T {
+        pos one 1;
+    } T;
+} test;
+
+feature test {
+    lookup U {
+        pos one 1;
+    } U;
+    lookup V {
+        lookupflag IgnoreMarks;
+        pos one 1;
+    } V;
+} test;
+
+feature test {
+    lookup W {
+        lookupflag IgnoreMarks;
+        script latn;
+        pos one 1;
+    } W;
+    lookup X {
+        lookupflag IgnoreMarks;
+        script latn;
+        pos two 2;
+    } X;
+} test;
+
+lookup Y {
+    lookupflag UseMarkFilteringSet [acute grave macron];
+    pos Y 1;
+} Y;
+
+lookup Z {
+    lookupflag MarkAttachmentType [acute grave macron];
+    pos Z 1;
+} Z;
diff --git a/Tests/feaLib/data/lookupflag.ttx b/Tests/feaLib/data/lookupflag.ttx
index bb05b9a..16ea751 100644
--- a/Tests/feaLib/data/lookupflag.ttx
+++ b/Tests/feaLib/data/lookupflag.ttx
@@ -43,7 +43,7 @@
   <GPOS>
     <Version value="0x00010000"/>
     <ScriptList>
-      <!-- ScriptCount=1 -->
+      <!-- ScriptCount=2 -->
       <ScriptRecord index="0">
         <ScriptTag value="DFLT"/>
         <Script>
@@ -55,13 +55,24 @@
           <!-- 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=1 -->
+      <!-- FeatureCount=2 -->
       <FeatureRecord index="0">
         <FeatureTag value="test"/>
         <Feature>
-          <!-- LookupCount=14 -->
+          <!-- LookupCount=22 -->
           <LookupListIndex index="0" value="0"/>
           <LookupListIndex index="1" value="1"/>
           <LookupListIndex index="2" value="2"/>
@@ -76,14 +87,30 @@
           <LookupListIndex index="11" value="11"/>
           <LookupListIndex index="12" value="12"/>
           <LookupListIndex index="13" value="13"/>
+          <LookupListIndex index="14" value="14"/>
+          <LookupListIndex index="15" value="15"/>
+          <LookupListIndex index="16" value="16"/>
+          <LookupListIndex index="17" value="17"/>
+          <LookupListIndex index="18" value="18"/>
+          <LookupListIndex index="19" value="19"/>
+          <LookupListIndex index="20" value="20"/>
+          <LookupListIndex index="21" value="21"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="22"/>
+          <LookupListIndex index="1" value="23"/>
         </Feature>
       </FeatureRecord>
     </FeatureList>
     <LookupList>
-      <!-- LookupCount=14 -->
+      <!-- LookupCount=26 -->
       <Lookup index="0">
         <LookupType value="1"/>
-        <LookupFlag value="1"/>
+        <LookupFlag value="1"/><!-- rightToLeft -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -95,7 +122,7 @@
       </Lookup>
       <Lookup index="1">
         <LookupType value="1"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -107,7 +134,7 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="1"/>
-        <LookupFlag value="4"/>
+        <LookupFlag value="4"/><!-- ignoreLigatures -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -119,7 +146,7 @@
       </Lookup>
       <Lookup index="3">
         <LookupType value="1"/>
-        <LookupFlag value="7"/>
+        <LookupFlag value="7"/><!-- rightToLeft ignoreBaseGlyphs ignoreLigatures -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -131,7 +158,7 @@
       </Lookup>
       <Lookup index="4">
         <LookupType value="1"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -143,7 +170,7 @@
       </Lookup>
       <Lookup index="5">
         <LookupType value="1"/>
-        <LookupFlag value="256"/>
+        <LookupFlag value="256"/><!-- markAttachmentType[1] -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -155,7 +182,7 @@
       </Lookup>
       <Lookup index="6">
         <LookupType value="1"/>
-        <LookupFlag value="512"/>
+        <LookupFlag value="512"/><!-- markAttachmentType[2] -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -167,7 +194,7 @@
       </Lookup>
       <Lookup index="7">
         <LookupType value="1"/>
-        <LookupFlag value="260"/>
+        <LookupFlag value="260"/><!-- ignoreLigatures markAttachmentType[1] -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -179,7 +206,7 @@
       </Lookup>
       <Lookup index="8">
         <LookupType value="1"/>
-        <LookupFlag value="16"/>
+        <LookupFlag value="16"/><!-- useMarkFilteringSet -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -192,7 +219,7 @@
       </Lookup>
       <Lookup index="9">
         <LookupType value="1"/>
-        <LookupFlag value="16"/>
+        <LookupFlag value="16"/><!-- useMarkFilteringSet -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -205,7 +232,7 @@
       </Lookup>
       <Lookup index="10">
         <LookupType value="1"/>
-        <LookupFlag value="20"/>
+        <LookupFlag value="20"/><!-- ignoreLigatures useMarkFilteringSet -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -230,7 +257,7 @@
       </Lookup>
       <Lookup index="12">
         <LookupType value="1"/>
-        <LookupFlag value="16"/>
+        <LookupFlag value="16"/><!-- useMarkFilteringSet -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -243,7 +270,7 @@
       </Lookup>
       <Lookup index="13">
         <LookupType value="1"/>
-        <LookupFlag value="768"/>
+        <LookupFlag value="768"/><!-- markAttachmentType[3] -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
           <Coverage>
@@ -253,6 +280,151 @@
           <Value XAdvance="1"/>
         </SinglePos>
       </Lookup>
+      <Lookup index="14">
+        <LookupType value="1"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="15">
+        <LookupType value="1"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="16">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="17">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="18">
+        <LookupType value="1"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="19">
+        <LookupType value="1"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="20">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="21">
+        <LookupType value="1"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="22">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="one"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="23">
+        <LookupType value="1"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="two"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="2"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="24">
+        <LookupType value="1"/>
+        <LookupFlag value="16"/><!-- useMarkFilteringSet -->
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="Y"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+        <MarkFilteringSet value="0"/>
+      </Lookup>
+      <Lookup index="25">
+        <LookupType value="1"/>
+        <LookupFlag value="256"/><!-- markAttachmentType[1] -->
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="Z"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="1"/>
+        </SinglePos>
+      </Lookup>
     </LookupList>
   </GPOS>
 
diff --git a/Tests/feaLib/data/size2.ttx b/Tests/feaLib/data/size2.ttx
index a822af3..1a12ddf 100644
--- a/Tests/feaLib/data/size2.ttx
+++ b/Tests/feaLib/data/size2.ttx
@@ -26,8 +26,8 @@
             <DesignSize value="10.0"/>
             <SubfamilyID value="0"/>
             <SubfamilyNameID value="0"/>
-            <RangeStart value="0"/>
-            <RangeEnd value="0"/>
+            <RangeStart value="0.0"/>
+            <RangeEnd value="0.0"/>
           </FeatureParamsSize>
           <!-- LookupCount=0 -->
         </Feature>
diff --git a/Tests/feaLib/data/spec4h1.fea b/Tests/feaLib/data/spec4h1.fea
index b43e13b..a3d2494 100644
--- a/Tests/feaLib/data/spec4h1.fea
+++ b/Tests/feaLib/data/spec4h1.fea
@@ -8,7 +8,7 @@
 languagesystem cyrl dflt;
 
 feature smcp {
-    sub [a-z] by [A.sc-Z.sc];
+    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
diff --git a/Tests/feaLib/data/spec5f_ii_2.fea b/Tests/feaLib/data/spec5f_ii_2.fea
index b20a74c..916f797 100644
--- a/Tests/feaLib/data/spec5f_ii_2.fea
+++ b/Tests/feaLib/data/spec5f_ii_2.fea
@@ -3,7 +3,7 @@
 # http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
 
 feature test {
-    @LETTER = [a-z];
+    @LETTER = [a - z];
     ignore sub @LETTER f' i';
     sub f' i' by f_i.begin;
 } test;
diff --git a/Tests/feaLib/data/spec5f_ii_3.fea b/Tests/feaLib/data/spec5f_ii_3.fea
index 5fd1991..af06770 100644
--- a/Tests/feaLib/data/spec5f_ii_3.fea
+++ b/Tests/feaLib/data/spec5f_ii_3.fea
@@ -3,7 +3,7 @@
 # http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
 
 feature test {
-    @LETTER = [a-z];
+    @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
index a550f0b..a94efce 100644
--- a/Tests/feaLib/data/spec5f_ii_3.ttx
+++ b/Tests/feaLib/data/spec5f_ii_3.ttx
@@ -32,111 +32,115 @@
       <Lookup index="0">
         <LookupType value="6"/>
         <LookupFlag value="0"/>
-        <!-- SubTableCount=3 -->
-        <ChainContextSubst index="0" Format="3">
-          <!-- BacktrackGlyphCount=1 -->
-          <BacktrackCoverage index="0">
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage>
             <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>
+          </Coverage>
+          <BacktrackClassDef>
+            <ClassDef glyph="a" class="1"/>
+            <ClassDef glyph="b" class="1"/>
+            <ClassDef glyph="c" class="1"/>
+            <ClassDef glyph="d" class="1"/>
+            <ClassDef glyph="e" class="1"/>
+            <ClassDef glyph="f" class="1"/>
+            <ClassDef glyph="g" class="1"/>
+            <ClassDef glyph="h" class="1"/>
+            <ClassDef glyph="i" class="1"/>
+            <ClassDef glyph="j" class="1"/>
+            <ClassDef glyph="k" class="1"/>
+            <ClassDef glyph="l" class="1"/>
+            <ClassDef glyph="m" class="1"/>
+            <ClassDef glyph="n" class="1"/>
+            <ClassDef glyph="o" class="1"/>
+            <ClassDef glyph="p" class="1"/>
+            <ClassDef glyph="q" class="1"/>
+            <ClassDef glyph="r" class="1"/>
+            <ClassDef glyph="s" class="1"/>
+            <ClassDef glyph="t" class="1"/>
+            <ClassDef glyph="u" class="1"/>
+            <ClassDef glyph="v" class="1"/>
+            <ClassDef glyph="w" class="1"/>
+            <ClassDef glyph="x" class="1"/>
+            <ClassDef glyph="y" class="1"/>
+            <ClassDef glyph="z" class="1"/>
+          </BacktrackClassDef>
+          <InputClassDef>
+            <ClassDef glyph="a" class="3"/>
+            <ClassDef glyph="d" class="2"/>
+            <ClassDef glyph="n" class="1"/>
+          </InputClassDef>
+          <LookAheadClassDef>
+            <ClassDef glyph="a" class="1"/>
+            <ClassDef glyph="b" class="1"/>
+            <ClassDef glyph="c" class="1"/>
+            <ClassDef glyph="d" class="1"/>
+            <ClassDef glyph="e" class="1"/>
+            <ClassDef glyph="f" class="1"/>
+            <ClassDef glyph="g" class="1"/>
+            <ClassDef glyph="h" class="1"/>
+            <ClassDef glyph="i" class="1"/>
+            <ClassDef glyph="j" class="1"/>
+            <ClassDef glyph="k" class="1"/>
+            <ClassDef glyph="l" class="1"/>
+            <ClassDef glyph="m" class="1"/>
+            <ClassDef glyph="n" class="1"/>
+            <ClassDef glyph="o" class="1"/>
+            <ClassDef glyph="p" class="1"/>
+            <ClassDef glyph="q" class="1"/>
+            <ClassDef glyph="r" class="1"/>
+            <ClassDef glyph="s" class="1"/>
+            <ClassDef glyph="t" class="1"/>
+            <ClassDef glyph="u" class="1"/>
+            <ClassDef glyph="v" class="1"/>
+            <ClassDef glyph="w" class="1"/>
+            <ClassDef glyph="x" class="1"/>
+            <ClassDef glyph="y" class="1"/>
+            <ClassDef glyph="z" class="1"/>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=4 -->
+          <ChainSubClassSet index="0">
+            <!-- ChainSubClassRuleCount=0 -->
+          </ChainSubClassSet>
+          <ChainSubClassSet index="1">
+            <!-- ChainSubClassRuleCount=0 -->
+          </ChainSubClassSet>
+          <ChainSubClassSet index="2">
+            <!-- ChainSubClassRuleCount=0 -->
+          </ChainSubClassSet>
+          <ChainSubClassSet index="3">
+            <!-- ChainSubClassRuleCount=3 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="1"/>
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="1"/>
+              <Input index="1" value="2"/>
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=0 -->
+            </ChainSubClassRule>
+            <ChainSubClassRule index="1">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="1"/>
+              <Input index="1" value="2"/>
+              <!-- LookAheadGlyphCount=1 -->
+              <LookAhead index="0" value="1"/>
+              <!-- SubstCount=0 -->
+            </ChainSubClassRule>
+            <ChainSubClassRule index="2">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="1"/>
+              <Input index="1" value="2"/>
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="1"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
         </ChainContextSubst>
       </Lookup>
       <Lookup index="1">
diff --git a/Tests/feaLib/data/spec5f_ii_4.fea b/Tests/feaLib/data/spec5f_ii_4.fea
index 731a1f6..bc6fda4 100644
--- a/Tests/feaLib/data/spec5f_ii_4.fea
+++ b/Tests/feaLib/data/spec5f_ii_4.fea
@@ -2,13 +2,13 @@
 # "Specifying exceptions to the Chain Sub rule"
 # http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
 
-@LETTER = [A-Z a-z];
+@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];
+    @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];
 
diff --git a/Tests/feaLib/data/spec5fi2.ttx b/Tests/feaLib/data/spec5fi2.ttx
index 0842cf9..6485c34 100644
--- a/Tests/feaLib/data/spec5fi2.ttx
+++ b/Tests/feaLib/data/spec5fi2.ttx
@@ -31,7 +31,7 @@
       <!-- LookupCount=2 -->
       <Lookup index="0">
         <LookupType value="6"/>
-        <LookupFlag value="7"/>
+        <LookupFlag value="7"/><!-- rightToLeft ignoreBaseGlyphs ignoreLigatures -->
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=1 -->
@@ -54,7 +54,7 @@
       </Lookup>
       <Lookup index="1">
         <LookupType value="1"/>
-        <LookupFlag value="7"/>
+        <LookupFlag value="7"/><!-- rightToLeft ignoreBaseGlyphs ignoreLigatures -->
         <!-- SubTableCount=1 -->
         <SingleSubst index="0">
           <Substitution in="d" out="d.alt"/>
diff --git a/Tests/feaLib/data/spec5fi3.fea b/Tests/feaLib/data/spec5fi3.fea
index e47a6f8..e44a732 100644
--- a/Tests/feaLib/data/spec5fi3.fea
+++ b/Tests/feaLib/data/spec5fi3.fea
@@ -5,5 +5,5 @@
 languagesystem latn dflt;
 
 feature test {
-    sub [A-Z] [A.sc-Z.sc]' by [a-z];
+    sub [A - Z] [A.sc - Z.sc]' by [a - z];
 } test;
diff --git a/Tests/feaLib/data/spec6b_ii.ttx b/Tests/feaLib/data/spec6b_ii.ttx
index a7131de..c7b8de8 100644
--- a/Tests/feaLib/data/spec6b_ii.ttx
+++ b/Tests/feaLib/data/spec6b_ii.ttx
@@ -91,6 +91,7 @@
           <!-- Class2Count=2 -->
           <Class1Record index="0">
             <Class2Record index="0">
+              <Value1 XAdvance="0"/>
             </Class2Record>
             <Class2Record index="1">
               <Value1 XAdvance="-100"/>
diff --git a/Tests/feaLib/data/spec6d2.fea b/Tests/feaLib/data/spec6d2.fea
index ead224f..5c2620d 100644
--- a/Tests/feaLib/data/spec6d2.fea
+++ b/Tests/feaLib/data/spec6d2.fea
@@ -9,7 +9,11 @@
 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;
+    pos base [e o]
+        <anchor 250 450> mark @TOP_MARKS
+        <anchor 250 -12> mark @BOTTOM_MARKS;
+#test-fea2fea: pos base [a u]
+    position base [a u]
+        <anchor 265 450> mark @TOP_MARKS
+        <anchor 250 -10> mark @BOTTOM_MARKS;
 } test;
diff --git a/Tests/feaLib/data/spec6e.fea b/Tests/feaLib/data/spec6e.fea
index ed956c8..6461223 100644
--- a/Tests/feaLib/data/spec6e.fea
+++ b/Tests/feaLib/data/spec6e.fea
@@ -4,7 +4,10 @@
 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
+    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/spec6f.fea b/Tests/feaLib/data/spec6f.fea
index 8d32008..277bdb4 100644
--- a/Tests/feaLib/data/spec6f.fea
+++ b/Tests/feaLib/data/spec6f.fea
@@ -2,5 +2,6 @@
 
 feature test {
     markClass damma <anchor 189 -103> @MARK_CLASS_1;
-    pos mark hamza <anchor 221 301> mark @MARK_CLASS_1;
+    pos mark hamza
+        <anchor 221 301> mark @MARK_CLASS_1;
 } test;
diff --git a/Tests/feaLib/data/spec6h_ii.fea b/Tests/feaLib/data/spec6h_ii.fea
index 36a1f03..690d2a3 100644
--- a/Tests/feaLib/data/spec6h_ii.fea
+++ b/Tests/feaLib/data/spec6h_ii.fea
@@ -12,8 +12,10 @@
 } 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;
+    pos base o
+        <anchor 250 450> mark @ALL_MARKS;
+    pos base c
+        <anchor 250 450> mark @ALL_MARKS;
 } CNTXT_MARK_TO_BASE;
 
 feature test {
diff --git a/Tests/feaLib/data/spec6h_ii.ttx b/Tests/feaLib/data/spec6h_ii.ttx
index e8ec85f..2f0efc6 100644
--- a/Tests/feaLib/data/spec6h_ii.ttx
+++ b/Tests/feaLib/data/spec6h_ii.ttx
@@ -112,25 +112,23 @@
         </MarkBasePos>
       </Lookup>
       <Lookup index="2">
-        <LookupType value="8"/>
+        <LookupType value="7"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <ChainContextPos index="0" Format="3">
-          <!-- BacktrackGlyphCount=0 -->
-          <!-- InputGlyphCount=3 -->
-          <InputCoverage index="0">
+        <ContextPos index="0" Format="3">
+          <!-- GlyphCount=3 -->
+          <!-- PosCount=2 -->
+          <Coverage index="0">
             <Glyph value="T"/>
-          </InputCoverage>
-          <InputCoverage index="1">
+          </Coverage>
+          <Coverage index="1">
             <Glyph value="c"/>
             <Glyph value="o"/>
-          </InputCoverage>
-          <InputCoverage index="2">
+          </Coverage>
+          <Coverage index="2">
             <Glyph value="grave"/>
             <Glyph value="acute"/>
-          </InputCoverage>
-          <!-- LookAheadGlyphCount=0 -->
-          <!-- PosCount=2 -->
+          </Coverage>
           <PosLookupRecord index="0">
             <SequenceIndex value="0"/>
             <LookupListIndex value="0"/>
@@ -139,7 +137,7 @@
             <SequenceIndex value="2"/>
             <LookupListIndex value="1"/>
           </PosLookupRecord>
-        </ChainContextPos>
+        </ContextPos>
       </Lookup>
     </LookupList>
   </GPOS>
diff --git a/Tests/feaLib/data/spec6h_iii_3d.ttx b/Tests/feaLib/data/spec6h_iii_3d.ttx
index a608f0e..2335dd0 100644
--- a/Tests/feaLib/data/spec6h_iii_3d.ttx
+++ b/Tests/feaLib/data/spec6h_iii_3d.ttx
@@ -30,25 +30,23 @@
     <LookupList>
       <!-- LookupCount=2 -->
       <Lookup index="0">
-        <LookupType value="8"/>
+        <LookupType value="7"/>
         <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 -->
+        <ContextPos index="0" Format="3">
+          <!-- GlyphCount=2 -->
           <!-- PosCount=1 -->
+          <Coverage index="0">
+            <Glyph value="L"/>
+          </Coverage>
+          <Coverage index="1">
+            <Glyph value="quoteright"/>
+          </Coverage>
           <PosLookupRecord index="0">
             <SequenceIndex value="1"/>
             <LookupListIndex value="1"/>
           </PosLookupRecord>
-        </ChainContextPos>
+        </ContextPos>
       </Lookup>
       <Lookup index="1">
         <LookupType value="1"/>
diff --git a/Tests/feaLib/data/spec8a.fea b/Tests/feaLib/data/spec8a.fea
index b4d7dd2..4054821 100644
--- a/Tests/feaLib/data/spec8a.fea
+++ b/Tests/feaLib/data/spec8a.fea
@@ -10,7 +10,7 @@
 } aalt;
 
 feature smcp {
-    sub [a-c] by [A.sc-C.sc];
+    sub [a - c] by [A.sc - C.sc];
     sub f i by f_i;  # not considered for aalt
 } smcp;
 
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/test.fea b/Tests/feaLib/data/test.fea
new file mode 100644
index 0000000..c01ade2
--- /dev/null
+++ b/Tests/feaLib/data/test.fea
@@ -0,0 +1 @@
+# Nothing
diff --git a/Tests/feaLib/error_test.py b/Tests/feaLib/error_test.py
index 87cbecb..2ebb3e4 100644
--- a/Tests/feaLib/error_test.py
+++ b/Tests/feaLib/error_test.py
@@ -1,12 +1,11 @@
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
 from fontTools.feaLib.error import FeatureLibError
+from fontTools.feaLib.location import FeatureLibLocation
 import unittest
 
 
 class FeatureLibErrorTest(unittest.TestCase):
     def test_str(self):
-        err = FeatureLibError("Squeak!", ("foo.fea", 23, 42))
+        err = FeatureLibError("Squeak!", FeatureLibLocation("foo.fea", 23, 42))
         self.assertEqual(str(err), "foo.fea:23:42: Squeak!")
 
     def test_str_nolocation(self):
diff --git a/Tests/feaLib/lexer_test.py b/Tests/feaLib/lexer_test.py
index bb95a5b..24dc5db 100644
--- a/Tests/feaLib/lexer_test.py
+++ b/Tests/feaLib/lexer_test.py
@@ -1,14 +1,17 @@
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
-from fontTools.feaLib.error import FeatureLibError
+from fontTools.misc.py23 import tobytes
+from fontTools.feaLib.error import FeatureLibError, IncludedFeaNotFound
 from fontTools.feaLib.lexer import IncludingLexer, Lexer
+from io import StringIO
 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)
@@ -35,6 +38,7 @@
 
     def test_glyphclass(self):
         self.assertEqual(lex("@Vowel.sc"), [(Lexer.GLYPHCLASS, "Vowel.sc")])
+        self.assertEqual(lex("@Vowel-sc"), [(Lexer.GLYPHCLASS, "Vowel-sc")])
         self.assertRaisesRegex(FeatureLibError,
                                "Expected glyph class", lex, "@(a)")
         self.assertRaisesRegex(FeatureLibError,
@@ -64,8 +68,9 @@
     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)])
+        self.assertEqual(lex("0xCAFED00D"), [(Lexer.HEXADECIMAL, 0xCAFED00D)])
+        self.assertEqual(lex("0xcafed00d"), [(Lexer.HEXADECIMAL, 0xCAFED00D)])
+        self.assertEqual(lex("010"), [(Lexer.OCTAL, 0o10)])
 
     def test_float(self):
         self.assertEqual(lex("1.23 -4.5"),
@@ -103,7 +108,7 @@
 
     def test_newline(self):
         def lines(s):
-            return [loc[1] for (_, _, loc) in Lexer(s, "test.fea")]
+            return [loc.line 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
@@ -111,7 +116,7 @@
 
     def test_location(self):
         def locs(s):
-            return ["%s:%d:%d" % loc for (_, _, loc) in Lexer(s, "test.fea")]
+            return [str(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"
@@ -146,7 +151,7 @@
 
     def test_include(self):
         lexer = IncludingLexer(self.getpath("include/include4.fea"))
-        result = ['%s %s:%d' % (token, os.path.split(loc[0])[1], loc[1])
+        result = ['%s %s:%d' % (token, os.path.split(loc.file)[1], loc.line)
                   for _, token, loc in lexer]
         self.assertEqual(result, [
             "I4a include4.fea:1",
@@ -173,7 +178,61 @@
 
     def test_include_missing_file(self):
         lexer = IncludingLexer(self.getpath("include/includemissingfile.fea"))
-        self.assertRaises(FeatureLibError, lambda: list(lexer))
+        self.assertRaisesRegex(IncludedFeaNotFound,
+                               "includemissingfile.fea:1:8: The following feature file "
+                               "should be included but cannot be found: "
+                               "missingfile.fea",
+                               lambda: list(lexer))
+
+    def test_featurefilepath_None(self):
+        lexer = IncludingLexer(StringIO("# foobar"))
+        self.assertIsNone(lexer.featurefilepath)
+        files = set(loc.file 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 = StringIO("include(%s);" % included.name)
+        try:
+            lexer = IncludingLexer(including)
+            files = set(loc.file 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(StringIO("include(included.fea);"))
+            files = set(os.path.realpath(loc.file) 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__":
diff --git a/Tests/feaLib/parser_test.py b/Tests/feaLib/parser_test.py
index 6149865..1180819 100644
--- a/Tests/feaLib/parser_test.py
+++ b/Tests/feaLib/parser_test.py
@@ -1,9 +1,8 @@
 # -*- coding: utf-8 -*-
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
+from fontTools.misc.loggingTools import CapturingLogHandler
 from fontTools.feaLib.error import FeatureLibError
 from fontTools.feaLib.parser import Parser, SymbolTable
-from fontTools.misc.py23 import *
+from io import StringIO
 import warnings
 import fontTools.feaLib.ast as ast
 import os
@@ -41,6 +40,14 @@
     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
+    one two five six acute grave dieresis umlaut cedilla ogonek macron
+    a_f_f_i o_f_f_i f_i f_f_i one.fitted one.oldstyle a.1 a.2 a.3 c_t
+    PRE SUF FIX BACK TRACK LOOK AHEAD ampersand ampersand.1 ampersand.2
+    cid00001 cid00002 cid00003 cid00004 cid00005 cid00006 cid00007
+    cid12345 cid78987 cid00999 cid01000 cid01001 cid00998 cid00995
+    cid00111 cid00222
+    comma endash emdash figuredash damma hamza
+    c_d d.alt n.end s.end f_f
 """).split() + ["foo.%d" % i for i in range(1, 200)]
 
 
@@ -56,7 +63,7 @@
         glyphMap = {'a': 0, 'b': 1, 'c': 2}
         with warnings.catch_warnings(record=True) as w:
             warnings.simplefilter("always")
-            parser = Parser(UnicodeIO(), glyphMap=glyphMap)
+            parser = Parser(StringIO(), glyphMap=glyphMap)
 
             self.assertEqual(len(w), 1)
             self.assertEqual(w[-1].category, UserWarning)
@@ -65,11 +72,11 @@
 
             self.assertRaisesRegex(
                 TypeError, "mutually exclusive",
-                Parser, UnicodeIO(), ("a",), glyphMap={"a": 0})
+                Parser, StringIO(), ("a",), glyphMap={"a": 0})
 
             self.assertRaisesRegex(
                 TypeError, "unsupported keyword argument",
-                Parser, UnicodeIO(), foo="bar")
+                Parser, StringIO(), foo="bar")
 
     def test_comments(self):
         doc = self.parse(
@@ -86,6 +93,15 @@
         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 {"
@@ -206,6 +222,8 @@
         [liga] = self.parse("feature liga useExtension {} liga;").statements
         self.assertEqual(liga.name, "liga")
         self.assertTrue(liga.use_extension)
+        self.assertEqual(liga.asFea(),
+                         "feature liga useExtension {\n    \n} liga;\n")
 
     def test_feature_comment(self):
         [liga] = self.parse("feature liga { # Comment\n } liga;").statements
@@ -228,7 +246,7 @@
         [feature] = self.parse(
             "feature ss01 { featureNames { # Comment\n }; } ss01;").statements
         [featureNames] = feature.statements
-        self.assertIsInstance(featureNames, ast.FeatureNamesBlock)
+        self.assertIsInstance(featureNames, ast.NestedBlock)
         [comment] = featureNames.statements
         self.assertIsInstance(comment, ast.Comment)
         self.assertEqual(comment.text, "# Comment")
@@ -237,7 +255,7 @@
         [feature] = self.parse(
             "feature ss01 { featureNames { ;;; }; } ss01;").statements
         [featureNames] = feature.statements
-        self.assertIsInstance(featureNames, ast.FeatureNamesBlock)
+        self.assertIsInstance(featureNames, ast.NestedBlock)
         self.assertEqual(featureNames.statements, [])
 
     def test_FontRevision(self):
@@ -251,6 +269,12 @@
             FeatureLibError, "Font revision numbers must be positive",
             self.parse, "table head {FontRevision -17.2;} head;")
 
+    def test_strict_glyph_name_check(self):
+        self.parse("@bad = [a b ccc];", glyphNames=("a", "b", "ccc"))
+
+        with self.assertRaisesRegex(FeatureLibError, "missing from the glyph set: ccc"):
+            self.parse("@bad = [a b ccc];", glyphNames=("a", "b"))
+
     def test_glyphclass(self):
         [gc] = self.parse("@dash = [endash emdash figuredash];").statements
         self.assertEqual(gc.name, "dash")
@@ -323,6 +347,19 @@
         [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_ambiguous_dash_no_glyph_names(self):
+        # If Parser is initialized without a glyphNames parameter (or with empty one)
+        # it cannot distinguish between a glyph name containing an hyphen, or a
+        # range of glyph names; thus it will interpret them as literal glyph names
+        # while also outputting a logging warning to alert user about the ambiguity.
+        # https://github.com/fonttools/fonttools/issues/1768
+        glyphNames = ()
+        with CapturingLogHandler("fontTools.feaLib.parser", level="WARNING") as caplog:
+            [gc] = self.parse("@class = [A-foo.sc B-foo.sc C D];", glyphNames).statements
+        self.assertEqual(gc.glyphSet(), ("A-foo.sc", "B-foo.sc", "C", "D"))
+        self.assertEqual(len(caplog.records), 2)
+        caplog.assertRegex("Ambiguous glyph name that looks like a range:")
+
     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
@@ -397,7 +434,7 @@
         self.assertEqual(smcp.statements[0].glyphSet(), ("a", "b", "s"))
 
     def test_glyphclass_scoping_bug496(self):
-        # https://github.com/behdad/fonttools/issues/496
+        # https://github.com/fonttools/fonttools/issues/496
         f1, f2 = self.parse(
             "feature F1 { lookup L { @GLYPHCLASS = [A B C];} L; } F1;"
             "feature F2 { sub @GLYPHCLASS by D; } F2;"
@@ -486,6 +523,32 @@
             "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]
@@ -573,6 +636,8 @@
         [lookup] = self.parse("lookup Foo useExtension {} Foo;").statements
         self.assertEqual(lookup.name, "Foo")
         self.assertTrue(lookup.use_extension)
+        self.assertEqual(lookup.asFea(),
+                         "lookup Foo useExtension {\n    \n} Foo;\n")
 
     def test_lookup_block_name_mismatch(self):
         self.assertRaisesRegex(
@@ -638,6 +703,7 @@
         self.assertEqual(flag.value, 9)
         self.assertIsNone(flag.markAttachment)
         self.assertIsNone(flag.markFilteringSet)
+        self.assertEqual(flag.asFea(), "lookupflag RightToLeft IgnoreMarks;")
 
     def test_lookupflag_format_A_MarkAttachmentType(self):
         flag = self.parse_lookupflag_(
@@ -649,6 +715,20 @@
         self.assertEqual(flag.markAttachment.glyphSet(),
                          ("acute", "grave", "macron"))
         self.assertIsNone(flag.markFilteringSet)
+        self.assertEqual(flag.asFea(),
+            "lookupflag RightToLeft MarkAttachmentType @TOP_MARKS;")
+
+    def test_lookupflag_format_A_MarkAttachmentType_glyphClass(self):
+        flag = self.parse_lookupflag_(
+            "lookupflag RightToLeft MarkAttachmentType [acute grave macron];")
+        self.assertIsInstance(flag, ast.LookupFlagStatement)
+        self.assertEqual(flag.value, 1)
+        self.assertIsInstance(flag.markAttachment, ast.GlyphClass)
+        self.assertEqual(flag.markAttachment.glyphSet(),
+                         ("acute", "grave", "macron"))
+        self.assertIsNone(flag.markFilteringSet)
+        self.assertEqual(flag.asFea(),
+            "lookupflag RightToLeft MarkAttachmentType [acute grave macron];")
 
     def test_lookupflag_format_A_UseMarkFilteringSet(self):
         flag = self.parse_lookupflag_(
@@ -660,6 +740,20 @@
         self.assertIsInstance(flag.markFilteringSet, ast.GlyphClassName)
         self.assertEqual(flag.markFilteringSet.glyphSet(),
                          ("cedilla", "ogonek"))
+        self.assertEqual(flag.asFea(),
+            "lookupflag IgnoreLigatures UseMarkFilteringSet @BOTTOM_MARKS;")
+
+    def test_lookupflag_format_A_UseMarkFilteringSet_glyphClass(self):
+        flag = self.parse_lookupflag_(
+            "lookupflag UseMarkFilteringSet [cedilla ogonek] IgnoreLigatures;")
+        self.assertIsInstance(flag, ast.LookupFlagStatement)
+        self.assertEqual(flag.value, 4)
+        self.assertIsNone(flag.markAttachment)
+        self.assertIsInstance(flag.markFilteringSet, ast.GlyphClass)
+        self.assertEqual(flag.markFilteringSet.glyphSet(),
+                         ("cedilla", "ogonek"))
+        self.assertEqual(flag.asFea(),
+            "lookupflag IgnoreLigatures UseMarkFilteringSet [cedilla ogonek];")
 
     def test_lookupflag_format_B(self):
         flag = self.parse_lookupflag_("lookupflag 7;")
@@ -667,6 +761,23 @@
         self.assertEqual(flag.value, 7)
         self.assertIsNone(flag.markAttachment)
         self.assertIsNone(flag.markFilteringSet)
+        self.assertEqual(flag.asFea(),
+            "lookupflag RightToLeft IgnoreBaseGlyphs IgnoreLigatures;")
+
+    def test_lookupflag_format_B_zero(self):
+        flag = self.parse_lookupflag_("lookupflag 0;")
+        self.assertIsInstance(flag, ast.LookupFlagStatement)
+        self.assertEqual(flag.value, 0)
+        self.assertIsNone(flag.markAttachment)
+        self.assertIsNone(flag.markFilteringSet)
+        self.assertEqual(flag.asFea(), "lookupflag 0;")
+
+    def test_lookupflag_no_value(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'lookupflag must have a value',
+            self.parse,
+            "feature test {lookupflag;} test;")
 
     def test_lookupflag_repeated(self):
         self.assertRaisesRegex(
@@ -687,7 +798,7 @@
         self.assertIsInstance(pos, ast.SinglePosStatement)
         [(glyphs, value)] = pos.pos
         self.assertEqual(glyphstr([glyphs]), "one")
-        self.assertEqual(value.makeString(vertical=False), "<1 2 3 4>")
+        self.assertEqual(value.asFea(), "<1 2 3 4>")
 
     def test_gpos_type_1_glyphclass_horizontal(self):
         doc = self.parse("feature kern {pos [one two] -300;} kern;")
@@ -695,7 +806,7 @@
         self.assertIsInstance(pos, ast.SinglePosStatement)
         [(glyphs, value)] = pos.pos
         self.assertEqual(glyphstr([glyphs]), "[one two]")
-        self.assertEqual(value.makeString(vertical=False), "-300")
+        self.assertEqual(value.asFea(), "-300")
 
     def test_gpos_type_1_glyphclass_vertical(self):
         doc = self.parse("feature vkrn {pos [one two] -300;} vkrn;")
@@ -703,7 +814,7 @@
         self.assertIsInstance(pos, ast.SinglePosStatement)
         [(glyphs, value)] = pos.pos
         self.assertEqual(glyphstr([glyphs]), "[one two]")
-        self.assertEqual(value.makeString(vertical=True), "-300")
+        self.assertEqual(value.asFea(), "-300")
 
     def test_gpos_type_1_multiple(self):
         doc = self.parse("feature f {pos one'1 two'2 [five six]'56;} f;")
@@ -711,11 +822,11 @@
         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(val1.asFea(), "1")
         self.assertEqual(glyphstr([glyphs2]), "two")
-        self.assertEqual(val2.makeString(vertical=False), "2")
+        self.assertEqual(val2.asFea(), "2")
         self.assertEqual(glyphstr([glyphs3]), "[five six]")
-        self.assertEqual(val3.makeString(vertical=False), "56")
+        self.assertEqual(val3.asFea(), "56")
         self.assertEqual(pos.prefix, [])
         self.assertEqual(pos.suffix, [])
 
@@ -735,10 +846,65 @@
         self.assertIsInstance(pos, ast.SinglePosStatement)
         [(glyphs, value)] = pos.pos
         self.assertEqual(glyphstr([glyphs]), "[T Y]")
-        self.assertEqual(value.makeString(vertical=False), "20")
+        self.assertEqual(value.asFea(), "20")
         self.assertEqual(glyphstr(pos.prefix), "[A B]")
         self.assertEqual(glyphstr(pos.suffix), "comma")
 
+    def test_gpos_type_1_chained_special_kern_format_valuerecord_format_a(self):
+        doc = self.parse("feature kern {pos [A B] [T Y]' comma 20;} 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.asFea(), "20")
+        self.assertEqual(glyphstr(pos.prefix), "[A B]")
+        self.assertEqual(glyphstr(pos.suffix), "comma")
+
+    def test_gpos_type_1_chained_special_kern_format_valuerecord_format_b(self):
+        doc = self.parse("feature kern {pos [A B] [T Y]' comma <0 0 0 0>;} 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.asFea(), "<0 0 0 0>")
+        self.assertEqual(glyphstr(pos.prefix), "[A B]")
+        self.assertEqual(glyphstr(pos.suffix), "comma")
+
+    def test_gpos_type_1_chained_special_kern_format_valuerecord_format_b_bug2293(self):
+        # https://github.com/fonttools/fonttools/issues/2293
+        doc = self.parse("feature kern {pos [A B] [T Y]' comma a <0 0 0 0>;} 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.asFea(), "<0 0 0 0>")
+        self.assertEqual(glyphstr(pos.prefix), "[A B]")
+        self.assertEqual(glyphstr(pos.suffix), "comma a")
+
+    def test_gpos_type_1_chained_exception1(self):
+        with self.assertRaisesRegex(FeatureLibError, "Positioning values are allowed"):
+            doc = self.parse("feature kern {"
+                             "    pos [A B]' [T Y]' comma a <0 0 0 0>;"
+                             "} kern;")
+
+    def test_gpos_type_1_chained_exception2(self):
+        with self.assertRaisesRegex(FeatureLibError, "Positioning values are allowed"):
+            doc = self.parse("feature kern {"
+                             "    pos [A B]' <0 0 0 0> [T Y]' comma a <0 0 0 0>;"
+                             "} kern;")
+
+    def test_gpos_type_1_chained_exception3(self):
+        with self.assertRaisesRegex(FeatureLibError, "Positioning cannot be applied"):
+            doc = self.parse("feature kern {"
+                             "    pos [A B] <0 0 0 0> [T Y]' comma a <0 0 0 0>;"
+                             "} kern;")
+
+    def test_gpos_type_1_chained_exception4(self):
+        with self.assertRaisesRegex(FeatureLibError, "Positioning values are allowed"):
+            doc = self.parse("feature kern {"
+                             "    pos a' b c 123 d;"
+                             "} kern;")
+
     def test_gpos_type_2_format_a(self):
         doc = self.parse("feature kern {"
                          "    pos [T V] -60 [a b c] <1 2 3 4>;"
@@ -747,10 +913,9 @@
         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(pos.valuerecord1.asFea(), "-60")
         self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
-        self.assertEqual(pos.valuerecord2.makeString(vertical=False),
-                         "<1 2 3 4>")
+        self.assertEqual(pos.valuerecord2.asFea(), "<1 2 3 4>")
 
     def test_gpos_type_2_format_a_enumerated(self):
         doc = self.parse("feature kern {"
@@ -760,12 +925,25 @@
         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(pos.valuerecord1.asFea(), "-60")
         self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
-        self.assertEqual(pos.valuerecord2.makeString(vertical=False),
-                         "<1 2 3 4>")
+        self.assertEqual(pos.valuerecord2.asFea(), "<1 2 3 4>")
 
-    def test_gpos_type_2_format_a_with_null(self):
+    def test_gpos_type_2_format_a_with_null_first(self):
+        doc = self.parse("feature kern {"
+                         "    pos [T V] <NULL> [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.assertFalse(pos.valuerecord1)
+        self.assertEqual(pos.valuerecord1.asFea(), "<NULL>")
+        self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
+        self.assertEqual(pos.valuerecord2.asFea(), "<1 2 3 4>")
+        self.assertEqual(pos.asFea(), "pos [T V] <NULL> [a b c] <1 2 3 4>;")
+
+    def test_gpos_type_2_format_a_with_null_second(self):
         doc = self.parse("feature kern {"
                          "    pos [T V] <1 2 3 4> [a b c] <NULL>;"
                          "} kern;")
@@ -773,10 +951,10 @@
         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(pos.valuerecord1.asFea(), "<1 2 3 4>")
         self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
-        self.assertIsNone(pos.valuerecord2)
+        self.assertFalse(pos.valuerecord2)
+        self.assertEqual(pos.asFea(), "pos [T V] [a b c] <1 2 3 4>;")
 
     def test_gpos_type_2_format_b(self):
         doc = self.parse("feature kern {"
@@ -786,8 +964,7 @@
         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(pos.valuerecord1.asFea(), "<1 2 3 4>")
         self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
         self.assertIsNone(pos.valuerecord2)
 
@@ -799,8 +976,7 @@
         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(pos.valuerecord1.asFea(), "<1 2 3 4>")
         self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
         self.assertIsNone(pos.valuerecord2)
 
@@ -948,7 +1124,7 @@
         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])
+        self.assertEqual(pos.lookups, [[lookup1], [lookup2], None])
 
     def test_gpos_type_8_lookup_with_values(self):
         self.assertRaisesRegex(
@@ -1028,6 +1204,36 @@
             FeatureLibError, "Expected platform id 1 or 3",
             self.parse, 'table name { nameid 9 666 "Foo"; } name;')
 
+    def test_nameid_hexadecimal(self):
+        doc = self.parse(
+            r'table name { nameid 0x9 0x3 0x1 0x0409 "Test"; } name;')
+        name = doc.statements[0].statements[0]
+        self.assertEqual(name.nameID, 9)
+        self.assertEqual(name.platformID, 3)
+        self.assertEqual(name.platEncID, 1)
+        self.assertEqual(name.langID, 0x0409)
+
+    def test_nameid_octal(self):
+        doc = self.parse(
+            r'table name { nameid 011 03 012 02011 "Test"; } name;')
+        name = doc.statements[0].statements[0]
+        self.assertEqual(name.nameID, 9)
+        self.assertEqual(name.platformID, 3)
+        self.assertEqual(name.platEncID, 10)
+        self.assertEqual(name.langID, 0o2011)
+
+    def test_cv_hexadecimal(self):
+        doc = self.parse(
+            r'feature cv01 { cvParameters { Character 0x5DDE; }; } cv01;')
+        cv = doc.statements[0].statements[0].statements[0]
+        self.assertEqual(cv.character, 0x5DDE)
+
+    def test_cv_octal(self):
+        doc = self.parse(
+            r'feature cv01 { cvParameters { Character 056736; }; } cv01;')
+        cv = doc.statements[0].statements[0].statements[0]
+        self.assertEqual(cv.character, 0o56736)
+
     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]
@@ -1094,7 +1300,7 @@
         self.assertRaisesRegex(
             FeatureLibError,
             'In reverse chaining single substitutions, the replacement '
-            '\(after "by"\) must be a single glyph or glyph class',
+            r'\(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):
@@ -1109,6 +1315,76 @@
             '"dflt" is not a valid script tag; use "DFLT" instead',
             self.parse, "feature test {script dflt;} test;")
 
+    def test_stat_design_axis(self):  # STAT DesignAxis
+        doc = self.parse('table STAT { DesignAxis opsz 0 '
+                         '{name "Optical Size";}; } STAT;')
+        da = doc.statements[0].statements[0]
+        self.assertIsInstance(da, ast.STATDesignAxisStatement)
+        self.assertEqual(da.tag, 'opsz')
+        self.assertEqual(da.axisOrder, 0)
+        self.assertEqual(da.names[0].string, 'Optical Size')
+
+    def test_stat_axis_value_format1(self):  # STAT AxisValue
+        doc = self.parse('table STAT { DesignAxis opsz 0 '
+                         '{name "Optical Size";}; '
+                         'AxisValue {location opsz 8; name "Caption";}; } '
+                         'STAT;')
+        avr = doc.statements[0].statements[1]
+        self.assertIsInstance(avr, ast.STATAxisValueStatement)
+        self.assertEqual(avr.locations[0].tag, 'opsz')
+        self.assertEqual(avr.locations[0].values[0], 8)
+        self.assertEqual(avr.names[0].string, 'Caption')
+
+    def test_stat_axis_value_format2(self):  # STAT AxisValue
+        doc = self.parse('table STAT { DesignAxis opsz 0 '
+                         '{name "Optical Size";}; '
+                         'AxisValue {location opsz 8 6 10; name "Caption";}; } '
+                         'STAT;')
+        avr = doc.statements[0].statements[1]
+        self.assertIsInstance(avr, ast.STATAxisValueStatement)
+        self.assertEqual(avr.locations[0].tag, 'opsz')
+        self.assertEqual(avr.locations[0].values, [8, 6, 10])
+        self.assertEqual(avr.names[0].string, 'Caption')
+
+    def test_stat_axis_value_format2_bad_range(self):  # STAT AxisValue
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Default value 5 is outside of specified range 6-10.',
+            self.parse, 'table STAT { DesignAxis opsz 0 '
+                        '{name "Optical Size";}; '
+                        'AxisValue {location opsz 5 6 10; name "Caption";}; } '
+                        'STAT;')
+
+    def test_stat_axis_value_format4(self):  # STAT AxisValue
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'Only one value is allowed in a Format 4 Axis Value Record, but 3 were found.',
+            self.parse, 'table STAT { '
+                         'DesignAxis opsz 0 {name "Optical Size";}; '
+                         'DesignAxis wdth 0 {name "Width";}; '
+                         'AxisValue {'
+                         'location opsz 8 6 10; '
+                         'location wdth 400; '
+                         'name "Caption";}; } '
+                         'STAT;')
+
+    def test_stat_elidedfallbackname(self):  # STAT ElidedFallbackName
+        doc = self.parse('table STAT { ElidedFallbackName {name "Roman"; '
+                         'name 3 1 0x0411 "ローマン"; }; '
+                         '} STAT;')
+        nameRecord = doc.statements[0].statements[0]
+        self.assertIsInstance(nameRecord, ast.ElidedFallbackName)
+        self.assertEqual(nameRecord.names[0].string, 'Roman')
+        self.assertEqual(nameRecord.names[1].string, 'ローマン')
+
+    def test_stat_elidedfallbacknameid(self):  # STAT ElidedFallbackNameID
+        doc = self.parse('table name { nameid 278 "Roman"; } name; '
+                         'table STAT { ElidedFallbackNameID 278; '
+                         '} STAT;')
+        nameRecord = doc.statements[0].statements[0]
+        self.assertIsInstance(nameRecord, ast.NameRecord)
+        self.assertEqual(nameRecord.string, 'Roman')
+
     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]
@@ -1220,6 +1496,22 @@
         self.assertEqual(sub.glyph, "f_f_i")
         self.assertEqual(sub.replacement, ("f", "f", "i"))
 
+    def test_substitute_multiple_force_chained(self):
+        doc = self.parse("lookup L {sub f_f_i' 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"))
+        self.assertEqual(sub.asFea(), "sub f_f_i' by 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,
@@ -1256,6 +1548,28 @@
                         "    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;"
+                         "  sub [a a.sc] by a;"
+                         "  sub [a a.sc] by [b b.sc];"
+                         "} 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"])
+        self.assertEqual(statements[3].glyph, "a")
+        self.assertEqual(statements[3].replacement, ["a"])
+        self.assertEqual(statements[4].glyph, "a.sc")
+        self.assertEqual(statements[4].replacement, ["a"])
+        self.assertEqual(statements[5].glyph, "a")
+        self.assertEqual(statements[5].replacement, ["b"])
+        self.assertEqual(statements[6].glyph, "a.sc")
+        self.assertEqual(statements[6].replacement, ["b.sc"])
+
     def test_substitute_from(self):  # GSUB LookupType 3
         doc = self.parse("feature test {"
                          "  substitute a from [a.1 a.2 a.3];"
@@ -1323,14 +1637,21 @@
     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])
+        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_substitute_invalid_statement(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            "Invalid substitution statement",
+            Parser(self.getpath("GSUB_error.fea"), GLYPHNAMES).parse
+        )
 
     def test_subtable(self):
         doc = self.parse("feature test {subtable;} test;")
@@ -1356,7 +1677,8 @@
 
     def test_valuerecord_format_a_horizontal(self):
         doc = self.parse("feature liga {valueRecordDef 123 foo;} liga;")
-        value = doc.statements[0].statements[0].value
+        valuedef = doc.statements[0].statements[0]
+        value = valuedef.value
         self.assertIsNone(value.xPlacement)
         self.assertIsNone(value.yPlacement)
         self.assertEqual(value.xAdvance, 123)
@@ -1365,11 +1687,13 @@
         self.assertIsNone(value.yPlaDevice)
         self.assertIsNone(value.xAdvDevice)
         self.assertIsNone(value.yAdvDevice)
-        self.assertEqual(value.makeString(vertical=False), "123")
+        self.assertEqual(valuedef.asFea(), "valueRecordDef 123 foo;")
+        self.assertEqual(value.asFea(), "123")
 
     def test_valuerecord_format_a_vertical(self):
         doc = self.parse("feature vkrn {valueRecordDef 123 foo;} vkrn;")
-        value = doc.statements[0].statements[0].value
+        valuedef = doc.statements[0].statements[0]
+        value = valuedef.value
         self.assertIsNone(value.xPlacement)
         self.assertIsNone(value.yPlacement)
         self.assertIsNone(value.xAdvance)
@@ -1378,11 +1702,13 @@
         self.assertIsNone(value.yPlaDevice)
         self.assertIsNone(value.xAdvDevice)
         self.assertIsNone(value.yAdvDevice)
-        self.assertEqual(value.makeString(vertical=True), "123")
+        self.assertEqual(valuedef.asFea(), "valueRecordDef 123 foo;")
+        self.assertEqual(value.asFea(), "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
+        valuedef = doc.statements[0].statements[0]
+        value = valuedef.value
         self.assertIsNone(value.xPlacement)
         self.assertIsNone(value.yPlacement)
         self.assertEqual(value.xAdvance, 0)
@@ -1391,11 +1717,13 @@
         self.assertIsNone(value.yPlaDevice)
         self.assertIsNone(value.xAdvDevice)
         self.assertIsNone(value.yAdvDevice)
-        self.assertEqual(value.makeString(vertical=False), "0")
+        self.assertEqual(valuedef.asFea(), "valueRecordDef 0 foo;")
+        self.assertEqual(value.asFea(), "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
+        valuedef = doc.statements[0].statements[0]
+        value = valuedef.value
         self.assertIsNone(value.xPlacement)
         self.assertIsNone(value.yPlacement)
         self.assertIsNone(value.xAdvance)
@@ -1404,7 +1732,8 @@
         self.assertIsNone(value.yPlaDevice)
         self.assertIsNone(value.xAdvDevice)
         self.assertIsNone(value.yAdvDevice)
-        self.assertEqual(value.makeString(vertical=True), "0")
+        self.assertEqual(valuedef.asFea(), "valueRecordDef 0 foo;")
+        self.assertEqual(value.asFea(), "0")
 
     def test_valuerecord_format_a_vertical_contexts_(self):
         for tag in "vkrn vpal vhal valt".split():
@@ -1417,7 +1746,8 @@
 
     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
+        valuedef = doc.statements[0].statements[0]
+        value = valuedef.value
         self.assertEqual(value.xPlacement, 1)
         self.assertEqual(value.yPlacement, 2)
         self.assertEqual(value.xAdvance, 3)
@@ -1426,11 +1756,13 @@
         self.assertIsNone(value.yPlaDevice)
         self.assertIsNone(value.xAdvDevice)
         self.assertIsNone(value.yAdvDevice)
-        self.assertEqual(value.makeString(vertical=False), "<1 2 3 4>")
+        self.assertEqual(valuedef.asFea(), "valueRecordDef <1 2 3 4> foo;")
+        self.assertEqual(value.asFea(), "<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
+        valuedef = doc.statements[0].statements[0]
+        value = valuedef.value
         self.assertEqual(value.xPlacement, 0)
         self.assertEqual(value.yPlacement, 0)
         self.assertEqual(value.xAdvance, 0)
@@ -1439,7 +1771,8 @@
         self.assertIsNone(value.yPlaDevice)
         self.assertIsNone(value.xAdvDevice)
         self.assertIsNone(value.yAdvDevice)
-        self.assertEqual(value.makeString(vertical=False), "<0 0 0 0>")
+        self.assertEqual(valuedef.asFea(), "valueRecordDef <0 0 0 0> foo;")
+        self.assertEqual(value.asFea(), "<0 0 0 0>")
 
     def test_valuerecord_format_c(self):
         doc = self.parse(
@@ -1461,14 +1794,15 @@
         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),
+        self.assertEqual(value.asFea(),
                          "<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)
+        self.assertFalse(value)
+        self.assertEqual(value.asFea(), "<NULL>")
 
     def test_valuerecord_named(self):
         doc = self.parse("valueRecordDef <1 2 3 4> foo;"
@@ -1505,10 +1839,9 @@
         [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;")
+        [langsys] = self.parse("languagesystem DFLT DEU;").statements
+        self.assertEqual(langsys.script, "DFLT")
+        self.assertEqual(langsys.language, "DEU ")
         self.assertRaisesRegex(
             FeatureLibError,
             '"dflt" is not a valid script tag; use "DFLT" instead',
@@ -1536,9 +1869,15 @@
             doc = self.parse("table %s { ;;; } %s;" % (table, table))
             self.assertEqual(doc.statements[0].statements, [])
 
-    def parse(self, text, glyphNames=GLYPHNAMES):
-        featurefile = UnicodeIO(text)
-        p = Parser(featurefile, glyphNames)
+    def test_ufo_features_parse_include_dir(self):
+        fea_path = self.getpath("include/test.ufo/features.fea")
+        include_dir = os.path.dirname(os.path.dirname(fea_path))
+        doc = Parser(fea_path, includeDir=include_dir).parse()
+        assert len(doc.statements) == 1 and doc.statements[0].text == "# Nothing"
+
+    def parse(self, text, glyphNames=GLYPHNAMES, followIncludes=True):
+        featurefile = StringIO(text)
+        p = Parser(featurefile, glyphNames, followIncludes=followIncludes)
         return p.parse()
 
     @staticmethod
diff --git a/Tests/fontBuilder/data/test.otf.ttx b/Tests/fontBuilder/data/test.otf.ttx
new file mode 100644
index 0000000..7924ef5
--- /dev/null
+++ b/Tests/fontBuilder/data/test.otf.ttx
@@ -0,0 +1,299 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.31">
+
+  <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="A"/>
+    <GlyphID id="3" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x9198bee"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1024"/>
+    <created value="Wed Oct 31 19:40:57 2018"/>
+    <modified value="Wed Oct 31 19:40:57 2018"/>
+    <xMin value="100"/>
+    <yMin value="100"/>
+    <xMax value="500"/>
+    <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="824"/>
+    <descent value="200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="100"/>
+    <minRightSideBearing value="100"/>
+    <xMaxExtent value="500"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="4"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="600"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <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="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="????"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="0"/>
+    <sTypoDescender value="0"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="0"/>
+    <usWinDescent value="0"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="0"/>
+    <sCapHeight value="0"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="2"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      HelloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TotallyNormal
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      HelloTestFont-TotallyNormal
+    </namerecord>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x4" unicode="True">
+      HalloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x4" unicode="True">
+      TotaalNormaal
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      HelloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      TotallyNormal
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      HelloTestFont-TotallyNormal
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x413">
+      HalloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x413">
+      TotaalNormaal
+    </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="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <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="HelloTestFont-TotallyNormal">
+      <FullName value="HelloTestFont-TotallyNormal"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-100"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.0009765625 0 0 0.0009765625 0 0"/>
+      <FontBBox value="100 100 500 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="0"/>
+        <nominalWidthX value="0"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          600 100 100 rmoveto
+          900 vlineto
+          -67 67 66 -33 67 hhcurveto
+          67 66 33 67 67 hvcurveto
+          -900 vlineto
+          endchar
+        </CharString>
+        <CharString name=".null">
+          600 100 100 rmoveto
+          900 vlineto
+          -67 67 66 -33 67 hhcurveto
+          67 66 33 67 67 hvcurveto
+          -900 vlineto
+          endchar
+        </CharString>
+        <CharString name="A">
+          600 100 100 rmoveto
+          900 vlineto
+          -67 67 66 -33 67 hhcurveto
+          67 66 33 67 67 hvcurveto
+          -900 vlineto
+          endchar
+        </CharString>
+        <CharString name="a">
+          600 100 100 rmoveto
+          900 vlineto
+          -67 67 66 -33 67 hhcurveto
+          67 66 33 67 67 hvcurveto
+          -900 vlineto
+          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="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"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="a"/>
+              <Value1 XAdvance="-50"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="100"/>
+    <mtx name=".null" width="600" lsb="100"/>
+    <mtx name="A" width="600" lsb="100"/>
+    <mtx name="a" width="600" lsb="100"/>
+  </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/fontBuilder/data/test.ttf.ttx b/Tests/fontBuilder/data/test.ttf.ttx
new file mode 100644
index 0000000..8c3f00e
--- /dev/null
+++ b/Tests/fontBuilder/data/test.ttf.ttx
@@ -0,0 +1,304 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.31">
+
+  <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="A"/>
+    <GlyphID id="3" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x7adb7b76"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1024"/>
+    <created value="Wed Oct 31 19:40:57 2018"/>
+    <modified value="Wed Oct 31 19:40:57 2018"/>
+    <xMin value="100"/>
+    <yMin value="100"/>
+    <xMax value="500"/>
+    <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="824"/>
+    <descent value="200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="100"/>
+    <minRightSideBearing value="100"/>
+    <xMaxExtent value="500"/>
+    <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>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="4"/>
+    <maxPoints value="6"/>
+    <maxContours value="1"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <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="3"/>
+    <xAvgCharWidth value="600"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <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="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="????"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="0"/>
+    <sTypoDescender value="0"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="0"/>
+    <usWinDescent value="0"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="0"/>
+    <sCapHeight value="0"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="100"/>
+    <mtx name=".null" width="600" lsb="100"/>
+    <mtx name="A" width="600" lsb="100"/>
+    <mtx name="a" width="600" lsb="100"/>
+  </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="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER 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="100" yMin="100" xMax="500" yMax="1000">
+      <contour>
+        <pt x="100" y="100" on="1"/>
+        <pt x="100" y="1000" on="1"/>
+        <pt x="200" y="900" on="0"/>
+        <pt x="400" y="900" on="0"/>
+        <pt x="500" y="1000" on="1"/>
+        <pt x="500" y="100" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name=".null" xMin="100" yMin="100" xMax="500" yMax="1000">
+      <contour>
+        <pt x="100" y="100" on="1"/>
+        <pt x="100" y="1000" on="1"/>
+        <pt x="200" y="900" on="0"/>
+        <pt x="400" y="900" on="0"/>
+        <pt x="500" y="1000" on="1"/>
+        <pt x="500" y="100" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="100" yMin="100" xMax="500" yMax="1000">
+      <contour>
+        <pt x="100" y="100" on="1"/>
+        <pt x="100" y="1000" on="1"/>
+        <pt x="200" y="900" on="0"/>
+        <pt x="400" y="900" on="0"/>
+        <pt x="500" y="1000" on="1"/>
+        <pt x="500" y="100" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="100" yMin="100" xMax="500" yMax="1000">
+      <contour>
+        <pt x="100" y="100" on="1"/>
+        <pt x="100" y="1000" on="1"/>
+        <pt x="200" y="900" on="0"/>
+        <pt x="400" y="900" on="0"/>
+        <pt x="500" y="1000" on="1"/>
+        <pt x="500" y="100" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      HelloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TotallyNormal
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      HelloTestFont-TotallyNormal
+    </namerecord>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x4" unicode="True">
+      HalloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x4" unicode="True">
+      TotaalNormaal
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      HelloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      TotallyNormal
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      HelloTestFont-TotallyNormal
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x413">
+      HalloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x413">
+      TotaalNormaal
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <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>
+
+  <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="salt"/>
+        <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="a"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/fontBuilder/data/test_uvs.ttf.ttx b/Tests/fontBuilder/data/test_uvs.ttf.ttx
new file mode 100644
index 0000000..55b0a80
--- /dev/null
+++ b/Tests/fontBuilder/data/test_uvs.ttf.ttx
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+    </cmap_format_4>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uv="0x30" uvs="0xfe00" name="zero.slash"/>
+      <map uv="0x30" uvs="0xfe01"/>
+    </cmap_format_14>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+    </cmap_format_4>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/fontBuilder/data/test_var.otf.ttx b/Tests/fontBuilder/data/test_var.otf.ttx
new file mode 100644
index 0000000..ff14868
--- /dev/null
+++ b/Tests/fontBuilder/data/test_var.otf.ttx
@@ -0,0 +1,299 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.38">
+
+  <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="A"/>
+    <GlyphID id="3" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x79eab779"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Mar 27 00:23:21 2019"/>
+    <modified value="Wed Mar 27 00:23:21 2019"/>
+    <xMin value="100"/>
+    <yMin value="100"/>
+    <xMax value="600"/>
+    <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="824"/>
+    <descent value="200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="200"/>
+    <xMaxExtent value="400"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="4"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="600"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <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="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="????"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="825"/>
+    <sTypoDescender value="200"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="824"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
+    <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="600" lsb="0"/>
+    <mtx name=".null" width="600" lsb="0"/>
+    <mtx name="A" width="600" lsb="0"/>
+    <mtx name="a" width="600" 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="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      HelloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TotallyNormal
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      HelloTestFont-TotallyNormal
+    </namerecord>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Test Axis
+    </namerecord>
+    <namerecord nameID="257" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TotallyTested
+    </namerecord>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x4" unicode="True">
+      HalloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x4" unicode="True">
+      TotaalNormaal
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      HelloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      TotallyNormal
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      HelloTestFont-TotallyNormal
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Test Axis
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      TotallyTested
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x413">
+      HalloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x413">
+      TotaalNormaal
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <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>
+
+  <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>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="1"/>
+            <LanguageGroup value="0"/>
+            <ExpansionFactor value="0.06"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+          100 100 rmoveto
+          900 vlineto
+          -67 67 66 -33 67 hhcurveto
+          67 66 33 67 67 hvcurveto
+          -900 vlineto
+        </CharString>
+        <CharString name=".null">
+          100 100 rmoveto
+          900 vlineto
+          -67 67 66 -33 67 hhcurveto
+          67 66 33 67 67 hvcurveto
+          -900 vlineto
+        </CharString>
+        <CharString name="A">
+          100 100 rmoveto
+          900 vlineto
+          -67 67 66 -33 67 hhcurveto
+          67 66 33 67 67 hvcurveto
+          -900 vlineto
+        </CharString>
+        <CharString name="a">
+          200 200 -200 -200 2 blend
+          rmoveto
+          400 400 1 blend
+          hlineto
+          400 400 1 blend
+          vlineto
+          -400 -400 1 blend
+          hlineto
+        </CharString>
+      </CharStrings>
+      <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=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=1 -->
+          <VarRegionIndex index="0" value="0"/>
+        </VarData>
+      </VarStore>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+  <fvar>
+
+    <!-- Test Axis -->
+    <Axis>
+      <AxisTag>TEST</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>100.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- TotallyNormal -->
+    <NamedInstance flags="0x0" subfamilyNameID="2">
+      <coord axis="TEST" value="0.0"/>
+    </NamedInstance>
+
+    <!-- TotallyTested -->
+    <NamedInstance flags="0x0" subfamilyNameID="257">
+      <coord axis="TEST" value="100.0"/>
+    </NamedInstance>
+  </fvar>
+
+</ttFont>
diff --git a/Tests/fontBuilder/data/test_var.ttf.ttx b/Tests/fontBuilder/data/test_var.ttf.ttx
new file mode 100644
index 0000000..e6d4d8d
--- /dev/null
+++ b/Tests/fontBuilder/data/test_var.ttf.ttx
@@ -0,0 +1,545 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.2">
+
+  <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="A"/>
+    <GlyphID id="3" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x18e72247"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1024"/>
+    <created value="Thu Nov  1 20:29:01 2018"/>
+    <modified value="Thu Nov  1 20:29:01 2018"/>
+    <xMin value="50"/>
+    <yMin value="0"/>
+    <xMax value="500"/>
+    <yMax value="400"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="824"/>
+    <descent value="200"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="100"/>
+    <xMaxExtent value="500"/>
+    <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>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="4"/>
+    <maxPoints value="4"/>
+    <maxContours value="1"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <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="3"/>
+    <xAvgCharWidth value="600"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <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="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="????"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="97"/>
+    <sTypoAscender value="0"/>
+    <sTypoDescender value="0"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="0"/>
+    <usWinDescent value="0"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
+    <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="600" lsb="0"/>
+    <mtx name=".null" width="600" lsb="0"/>
+    <mtx name="A" width="600" lsb="100"/>
+    <mtx name="a" width="600" lsb="50"/>
+  </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="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER 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"/><!-- contains no outline data -->
+
+    <TTGlyph name=".null"/><!-- contains no outline data -->
+
+    <TTGlyph name="A" xMin="100" yMin="0" xMax="500" yMax="400">
+      <contour>
+        <pt x="100" y="0" on="1"/>
+        <pt x="100" y="400" on="1"/>
+        <pt x="500" y="400" on="1"/>
+        <pt x="500" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="50" yMin="0" xMax="250" yMax="200">
+      <contour>
+        <pt x="50" y="0" on="1"/>
+        <pt x="50" y="200" on="1"/>
+        <pt x="250" y="200" on="1"/>
+        <pt x="250" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      HelloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TotallyNormal
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      HelloTestFont-TotallyNormal
+    </namerecord>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Left
+    </namerecord>
+    <namerecord nameID="257" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Right
+    </namerecord>
+    <namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Up
+    </namerecord>
+    <namerecord nameID="259" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Down
+    </namerecord>
+    <namerecord nameID="260" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Right Up
+    </namerecord>
+    <namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Neutral
+    </namerecord>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x4" unicode="True">
+      HalloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x4" unicode="True">
+      TotaalNormaal
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      HelloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      TotallyNormal
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      HelloTestFont-TotallyNormal
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Left
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Right
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      Up
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      Down
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      Right Up
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Neutral
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x413">
+      HalloTestFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x413">
+      TotaalNormaal
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <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>
+
+  <GSUB>
+    <Version value="0x00010001"/>
+    <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="rclt"/>
+        <Feature>
+          <!-- LookupCount=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="a"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+    <FeatureVariations>
+      <Version value="0x00010000"/>
+      <!-- FeatureVariationCount=2 -->
+      <FeatureVariationRecord index="0">
+        <ConditionSet>
+          <!-- ConditionCount=2 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="3"/>
+            <FilterRangeMinValue value="0.8"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+          <ConditionTable index="1" Format="1">
+            <AxisIndex value="0"/>
+            <FilterRangeMinValue value="0.8"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=1 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=1 -->
+              <LookupListIndex index="0" value="0"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+      <FeatureVariationRecord index="1">
+        <ConditionSet>
+          <!-- ConditionCount=2 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="1"/>
+            <FilterRangeMinValue value="0.8"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+          <ConditionTable index="1" Format="1">
+            <AxisIndex value="2"/>
+            <FilterRangeMinValue value="0.8"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=1 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=1 -->
+              <LookupListIndex index="0" value="0"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+    </FeatureVariations>
+  </GSUB>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=4 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="LEFT"/>
+        <AxisNameID value="256"/>  <!-- Left -->
+        <AxisOrdering value="0"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="RGHT"/>
+        <AxisNameID value="257"/>  <!-- Right -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="2">
+        <AxisTag value="UPPP"/>
+        <AxisNameID value="258"/>  <!-- Up -->
+        <AxisOrdering value="2"/>
+      </Axis>
+      <Axis index="3">
+        <AxisTag value="DOWN"/>
+        <AxisNameID value="259"/>  <!-- Down -->
+        <AxisOrdering value="3"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=8 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="261"/>  <!-- Neutral -->
+        <Value value="0.0"/>
+      </AxisValue>
+      <AxisValue index="1" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="256"/>  <!-- Left -->
+        <Value value="100.0"/>
+      </AxisValue>
+      <AxisValue index="2" Format="1">
+        <AxisIndex value="1"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="261"/>  <!-- Neutral -->
+        <Value value="0.0"/>
+      </AxisValue>
+      <AxisValue index="3" Format="1">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="257"/>  <!-- Right -->
+        <Value value="100.0"/>
+      </AxisValue>
+      <AxisValue index="4" Format="1">
+        <AxisIndex value="2"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="261"/>  <!-- Neutral -->
+        <Value value="0.0"/>
+      </AxisValue>
+      <AxisValue index="5" Format="1">
+        <AxisIndex value="2"/>
+        <Flags value="0"/>
+        <ValueNameID value="258"/>  <!-- Up -->
+        <Value value="100.0"/>
+      </AxisValue>
+      <AxisValue index="6" Format="1">
+        <AxisIndex value="3"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="261"/>  <!-- Neutral -->
+        <Value value="0.0"/>
+      </AxisValue>
+      <AxisValue index="7" Format="1">
+        <AxisIndex value="3"/>
+        <Flags value="0"/>
+        <ValueNameID value="259"/>  <!-- Down -->
+        <Value value="100.0"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="2"/>  <!-- TotallyNormal -->
+  </STAT>
+
+  <fvar>
+
+    <!-- Left -->
+    <Axis>
+      <AxisTag>LEFT</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>100.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- Right -->
+    <Axis>
+      <AxisTag>RGHT</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>100.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+
+    <!-- Up -->
+    <Axis>
+      <AxisTag>UPPP</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>100.0</MaxValue>
+      <AxisNameID>258</AxisNameID>
+    </Axis>
+
+    <!-- Down -->
+    <Axis>
+      <AxisTag>DOWN</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>0.0</MinValue>
+      <DefaultValue>0.0</DefaultValue>
+      <MaxValue>100.0</MaxValue>
+      <AxisNameID>259</AxisNameID>
+    </Axis>
+
+    <!-- TotallyNormal -->
+    <NamedInstance flags="0x0" subfamilyNameID="2">
+      <coord axis="LEFT" value="0.0"/>
+      <coord axis="RGHT" value="0.0"/>
+      <coord axis="UPPP" value="0.0"/>
+      <coord axis="DOWN" value="0.0"/>
+    </NamedInstance>
+
+    <!-- Right Up -->
+    <NamedInstance flags="0x0" subfamilyNameID="260">
+      <coord axis="LEFT" value="0.0"/>
+      <coord axis="RGHT" value="100.0"/>
+      <coord axis="UPPP" value="100.0"/>
+      <coord axis="DOWN" value="0.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph="a">
+      <tuple>
+        <coord axis="RGHT" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="200" y="0"/>
+        <delta pt="3" x="200" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="LEFT" value="1.0"/>
+        <delta pt="0" x="-200" y="0"/>
+        <delta pt="1" x="-200" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="UPPP" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="0" y="200"/>
+        <delta pt="2" x="0" y="200"/>
+        <delta pt="3" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="DOWN" value="1.0"/>
+        <delta pt="0" x="0" y="-200"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="-200"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/fontBuilder/fontBuilder_test.py b/Tests/fontBuilder/fontBuilder_test.py
new file mode 100644
index 0000000..6368cb8
--- /dev/null
+++ b/Tests/fontBuilder/fontBuilder_test.py
@@ -0,0 +1,355 @@
+
+import os
+import pytest
+import re
+from fontTools.ttLib import TTFont
+from fontTools.pens.ttGlyphPen import TTGlyphPen
+from fontTools.pens.t2CharStringPen import T2CharStringPen
+from fontTools.fontBuilder import FontBuilder
+from fontTools.ttLib.tables.TupleVariation import TupleVariation
+from fontTools.misc.psCharStrings import T2CharString
+
+
+def getTestData(fileName, mode="r"):
+    path = os.path.join(os.path.dirname(__file__), "data", fileName)
+    with open(path, mode) as f:
+        return f.read()
+
+
+def strip_VariableItems(string):
+    # ttlib changes with the fontTools version
+    string = re.sub(' ttLibVersion=".*"', '', string)
+    # head table checksum and creation and mod date changes with each save.
+    string = re.sub('<checkSumAdjustment value="[^"]+"/>', '', string)
+    string = re.sub('<modified value="[^"]+"/>', '', string)
+    string = re.sub('<created value="[^"]+"/>', '', string)
+    return string
+
+
+def drawTestGlyph(pen):
+    pen.moveTo((100, 100))
+    pen.lineTo((100, 1000))
+    pen.qCurveTo((200, 900), (400, 900), (500, 1000))
+    pen.lineTo((500, 100))
+    pen.closePath()
+
+
+def _setupFontBuilder(isTTF, unitsPerEm=1024):
+    fb = FontBuilder(unitsPerEm, isTTF=isTTF)
+    fb.setupGlyphOrder([".notdef", ".null", "A", "a"])
+    fb.setupCharacterMap({65: "A", 97: "a"})
+
+    advanceWidths = {".notdef": 600, "A": 600, "a": 600, ".null": 600}
+
+    familyName = "HelloTestFont"
+    styleName = "TotallyNormal"
+    nameStrings = dict(familyName=dict(en="HelloTestFont", nl="HalloTestFont"),
+                       styleName=dict(en="TotallyNormal", nl="TotaalNormaal"))
+    nameStrings['psName'] = familyName + "-" + styleName
+
+    return fb, advanceWidths, nameStrings
+
+
+def _setupFontBuilderFvar(fb):
+    assert 'name' in fb.font, 'Must run setupNameTable() first.'
+
+    axes = [
+        ('TEST', 0, 0, 100, "Test Axis"),
+    ]
+    instances = [
+        dict(location=dict(TEST=0), stylename="TotallyNormal"),
+        dict(location=dict(TEST=100), stylename="TotallyTested"),
+    ]
+    fb.setupFvar(axes, instances)
+
+    return fb
+
+
+def _setupFontBuilderCFF2(fb):
+    assert 'fvar' in fb.font, 'Must run _setupFontBuilderFvar() first.'
+
+    pen = T2CharStringPen(None, None, CFF2=True)
+    drawTestGlyph(pen)
+    charString = pen.getCharString()
+
+    program = [
+        200, 200, -200, -200, 2, "blend", "rmoveto",
+        400, 400, 1, "blend", "hlineto",
+        400, 400, 1, "blend", "vlineto",
+        -400, -400, 1, "blend", "hlineto"
+    ]
+    charStringVariable = T2CharString(program=program)
+
+    charStrings = {".notdef": charString, "A": charString,
+                   "a": charStringVariable, ".null": charString}
+    fb.setupCFF2(charStrings, regions=[{"TEST": (0, 1, 1)}])
+
+    return fb
+
+
+def _verifyOutput(outPath, tables=None):
+    f = TTFont(outPath)
+    f.saveXML(outPath + ".ttx", tables=tables)
+    with open(outPath + ".ttx") as f:
+        testData = strip_VariableItems(f.read())
+    refData = strip_VariableItems(getTestData(os.path.basename(outPath) + ".ttx"))
+    assert refData == testData
+
+
+def test_build_ttf(tmpdir):
+    outPath = os.path.join(str(tmpdir), "test.ttf")
+
+    fb, advanceWidths, nameStrings = _setupFontBuilder(True)
+
+    pen = TTGlyphPen(None)
+    drawTestGlyph(pen)
+    glyph = pen.glyph()
+    glyphs = {".notdef": glyph, "A": glyph, "a": glyph, ".null": glyph}
+    fb.setupGlyf(glyphs)
+    metrics = {}
+    glyphTable = fb.font["glyf"]
+    for gn, advanceWidth in advanceWidths.items():
+        metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
+    fb.setupHorizontalMetrics(metrics)
+
+    fb.setupHorizontalHeader(ascent=824, descent=200)
+    fb.setupNameTable(nameStrings)
+    fb.setupOS2()
+    fb.addOpenTypeFeatures("feature salt { sub A by a; } salt;")
+    fb.setupPost()
+    fb.setupDummyDSIG()
+
+    fb.save(outPath)
+
+    _verifyOutput(outPath)
+
+
+def test_build_otf(tmpdir):
+    outPath = os.path.join(str(tmpdir), "test.otf")
+
+    fb, advanceWidths, nameStrings = _setupFontBuilder(False)
+
+    pen = T2CharStringPen(600, None)
+    drawTestGlyph(pen)
+    charString = pen.getCharString()
+    charStrings = {".notdef": charString, "A": charString, "a": charString, ".null": charString}
+    fb.setupCFF(nameStrings['psName'], {"FullName": nameStrings['psName']}, charStrings, {})
+
+    lsb = {gn: cs.calcBounds(None)[0] for gn, cs in charStrings.items()}
+    metrics = {}
+    for gn, advanceWidth in advanceWidths.items():
+        metrics[gn] = (advanceWidth, lsb[gn])
+    fb.setupHorizontalMetrics(metrics)
+
+    fb.setupHorizontalHeader(ascent=824, descent=200)
+    fb.setupNameTable(nameStrings)
+    fb.setupOS2()
+    fb.addOpenTypeFeatures("feature kern { pos A a -50; } kern;")
+    fb.setupPost()
+    fb.setupDummyDSIG()
+
+    fb.save(outPath)
+
+    _verifyOutput(outPath)
+
+
+def test_build_var(tmpdir):
+    outPath = os.path.join(str(tmpdir), "test_var.ttf")
+
+    fb, advanceWidths, nameStrings = _setupFontBuilder(True)
+
+    pen = TTGlyphPen(None)
+    pen.moveTo((100, 0))
+    pen.lineTo((100, 400))
+    pen.lineTo((500, 400))
+    pen.lineTo((500, 000))
+    pen.closePath()
+    glyph1 = pen.glyph()
+
+    pen = TTGlyphPen(None)
+    pen.moveTo((50, 0))
+    pen.lineTo((50, 200))
+    pen.lineTo((250, 200))
+    pen.lineTo((250, 0))
+    pen.closePath()
+    glyph2 = pen.glyph()
+
+    pen = TTGlyphPen(None)
+    emptyGlyph = pen.glyph()
+
+    glyphs = {".notdef": emptyGlyph, "A": glyph1, "a": glyph2, ".null": emptyGlyph}
+    fb.setupGlyf(glyphs)
+    metrics = {}
+    glyphTable = fb.font["glyf"]
+    for gn, advanceWidth in advanceWidths.items():
+        metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
+    fb.setupHorizontalMetrics(metrics)
+
+    fb.setupHorizontalHeader(ascent=824, descent=200)
+    fb.setupNameTable(nameStrings)
+
+    axes = [
+        ('LEFT', 0, 0, 100, "Left"),
+        ('RGHT', 0, 0, 100, "Right"),
+        ('UPPP', 0, 0, 100, "Up"),
+        ('DOWN', 0, 0, 100, "Down"),
+    ]
+    instances = [
+        dict(location=dict(LEFT=0, RGHT=0, UPPP=0, DOWN=0), stylename="TotallyNormal"),
+        dict(location=dict(LEFT=0, RGHT=100, UPPP=100, DOWN=0), stylename="Right Up"),
+    ]
+    fb.setupFvar(axes, instances)
+    variations = {}
+    # Four (x, y) pairs and four phantom points:
+    leftDeltas = [(-200, 0), (-200, 0), (0, 0), (0, 0), None, None, None, None]
+    rightDeltas = [(0, 0), (0, 0), (200, 0), (200, 0), None, None, None, None]
+    upDeltas = [(0, 0), (0, 200), (0, 200), (0, 0), None, None, None, None]
+    downDeltas = [(0, -200), (0, 0), (0, 0), (0, -200), None, None, None, None]
+    variations['a'] = [
+        TupleVariation(dict(RGHT=(0, 1, 1)), rightDeltas),
+        TupleVariation(dict(LEFT=(0, 1, 1)), leftDeltas),
+        TupleVariation(dict(UPPP=(0, 1, 1)), upDeltas),
+        TupleVariation(dict(DOWN=(0, 1, 1)), downDeltas),
+    ]
+    fb.setupGvar(variations)
+
+    fb.addFeatureVariations(
+        [
+            (
+                [
+                    {"LEFT": (0.8, 1), "DOWN": (0.8, 1)},
+                    {"RGHT": (0.8, 1), "UPPP": (0.8, 1)},
+                  ],
+                {"A": "a"}
+            )
+        ],
+        featureTag="rclt",
+    )
+
+    statAxes = []
+    for tag, minVal, defaultVal, maxVal, name in axes:
+        values = [dict(name="Neutral", value=defaultVal, flags=0x2),
+                  dict(name=name, value=maxVal)]
+        statAxes.append(dict(tag=tag, name=name, values=values))
+    fb.setupStat(statAxes)
+
+    fb.setupOS2()
+    fb.setupPost()
+    fb.setupDummyDSIG()
+
+    fb.save(outPath)
+
+    _verifyOutput(outPath)
+
+
+def test_build_cff2(tmpdir):
+    outPath = os.path.join(str(tmpdir), "test_var.otf")
+
+    fb, advanceWidths, nameStrings = _setupFontBuilder(False, 1000)
+    fb.setupNameTable(nameStrings)
+    fb = _setupFontBuilderFvar(fb)
+    fb = _setupFontBuilderCFF2(fb)
+
+    metrics = {gn: (advanceWidth, 0) for gn, advanceWidth in advanceWidths.items()}
+    fb.setupHorizontalMetrics(metrics)
+
+    fb.setupHorizontalHeader(ascent=824, descent=200)
+    fb.setupOS2(sTypoAscender=825, sTypoDescender=200, usWinAscent=824, usWinDescent=200)
+    fb.setupPost()
+
+    fb.save(outPath)
+
+    _verifyOutput(outPath)
+
+
+def test_build_cff_to_cff2(tmpdir):
+    fb, _, _ = _setupFontBuilder(False, 1000)
+
+    pen = T2CharStringPen(600, None)
+    drawTestGlyph(pen)
+    charString = pen.getCharString()
+    charStrings = {".notdef": charString, "A": charString, "a": charString, ".null": charString}
+    fb.setupCFF("TestFont", {}, charStrings, {})
+
+    from fontTools.varLib.cff import convertCFFtoCFF2
+    convertCFFtoCFF2(fb.font)
+
+
+def test_setupNameTable_no_mac():
+    fb, _, nameStrings = _setupFontBuilder(True)
+    fb.setupNameTable(nameStrings, mac=False)
+
+    assert all(n for n in fb.font["name"].names if n.platformID == 3)
+    assert not any(n for n in fb.font["name"].names if n.platformID == 1)
+
+
+def test_setupNameTable_no_windows():
+    fb, _, nameStrings = _setupFontBuilder(True)
+    fb.setupNameTable(nameStrings, windows=False)
+
+    assert all(n for n in fb.font["name"].names if n.platformID == 1)
+    assert not any(n for n in fb.font["name"].names if n.platformID == 3)
+
+
+@pytest.mark.parametrize('is_ttf, keep_glyph_names, make_cff2, post_format', [
+    (True, True, False, 2),    # TTF with post table format 2.0
+    (True, False, False, 3),   # TTF with post table format 3.0
+    (False, True, False, 3),   # CFF with post table format 3.0
+    (False, False, False, 3),  # CFF with post table format 3.0
+    (False, True, True, 2),    # CFF2 with post table format 2.0
+    (False, False, True, 3),   # CFF2 with post table format 3.0
+])
+def test_setupPost(is_ttf, keep_glyph_names, make_cff2, post_format):
+    fb, _, nameStrings = _setupFontBuilder(is_ttf)
+
+    if make_cff2:
+        fb.setupNameTable(nameStrings)
+        fb = _setupFontBuilderCFF2(_setupFontBuilderFvar(fb))
+
+    if keep_glyph_names:
+        fb.setupPost()
+    else:
+        fb.setupPost(keepGlyphNames=keep_glyph_names)
+
+    assert fb.isTTF is is_ttf
+    assert ('CFF2' in fb.font) is make_cff2
+    assert fb.font["post"].formatType == post_format
+
+
+def test_unicodeVariationSequences(tmpdir):
+    familyName = "UVSTestFont"
+    styleName = "Regular"
+    nameStrings = dict(familyName=familyName, styleName=styleName)
+    nameStrings['psName'] = familyName + "-" + styleName
+    glyphOrder = [".notdef", "space", "zero", "zero.slash"]
+    cmap = {ord(" "): "space", ord("0"): "zero"}
+    uvs = [
+        (0x0030, 0xFE00, "zero.slash"),
+        (0x0030, 0xFE01, None),  # not an official sequence, just testing
+    ]
+    metrics = {gn: (600, 0) for gn in glyphOrder}
+    pen = TTGlyphPen(None)
+    glyph = pen.glyph()  # empty placeholder
+    glyphs = {gn: glyph for gn in glyphOrder}
+
+    fb = FontBuilder(1024, isTTF=True)
+    fb.setupGlyphOrder(glyphOrder)
+    fb.setupCharacterMap(cmap, uvs)
+    fb.setupGlyf(glyphs)
+    fb.setupHorizontalMetrics(metrics)
+    fb.setupHorizontalHeader(ascent=824, descent=200)
+    fb.setupNameTable(nameStrings)
+    fb.setupOS2()
+    fb.setupPost()
+
+    outPath = os.path.join(str(tmpdir), "test_uvs.ttf")
+    fb.save(outPath)
+    _verifyOutput(outPath, tables=["cmap"])
+
+    uvs = [
+        (0x0030, 0xFE00, "zero.slash"),
+        (0x0030, 0xFE01, "zero"),  # should result in the exact same subtable data, due to cmap[0x0030] == "zero"
+    ]
+    fb.setupCharacterMap(cmap, uvs)
+    fb.save(outPath)
+    _verifyOutput(outPath, tables=["cmap"])
diff --git a/Tests/merge_test.py b/Tests/merge_test.py
index 00e719b..015248d 100644
--- a/Tests/merge_test.py
+++ b/Tests/merge_test.py
@@ -1,7 +1,11 @@
-from fontTools.misc.py23 import *
+import io
+import itertools
 from fontTools import ttLib
-from fontTools.merge import *
+from fontTools.ttLib.tables._g_l_y_f import Glyph
+from fontTools.fontBuilder import FontBuilder
+from fontTools.merge import Merger
 import unittest
+import pytest
 
 
 class MergeIntegrationTest(unittest.TestCase):
@@ -113,6 +117,53 @@
 		self.assertEqual(self.merger.duplicateGlyphsPerFont, [{}, {'space#0': 'space#1'}])
 
 
+def _compile(ttFont):
+	buf = io.BytesIO()
+	ttFont.save(buf)
+	buf.seek(0)
+	return buf
+
+
+def _make_fontfile_with_OS2(*, version, **kwargs):
+	upem = 1000
+	glyphOrder = [".notdef", "a"]
+	cmap = {0x61: "a"}
+	glyphs = {gn: Glyph() for gn in glyphOrder}
+	hmtx = {gn: (500, 0) for gn in glyphOrder}
+	names = {"familyName": "TestOS2", "styleName": "Regular"}
+
+	fb = FontBuilder(unitsPerEm=upem)
+	fb.setupGlyphOrder(glyphOrder)
+	fb.setupCharacterMap(cmap)
+	fb.setupGlyf(glyphs)
+	fb.setupHorizontalMetrics(hmtx)
+	fb.setupHorizontalHeader()
+	fb.setupNameTable(names)
+	fb.setupOS2(version=version, **kwargs)
+
+	return _compile(fb.font)
+
+
+def _merge_and_recompile(fontfiles, options=None):
+	merger = Merger(options)
+	merged = merger.merge(fontfiles)
+	buf = _compile(merged)
+	return ttLib.TTFont(buf)
+
+
+@pytest.mark.parametrize(
+	"v1, v2", list(itertools.permutations(range(5+1), 2))
+)
+def test_merge_OS2_mixed_versions(v1, v2):
+	# https://github.com/fonttools/fonttools/issues/1865
+	fontfiles = [
+		_make_fontfile_with_OS2(version=v1),
+		_make_fontfile_with_OS2(version=v2),
+	]
+	merged = _merge_and_recompile(fontfiles)
+	assert merged["OS/2"].version == max(v1, v2)
+
+
 if __name__ == "__main__":
 	import sys
 	sys.exit(unittest.main())
diff --git a/Tests/misc/arrayTools_test.py b/Tests/misc/arrayTools_test.py
index 108b50d..45b186f 100644
--- a/Tests/misc/arrayTools_test.py
+++ b/Tests/misc/arrayTools_test.py
@@ -1,5 +1,3 @@
-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,
@@ -15,8 +13,13 @@
 
 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)
+        [(0.1, 40.1), (0.1, 100.1), (49.9, 49.9), (78.5, 9.5)]
+    ) == (0, 10, 79, 100)
+
+    assert calcIntBounds(
+        [(0.1, 40.1), (0.1, 100.1), (49.9, 49.9), (78.5, 9.5)],
+        round=round
+    ) == (0, 10, 78, 100)
 
 
 def test_updateBounds():
diff --git a/Tests/misc/bezierTools_test.py b/Tests/misc/bezierTools_test.py
index 1519543..c5cd1b7 100644
--- a/Tests/misc/bezierTools_test.py
+++ b/Tests/misc/bezierTools_test.py
@@ -1,7 +1,5 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc.bezierTools import (
-    calcQuadraticBounds, calcCubicBounds, splitLine, splitQuadratic,
+    calcQuadraticBounds, calcCubicBounds, segmentPointAtT, splitLine, splitQuadratic,
     splitCubic, splitQuadraticAtT, splitCubicAtT, solveCubic)
 import pytest
 
@@ -131,3 +129,22 @@
     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]
+
+
+_segmentPointAtT_testData = [
+    ([(0, 10), (200, 100)], 0.0, (0, 10)),
+    ([(0, 10), (200, 100)], 0.5, (100, 55)),
+    ([(0, 10), (200, 100)], 1.0, (200, 100)),
+    ([(0, 10), (100, 100), (200, 50)], 0.0, (0, 10)),
+    ([(0, 10), (100, 100), (200, 50)], 0.5, (100, 65.0)),
+    ([(0, 10), (100, 100), (200, 50)], 1.0, (200, 50.0)),
+    ([(0, 10), (100, 100), (200, 100), (300, 0)], 0.0, (0, 10)),
+    ([(0, 10), (100, 100), (200, 100), (300, 0)], 0.5, (150, 76.25)),
+    ([(0, 10), (100, 100), (200, 100), (300, 0)], 1.0, (300, 0)),
+]
+
+
+@pytest.mark.parametrize("segment, t, expectedPoint", _segmentPointAtT_testData)
+def test_segmentPointAtT(segment, t, expectedPoint):
+    point = segmentPointAtT(segment, t)
+    assert expectedPoint == point
diff --git a/Tests/misc/classifyTools_test.py b/Tests/misc/classifyTools_test.py
index ac1f656..72a9752 100644
--- a/Tests/misc/classifyTools_test.py
+++ b/Tests/misc/classifyTools_test.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc.classifyTools import classify
 
 
diff --git a/Tests/misc/eexec_test.py b/Tests/misc/eexec_test.py
index 2043095..f72760a 100644
--- a/Tests/misc/eexec_test.py
+++ b/Tests/misc/eexec_test.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc.eexec import decrypt, encrypt
 
 
diff --git a/Tests/misc/encodingTools_test.py b/Tests/misc/encodingTools_test.py
index 4c9628b..1a131f6 100644
--- a/Tests/misc/encodingTools_test.py
+++ b/Tests/misc/encodingTools_test.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import, unicode_literals
-from fontTools.misc.py23 import *
 import unittest
 from fontTools.misc.encodingTools import getEncoding
 
@@ -20,7 +18,7 @@
 	def test_extended_mac_encodings(self):
 		encoding = getEncoding(1, 1, 0) # Mac Japanese
 		decoded = b'\xfe'.decode(encoding)
-		self.assertEqual(decoded, unichr(0x2122))
+		self.assertEqual(decoded, chr(0x2122))
 
 	def test_extended_unknown(self):
 		self.assertEqual(getEncoding(10, 11, 12), None)
diff --git a/Tests/misc/etree_test.py b/Tests/misc/etree_test.py
new file mode 100644
index 0000000..3f3232e
--- /dev/null
+++ b/Tests/misc/etree_test.py
@@ -0,0 +1,54 @@
+# coding: utf-8
+from fontTools.misc import etree
+from collections import OrderedDict
+import io
+import pytest
+
+
+@pytest.mark.parametrize(
+    "xml",
+    [
+        (
+            "<root>"
+            '<element key="value">text</element>'
+            "<element>text</element>tail"
+            "<empty-element/>"
+            "</root>"
+        ),
+        (
+            "<root>\n"
+            '  <element key="value">text</element>\n'
+            "  <element>text</element>tail\n"
+            "  <empty-element/>\n"
+            "</root>"
+        ),
+        (
+            '<axis default="400" maximum="1000" minimum="1" name="weight" tag="wght">'
+            '<labelname xml:lang="fa-IR">قطر</labelname>'
+            "</axis>"
+        ),
+    ],
+    ids=["simple_xml_no_indent", "simple_xml_indent", "xml_ns_attrib_utf_8"],
+)
+def test_roundtrip_string(xml):
+    root = etree.fromstring(xml.encode("utf-8"))
+    result = etree.tostring(root, encoding="utf-8").decode("utf-8")
+    assert result == xml
+
+
+def test_pretty_print():
+    root = etree.Element("root")
+    attrs = OrderedDict([("c", "2"), ("b", "1"), ("a", "0")])
+    etree.SubElement(root, "element", attrs).text = "text"
+    etree.SubElement(root, "element").text = "text"
+    root.append(etree.Element("empty-element"))
+
+    result = etree.tostring(root, encoding="unicode", pretty_print=True)
+
+    assert result == (
+        "<root>\n"
+        '  <element c="2" b="1" a="0">text</element>\n'
+        "  <element>text</element>\n"
+        "  <empty-element/>\n"
+        "</root>\n"
+    )
diff --git a/Tests/misc/filenames_test.py b/Tests/misc/filenames_test.py
new file mode 100644
index 0000000..bb7b63c
--- /dev/null
+++ b/Tests/misc/filenames_test.py
@@ -0,0 +1,136 @@
+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
index 5ef1777..dea61b9 100644
--- a/Tests/misc/fixedTools_test.py
+++ b/Tests/misc/fixedTools_test.py
@@ -1,6 +1,11 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.misc.fixedTools import fixedToFloat, floatToFixed
+from fontTools.misc.fixedTools import (
+    fixedToFloat,
+    floatToFixed,
+    floatToFixedToStr,
+    fixedToStr,
+    strToFixed,
+    strToFixedToFloat,
+)
 import unittest
 
 
@@ -12,19 +17,33 @@
                 self.assertEqual(value, floatToFixed(fixedToFloat(value, bits), bits))
 
     def test_fixedToFloat_precision14(self):
-        self.assertEqual(0.8, fixedToFloat(13107, 14))
+        self.assertAlmostEqual(0.7999878, 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))
+        self.assertAlmostEqual(0.999939, fixedToFloat(16383, 14))
+        self.assertAlmostEqual(-0.999939, fixedToFloat(-16383, 14))
 
     def test_fixedToFloat_precision6(self):
-        self.assertAlmostEqual(-9.98, fixedToFloat(-639, 6))
+        self.assertAlmostEqual(-9.984375, fixedToFloat(-639, 6))
         self.assertAlmostEqual(-10.0, fixedToFloat(-640, 6))
-        self.assertAlmostEqual(9.98, fixedToFloat(639, 6))
+        self.assertAlmostEqual(9.984375, fixedToFloat(639, 6))
         self.assertAlmostEqual(10.0, fixedToFloat(640, 6))
 
+    def test_fixedToStr_precision14(self):
+        self.assertEqual('0.8', fixedToStr(13107, 14))
+        self.assertEqual('0.0', fixedToStr(0, 14))
+        self.assertEqual('1.0', fixedToStr(16384, 14))
+        self.assertEqual('-1.0', fixedToStr(-16384, 14))
+        self.assertEqual('0.99994', fixedToStr(16383, 14))
+        self.assertEqual('-0.99994', fixedToStr(-16383, 14))
+
+    def test_fixedToStr_precision6(self):
+        self.assertAlmostEqual('-9.98', fixedToStr(-639, 6))
+        self.assertAlmostEqual('-10.0', fixedToStr(-640, 6))
+        self.assertAlmostEqual('9.98', fixedToStr(639, 6))
+        self.assertAlmostEqual('10.0', fixedToStr(640, 6))
+
     def test_floatToFixed_precision14(self):
         self.assertEqual(13107, floatToFixed(0.8, 14))
         self.assertEqual(16384, floatToFixed(1.0, 14))
@@ -33,6 +52,30 @@
         self.assertEqual(-16384, floatToFixed(-1, 14))
         self.assertEqual(0, floatToFixed(0, 14))
 
+    def test_strToFixed_precision14(self):
+        self.assertEqual(13107, strToFixed('0.8', 14))
+        self.assertEqual(16384, strToFixed('1.0', 14))
+        self.assertEqual(16384, strToFixed('1', 14))
+        self.assertEqual(-16384, strToFixed('-1.0', 14))
+        self.assertEqual(-16384, strToFixed('-1', 14))
+        self.assertEqual(0, strToFixed('0', 14))
+
+    def test_strToFixedToFloat_precision14(self):
+        self.assertAlmostEqual(0.7999878, strToFixedToFloat('0.8', 14))
+        self.assertEqual(0.0, strToFixedToFloat('0', 14))
+        self.assertEqual(1.0, strToFixedToFloat('1.0', 14))
+        self.assertEqual(-1.0, strToFixedToFloat('-1.0', 14))
+        self.assertAlmostEqual(0.999939, strToFixedToFloat('0.99994', 14))
+        self.assertAlmostEqual(-0.999939, strToFixedToFloat('-0.99994', 14))
+
+    def test_floatToFixedToStr_precision14(self):
+        self.assertEqual('0.8', floatToFixedToStr(0.7999878, 14))
+        self.assertEqual('1.0', floatToFixedToStr(1.0, 14))
+        self.assertEqual('1.0', floatToFixedToStr(1, 14))
+        self.assertEqual('-1.0', floatToFixedToStr(-1.0, 14))
+        self.assertEqual('-1.0', floatToFixedToStr(-1, 14))
+        self.assertEqual('0.0', floatToFixedToStr(0, 14))
+
     def test_fixedToFloat_return_float(self):
         value = fixedToFloat(16384, 14)
         self.assertIsInstance(value, float)
diff --git a/Tests/misc/loggingTools_test.py b/Tests/misc/loggingTools_test.py
index 81ffb8d..fd13044 100644
--- a/Tests/misc/loggingTools_test.py
+++ b/Tests/misc/loggingTools_test.py
@@ -1,7 +1,11 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc.loggingTools import (
-    LevelFormatter, Timer, configLogger, ChannelsFilter, LogMixin)
+    LevelFormatter,
+    Timer,
+    configLogger,
+    ChannelsFilter,
+    LogMixin,
+)
+from io import StringIO
 import logging
 import textwrap
 import time
@@ -80,7 +84,7 @@
             time.sleep(0.01)
 
         assert re.match(
-            "Took [0-9]\.[0-9]{3}s to do something",
+            r"Took [0-9]\.[0-9]{3}s to do something",
             logger.handlers[0].stream.getvalue())
 
     def test_using_logger_calling_instance(self, logger):
@@ -89,7 +93,7 @@
             time.sleep(0.01)
 
         assert re.match(
-            "elapsed time: [0-9]\.[0-9]{3}s",
+            r"elapsed time: [0-9]\.[0-9]{3}s",
             logger.handlers[0].stream.getvalue())
 
         # do it again but with custom level
@@ -97,7 +101,7 @@
             time.sleep(0.02)
 
         assert re.search(
-            "WARNING: Took [0-9]\.[0-9]{3}s to redo it",
+            r"WARNING: Took [0-9]\.[0-9]{3}s to redo it",
             logger.handlers[0].stream.getvalue())
 
     def test_function_decorator(self, logger):
@@ -113,13 +117,13 @@
         test1()
 
         assert re.match(
-            "Took [0-9]\.[0-9]{3}s to run 'test1'",
+            r"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",
+            r"Took [0-9]\.[0-9]{3}s to run test 2",
             logger.handlers[0].stream.getvalue())
 
 
diff --git a/Tests/misc/macRes_test.py b/Tests/misc/macRes_test.py
index fde13f8..a6a8e9d 100644
--- a/Tests/misc/macRes_test.py
+++ b/Tests/misc/macRes_test.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from io import BytesIO
 import sys
 import os
 import tempfile
diff --git a/Tests/misc/plistlib_test.py b/Tests/misc/plistlib_test.py
new file mode 100644
index 0000000..b2ce408
--- /dev/null
+++ b/Tests/misc/plistlib_test.py
@@ -0,0 +1,544 @@
+import sys
+import os
+import datetime
+import codecs
+import collections
+from io import BytesIO
+from numbers import Integral
+from fontTools.misc.py23 import tostr
+from fontTools.misc import etree
+from fontTools.misc import plistlib
+from fontTools.ufoLib.plistlib import (
+    readPlist, readPlistFromString, writePlist, writePlistToString,
+)
+import pytest
+from collections.abc import Mapping
+
+
+# The testdata is generated using https://github.com/python/cpython/...
+# Mac/Tools/plistlib_generate_testdata.py
+# which uses PyObjC to control the Cocoa classes for generating plists
+datadir = os.path.join(os.path.dirname(__file__), "testdata")
+with open(os.path.join(datadir, "test.plist"), "rb") as fp:
+    TESTDATA = fp.read()
+
+
+def _test_pl(use_builtin_types):
+    DataClass = bytes if use_builtin_types else plistlib.Data
+    pl = dict(
+        aString="Doodah",
+        aList=["A", "B", 12, 32.5, [1, 2, 3]],
+        aFloat=0.5,
+        anInt=728,
+        aBigInt=2 ** 63 - 44,
+        aBigInt2=2 ** 63 + 44,
+        aNegativeInt=-5,
+        aNegativeBigInt=-80000000000,
+        aDict=dict(
+            anotherString="<hello & 'hi' there!>",
+            aUnicodeValue="M\xe4ssig, Ma\xdf",
+            aTrueValue=True,
+            aFalseValue=False,
+            deeperDict=dict(a=17, b=32.5, c=[1, 2, "text"]),
+        ),
+        someData=DataClass(b"<binary gunk>"),
+        someMoreData=DataClass(b"<lots of binary gunk>\0\1\2\3" * 10),
+        nestedData=[DataClass(b"<lots of binary gunk>\0\1\2\3" * 10)],
+        aDate=datetime.datetime(2004, 10, 26, 10, 33, 33),
+        anEmptyDict=dict(),
+        anEmptyList=list(),
+    )
+    pl["\xc5benraa"] = "That was a unicode key."
+    return pl
+
+
+@pytest.fixture
+def pl():
+    return _test_pl(use_builtin_types=True)
+
+
+@pytest.fixture
+def pl_no_builtin_types():
+    return _test_pl(use_builtin_types=False)
+
+
+@pytest.fixture(
+    params=[True, False],
+    ids=["builtin=True", "builtin=False"],
+)
+def use_builtin_types(request):
+    return request.param
+
+
+@pytest.fixture
+def parametrized_pl(use_builtin_types):
+    return _test_pl(use_builtin_types), use_builtin_types
+
+
+def test__test_pl():
+    # sanity test that checks that the two values are equivalent
+    # (plistlib.Data implements __eq__ against bytes values)
+    pl = _test_pl(use_builtin_types=False)
+    pl2 = _test_pl(use_builtin_types=True)
+    assert pl == pl2
+
+
+def test_io(tmpdir, parametrized_pl):
+    pl, use_builtin_types = parametrized_pl
+    testpath = tmpdir / "test.plist"
+    with testpath.open("wb") as fp:
+        plistlib.dump(pl, fp, use_builtin_types=use_builtin_types)
+
+    with testpath.open("rb") as fp:
+        pl2 = plistlib.load(fp, use_builtin_types=use_builtin_types)
+
+    assert pl == pl2
+
+    with pytest.raises(AttributeError):
+        plistlib.dump(pl, "filename")
+
+    with pytest.raises(AttributeError):
+        plistlib.load("filename")
+
+
+def test_invalid_type():
+    pl = [object()]
+
+    with pytest.raises(TypeError):
+        plistlib.dumps(pl)
+
+
+@pytest.mark.parametrize(
+    "pl",
+    [
+        0,
+        2 ** 8 - 1,
+        2 ** 8,
+        2 ** 16 - 1,
+        2 ** 16,
+        2 ** 32 - 1,
+        2 ** 32,
+        2 ** 63 - 1,
+        2 ** 64 - 1,
+        1,
+        -2 ** 63,
+    ],
+)
+def test_int(pl):
+    data = plistlib.dumps(pl)
+    pl2 = plistlib.loads(data)
+    assert isinstance(pl2, Integral)
+    assert pl == pl2
+    data2 = plistlib.dumps(pl2)
+    assert data == data2
+
+
+@pytest.mark.parametrize(
+    "pl", [2 ** 64 + 1, 2 ** 127 - 1, -2 ** 64, -2 ** 127]
+)
+def test_int_overflow(pl):
+    with pytest.raises(OverflowError):
+        plistlib.dumps(pl)
+
+
+def test_bytearray(use_builtin_types):
+    DataClass = bytes if use_builtin_types else plistlib.Data
+    pl = DataClass(b"<binary gunk\0\1\2\3>")
+    array = bytearray(pl) if use_builtin_types else bytearray(pl.data)
+    data = plistlib.dumps(array)
+    pl2 = plistlib.loads(data, use_builtin_types=use_builtin_types)
+    assert isinstance(pl2, DataClass)
+    assert pl2 == pl
+    data2 = plistlib.dumps(pl2, use_builtin_types=use_builtin_types)
+    assert data == data2
+
+
+@pytest.mark.parametrize(
+    "DataClass, use_builtin_types",
+    [(bytes, True), (plistlib.Data, True), (plistlib.Data, False)],
+    ids=[
+        "bytes|builtin_types=True",
+        "Data|builtin_types=True",
+        "Data|builtin_types=False",
+    ],
+)
+def test_bytes_data(DataClass, use_builtin_types):
+    pl = DataClass(b"<binary gunk\0\1\2\3>")
+    data = plistlib.dumps(pl, use_builtin_types=use_builtin_types)
+    pl2 = plistlib.loads(data, use_builtin_types=use_builtin_types)
+    assert isinstance(pl2, bytes if use_builtin_types else plistlib.Data)
+    assert pl2 == pl
+    data2 = plistlib.dumps(pl2, use_builtin_types=use_builtin_types)
+    assert data == data2
+
+
+def test_bytes_string(use_builtin_types):
+    pl = b"some ASCII bytes"
+    data = plistlib.dumps(pl, use_builtin_types=False)
+    pl2 = plistlib.loads(data, use_builtin_types=use_builtin_types)
+    assert isinstance(pl2, str)  # it's always a <string>
+    assert pl2 == pl.decode()
+
+
+def test_indentation_array():
+    data = [[[[[[[[{"test": "aaaaaa"}]]]]]]]]
+    assert plistlib.loads(plistlib.dumps(data)) == data
+
+
+def test_indentation_dict():
+    data = {
+        "1": {"2": {"3": {"4": {"5": {"6": {"7": {"8": {"9": "aaaaaa"}}}}}}}}
+    }
+    assert plistlib.loads(plistlib.dumps(data)) == data
+
+
+def test_indentation_dict_mix():
+    data = {"1": {"2": [{"3": [[[[[{"test": "aaaaaa"}]]]]]}]}}
+    assert plistlib.loads(plistlib.dumps(data)) == data
+
+
+@pytest.mark.xfail(reason="we use two spaces, Apple uses tabs")
+def test_apple_formatting(parametrized_pl):
+    # we also split base64 data into multiple lines differently:
+    # both right-justify data to 76 chars, but Apple's treats tabs
+    # as 8 spaces, whereas we use 2 spaces
+    pl, use_builtin_types = parametrized_pl
+    pl = plistlib.loads(TESTDATA, use_builtin_types=use_builtin_types)
+    data = plistlib.dumps(pl, use_builtin_types=use_builtin_types)
+    assert data == TESTDATA
+
+
+def test_apple_formatting_fromliteral(parametrized_pl):
+    pl, use_builtin_types = parametrized_pl
+    pl2 = plistlib.loads(TESTDATA, use_builtin_types=use_builtin_types)
+    assert pl == pl2
+
+
+def test_apple_roundtrips(use_builtin_types):
+    pl = plistlib.loads(TESTDATA, use_builtin_types=use_builtin_types)
+    data = plistlib.dumps(pl, use_builtin_types=use_builtin_types)
+    pl2 = plistlib.loads(data, use_builtin_types=use_builtin_types)
+    data2 = plistlib.dumps(pl2, use_builtin_types=use_builtin_types)
+    assert data == data2
+
+
+def test_bytesio(parametrized_pl):
+    pl, use_builtin_types = parametrized_pl
+    b = BytesIO()
+    plistlib.dump(pl, b, use_builtin_types=use_builtin_types)
+    pl2 = plistlib.load(
+        BytesIO(b.getvalue()), use_builtin_types=use_builtin_types
+    )
+    assert pl == pl2
+
+
+@pytest.mark.parametrize("sort_keys", [False, True])
+def test_keysort_bytesio(sort_keys):
+    pl = collections.OrderedDict()
+    pl["b"] = 1
+    pl["a"] = 2
+    pl["c"] = 3
+
+    b = BytesIO()
+
+    plistlib.dump(pl, b, sort_keys=sort_keys)
+    pl2 = plistlib.load(
+        BytesIO(b.getvalue()), dict_type=collections.OrderedDict
+    )
+
+    assert dict(pl) == dict(pl2)
+    if sort_keys:
+        assert list(pl2.keys()) == ["a", "b", "c"]
+    else:
+        assert list(pl2.keys()) == ["b", "a", "c"]
+
+
+@pytest.mark.parametrize("sort_keys", [False, True])
+def test_keysort(sort_keys):
+    pl = collections.OrderedDict()
+    pl["b"] = 1
+    pl["a"] = 2
+    pl["c"] = 3
+
+    data = plistlib.dumps(pl, sort_keys=sort_keys)
+    pl2 = plistlib.loads(data, dict_type=collections.OrderedDict)
+
+    assert dict(pl) == dict(pl2)
+    if sort_keys:
+        assert list(pl2.keys()) == ["a", "b", "c"]
+    else:
+        assert list(pl2.keys()) == ["b", "a", "c"]
+
+
+def test_keys_no_string():
+    pl = {42: "aNumber"}
+
+    with pytest.raises(TypeError):
+        plistlib.dumps(pl)
+
+    b = BytesIO()
+    with pytest.raises(TypeError):
+        plistlib.dump(pl, b)
+
+
+def test_skipkeys():
+    pl = {42: "aNumber", "snake": "aWord"}
+
+    data = plistlib.dumps(pl, skipkeys=True, sort_keys=False)
+
+    pl2 = plistlib.loads(data)
+    assert pl2 == {"snake": "aWord"}
+
+    fp = BytesIO()
+    plistlib.dump(pl, fp, skipkeys=True, sort_keys=False)
+    data = fp.getvalue()
+    pl2 = plistlib.loads(fp.getvalue())
+    assert pl2 == {"snake": "aWord"}
+
+
+def test_tuple_members():
+    pl = {"first": (1, 2), "second": (1, 2), "third": (3, 4)}
+
+    data = plistlib.dumps(pl)
+    pl2 = plistlib.loads(data)
+    assert pl2 == {"first": [1, 2], "second": [1, 2], "third": [3, 4]}
+    assert pl2["first"] is not pl2["second"]
+
+
+def test_list_members():
+    pl = {"first": [1, 2], "second": [1, 2], "third": [3, 4]}
+
+    data = plistlib.dumps(pl)
+    pl2 = plistlib.loads(data)
+    assert pl2 == {"first": [1, 2], "second": [1, 2], "third": [3, 4]}
+    assert pl2["first"] is not pl2["second"]
+
+
+def test_dict_members():
+    pl = {"first": {"a": 1}, "second": {"a": 1}, "third": {"b": 2}}
+
+    data = plistlib.dumps(pl)
+    pl2 = plistlib.loads(data)
+    assert pl2 == {"first": {"a": 1}, "second": {"a": 1}, "third": {"b": 2}}
+    assert pl2["first"] is not pl2["second"]
+
+
+def test_controlcharacters():
+    for i in range(128):
+        c = chr(i)
+        testString = "string containing %s" % c
+        if i >= 32 or c in "\r\n\t":
+            # \r, \n and \t are the only legal control chars in XML
+            data = plistlib.dumps(testString)
+            # the stdlib's plistlib writer, as well as the elementtree
+            # parser, always replace \r with \n inside string values;
+            # lxml doesn't (the ctrl character is escaped), so it roundtrips
+            if c != "\r" or etree._have_lxml:
+                assert plistlib.loads(data) == testString
+        else:
+            with pytest.raises(ValueError):
+                plistlib.dumps(testString)
+
+
+def test_non_bmp_characters():
+    pl = {"python": "\U0001f40d"}
+    data = plistlib.dumps(pl)
+    assert plistlib.loads(data) == pl
+
+
+def test_nondictroot():
+    test1 = "abc"
+    test2 = [1, 2, 3, "abc"]
+    result1 = plistlib.loads(plistlib.dumps(test1))
+    result2 = plistlib.loads(plistlib.dumps(test2))
+    assert test1 == result1
+    assert test2 == result2
+
+
+def test_invalidarray():
+    for i in [
+        "<key>key inside an array</key>",
+        "<key>key inside an array2</key><real>3</real>",
+        "<true/><key>key inside an array3</key>",
+    ]:
+        with pytest.raises(ValueError):
+            plistlib.loads(
+                ("<plist><array>%s</array></plist>" % i).encode("utf-8")
+            )
+
+
+def test_invaliddict():
+    for i in [
+        "<key><true/>k</key><string>compound key</string>",
+        "<key>single key</key>",
+        "<string>missing key</string>",
+        "<key>k1</key><string>v1</string><real>5.3</real>"
+        "<key>k1</key><key>k2</key><string>double key</string>",
+    ]:
+        with pytest.raises(ValueError):
+            plistlib.loads(("<plist><dict>%s</dict></plist>" % i).encode())
+        with pytest.raises(ValueError):
+            plistlib.loads(
+                ("<plist><array><dict>%s</dict></array></plist>" % i).encode()
+            )
+
+
+def test_invalidinteger():
+    with pytest.raises(ValueError):
+        plistlib.loads(b"<plist><integer>not integer</integer></plist>")
+
+
+def test_invalidreal():
+    with pytest.raises(ValueError):
+        plistlib.loads(b"<plist><integer>not real</integer></plist>")
+
+
+@pytest.mark.parametrize(
+    "xml_encoding, encoding, bom",
+    [
+        (b"utf-8", "utf-8", codecs.BOM_UTF8),
+        (b"utf-16", "utf-16-le", codecs.BOM_UTF16_LE),
+        (b"utf-16", "utf-16-be", codecs.BOM_UTF16_BE),
+        # expat parser (used by ElementTree) does't support UTF-32
+        # (b"utf-32", "utf-32-le", codecs.BOM_UTF32_LE),
+        # (b"utf-32", "utf-32-be", codecs.BOM_UTF32_BE),
+    ],
+)
+def test_xml_encodings(parametrized_pl, xml_encoding, encoding, bom):
+    pl, use_builtin_types = parametrized_pl
+    data = TESTDATA.replace(b"UTF-8", xml_encoding)
+    data = bom + data.decode("utf-8").encode(encoding)
+    pl2 = plistlib.loads(data, use_builtin_types=use_builtin_types)
+    assert pl == pl2
+
+
+def test_fromtree(parametrized_pl):
+    pl, use_builtin_types = parametrized_pl
+    tree = etree.fromstring(TESTDATA)
+    pl2 = plistlib.fromtree(tree, use_builtin_types=use_builtin_types)
+    assert pl == pl2
+
+
+def _strip(txt):
+    return (
+        "".join(l.strip() for l in tostr(txt, "utf-8").splitlines())
+        if txt is not None
+        else ""
+    )
+
+
+def test_totree(parametrized_pl):
+    pl, use_builtin_types = parametrized_pl
+    tree = etree.fromstring(TESTDATA)[0]  # ignore root 'plist' element
+    tree2 = plistlib.totree(pl, use_builtin_types=use_builtin_types)
+    assert tree.tag == tree2.tag == "dict"
+    for (_, e1), (_, e2) in zip(etree.iterwalk(tree), etree.iterwalk(tree2)):
+        assert e1.tag == e2.tag
+        assert e1.attrib == e2.attrib
+        assert len(e1) == len(e2)
+        # ignore whitespace
+        assert _strip(e1.text) == _strip(e2.text)
+
+
+def test_no_pretty_print(use_builtin_types):
+    data = plistlib.dumps(
+        {"data": b"hello" if use_builtin_types else plistlib.Data(b"hello")},
+        pretty_print=False,
+        use_builtin_types=use_builtin_types,
+    )
+    assert data == (
+        plistlib.XML_DECLARATION
+        + plistlib.PLIST_DOCTYPE
+        + b'<plist version="1.0">'
+        b"<dict>"
+        b"<key>data</key>"
+        b"<data>aGVsbG8=</data>"
+        b"</dict>"
+        b"</plist>"
+    )
+
+
+def test_readPlist_from_path(pl):
+    path = os.path.join(datadir, "test.plist")
+    pl2 = readPlist(path)
+    assert isinstance(pl2["someData"], plistlib.Data)
+    assert pl2 == pl
+
+
+def test_readPlist_from_file(pl):
+    with open(os.path.join(datadir, "test.plist"), "rb") as f:
+        pl2 = readPlist(f)
+        assert isinstance(pl2["someData"], plistlib.Data)
+        assert pl2 == pl
+        assert not f.closed
+
+
+def test_readPlistFromString(pl):
+    pl2 = readPlistFromString(TESTDATA)
+    assert isinstance(pl2["someData"], plistlib.Data)
+    assert pl2 == pl
+
+
+def test_writePlist_to_path(tmpdir, pl_no_builtin_types):
+    testpath = tmpdir / "test.plist"
+    writePlist(pl_no_builtin_types, str(testpath))
+    with testpath.open("rb") as fp:
+        pl2 = plistlib.load(fp, use_builtin_types=False)
+    assert pl2 == pl_no_builtin_types
+
+
+def test_writePlist_to_file(tmpdir, pl_no_builtin_types):
+    testpath = tmpdir / "test.plist"
+    with testpath.open("wb") as fp:
+        writePlist(pl_no_builtin_types, fp)
+    with testpath.open("rb") as fp:
+        pl2 = plistlib.load(fp, use_builtin_types=False)
+    assert pl2 == pl_no_builtin_types
+
+
+def test_writePlistToString(pl_no_builtin_types):
+    data = writePlistToString(pl_no_builtin_types)
+    pl2 = plistlib.loads(data)
+    assert pl2 == pl_no_builtin_types
+
+
+def test_load_use_builtin_types_default():
+    pl = plistlib.loads(TESTDATA)
+    assert isinstance(pl["someData"], bytes)
+
+
+def test_dump_use_builtin_types_default(pl_no_builtin_types):
+    data = plistlib.dumps(pl_no_builtin_types)
+    pl2 = plistlib.loads(data)
+    assert isinstance(pl2["someData"], bytes)
+    assert pl2 == pl_no_builtin_types
+
+
+def test_non_ascii_bytes():
+    with pytest.raises(ValueError, match="invalid non-ASCII bytes"):
+        plistlib.dumps("\U0001f40d".encode("utf-8"), use_builtin_types=False)
+
+
+class CustomMapping(Mapping):
+    a = {"a": 1, "b": 2}
+
+    def __getitem__(self, key):
+        return self.a[key]
+
+    def __iter__(self):
+        return iter(self.a)
+
+    def __len__(self):
+        return len(self.a)
+
+
+def test_custom_mapping():
+    test_mapping = CustomMapping()
+    data = plistlib.dumps(test_mapping)
+    assert plistlib.loads(data) == {"a": 1, "b": 2}
+
+
+if __name__ == "__main__":
+    import sys
+
+    sys.exit(pytest.main(sys.argv))
diff --git a/Tests/misc/psCharStrings_test.py b/Tests/misc/psCharStrings_test.py
index 552565b..47ff4fd 100644
--- a/Tests/misc/psCharStrings_test.py
+++ b/Tests/misc/psCharStrings_test.py
@@ -1,10 +1,20 @@
-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
+from fontTools.misc.testTools import getXML, parseXML
+from fontTools.misc.psCharStrings import (
+    T2CharString,
+    encodeFloat,
+    encodeFixed,
+    read_fixed1616,
+    read_realNumber,
+)
 import unittest
 
 
+def hexenc(s):
+    return ' '.join('%02x' % x for x in s)
+
+
 class T2CharStringTest(unittest.TestCase):
 
     @classmethod
@@ -13,19 +23,141 @@
 
     def test_calcBounds_empty(self):
         cs = self.stringToT2CharString("endchar")
-        bounds = cs.calcBounds()
+        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()
+        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()
+        bounds = cs.calcBounds(None)
         self.assertEqual(bounds, (91.90524980688875, -12.5, 208.09475019311125, 100))
 
+    def test_charstring_bytecode_optimization(self):
+        cs = self.stringToT2CharString(
+            "100.0 100 rmoveto -50.0 -150 200.5 0.0 -50 150 rrcurveto endchar")
+        cs.isCFF2 = False
+        cs.private._isCFF2 = False
+        cs.compile()
+        cs.decompile()
+        self.assertEqual(
+            cs.program, [100, 100, 'rmoveto', -50, -150, 200.5, 0, -50, 150,
+                         'rrcurveto', 'endchar'])
+
+        cs2 = self.stringToT2CharString(
+            "100.0 rmoveto -50.0 -150 200.5 0.0 -50 150 rrcurveto")
+        cs2.isCFF2 = True
+        cs2.private._isCFF2 = True
+        cs2.compile(isCFF2=True)
+        cs2.decompile()
+        self.assertEqual(
+            cs2.program, [100, 'rmoveto', -50, -150, 200.5, 0, -50, 150,
+                          'rrcurveto'])
+
+    def test_encodeFloat(self):
+        testNums = [
+            # value                expected result
+            (-9.399999999999999,   '1e e9 a4 ff'),  # -9.4
+            (9.399999999999999999, '1e 9a 4f'),  # 9.4
+            (456.8,                '1e 45 6a 8f'),  # 456.8
+            (0.0,                  '1e 0f'),  # 0
+            (-0.0,                 '1e 0f'),  # 0
+            (1.0,                  '1e 1f'),  # 1
+            (-1.0,                 '1e e1 ff'),  # -1
+            (98765.37e2,           '1e 98 76 53 7f'),  # 9876537
+            (1234567890.0,         '1e 1a 23 45 67 9b 09 ff'),  # 1234567890
+            (9.876537e-4,          '1e a0 00 98 76 53 7f'),  # 9.876537e-24
+            (9.876537e+4,          '1e 98 76 5a 37 ff'),  # 9.876537e+24
+        ]
+
+        for sample in testNums:
+            encoded_result = encodeFloat(sample[0])
+
+            # check to see if we got the expected bytes
+            self.assertEqual(hexenc(encoded_result), sample[1])
+
+            # check to see if we get the same value by decoding the data
+            decoded_result = read_realNumber(
+                None,
+                None,
+                encoded_result,
+                1,
+            )
+            self.assertEqual(decoded_result[0], float('%.8g' % sample[0]))
+            # We limit to 8 digits of precision to match the implementation
+            # of encodeFloat.
+
+    def test_encode_decode_fixed(self):
+        testNums = [
+            # value                expected hex      expected float
+            (-9.399999999999999,   'ff ff f6 99 9a', -9.3999939),
+            (-9.4,                 'ff ff f6 99 9a', -9.3999939),
+            (9.399999999999999999, 'ff 00 09 66 66', 9.3999939),
+            (9.4,                  'ff 00 09 66 66', 9.3999939),
+            (456.8,                'ff 01 c8 cc cd', 456.8000031),
+            (-456.8,               'ff fe 37 33 33', -456.8000031),
+        ]
+
+        for (value, expected_hex, expected_float) in testNums:
+            encoded_result = encodeFixed(value)
+
+            # check to see if we got the expected bytes
+            self.assertEqual(hexenc(encoded_result), expected_hex)
+
+            # check to see if we get the same value by decoding the data
+            decoded_result = read_fixed1616(
+                None,
+                None,
+                encoded_result,
+                1,
+            )
+            self.assertAlmostEqual(decoded_result[0], expected_float)
+
+    def test_toXML(self):
+        program = [
+            '107 53.4004 166.199 hstem',
+            '174.6 163.801 vstem',
+            '338.4 142.8 rmoveto',
+            '28 0 21.9 9 15.8 18 15.8 18 7.9 20.79959 0 23.6 rrcurveto',
+            'endchar'
+        ]
+        cs = self.stringToT2CharString(" ".join(program))
+
+        self.assertEqual(getXML(cs.toXML), program)
+
+    def test_fromXML(self):
+        cs = T2CharString()
+        for name, attrs, content in parseXML(
+            [
+                '<CharString name="period">'
+                '  338.4 142.8 rmoveto',
+                '  28 0 21.9 9 15.8 18 15.8 18 7.9 20.79959 0 23.6 rrcurveto',
+                '  endchar'
+                '</CharString>'
+            ]
+        ):
+            cs.fromXML(name, attrs, content)
+
+        expected_program = [
+            338.3999939, 142.8000031, 'rmoveto',
+            28, 0, 21.8999939, 9, 15.8000031,
+            18, 15.8000031, 18, 7.8999939,
+            20.7995911, 0, 23.6000061, 'rrcurveto',
+            'endchar'
+        ]
+
+        self.assertEqual(len(cs.program), len(expected_program))
+        for arg, expected_arg in zip(cs.program, expected_program):
+            if isinstance(arg, str):
+                self.assertIsInstance(expected_arg, str)
+                self.assertEqual(arg, expected_arg)
+            else:
+                self.assertNotIsInstance(expected_arg, str)
+                self.assertAlmostEqual(arg, expected_arg)
+
 
 if __name__ == "__main__":
     import sys
diff --git a/Tests/misc/py23_test.py b/Tests/misc/py23_test.py
index 22b8044..61274cc 100644
--- a/Tests/misc/py23_test.py
+++ b/Tests/misc/py23_test.py
@@ -1,7 +1,7 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import tobytes
 from fontTools.misc.textTools import deHexStr
 import filecmp
+from io import StringIO
 import tempfile
 from subprocess import check_call
 import sys
@@ -390,35 +390,6 @@
 		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
diff --git a/Tests/misc/testTools_test.py b/Tests/misc/testTools_test.py
index 11c04b1..80d4d2b 100644
--- a/Tests/misc/testTools_test.py
+++ b/Tests/misc/testTools_test.py
@@ -1,7 +1,3 @@
-# -*- 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
 
diff --git a/Tests/misc/testdata/test.plist b/Tests/misc/testdata/test.plist
new file mode 100644
index 0000000..864605f
--- /dev/null
+++ b/Tests/misc/testdata/test.plist
@@ -0,0 +1,87 @@
+<?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>aBigInt</key>
+	<integer>9223372036854775764</integer>
+	<key>aBigInt2</key>
+	<integer>9223372036854775852</integer>
+	<key>aDate</key>
+	<date>2004-10-26T10:33:33Z</date>
+	<key>aDict</key>
+	<dict>
+		<key>aFalseValue</key>
+		<false/>
+		<key>aTrueValue</key>
+		<true/>
+		<key>aUnicodeValue</key>
+		<string>Mässig, Maß</string>
+		<key>anotherString</key>
+		<string>&lt;hello &amp; 'hi' there!&gt;</string>
+		<key>deeperDict</key>
+		<dict>
+			<key>a</key>
+			<integer>17</integer>
+			<key>b</key>
+			<real>32.5</real>
+			<key>c</key>
+			<array>
+				<integer>1</integer>
+				<integer>2</integer>
+				<string>text</string>
+			</array>
+		</dict>
+	</dict>
+	<key>aFloat</key>
+	<real>0.5</real>
+	<key>aList</key>
+	<array>
+		<string>A</string>
+		<string>B</string>
+		<integer>12</integer>
+		<real>32.5</real>
+		<array>
+			<integer>1</integer>
+			<integer>2</integer>
+			<integer>3</integer>
+		</array>
+	</array>
+	<key>aNegativeBigInt</key>
+	<integer>-80000000000</integer>
+	<key>aNegativeInt</key>
+	<integer>-5</integer>
+	<key>aString</key>
+	<string>Doodah</string>
+	<key>anEmptyDict</key>
+	<dict/>
+	<key>anEmptyList</key>
+	<array/>
+	<key>anInt</key>
+	<integer>728</integer>
+	<key>nestedData</key>
+	<array>
+		<data>
+		PGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5r
+		PgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5
+		IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBi
+		aW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3Rz
+		IG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQID
+		PGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAw==
+		</data>
+	</array>
+	<key>someData</key>
+	<data>
+	PGJpbmFyeSBndW5rPg==
+	</data>
+	<key>someMoreData</key>
+	<data>
+	PGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8
+	bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxs
+	b3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxv
+	dHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90
+	cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAw==
+	</data>
+	<key>Åbenraa</key>
+	<string>That was a unicode key.</string>
+</dict>
+</plist>
diff --git a/Tests/misc/textTools_test.py b/Tests/misc/textTools_test.py
index 547569a..f83abf9 100644
--- a/Tests/misc/textTools_test.py
+++ b/Tests/misc/textTools_test.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.misc.textTools import pad
 
 
diff --git a/Tests/misc/timeTools_test.py b/Tests/misc/timeTools_test.py
index 6c98f64..4d75ce4 100644
--- a/Tests/misc/timeTools_test.py
+++ b/Tests/misc/timeTools_test.py
@@ -1,13 +1,12 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.misc.timeTools import asctime, timestampNow, epoch_diff
+from fontTools.misc.timeTools import asctime, timestampNow, timestampToString, timestampFromString, epoch_diff
 import os
 import time
+import locale
 import pytest
 
 
 def test_asctime():
-    assert isinstance(asctime(), basestring)
+    assert isinstance(asctime(), str)
     assert asctime(time.gmtime(0)) == 'Thu Jan  1 00:00:00 1970'
 
 
@@ -22,3 +21,17 @@
 
     del os.environ["SOURCE_DATE_EPOCH"]
     assert timestampNow() + epoch_diff != 150687315
+
+
+# test for issue #1838
+def test_date_parsing_with_locale():
+    l = locale.getlocale(locale.LC_TIME)
+    try:
+        locale.setlocale(locale.LC_TIME, 'de_DE.utf8')
+    except locale.Error:
+        pytest.skip("Locale de_DE not available")
+
+    try:
+        assert timestampFromString(timestampToString(timestampNow()))
+    finally:
+        locale.setlocale(locale.LC_TIME, l)
diff --git a/Tests/misc/transform_test.py b/Tests/misc/transform_test.py
index 7a7a67d..53d4a20 100644
--- a/Tests/misc/transform_test.py
+++ b/Tests/misc/transform_test.py
@@ -1,5 +1,3 @@
-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
@@ -25,6 +23,14 @@
             [(0, 0), (0, 100), (100, 100), (100, 0)]
         ) == [(0, 0), (0, 300), (200, 300), (200, 0)]
 
+    def test_transformVector(self):
+        t = Transform(2, 0, 0, 3, -10, 30)
+        assert t.transformVector((-4, 5)) == (-8, 15)
+
+    def test_transformVectors(self):
+        t = Transform(2, 0, 0, 3, -10, 30)
+        assert t.transformVectors([(-4, 5), (-6, 7)]) == [(-8, 15), (-12, 21)]
+
     def test_translate(self):
         t = Transform()
         assert t.translate(20, 30) == Transform(1, 0, 0, 1, 20, 30)
diff --git a/Tests/misc/vector_test.py b/Tests/misc/vector_test.py
new file mode 100644
index 0000000..236a3ba
--- /dev/null
+++ b/Tests/misc/vector_test.py
@@ -0,0 +1,71 @@
+import math
+import pytest
+from fontTools.misc.arrayTools import Vector as ArrayVector
+from fontTools.misc.vector import Vector
+
+
+def test_Vector():
+    v = Vector((100, 200))
+    assert repr(v) == "Vector((100, 200))"
+    assert v == Vector((100, 200))
+    assert v == Vector([100, 200])
+    assert v == (100, 200)
+    assert (100, 200) == v
+    assert v == [100, 200]
+    assert [100, 200] == v
+    assert v is Vector(v)
+    assert v + 10 == (110, 210)
+    assert 10 + v == (110, 210)
+    assert v + Vector((1, 2)) == (101, 202)
+    assert v - Vector((1, 2)) == (99, 198)
+    assert v * 2 == (200, 400)
+    assert 2 * v == (200, 400)
+    assert v * 0.5 == (50, 100)
+    assert v / 2 == (50, 100)
+    assert 2 / v == (0.02, 0.01)
+    v = Vector((3, 4))
+    assert abs(v) == 5  # length
+    assert v.length() == 5
+    assert v.normalized() == Vector((0.6, 0.8))
+    assert abs(Vector((1, 1, 1))) == math.sqrt(3)
+    assert bool(Vector((0, 0, 1)))
+    assert not bool(Vector((0, 0, 0)))
+    v1 = Vector((2, 3))
+    v2 = Vector((3, 4))
+    assert v1.dot(v2) == 18
+    v = Vector((2, 4))
+    assert round(v / 3) == (1, 1)
+    with pytest.raises(
+        AttributeError,
+        match="'Vector' object has no attribute 'newAttr'",
+    ):
+        v.newAttr = 12
+
+
+def test_deprecated():
+    with pytest.warns(
+        DeprecationWarning,
+        match="fontTools.misc.arrayTools.Vector has been deprecated",
+    ):
+        ArrayVector((1, 2))
+    with pytest.warns(
+        DeprecationWarning,
+        match="the 'keep' argument has been deprecated",
+    ):
+        Vector((1, 2), keep=True)
+    v = Vector((1, 2))
+    with pytest.warns(
+        DeprecationWarning,
+        match="the 'toInt' method has been deprecated",
+    ):
+        v.toInt()
+    with pytest.warns(
+        DeprecationWarning,
+        match="the 'values' attribute has been deprecated",
+    ):
+        v.values
+    with pytest.raises(
+        AttributeError,
+        match="the 'values' attribute has been deprecated",
+    ):
+        v.values = [12, 23]
diff --git a/Tests/misc/xmlReader_test.py b/Tests/misc/xmlReader_test.py
index 14b6978..f6775cb 100644
--- a/Tests/misc/xmlReader_test.py
+++ b/Tests/misc/xmlReader_test.py
@@ -1,7 +1,5 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import print_function, division, absolute_import, unicode_literals
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import strjoin
+from io import BytesIO
 import os
 import unittest
 from fontTools.ttLib import TTFont
@@ -144,7 +142,7 @@
 
 	def test_read_sub_file(self):
 		# Verifies that sub-file content is able to be read to a table.
-		expectedContent = u'testContent'
+		expectedContent = 'testContent'
 		expectedNameID = '1'
 		expectedPlatform = '3'
 		expectedLangId = '0x409'
diff --git a/Tests/misc/xmlWriter_test.py b/Tests/misc/xmlWriter_test.py
index 0930e1c..7b9894c 100644
--- a/Tests/misc/xmlWriter_test.py
+++ b/Tests/misc/xmlWriter_test.py
@@ -1,11 +1,10 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin, tobytes
+from io import BytesIO
 import os
 import unittest
 from fontTools.misc.xmlWriter import XMLWriter
 
-linesep = tobytes(os.linesep)
-HEADER = b'<?xml version="1.0" encoding="UTF-8"?>' + linesep
+HEADER = b'<?xml version="1.0" encoding="UTF-8"?>\n'
 
 class TestXMLWriter(unittest.TestCase):
 
@@ -17,30 +16,30 @@
 	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? -->",
+		self.assertEqual(HEADER + b"<!-- Hello world\n     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,
+		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>\n',
 				 writer.file.getvalue())
 
 	def test_encoding_utf8(self):
-		# https://github.com/behdad/fonttools/issues/246
+		# https://github.com/fonttools/fonttools/issues/246
 		writer = XMLWriter(BytesIO(), encoding="utf8")
-		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
+		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>\n',
 				 writer.file.getvalue())
 
 	def test_encoding_UTF_8(self):
-		# https://github.com/behdad/fonttools/issues/246
+		# https://github.com/fonttools/fonttools/issues/246
 		writer = XMLWriter(BytesIO(), encoding="UTF-8")
-		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
+		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>\n',
 				 writer.file.getvalue())
 
 	def test_encoding_UTF8(self):
-		# https://github.com/behdad/fonttools/issues/246
+		# https://github.com/fonttools/fonttools/issues/246
 		writer = XMLWriter(BytesIO(), encoding="UTF8")
-		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
+		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>\n',
 				 writer.file.getvalue())
 
 	def test_encoding_other(self):
@@ -61,7 +60,7 @@
 		writer.newline()
 		writer.dedent()
 		writer.write("baz")
-		self.assertEqual(HEADER + bytesjoin(["foo", "  bar", "baz"], linesep),
+		self.assertEqual(HEADER + bytesjoin(["foo", "  bar", "baz"], "\n"),
 				 writer.file.getvalue())
 
 	def test_writecdata(self):
@@ -89,7 +88,7 @@
 		    "66756c20 67726f75 70206f66 206c6574",
 		    "74657273 2c206e6f 74206120 67726f75",
 		    "70206f66 20626561 75746966 756c206c",
-		    "65747465 72732e  ", ""], joiner=linesep), writer.file.getvalue())
+		    "65747465 72732e  ", ""], joiner="\n"), writer.file.getvalue())
 
 	def test_stringifyattrs(self):
 		writer = XMLWriter(BytesIO())
diff --git a/Tests/mtiLib/data/featurename-backward.ttx.GSUB b/Tests/mtiLib/data/featurename-backward.ttx.GSUB
index 9469c79..cc893cd 100644
--- a/Tests/mtiLib/data/featurename-backward.ttx.GSUB
+++ b/Tests/mtiLib/data/featurename-backward.ttx.GSUB
@@ -51,7 +51,7 @@
       <LookupType value="1"/>
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
-      <SingleSubst index="0" Format="1">
+      <SingleSubst index="0">
         <Substitution in="a" out="b"/>
       </SingleSubst>
     </Lookup>
diff --git a/Tests/mtiLib/data/featurename-forward.ttx.GSUB b/Tests/mtiLib/data/featurename-forward.ttx.GSUB
index 9469c79..cc893cd 100644
--- a/Tests/mtiLib/data/featurename-forward.ttx.GSUB
+++ b/Tests/mtiLib/data/featurename-forward.ttx.GSUB
@@ -51,7 +51,7 @@
       <LookupType value="1"/>
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
-      <SingleSubst index="0" Format="1">
+      <SingleSubst index="0">
         <Substitution in="a" out="b"/>
       </SingleSubst>
     </Lookup>
diff --git a/Tests/mtiLib/data/lookupnames-backward.ttx.GSUB b/Tests/mtiLib/data/lookupnames-backward.ttx.GSUB
index 698012c..cb358d7 100644
--- a/Tests/mtiLib/data/lookupnames-backward.ttx.GSUB
+++ b/Tests/mtiLib/data/lookupnames-backward.ttx.GSUB
@@ -39,7 +39,7 @@
       <LookupType value="1"/>
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
-      <SingleSubst index="0" Format="1">
+      <SingleSubst index="0">
         <Substitution in="uuvowelsignkannada" out="uuvowelsignaltkannada"/>
         <Substitution in="uvowelsignkannada" out="uvowelsignaltkannada"/>
       </SingleSubst>
@@ -49,11 +49,11 @@
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
       <ChainContextSubst index="0" Format="2">
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="uvowelsignkannada"/>
           <Glyph value="uuvowelsignkannada"/>
         </Coverage>
-        <BacktrackClassDef Format="2">
+        <BacktrackClassDef>
           <ClassDef glyph="pakannada" class="1"/>
           <ClassDef glyph="pevowelkannada" class="1"/>
           <ClassDef glyph="phakannada" class="1"/>
@@ -61,7 +61,7 @@
           <ClassDef glyph="vakannada" class="1"/>
           <ClassDef glyph="vevowelkannada" class="1"/>
         </BacktrackClassDef>
-        <InputClassDef Format="1">
+        <InputClassDef>
           <ClassDef glyph="uuvowelsignkannada" class="1"/>
           <ClassDef glyph="uvowelsignkannada" class="1"/>
         </InputClassDef>
diff --git a/Tests/mtiLib/data/lookupnames-forward.ttx.GSUB b/Tests/mtiLib/data/lookupnames-forward.ttx.GSUB
index 15e48d0..249d605 100644
--- a/Tests/mtiLib/data/lookupnames-forward.ttx.GSUB
+++ b/Tests/mtiLib/data/lookupnames-forward.ttx.GSUB
@@ -40,11 +40,11 @@
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
       <ChainContextSubst index="0" Format="2">
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="uvowelsignkannada"/>
           <Glyph value="uuvowelsignkannada"/>
         </Coverage>
-        <BacktrackClassDef Format="2">
+        <BacktrackClassDef>
           <ClassDef glyph="pakannada" class="1"/>
           <ClassDef glyph="pevowelkannada" class="1"/>
           <ClassDef glyph="phakannada" class="1"/>
@@ -52,7 +52,7 @@
           <ClassDef glyph="vakannada" class="1"/>
           <ClassDef glyph="vevowelkannada" class="1"/>
         </BacktrackClassDef>
-        <InputClassDef Format="1">
+        <InputClassDef>
           <ClassDef glyph="uuvowelsignkannada" class="1"/>
           <ClassDef glyph="uvowelsignkannada" class="1"/>
         </InputClassDef>
@@ -78,7 +78,7 @@
       <LookupType value="1"/>
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
-      <SingleSubst index="0" Format="1">
+      <SingleSubst index="0">
         <Substitution in="uuvowelsignkannada" out="uuvowelsignaltkannada"/>
         <Substitution in="uvowelsignkannada" out="uvowelsignaltkannada"/>
       </SingleSubst>
diff --git a/Tests/mtiLib/data/mixed-toplevels.ttx.GSUB b/Tests/mtiLib/data/mixed-toplevels.ttx.GSUB
index 15e48d0..249d605 100644
--- a/Tests/mtiLib/data/mixed-toplevels.ttx.GSUB
+++ b/Tests/mtiLib/data/mixed-toplevels.ttx.GSUB
@@ -40,11 +40,11 @@
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
       <ChainContextSubst index="0" Format="2">
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="uvowelsignkannada"/>
           <Glyph value="uuvowelsignkannada"/>
         </Coverage>
-        <BacktrackClassDef Format="2">
+        <BacktrackClassDef>
           <ClassDef glyph="pakannada" class="1"/>
           <ClassDef glyph="pevowelkannada" class="1"/>
           <ClassDef glyph="phakannada" class="1"/>
@@ -52,7 +52,7 @@
           <ClassDef glyph="vakannada" class="1"/>
           <ClassDef glyph="vevowelkannada" class="1"/>
         </BacktrackClassDef>
-        <InputClassDef Format="1">
+        <InputClassDef>
           <ClassDef glyph="uuvowelsignkannada" class="1"/>
           <ClassDef glyph="uvowelsignkannada" class="1"/>
         </InputClassDef>
@@ -78,7 +78,7 @@
       <LookupType value="1"/>
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
-      <SingleSubst index="0" Format="1">
+      <SingleSubst index="0">
         <Substitution in="uuvowelsignkannada" out="uuvowelsignaltkannada"/>
         <Substitution in="uvowelsignkannada" out="uvowelsignaltkannada"/>
       </SingleSubst>
diff --git a/Tests/mtiLib/data/mti/chained-glyph.ttx.GPOS b/Tests/mtiLib/data/mti/chained-glyph.ttx.GPOS
index 811b1df..b550c70 100644
--- a/Tests/mtiLib/data/mti/chained-glyph.ttx.GPOS
+++ b/Tests/mtiLib/data/mti/chained-glyph.ttx.GPOS
@@ -5,10 +5,10 @@
     <!-- LookupCount=2 -->
     <Lookup index="0">
       <LookupType value="8"/>
-      <LookupFlag value="512"/>
+      <LookupFlag value="512"/><!-- markAttachmentType[2] -->
       <!-- SubTableCount=1 -->
       <ChainContextPos index="0" Format="1">
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="uuvowelsignsinh"/>
           <Glyph value="uvowelsignsinh"/>
         </Coverage>
diff --git a/Tests/mtiLib/data/mti/chained-glyph.ttx.GSUB b/Tests/mtiLib/data/mti/chained-glyph.ttx.GSUB
index 67aa902..7dfdb84 100644
--- a/Tests/mtiLib/data/mti/chained-glyph.ttx.GSUB
+++ b/Tests/mtiLib/data/mti/chained-glyph.ttx.GSUB
@@ -5,10 +5,10 @@
     <!-- LookupCount=2 -->
     <Lookup index="0">
       <LookupType value="6"/>
-      <LookupFlag value="512"/>
+      <LookupFlag value="512"/><!-- markAttachmentType[2] -->
       <!-- SubTableCount=1 -->
       <ChainContextSubst index="0" Format="1">
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="uuvowelsignsinh"/>
           <Glyph value="uvowelsignsinh"/>
         </Coverage>
diff --git a/Tests/mtiLib/data/mti/chainedclass.ttx.GSUB b/Tests/mtiLib/data/mti/chainedclass.ttx.GSUB
index cfa391f..fcd7569 100644
--- a/Tests/mtiLib/data/mti/chainedclass.ttx.GSUB
+++ b/Tests/mtiLib/data/mti/chainedclass.ttx.GSUB
@@ -8,11 +8,11 @@
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
       <ChainContextSubst index="0" Format="2">
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="uvowelsignkannada"/>
           <Glyph value="uuvowelsignkannada"/>
         </Coverage>
-        <BacktrackClassDef Format="2">
+        <BacktrackClassDef>
           <ClassDef glyph="pakannada" class="1"/>
           <ClassDef glyph="pevowelkannada" class="1"/>
           <ClassDef glyph="phakannada" class="1"/>
@@ -20,7 +20,7 @@
           <ClassDef glyph="vakannada" class="1"/>
           <ClassDef glyph="vevowelkannada" class="1"/>
         </BacktrackClassDef>
-        <InputClassDef Format="1">
+        <InputClassDef>
           <ClassDef glyph="uuvowelsignkannada" class="1"/>
           <ClassDef glyph="uvowelsignkannada" class="1"/>
         </InputClassDef>
@@ -46,7 +46,7 @@
       <LookupType value="1"/>
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
-      <SingleSubst index="0" Format="1">
+      <SingleSubst index="0">
         <Substitution in="uuvowelsignkannada" out="uuvowelsignaltkannada"/>
         <Substitution in="uvowelsignkannada" out="uvowelsignaltkannada"/>
       </SingleSubst>
diff --git a/Tests/mtiLib/data/mti/chainedcoverage.ttx.GSUB b/Tests/mtiLib/data/mti/chainedcoverage.ttx.GSUB
index 7c807a4..4f312c6 100644
--- a/Tests/mtiLib/data/mti/chainedcoverage.ttx.GSUB
+++ b/Tests/mtiLib/data/mti/chainedcoverage.ttx.GSUB
@@ -9,7 +9,7 @@
       <!-- SubTableCount=1 -->
       <ChainContextSubst index="0" Format="3">
         <!-- BacktrackGlyphCount=1 -->
-        <BacktrackCoverage index="0" Format="2">
+        <BacktrackCoverage index="0">
           <Glyph value="zero"/>
           <Glyph value="one"/>
           <Glyph value="two"/>
@@ -22,11 +22,11 @@
           <Glyph value="nine"/>
         </BacktrackCoverage>
         <!-- InputGlyphCount=1 -->
-        <InputCoverage index="0" Format="1">
+        <InputCoverage index="0">
           <Glyph value="slash"/>
         </InputCoverage>
         <!-- LookAheadGlyphCount=1 -->
-        <LookAheadCoverage index="0" Format="2">
+        <LookAheadCoverage index="0">
           <Glyph value="zero"/>
           <Glyph value="one"/>
           <Glyph value="two"/>
@@ -49,7 +49,7 @@
       <LookupType value="1"/>
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
-      <SingleSubst index="0" Format="1">
+      <SingleSubst index="0">
         <Substitution in="slash" out="fraction"/>
       </SingleSubst>
     </Lookup>
diff --git a/Tests/mtiLib/data/mti/gdefattach.ttx.GDEF b/Tests/mtiLib/data/mti/gdefattach.ttx.GDEF
index cc4c70d..79aed8f 100644
--- a/Tests/mtiLib/data/mti/gdefattach.ttx.GDEF
+++ b/Tests/mtiLib/data/mti/gdefattach.ttx.GDEF
@@ -2,7 +2,7 @@
 <GDEF>
   <Version value="0x00010000"/>
   <AttachList>
-    <Coverage Format="1">
+    <Coverage>
       <Glyph value="A"/>
       <Glyph value="B"/>
     </Coverage>
diff --git a/Tests/mtiLib/data/mti/gdefclasses.ttx.GDEF b/Tests/mtiLib/data/mti/gdefclasses.ttx.GDEF
index e298c28..c3c9d68 100644
--- a/Tests/mtiLib/data/mti/gdefclasses.ttx.GDEF
+++ b/Tests/mtiLib/data/mti/gdefclasses.ttx.GDEF
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <GDEF>
   <Version value="0x00010000"/>
-  <GlyphClassDef Format="1">
+  <GlyphClassDef>
     <ClassDef glyph="A" class="1"/>
     <ClassDef glyph="C" class="1"/>
     <ClassDef glyph="acute" class="3"/>
diff --git a/Tests/mtiLib/data/mti/gdefligcaret.ttx.GDEF b/Tests/mtiLib/data/mti/gdefligcaret.ttx.GDEF
index 48f7347..174af8e 100644
--- a/Tests/mtiLib/data/mti/gdefligcaret.ttx.GDEF
+++ b/Tests/mtiLib/data/mti/gdefligcaret.ttx.GDEF
@@ -2,7 +2,7 @@
 <GDEF>
   <Version value="0x00010000"/>
   <LigCaretList>
-    <Coverage Format="1">
+    <Coverage>
       <Glyph value="uniFB01"/>
       <Glyph value="ffi"/>
     </Coverage>
diff --git a/Tests/mtiLib/data/mti/gdefmarkattach.ttx.GDEF b/Tests/mtiLib/data/mti/gdefmarkattach.ttx.GDEF
index b6cbe10..97683ab 100644
--- a/Tests/mtiLib/data/mti/gdefmarkattach.ttx.GDEF
+++ b/Tests/mtiLib/data/mti/gdefmarkattach.ttx.GDEF
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <GDEF>
   <Version value="0x00010000"/>
-  <MarkAttachClassDef Format="1">
+  <MarkAttachClassDef>
     <ClassDef glyph="breve" class="1"/>
     <ClassDef glyph="commaacent" class="2"/>
     <ClassDef glyph="dotbelow" class="2"/>
diff --git a/Tests/mtiLib/data/mti/gdefmarkfilter.ttx.GDEF b/Tests/mtiLib/data/mti/gdefmarkfilter.ttx.GDEF
index 3b23481..4f3593e 100644
--- a/Tests/mtiLib/data/mti/gdefmarkfilter.ttx.GDEF
+++ b/Tests/mtiLib/data/mti/gdefmarkfilter.ttx.GDEF
@@ -5,17 +5,17 @@
     <MarkSetTableFormat value="1"/>
     <!-- MarkSetCount=4 -->
     <Coverage index="0" empty="1"/>
-    <Coverage index="1" Format="1">
+    <Coverage index="1">
       <Glyph value="breve"/>
       <Glyph value="acute"/>
       <Glyph value="dotabove"/>
     </Coverage>
-    <Coverage index="2" Format="1">
+    <Coverage index="2">
       <Glyph value="dotbelow"/>
       <Glyph value="cedilla"/>
       <Glyph value="commaaccent"/>
     </Coverage>
-    <Coverage index="3" Format="1">
+    <Coverage index="3">
       <Glyph value="dotbelow"/>
       <Glyph value="dotabove"/>
     </Coverage>
diff --git a/Tests/mtiLib/data/mti/gposcursive.ttx.GPOS b/Tests/mtiLib/data/mti/gposcursive.ttx.GPOS
index 2965139..6c08c50 100644
--- a/Tests/mtiLib/data/mti/gposcursive.ttx.GPOS
+++ b/Tests/mtiLib/data/mti/gposcursive.ttx.GPOS
@@ -8,7 +8,7 @@
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
       <CursivePos index="0" Format="1">
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="A"/>
           <Glyph value="B"/>
         </Coverage>
diff --git a/Tests/mtiLib/data/mti/gposkernset.ttx.GPOS b/Tests/mtiLib/data/mti/gposkernset.ttx.GPOS
index edfea10..a837123 100644
--- a/Tests/mtiLib/data/mti/gposkernset.ttx.GPOS
+++ b/Tests/mtiLib/data/mti/gposkernset.ttx.GPOS
@@ -8,7 +8,7 @@
       <LookupFlag value="0"/>
       <!-- SubTableCount=2 -->
       <PairPos index="0" Format="1">
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="Acircumflex"/>
           <Glyph value="T"/>
         </Coverage>
@@ -31,7 +31,7 @@
         </PairSet>
       </PairPos>
       <PairPos index="1" Format="2">
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="A"/>
           <Glyph value="Acircumflex"/>
           <Glyph value="T"/>
@@ -44,7 +44,7 @@
         </Coverage>
         <ValueFormat1 value="4"/>
         <ValueFormat2 value="0"/>
-        <ClassDef1 Format="2">
+        <ClassDef1>
           <ClassDef glyph="A" class="1"/>
           <ClassDef glyph="Aacute" class="1"/>
           <ClassDef glyph="Acircumflex" class="1"/>
@@ -55,7 +55,7 @@
           <ClassDef glyph="Ograve" class="2"/>
           <ClassDef glyph="T" class="3"/>
         </ClassDef1>
-        <ClassDef2 Format="2">
+        <ClassDef2>
           <ClassDef glyph="V" class="1"/>
           <ClassDef glyph="a" class="2"/>
           <ClassDef glyph="aacute" class="2"/>
diff --git a/Tests/mtiLib/data/mti/gposmarktobase.ttx.GPOS b/Tests/mtiLib/data/mti/gposmarktobase.ttx.GPOS
index b0a74e7..e6e2102 100644
--- a/Tests/mtiLib/data/mti/gposmarktobase.ttx.GPOS
+++ b/Tests/mtiLib/data/mti/gposmarktobase.ttx.GPOS
@@ -8,7 +8,7 @@
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
       <MarkBasePos index="0" Format="1">
-        <MarkCoverage Format="2">
+        <MarkCoverage>
           <Glyph value="aimatrabindigurmukhi"/>
           <Glyph value="aimatragurmukhi"/>
           <Glyph value="aimatratippigurmukhi"/>
@@ -22,7 +22,7 @@
           <Glyph value="oomatragurmukhi"/>
           <Glyph value="oomatratippigurmukhi"/>
         </MarkCoverage>
-        <BaseCoverage Format="2">
+        <BaseCoverage>
           <Glyph value="lagurmukhi"/>
           <Glyph value="lanuktagurmukhi"/>
           <Glyph value="nagurmukhi"/>
diff --git a/Tests/mtiLib/data/mti/gpospairclass.ttx.GPOS b/Tests/mtiLib/data/mti/gpospairclass.ttx.GPOS
index 567b2a7..32b35ae 100644
--- a/Tests/mtiLib/data/mti/gpospairclass.ttx.GPOS
+++ b/Tests/mtiLib/data/mti/gpospairclass.ttx.GPOS
@@ -8,7 +8,7 @@
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
       <PairPos index="0" Format="2">
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="A"/>
           <Glyph value="Acircumflex"/>
           <Glyph value="T"/>
@@ -21,7 +21,7 @@
         </Coverage>
         <ValueFormat1 value="4"/>
         <ValueFormat2 value="0"/>
-        <ClassDef1 Format="2">
+        <ClassDef1>
           <ClassDef glyph="A" class="1"/>
           <ClassDef glyph="Aacute" class="1"/>
           <ClassDef glyph="Acircumflex" class="1"/>
@@ -32,7 +32,7 @@
           <ClassDef glyph="Ograve" class="2"/>
           <ClassDef glyph="T" class="3"/>
         </ClassDef1>
-        <ClassDef2 Format="2">
+        <ClassDef2>
           <ClassDef glyph="V" class="1"/>
           <ClassDef glyph="a" class="2"/>
           <ClassDef glyph="aacute" class="2"/>
diff --git a/Tests/mtiLib/data/mti/gpospairglyph.ttx.GPOS b/Tests/mtiLib/data/mti/gpospairglyph.ttx.GPOS
index ea0161b..f03a90e 100644
--- a/Tests/mtiLib/data/mti/gpospairglyph.ttx.GPOS
+++ b/Tests/mtiLib/data/mti/gpospairglyph.ttx.GPOS
@@ -8,7 +8,7 @@
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
       <PairPos index="0" Format="1">
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="A"/>
           <Glyph value="Acircumflex"/>
           <Glyph value="T"/>
diff --git a/Tests/mtiLib/data/mti/gpossingle.ttx.GPOS b/Tests/mtiLib/data/mti/gpossingle.ttx.GPOS
index adbb44f..c3bdbf6 100644
--- a/Tests/mtiLib/data/mti/gpossingle.ttx.GPOS
+++ b/Tests/mtiLib/data/mti/gpossingle.ttx.GPOS
@@ -8,7 +8,7 @@
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
       <SinglePos index="0" Format="1">
-        <Coverage Format="2">
+        <Coverage>
           <Glyph value="bsuperior"/>
           <Glyph value="isuperior"/>
           <Glyph value="vsuperior"/>
diff --git a/Tests/mtiLib/data/mti/gsubalternate.ttx.GSUB b/Tests/mtiLib/data/mti/gsubalternate.ttx.GSUB
index 4184325..86b0b73 100644
--- a/Tests/mtiLib/data/mti/gsubalternate.ttx.GSUB
+++ b/Tests/mtiLib/data/mti/gsubalternate.ttx.GSUB
@@ -7,7 +7,7 @@
       <LookupType value="3"/>
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
-      <AlternateSubst index="0" Format="1">
+      <AlternateSubst index="0">
         <AlternateSet glyph="eight">
           <Alternate glyph="uniF738"/>
           <Alternate glyph="uniE0C0"/>
diff --git a/Tests/mtiLib/data/mti/gsubligature.ttx.GSUB b/Tests/mtiLib/data/mti/gsubligature.ttx.GSUB
index ad8f505..26c88c8 100644
--- a/Tests/mtiLib/data/mti/gsubligature.ttx.GSUB
+++ b/Tests/mtiLib/data/mti/gsubligature.ttx.GSUB
@@ -7,7 +7,7 @@
       <LookupType value="4"/>
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
-      <LigatureSubst index="0" Format="1">
+      <LigatureSubst index="0">
         <LigatureSet glyph="I">
           <Ligature components="J" glyph="IJ"/>
         </LigatureSet>
diff --git a/Tests/mtiLib/data/mti/gsubmultiple.ttx.GSUB b/Tests/mtiLib/data/mti/gsubmultiple.ttx.GSUB
index a68a45a..5bedfba 100644
--- a/Tests/mtiLib/data/mti/gsubmultiple.ttx.GSUB
+++ b/Tests/mtiLib/data/mti/gsubmultiple.ttx.GSUB
@@ -7,7 +7,7 @@
       <LookupType value="2"/>
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
-      <MultipleSubst index="0" Format="1">
+      <MultipleSubst index="0">
         <Substitution in="janyevoweltelugu" out="jaivoweltelugu,nyasubscripttelugu"/>
         <Substitution in="kassevoweltelugu" out="kaivoweltelugu,ssasubscripttelugu"/>
       </MultipleSubst>
diff --git a/Tests/mtiLib/data/mti/gsubreversechanined.ttx.GSUB b/Tests/mtiLib/data/mti/gsubreversechanined.ttx.GSUB
index 62ba14f..d705af5 100644
--- a/Tests/mtiLib/data/mti/gsubreversechanined.ttx.GSUB
+++ b/Tests/mtiLib/data/mti/gsubreversechanined.ttx.GSUB
@@ -5,17 +5,17 @@
     <!-- LookupCount=1 -->
     <Lookup index="0">
       <LookupType value="8"/>
-      <LookupFlag value="9"/>
+      <LookupFlag value="9"/><!-- rightToLeft ignoreMarks -->
       <!-- SubTableCount=3 -->
       <ReverseChainSingleSubst index="0" Format="1">
-        <Coverage Format="2">
+        <Coverage>
           <Glyph value="rayf2"/>
           <Glyph value="reyf2"/>
           <Glyph value="yayf2"/>
           <Glyph value="zayf2"/>
         </Coverage>
         <!-- BacktrackGlyphCount=1 -->
-        <BacktrackCoverage index="0" Format="2">
+        <BacktrackCoverage index="0">
           <Glyph value="bayi1"/>
           <Glyph value="jeemi1"/>
           <Glyph value="kafi1"/>
@@ -33,14 +33,14 @@
         <Substitute index="3" value="zayf1"/>
       </ReverseChainSingleSubst>
       <ReverseChainSingleSubst index="1" Format="1">
-        <Coverage Format="2">
+        <Coverage>
           <Glyph value="ayehf2"/>
           <Glyph value="hamzayeharabf2"/>
           <Glyph value="hamzayehf2"/>
           <Glyph value="yehf2"/>
         </Coverage>
         <!-- BacktrackGlyphCount=1 -->
-        <BacktrackCoverage index="0" Format="1">
+        <BacktrackCoverage index="0">
           <Glyph value="bayi1"/>
           <Glyph value="kafi1"/>
           <Glyph value="ghafi1"/>
@@ -58,14 +58,14 @@
         <Substitute index="3" value="yehf1"/>
       </ReverseChainSingleSubst>
       <ReverseChainSingleSubst index="2" Format="1">
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="dal"/>
           <Glyph value="del"/>
           <Glyph value="zal"/>
         </Coverage>
         <!-- BacktrackGlyphCount=0 -->
         <!-- LookAheadGlyphCount=1 -->
-        <LookAheadCoverage index="0" Format="2">
+        <LookAheadCoverage index="0">
           <Glyph value="ray"/>
           <Glyph value="rey"/>
           <Glyph value="zay"/>
diff --git a/Tests/mtiLib/data/mti/gsubsingle.ttx.GSUB b/Tests/mtiLib/data/mti/gsubsingle.ttx.GSUB
index 525b365..dc6a295 100644
--- a/Tests/mtiLib/data/mti/gsubsingle.ttx.GSUB
+++ b/Tests/mtiLib/data/mti/gsubsingle.ttx.GSUB
@@ -7,7 +7,7 @@
       <LookupType value="1"/>
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
-      <SingleSubst index="0" Format="1">
+      <SingleSubst index="0">
         <Substitution in="onehalf" out="onehalf.alt"/>
         <Substitution in="onequarter" out="onequarter.alt"/>
         <Substitution in="threequarters" out="threequarters.alt"/>
diff --git a/Tests/mtiLib/data/mti/mark-to-ligature.ttx.GPOS b/Tests/mtiLib/data/mti/mark-to-ligature.ttx.GPOS
index 7e02fe0..b5f275e 100644
--- a/Tests/mtiLib/data/mti/mark-to-ligature.ttx.GPOS
+++ b/Tests/mtiLib/data/mti/mark-to-ligature.ttx.GPOS
@@ -8,7 +8,7 @@
       <LookupFlag value="0"/>
       <!-- SubTableCount=1 -->
       <MarkLigPos index="0" Format="1">
-        <MarkCoverage Format="2">
+        <MarkCoverage>
           <Glyph value="AlefSuperiorNS"/>
           <Glyph value="DammaNS"/>
           <Glyph value="DammaRflxNS"/>
@@ -37,7 +37,7 @@
           <Glyph value="UltapeshNS"/>
           <Glyph value="WaslaNS"/>
         </MarkCoverage>
-        <LigatureCoverage Format="2">
+        <LigatureCoverage>
           <Glyph value="AinIni.12m_MeemFin.02"/>
           <Glyph value="AinIni_YehBarreeFin"/>
           <Glyph value="AinMed_YehBarreeFin"/>
diff --git a/Tests/mtiLib/mti_test.py b/Tests/mtiLib/mti_test.py
index 4916828..8a80113 100644
--- a/Tests/mtiLib/mti_test.py
+++ b/Tests/mtiLib/mti_test.py
@@ -1,10 +1,8 @@
-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
+from io import StringIO
 import os
 import sys
 import unittest
@@ -184,14 +182,14 @@
         decompiled.decompile(blob, font)
 
         # XML from built object.
-        writer = XMLWriter(StringIO(), newlinestr='\n')
+        writer = XMLWriter(StringIO())
         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 = XMLWriter(StringIO())
         writer.begintag(tableTag); writer.newline()
         decompiled.toXML(writer, font)
         writer.endtag(tableTag); writer.newline()
@@ -210,7 +208,7 @@
         reader.read(rootless=True)
 
         # XML from object read from XML.
-        writer = XMLWriter(StringIO(), newlinestr='\n')
+        writer = XMLWriter(StringIO())
         writer.begintag(tableTag); writer.newline()
         font2[tableTag].toXML(writer, font)
         writer.endtag(tableTag); writer.newline()
diff --git a/Tests/otlLib/__init__.py b/Tests/otlLib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/otlLib/__init__.py
diff --git a/Tests/otlLib/builder_test.py b/Tests/otlLib/builder_test.py
index 63a35d6..1c2c324 100644
--- a/Tests/otlLib/builder_test.py
+++ b/Tests/otlLib/builder_test.py
@@ -1,1016 +1,1078 @@
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
+import io
+import struct
+from fontTools.misc.fixedTools import floatToFixed
 from fontTools.misc.testTools import getXML
-from fontTools.otlLib import builder
+from fontTools.otlLib import builder, error
+from fontTools import ttLib
 from fontTools.ttLib.tables import otTables
-from itertools import chain
-import unittest
+import pytest
 
 
-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()
+class BuilderTest(object):
+    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>'])
+        assert 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>'])
+        assert 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,
+            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>'])
+            deviceY=builder.buildDevice({7: 7}),
+        )
+        assert 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>'])
+        attachList = builder.buildAttachList(
+            {"zero": [23, 7], "one": [1]}, self.GLYPHMAP
+        )
+        assert 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))
+        assert builder.buildAttachList({}, self.GLYPHMAP) is None
 
     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>'])
+        assert 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([]))
+        assert builder.buildAttachPoint([]) is None
 
     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>'])
-
+        assert 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>'])
+        baseArray = builder.buildBaseArray(
+            {"a": {2: anchor(300, 80)}, "c": {1: anchor(300, 80), 2: anchor(300, -20)}},
+            numMarkClasses=4,
+            glyphMap=self.GLYPHMAP,
+        )
+        assert 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>'])
+        assert 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>'])
+        assert 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>'])
+        assert 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>'])
+        assert 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([]))
+        assert builder.buildComponentRecord([]) is None
 
     def test_buildComponentRecord_None(self):
-        self.assertIsNone(builder.buildComponentRecord(None))
+        assert builder.buildComponentRecord(None) is 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>'])
+        assert 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>'])
+        pos = builder.buildCursivePosSubtable(
+            {"two": (self.ANCHOR1, self.ANCHOR2), "four": (self.ANCHOR3, self.ANCHOR1)},
+            self.GLYPHMAP,
+        )
+        assert 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>'])
+        device = builder.buildDevice({1: 1, 0: 0})
+        assert 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>'])
+        device = builder.buildDevice({2: 2, 0: 1, 1: 0})
+        assert 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>'])
+        device = builder.buildDevice({5: 3, 1: 77})
+        assert 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>'])
+        ligatureArray = builder.buildLigatureArray(
+            {
+                "f_i": [{2: anchor(300, -20)}, {}],
+                "c_t": [{}, {1: anchor(500, 350), 2: anchor(1300, -20)}],
+            },
+            numMarkClasses=4,
+            glyphMap=self.GLYPHMAP,
+        )
+        assert 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>'])
+        attach = builder.buildLigatureAttach(
+            [[anchor(500, -10), None], [None, anchor(300, -20), None]]
+        )
+        assert 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>'])
+        assert 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>'])
+        assert 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>'])
+            {"f_f_i": [300, 600]}, {"c_t": [42]}, self.GLYPHMAP
+        )
+        assert 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>'])
+            {"f_f_i": [300]}, {"f_f_i": [7]}, self.GLYPHMAP
+        )
+        assert 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))
+        assert builder.buildLigCaretList({}, {}, self.GLYPHMAP) is None
 
     def test_buildLigCaretList_None(self):
-        self.assertIsNone(builder.buildLigCaretList(None, None, self.GLYPHMAP))
+        assert builder.buildLigCaretList(None, None, self.GLYPHMAP) is None
 
     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>'])
+        assert 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([], []))
+        assert builder.buildLigGlyph([], []) is None
 
     def test_buildLigGlyph_None(self):
-        self.assertIsNone(builder.buildLigGlyph(None, None))
+        assert builder.buildLigGlyph(None, None) is 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>'])
+        assert 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>'])
+        assert getXML(lookup.toXML) == [
+            "<Lookup>",
+            '  <LookupType value="1"/>',
+            '  <LookupFlag value="7"/><!-- rightToLeft ignoreBaseGlyphs ignoreLigatures -->',
+            "  <!-- 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)
+        with pytest.raises(
+            AssertionError,
+            match=(
+                "if markFilterSet is None, flags must not set "
+                "LOOKUP_FLAG_USE_MARK_FILTERING_SET; flags=0x0010"
+            ),
+        ) as excinfo:
+            builder.buildLookup([s], builder.LOOKUP_FLAG_USE_MARK_FILTERING_SET, None)
 
     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])
+        with pytest.raises(
+            AssertionError, match="all subtables must have the same LookupType"
+        ) as excinfo:
+            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]))
+        assert builder.buildLookup([]) is None
+        assert builder.buildLookup(None) is None
+        assert builder.buildLookup([None]) is None
+        assert builder.buildLookup([None, None]) is 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)
+        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>'])
+        assert getXML(lookup.toXML) == [
+            "<Lookup>",
+            '  <LookupType value="1"/>',
+            '  <LookupFlag value="17"/><!-- rightToLeft useMarkFilteringSet -->',
+            "  <!-- 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>'])
+        markArray = builder.buildMarkArray(
+            {
+                "acute": (7, builder.buildAnchor(300, 800)),
+                "grave": (2, builder.buildAnchor(10, 80)),
+            },
+            self.GLYPHMAP,
+        )
+        assert self.GLYPHMAP["grave"] < self.GLYPHMAP["acute"]
+        assert 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))
+            "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)}
+            "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>'])
+        assert 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>'])
+            [{"acute", "grave"}, {"cedilla", "grave"}], self.GLYPHMAP
+        )
+        assert 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))
+        assert builder.buildMarkGlyphSetsDef([], self.GLYPHMAP) is None
 
     def test_buildMarkGlyphSetsDef_None(self):
-        self.assertIsNone(builder.buildMarkGlyphSetsDef(None, self.GLYPHMAP))
+        assert builder.buildMarkGlyphSetsDef(None, self.GLYPHMAP) is None
 
     def test_buildMarkLigPosSubtable(self):
         anchor = builder.buildAnchor
         marks = {
             "acute": (0, anchor(300, 700)),
             "cedilla": (1, anchor(300, -100)),
-            "grave": (0, anchor(300, 700))
+            "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
-            ]
+                {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>'])
+        assert 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>'])
+        assert 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>'])
+        assert 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>'])
+        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,
+        )
+        assert 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">',
+            '      <Value1 XPlacement="0" YPlacement="0"/>',
+            '      <Value2 XPlacement="0"/>',
+            "    </Class2Record>",
+            '    <Class2Record index="1">',
+            '      <Value1 XPlacement="0" YPlacement="0"/>',
+            '      <Value2 XPlacement="0"/>',
+            "    </Class2Record>",
+            '    <Class2Record index="2">',
+            '      <Value1 XPlacement="-80" YPlacement="-20"/>',
+            '      <Value2 XPlacement="-50"/>',
+            "    </Class2Record>",
+            "  </Class1Record>",
+            '  <Class1Record index="1">',
+            '    <Class2Record index="0">',
+            '      <Value1 XPlacement="0" YPlacement="0"/>',
+            '      <Value2 XPlacement="0"/>',
+            "    </Class2Record>",
+            '    <Class2Record index="1">',
+            '      <Value1 XPlacement="0" YPlacement="0"/>',
+            '      <Value2 XPlacement="-20"/>',
+            "    </Class2Record>",
+            '    <Class2Record index="2">',
+            '      <Value1 XPlacement="0" YPlacement="0"/>',
+            '      <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>'])
+        subtables = builder.buildPairPosGlyphs(
+            {("A", "zero"): (None, d50), ("A", "one"): (d8020, d50)}, self.GLYPHMAP
+        )
+        assert 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>'])
+        subtable = builder.buildPairPosGlyphsSubtable(
+            {
+                ("A", "zero"): (d0, d50),
+                ("A", "one"): (None, d20),
+                ("B", "five"): (d8020, d50),
+
+            },
+            self.GLYPHMAP,
+        )
+
+        assert 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"/>',
+            '      <Value1 XPlacement="0" YPlacement="0"/>',
+            '      <Value2 XPlacement="-50"/>',
+            "    </PairValueRecord>",
+            '    <PairValueRecord index="1">',
+            '      <SecondGlyph value="one"/>',
+            '      <Value1 XPlacement="0" YPlacement="0"/>',
+            '      <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>'])
+        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,
+        )
+        assert sum([getXML(t.toXML) for t in subtables], []) == [
+            '<SinglePos Format="2">',
+            "  <Coverage>",
+            '    <Glyph value="one"/>',
+            '    <Glyph value="two"/>',
+            '    <Glyph value="three"/>',
+            '    <Glyph value="four"/>',
+            '    <Glyph value="five"/>',
+            "  </Coverage>",
+            '  <ValueFormat value="1"/>',
+            "  <!-- ValueCount=5 -->",
+            '  <Value index="0" XPlacement="500"/>',
+            '  <Value index="1" XPlacement="500"/>',
+            '  <Value index="2" XPlacement="200"/>',
+            '  <Value index="3" XPlacement="400"/>',
+            '  <Value index="4" XPlacement="500"/>',
+            "</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>'])
+        subtables = builder.buildSinglePos(
+            {"zero": builder.buildValue({})}, self.GLYPHMAP
+        )
+        assert 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>'])
+        subtable = builder.buildSinglePosSubtable(
+            {
+                "one": builder.buildValue({"XPlacement": 777}),
+                "two": builder.buildValue({"XPlacement": 777}),
+            },
+            self.GLYPHMAP,
+        )
+        assert 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>'])
+        subtable = builder.buildSinglePosSubtable(
+            {
+                "one": builder.buildValue({"XPlacement": 777}),
+                "two": builder.buildValue({"YPlacement": -888}),
+            },
+            self.GLYPHMAP,
+        )
+        assert getXML(subtable.toXML) == [
+            '<SinglePos Format="2">',
+            "  <Coverage>",
+            '    <Glyph value="one"/>',
+            '    <Glyph value="two"/>',
+            "  </Coverage>",
+            '  <ValueFormat value="3"/>',
+            "  <!-- ValueCount=2 -->",
+            '  <Value index="0" XPlacement="777" YPlacement="0"/>',
+            '  <Value index="1" XPlacement="0" 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"/>'])
+        assert 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"))
+        assert c == components("fff ffi ff fi fl")
 
     def test_getSinglePosValueKey(self):
-        device = builder.buildDevice({10:1, 11:3})
+        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))
+        assert keyA1 == keyA2
+        assert hash(keyA1) == hash(keyA2)
+        assert keyA1 != keyB
+        assert hash(keyA1) != hash(keyB)
 
 
-class ClassDefBuilderTest(unittest.TestCase):
+class ClassDefBuilderTest(object):
     def test_build_usingClass0(self):
         b = builder.ClassDefBuilder(useClass0=True)
         b.add({"aa", "bb"})
@@ -1018,14 +1080,8 @@
         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
-        })
+        assert isinstance(cdef, otTables.ClassDef)
+        assert cdef.classDefs == {"a": 2, "b": 2, "c": 3, "aa": 1, "bb": 1}
 
     def test_build_notUsingClass0(self):
         b = builder.ClassDefBuilder(useClass0=False)
@@ -1033,30 +1089,378 @@
         b.add({"c"})
         b.add({"e", "f", "g", "h"})
         cdef = b.build()
-        self.assertIsInstance(cdef, otTables.ClassDef)
-        self.assertEqual(cdef.classDefs, {
+        assert isinstance(cdef, otTables.ClassDef)
+        assert cdef.classDefs == {
             "a": 2,
             "b": 2,
             "c": 3,
             "e": 1,
             "f": 1,
             "g": 1,
-            "h": 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"}))
+        assert b.canAdd({"a", "b", "c", "d"})
+        assert b.canAdd({"e", "f"})
+        assert b.canAdd({"g", "h", "i"})
+        assert not b.canAdd({"b", "c", "d"})
+        assert not b.canAdd({"a", "b", "c", "d", "e", "f"})
+        assert not b.canAdd({"d", "e", "f"})
+        assert not b.canAdd({"f"})
+
+    def test_add_exception(self):
+        b = builder.ClassDefBuilder(useClass0=True)
+        b.add({"a", "b", "c"})
+        with pytest.raises(error.OpenTypeLibError):
+            b.add({"a", "d"})
+
+
+buildStatTable_test_data = [
+    ([
+        dict(
+            tag="wght",
+            name="Weight",
+            values=[
+                dict(value=100, name='Thin'),
+                dict(value=400, name='Regular', flags=0x2),
+                dict(value=900, name='Black')])], None, "Regular", [
+        '  <STAT>',
+        '    <Version value="0x00010001"/>',
+        '    <DesignAxisRecordSize value="8"/>',
+        '    <!-- DesignAxisCount=1 -->',
+        '    <DesignAxisRecord>',
+        '      <Axis index="0">',
+        '        <AxisTag value="wght"/>',
+        '        <AxisNameID value="257"/>  <!-- Weight -->',
+        '        <AxisOrdering value="0"/>',
+        '      </Axis>',
+        '    </DesignAxisRecord>',
+        '    <!-- AxisValueCount=3 -->',
+        '    <AxisValueArray>',
+        '      <AxisValue index="0" Format="1">',
+        '        <AxisIndex value="0"/>',
+        '        <Flags value="0"/>',
+        '        <ValueNameID value="258"/>  <!-- Thin -->',
+        '        <Value value="100.0"/>',
+        '      </AxisValue>',
+        '      <AxisValue index="1" Format="1">',
+        '        <AxisIndex value="0"/>',
+        '        <Flags value="2"/>  <!-- ElidableAxisValueName -->',
+        '        <ValueNameID value="256"/>  <!-- Regular -->',
+        '        <Value value="400.0"/>',
+        '      </AxisValue>',
+        '      <AxisValue index="2" Format="1">',
+        '        <AxisIndex value="0"/>',
+        '        <Flags value="0"/>',
+        '        <ValueNameID value="259"/>  <!-- Black -->',
+        '        <Value value="900.0"/>',
+        '      </AxisValue>',
+        '    </AxisValueArray>',
+        '    <ElidedFallbackNameID value="256"/>  <!-- Regular -->',
+        '  </STAT>']),
+    ([
+        dict(
+            tag="wght",
+            name=dict(en="Weight", nl="Gewicht"),
+            values=[
+                dict(value=100, name=dict(en='Thin', nl='Dun')),
+                dict(value=400, name='Regular', flags=0x2),
+                dict(value=900, name='Black'),
+            ]),
+        dict(
+            tag="wdth",
+            name="Width",
+            values=[
+                dict(value=50, name='Condensed'),
+                dict(value=100, name='Regular', flags=0x2),
+                dict(value=200, name='Extended')])], None, 2, [
+        '  <STAT>',
+        '    <Version value="0x00010001"/>',
+        '    <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="wdth"/>',
+        '        <AxisNameID value="260"/>  <!-- Width -->',
+        '        <AxisOrdering value="1"/>',
+        '      </Axis>',
+        '    </DesignAxisRecord>',
+        '    <!-- AxisValueCount=6 -->',
+        '    <AxisValueArray>',
+        '      <AxisValue index="0" Format="1">',
+        '        <AxisIndex value="0"/>',
+        '        <Flags value="0"/>',
+        '        <ValueNameID value="257"/>  <!-- Thin -->',
+        '        <Value value="100.0"/>',
+        '      </AxisValue>',
+        '      <AxisValue index="1" Format="1">',
+        '        <AxisIndex value="0"/>',
+        '        <Flags value="2"/>  <!-- ElidableAxisValueName -->',
+        '        <ValueNameID value="258"/>  <!-- Regular -->',
+        '        <Value value="400.0"/>',
+        '      </AxisValue>',
+        '      <AxisValue index="2" Format="1">',
+        '        <AxisIndex value="0"/>',
+        '        <Flags value="0"/>',
+        '        <ValueNameID value="259"/>  <!-- Black -->',
+        '        <Value value="900.0"/>',
+        '      </AxisValue>',
+        '      <AxisValue index="3" Format="1">',
+        '        <AxisIndex value="1"/>',
+        '        <Flags value="0"/>',
+        '        <ValueNameID value="261"/>  <!-- Condensed -->',
+        '        <Value value="50.0"/>',
+        '      </AxisValue>',
+        '      <AxisValue index="4" Format="1">',
+        '        <AxisIndex value="1"/>',
+        '        <Flags value="2"/>  <!-- ElidableAxisValueName -->',
+        '        <ValueNameID value="258"/>  <!-- Regular -->',
+        '        <Value value="100.0"/>',
+        '      </AxisValue>',
+        '      <AxisValue index="5" Format="1">',
+        '        <AxisIndex value="1"/>',
+        '        <Flags value="0"/>',
+        '        <ValueNameID value="262"/>  <!-- Extended -->',
+        '        <Value value="200.0"/>',
+        '      </AxisValue>',
+        '    </AxisValueArray>',
+        '    <ElidedFallbackNameID value="2"/>  <!-- missing from name table -->',
+        '  </STAT>']),
+    ([
+        dict(
+            tag="wght",
+            name="Weight",
+            values=[
+                dict(value=400, name='Regular', flags=0x2),
+                dict(value=600, linkedValue=650, name='Bold')])], None, 18, [
+        '  <STAT>',
+        '    <Version value="0x00010001"/>',
+        '    <DesignAxisRecordSize value="8"/>',
+        '    <!-- DesignAxisCount=1 -->',
+        '    <DesignAxisRecord>',
+        '      <Axis index="0">',
+        '        <AxisTag value="wght"/>',
+        '        <AxisNameID value="256"/>  <!-- Weight -->',
+        '        <AxisOrdering value="0"/>',
+        '      </Axis>',
+        '    </DesignAxisRecord>',
+        '    <!-- AxisValueCount=2 -->',
+        '    <AxisValueArray>',
+        '      <AxisValue index="0" Format="1">',
+        '        <AxisIndex value="0"/>',
+        '        <Flags value="2"/>  <!-- ElidableAxisValueName -->',
+        '        <ValueNameID value="257"/>  <!-- Regular -->',
+        '        <Value value="400.0"/>',
+        '      </AxisValue>',
+        '      <AxisValue index="1" Format="3">',
+        '        <AxisIndex value="0"/>',
+        '        <Flags value="0"/>',
+        '        <ValueNameID value="258"/>  <!-- Bold -->',
+        '        <Value value="600.0"/>',
+        '        <LinkedValue value="650.0"/>',
+        '      </AxisValue>',
+        '    </AxisValueArray>',
+        '    <ElidedFallbackNameID value="18"/>  <!-- missing from name table -->',
+        '  </STAT>']),
+    ([
+        dict(
+            tag="opsz",
+            name="Optical Size",
+            values=[
+                dict(nominalValue=6, rangeMaxValue=10, name='Small'),
+                dict(rangeMinValue=10, nominalValue=14, rangeMaxValue=24, name='Text', flags=0x2),
+                dict(rangeMinValue=24, nominalValue=600, name='Display')])], None, 2, [
+        '  <STAT>',
+        '    <Version value="0x00010001"/>',
+        '    <DesignAxisRecordSize value="8"/>',
+        '    <!-- DesignAxisCount=1 -->',
+        '    <DesignAxisRecord>',
+        '      <Axis index="0">',
+        '        <AxisTag value="opsz"/>',
+        '        <AxisNameID value="256"/>  <!-- Optical Size -->',
+        '        <AxisOrdering value="0"/>',
+        '      </Axis>',
+        '    </DesignAxisRecord>',
+        '    <!-- AxisValueCount=3 -->',
+        '    <AxisValueArray>',
+        '      <AxisValue index="0" Format="2">',
+        '        <AxisIndex value="0"/>',
+        '        <Flags value="0"/>',
+        '        <ValueNameID value="257"/>  <!-- Small -->',
+        '        <NominalValue value="6.0"/>',
+        '        <RangeMinValue value="-32768.0"/>',
+        '        <RangeMaxValue value="10.0"/>',
+        '      </AxisValue>',
+        '      <AxisValue index="1" Format="2">',
+        '        <AxisIndex value="0"/>',
+        '        <Flags value="2"/>  <!-- ElidableAxisValueName -->',
+        '        <ValueNameID value="258"/>  <!-- Text -->',
+        '        <NominalValue value="14.0"/>',
+        '        <RangeMinValue value="10.0"/>',
+        '        <RangeMaxValue value="24.0"/>',
+        '      </AxisValue>',
+        '      <AxisValue index="2" Format="2">',
+        '        <AxisIndex value="0"/>',
+        '        <Flags value="0"/>',
+        '        <ValueNameID value="259"/>  <!-- Display -->',
+        '        <NominalValue value="600.0"/>',
+        '        <RangeMinValue value="24.0"/>',
+        '        <RangeMaxValue value="32767.99998"/>',
+        '      </AxisValue>',
+        '    </AxisValueArray>',
+        '    <ElidedFallbackNameID value="2"/>  <!-- missing from name table -->',
+        '  </STAT>']),
+    ([
+        dict(
+            tag="wght",
+            name="Weight",
+            ordering=1,
+            values=[]),
+        dict(
+            tag="ABCD",
+            name="ABCDTest",
+            ordering=0,
+            values=[
+                dict(value=100, name="Regular", flags=0x2)])],
+     [dict(location=dict(wght=300, ABCD=100), name='Regular ABCD')], 18, [
+        '  <STAT>',
+        '    <Version value="0x00010002"/>',
+        '    <DesignAxisRecordSize value="8"/>',
+        '    <!-- DesignAxisCount=2 -->',
+        '    <DesignAxisRecord>',
+        '      <Axis index="0">',
+        '        <AxisTag value="wght"/>',
+        '        <AxisNameID value="256"/>  <!-- Weight -->',
+        '        <AxisOrdering value="1"/>',
+        '      </Axis>',
+        '      <Axis index="1">',
+        '        <AxisTag value="ABCD"/>',
+        '        <AxisNameID value="257"/>  <!-- ABCDTest -->',
+        '        <AxisOrdering value="0"/>',
+        '      </Axis>',
+        '    </DesignAxisRecord>',
+        '    <!-- AxisValueCount=2 -->',
+        '    <AxisValueArray>',
+        '      <AxisValue index="0" Format="4">',
+        '        <!-- AxisCount=2 -->',
+        '        <Flags value="0"/>',
+        '        <ValueNameID value="259"/>  <!-- Regular ABCD -->',
+        '        <AxisValueRecord index="0">',
+        '          <AxisIndex value="0"/>',
+        '          <Value value="300.0"/>',
+        '        </AxisValueRecord>',
+        '        <AxisValueRecord index="1">',
+        '          <AxisIndex value="1"/>',
+        '          <Value value="100.0"/>',
+        '        </AxisValueRecord>',
+        '      </AxisValue>',
+        '      <AxisValue index="1" Format="1">',
+        '        <AxisIndex value="1"/>',
+        '        <Flags value="2"/>  <!-- ElidableAxisValueName -->',
+        '        <ValueNameID value="258"/>  <!-- Regular -->',
+        '        <Value value="100.0"/>',
+        '      </AxisValue>',
+        '    </AxisValueArray>',
+        '    <ElidedFallbackNameID value="18"/>  <!-- missing from name table -->',
+        '  </STAT>']),
+]
+
+
+@pytest.mark.parametrize("axes, axisValues, elidedFallbackName, expected_ttx", buildStatTable_test_data)
+def test_buildStatTable(axes, axisValues, elidedFallbackName, expected_ttx):
+    font = ttLib.TTFont()
+    font["name"] = ttLib.newTable("name")
+    font["name"].names = []
+    # https://github.com/fonttools/fonttools/issues/1985
+    # Add nameID < 256 that matches a test axis name, to test whether
+    # the nameID is not reused: AxisNameIDs must be > 255 according
+    # to the spec.
+    font["name"].addMultilingualName(dict(en="ABCDTest"), nameID=6)
+    builder.buildStatTable(font, axes, axisValues, elidedFallbackName)
+    f = io.StringIO()
+    font.saveXML(f, tables=["STAT"])
+    ttx = f.getvalue().splitlines()
+    ttx = ttx[3:-2]  # strip XML header and <ttFont> element
+    assert expected_ttx == ttx
+    # Compile and round-trip
+    f = io.BytesIO()
+    font.save(f)
+    font = ttLib.TTFont(f)
+    f = io.StringIO()
+    font.saveXML(f, tables=["STAT"])
+    ttx = f.getvalue().splitlines()
+    ttx = ttx[3:-2]  # strip XML header and <ttFont> element
+    assert expected_ttx == ttx
+
+
+def test_stat_infinities():
+    negInf = floatToFixed(builder.AXIS_VALUE_NEGATIVE_INFINITY, 16)
+    assert struct.pack(">l", negInf) == b"\x80\x00\x00\x00"
+    posInf = floatToFixed(builder.AXIS_VALUE_POSITIVE_INFINITY, 16)
+    assert struct.pack(">l", posInf) == b"\x7f\xff\xff\xff"
+
+
+class ChainContextualRulesetTest(object):
+    def test_makeRulesets(self):
+        font = ttLib.TTFont()
+        font.setGlyphOrder(["a","b","c","d","A","B","C","D","E"])
+        sb = builder.ChainContextSubstBuilder(font, None)
+        prefix, input_, suffix, lookups = [["a"], ["b"]], [["c"]], [], [None]
+        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
+
+        prefix, input_, suffix, lookups = [["a"], ["d"]], [["c"]], [], [None]
+        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
+
+        sb.add_subtable_break(None)
+
+        # Second subtable has some glyph classes
+        prefix, input_, suffix, lookups = [["A"]], [["E"]], [], [None]
+        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
+        prefix, input_, suffix, lookups = [["A"]], [["C","D"]], [], [None]
+        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
+        prefix, input_, suffix, lookups = [["A", "B"]], [["E"]], [], [None]
+        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
+
+        sb.add_subtable_break(None)
+
+        # Third subtable has no pre/post context
+        prefix, input_, suffix, lookups = [], [["E"]], [], [None]
+        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
+        prefix, input_, suffix, lookups = [], [["C","D"]], [], [None]
+        sb.rules.append(builder.ChainContextualRule(prefix, input_, suffix, lookups))
+
+        rulesets = sb.rulesets()
+        assert len(rulesets) == 3
+        assert rulesets[0].hasPrefixOrSuffix
+        assert not rulesets[0].hasAnyGlyphClasses
+        cd = rulesets[0].format2ClassDefs()
+        assert set(cd[0].classes()[1:]) == set([("d",),("b",),("a",)])
+        assert set(cd[1].classes()[1:]) == set([("c",)])
+        assert set(cd[2].classes()[1:]) == set()
+
+        assert rulesets[1].hasPrefixOrSuffix
+        assert rulesets[1].hasAnyGlyphClasses
+        assert not rulesets[1].format2ClassDefs()
+
+        assert not rulesets[2].hasPrefixOrSuffix
+        assert rulesets[2].hasAnyGlyphClasses
+        assert rulesets[2].format2ClassDefs()
+        cd = rulesets[2].format2ClassDefs()
+        assert set(cd[0].classes()[1:]) == set()
+        assert set(cd[1].classes()[1:]) == set([("C","D"), ("E",)])
+        assert set(cd[2].classes()[1:]) == set()
 
 
 if __name__ == "__main__":
     import sys
-    sys.exit(unittest.main())
+
+    sys.exit(pytest.main(sys.argv))
diff --git a/Tests/otlLib/data/gpos_91.ttx b/Tests/otlLib/data/gpos_91.ttx
new file mode 100644
index 0000000..7befe7b
--- /dev/null
+++ b/Tests/otlLib/data/gpos_91.ttx
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.40">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+  </GlyphOrder>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="2"/>
+    <maxPoints value="0"/>
+    <maxContours value="0"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <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>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <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="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>
+              <Glyph value="A"/>
+            </Coverage>
+            <ValueFormat value="4"/>
+            <Value XAdvance="20"/>
+          </SinglePos>
+        </ExtensionPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/otlLib/data/gsub_51.ttx b/Tests/otlLib/data/gsub_51.ttx
new file mode 100644
index 0000000..0e6a413
--- /dev/null
+++ b/Tests/otlLib/data/gsub_51.ttx
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.40">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="g20"/>
+    <GlyphID id="2" name="g21"/>
+    <GlyphID id="3" name="g22"/>
+    <GlyphID id="4" name="g60"/>
+    <GlyphID id="5" name="g61"/>
+    <GlyphID id="6" name="g62"/>
+    <GlyphID id="7" name="g63"/>
+  </GlyphOrder>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="0"/>
+    <maxContours value="0"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <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>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <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>
+
+  <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">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <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">
+          <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>
+            <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/otlLib/data/gsub_52.ttx b/Tests/otlLib/data/gsub_52.ttx
new file mode 100644
index 0000000..4e83b96
--- /dev/null
+++ b/Tests/otlLib/data/gsub_52.ttx
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.40">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="g20"/>
+    <GlyphID id="2" name="g21"/>
+    <GlyphID id="3" name="g22"/>
+    <GlyphID id="4" name="g60"/>
+    <GlyphID id="5" name="g61"/>
+    <GlyphID id="6" name="g62"/>
+    <GlyphID id="7" name="g63"/>
+  </GlyphOrder>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="0"/>
+    <maxContours value="0"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="2"/>
+    <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>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <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>
+
+  <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">
+          <Substitution in="g20" out="g60"/>
+          <Substitution in="g21" out="g61"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="g21">
+            <Ligature components="g22" glyph="g61"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="4"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <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">
+          <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>
+            <Glyph value="g20"/>
+          </Coverage>
+          <ClassDef>
+            <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/otlLib/data/gsub_71.ttx b/Tests/otlLib/data/gsub_71.ttx
new file mode 100644
index 0000000..acf6cdb
--- /dev/null
+++ b/Tests/otlLib/data/gsub_71.ttx
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.40">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="g18"/>
+    <GlyphID id="2" name="g19"/>
+    <GlyphID id="3" name="g23"/>
+    <GlyphID id="4" name="g24"/>
+  </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="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <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>
+
+  <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>
+            <Substitution in="g18" out="g23"/>
+            <Substitution in="g19" out="g24"/>
+          </SingleSubst>
+        </ExtensionSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/otlLib/maxContextCalc_test.py b/Tests/otlLib/maxContextCalc_test.py
new file mode 100644
index 0000000..dc169c6
--- /dev/null
+++ b/Tests/otlLib/maxContextCalc_test.py
@@ -0,0 +1,74 @@
+
+import os
+import pytest
+from fontTools.ttLib import TTFont
+from fontTools.otlLib.maxContextCalc import maxCtxFont
+from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
+
+
+def test_max_ctx_calc_no_features():
+    font = TTFont()
+    assert maxCtxFont(font) == 0
+    font.setGlyphOrder(['.notdef'])
+    addOpenTypeFeaturesFromString(font, '')
+    assert maxCtxFont(font) == 0
+
+
+def test_max_ctx_calc_features():
+    glyphs = '.notdef space A B C a b c'.split()
+    features = """
+    lookup GSUB_EXT useExtension {
+        sub a by b;
+    } GSUB_EXT;
+
+    lookup GPOS_EXT useExtension {
+        pos a b -10;
+    } GPOS_EXT;
+
+    feature sub1 {
+        sub A by a;
+        sub A B by b;
+        sub A B C by c;
+        sub [A B] C by c;
+        sub [A B] C [A B] by c;
+        sub A by A B;
+        sub A' C by A B;
+        sub a' by b;
+        sub a' b by c;
+        sub a from [A B C];
+        rsub a by b;
+        rsub a' by b;
+        rsub a b' by c;
+        rsub a b' c by A;
+        rsub [a b] c' by A;
+        rsub [a b] c' [a b] by B;
+        lookup GSUB_EXT;
+    } sub1;
+
+    feature pos1 {
+        pos A 20;
+        pos A B -50;
+        pos A B' 10 C;
+        lookup GPOS_EXT;
+    } pos1;
+    """
+    font = TTFont()
+    font.setGlyphOrder(glyphs)
+    addOpenTypeFeaturesFromString(font, features)
+
+    assert maxCtxFont(font) == 3
+
+
+@pytest.mark.parametrize('file_name, max_context', [
+    ('gsub_51', 2),
+    ('gsub_52', 2),
+    ('gsub_71', 1),
+    ('gpos_91', 1),
+])
+def test_max_ctx_calc_features_ttx(file_name, max_context):
+    ttx_path = os.path.join(os.path.dirname(__file__),
+                            'data', '{}.ttx'.format(file_name))
+    font = TTFont()
+    font.importXML(ttx_path)
+
+    assert maxCtxFont(font) == max_context
diff --git a/Tests/otlLib/mock_builder_test.py b/Tests/otlLib/mock_builder_test.py
new file mode 100644
index 0000000..b3fecd8
--- /dev/null
+++ b/Tests/otlLib/mock_builder_test.py
@@ -0,0 +1,86 @@
+from fontTools.otlLib.builder import (
+    AlternateSubstBuilder,
+    ChainContextPosBuilder,
+    ChainContextSubstBuilder,
+    LigatureSubstBuilder,
+    MultipleSubstBuilder,
+    CursivePosBuilder,
+    MarkBasePosBuilder,
+    MarkLigPosBuilder,
+    MarkMarkPosBuilder,
+    ReverseChainSingleSubstBuilder,
+    SingleSubstBuilder,
+    ClassPairPosSubtableBuilder,
+    PairPosBuilder,
+    SinglePosBuilder,
+    ChainContextualRule
+)
+from fontTools.otlLib.error import OpenTypeLibError
+from fontTools.ttLib import TTFont
+from fontTools.misc.loggingTools import CapturingLogHandler
+import logging
+import pytest
+
+
+@pytest.fixture
+def ttfont():
+    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 uni0327 uni0328 e.fina
+    """.split()
+    glyphs.extend("cid{:05d}".format(cid) for cid in range(800, 1001 + 1))
+    font = TTFont()
+    font.setGlyphOrder(glyphs)
+    return font
+
+
+class MockBuilderLocation(object):
+    def __init__(self, location):
+        self.location = location
+
+    def __str__(self):
+        return "%s:%s" % self.location
+
+
+def test_unsupported_subtable_break_1(ttfont):
+    location = MockBuilderLocation((0, "alpha"))
+
+    logger = logging.getLogger("fontTools.otlLib.builder")
+
+    with CapturingLogHandler(logger, "INFO") as captor:
+        builder = SinglePosBuilder(ttfont, location)
+        builder.add_subtable_break(MockBuilderLocation((5, "beta")))
+        builder.build()
+
+    captor.assertRegex('5:beta: unsupported "subtable" statement for lookup type')
+
+def test_chain_pos_references_GSUB_lookup(ttfont):
+    location = MockBuilderLocation((0, "alpha"))
+    builder = ChainContextPosBuilder(ttfont, location)
+    builder2 = SingleSubstBuilder(ttfont, location)
+    builder.rules.append(ChainContextualRule([], [], [], [[builder2]]))
+
+    with pytest.raises(OpenTypeLibError, match="0:alpha: Missing index of the specified lookup, might be a substitution lookup"):
+        builder.build()
diff --git a/Tests/otlLib/optimize_test.py b/Tests/otlLib/optimize_test.py
new file mode 100644
index 0000000..40cf389
--- /dev/null
+++ b/Tests/otlLib/optimize_test.py
@@ -0,0 +1,175 @@
+import logging
+from pathlib import Path
+from subprocess import run
+import contextlib
+import os
+from typing import List, Optional, Tuple
+from fontTools.ttLib import TTFont
+
+import pytest
+
+from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
+from fontTools.fontBuilder import FontBuilder
+
+from fontTools.ttLib.tables.otBase import OTTableWriter, ValueRecord
+
+
+def test_main(tmpdir: Path):
+    """Check that calling the main function on an input TTF works."""
+    glyphs = ".notdef space A Aacute B D".split()
+    features = """
+    @A = [A Aacute];
+    @B = [B D];
+    feature kern {
+        pos @A @B -50;
+    } kern;
+    """
+    fb = FontBuilder(1000)
+    fb.setupGlyphOrder(glyphs)
+    addOpenTypeFeaturesFromString(fb.font, features)
+    input = tmpdir / "in.ttf"
+    fb.save(str(input))
+    output = tmpdir / "out.ttf"
+    run(
+        [
+            "fonttools",
+            "otlLib.optimize",
+            "--gpos-compact-mode",
+            "5",
+            str(input),
+            "-o",
+            str(output),
+        ],
+        check=True,
+    )
+    assert output.exists()
+
+
+# Copy-pasted from https://stackoverflow.com/questions/2059482/python-temporarily-modify-the-current-processs-environment
+# TODO: remove when moving to the Config class
+@contextlib.contextmanager
+def set_env(**environ):
+    """
+    Temporarily set the process environment variables.
+
+    >>> with set_env(PLUGINS_DIR=u'test/plugins'):
+    ...   "PLUGINS_DIR" in os.environ
+    True
+
+    >>> "PLUGINS_DIR" in os.environ
+    False
+
+    :type environ: dict[str, unicode]
+    :param environ: Environment variables to set
+    """
+    old_environ = dict(os.environ)
+    os.environ.update(environ)
+    try:
+        yield
+    finally:
+        os.environ.clear()
+        os.environ.update(old_environ)
+
+
+def count_pairpos_subtables(font: TTFont) -> int:
+    subtables = 0
+    for lookup in font["GPOS"].table.LookupList.Lookup:
+        if lookup.LookupType == 2:
+            subtables += len(lookup.SubTable)
+        elif lookup.LookupType == 9:
+            for subtable in lookup.SubTable:
+                if subtable.ExtensionLookupType == 2:
+                    subtables += 1
+    return subtables
+
+
+def count_pairpos_bytes(font: TTFont) -> int:
+    bytes = 0
+    gpos = font["GPOS"]
+    for lookup in font["GPOS"].table.LookupList.Lookup:
+        if lookup.LookupType == 2:
+            w = OTTableWriter(tableTag=gpos.tableTag)
+            lookup.compile(w, font)
+            bytes += len(w.getAllData())
+        elif lookup.LookupType == 9:
+            if any(subtable.ExtensionLookupType == 2 for subtable in lookup.SubTable):
+                w = OTTableWriter(tableTag=gpos.tableTag)
+                lookup.compile(w, font)
+                bytes += len(w.getAllData())
+    return bytes
+
+
+def get_kerning_by_blocks(blocks: List[Tuple[int, int]]) -> Tuple[List[str], str]:
+    """Generate a highly compressible font by generating a bunch of rectangular
+    blocks on the diagonal that can easily be sliced into subtables.
+
+    Returns the list of glyphs and feature code of the font.
+    """
+    value = 0
+    glyphs: List[str] = []
+    rules = []
+    # Each block is like a script in a multi-script font
+    for script, (width, height) in enumerate(blocks):
+        glyphs.extend(f"g_{script}_{i}" for i in range(max(width, height)))
+        for l in range(height):
+            for r in range(width):
+                value += 1
+                rules.append((f"g_{script}_{l}", f"g_{script}_{r}", value))
+    classes = "\n".join([f"@{g} = [{g}];" for g in glyphs])
+    statements = "\n".join([f"pos @{l} @{r} {v};" for (l, r, v) in rules])
+    features = f"""
+        {classes}
+        feature kern {{
+            {statements}
+        }} kern;
+    """
+    return glyphs, features
+
+
+@pytest.mark.parametrize(
+    ("blocks", "mode", "expected_subtables", "expected_bytes"),
+    [
+        # Mode = 0 = no optimization leads to 650 bytes of GPOS
+        ([(15, 3), (2, 10)], None, 1, 602),
+        # Optimization level 1 recognizes the 2 blocks and splits into 2
+        # subtables = adds 1 subtable leading to a size reduction of
+        # (602-298)/602 = 50%
+        ([(15, 3), (2, 10)], 1, 2, 298),
+        # On a bigger block configuration, we see that mode=5 doesn't create
+        # as many subtables as it could, because of the stop criteria
+        ([(4, 4) for _ in range(20)], 5, 14, 2042),
+        # while level=9 creates as many subtables as there were blocks on the
+        # diagonal and yields a better saving
+        ([(4, 4) for _ in range(20)], 9, 20, 1886),
+        # On a fully occupied kerning matrix, even the strategy 9 doesn't
+        # split anything.
+        ([(10, 10)], 9, 1, 304)
+    ],
+)
+def test_optimization_mode(
+    caplog,
+    blocks: List[Tuple[int, int]],
+    mode: Optional[int],
+    expected_subtables: int,
+    expected_bytes: int,
+):
+    """Check that the optimizations are off by default, and that increasing
+    the optimization level creates more subtables and a smaller byte size.
+    """
+    caplog.set_level(logging.DEBUG)
+
+    glyphs, features = get_kerning_by_blocks(blocks)
+    glyphs = [".notdef space"] + glyphs
+
+    env = {}
+    if mode is not None:
+        # NOTE: activating this optimization via the environment variable is
+        # experimental and may not be supported once an alternative mechanism
+        # is in place. See: https://github.com/fonttools/fonttools/issues/2349
+        env["FONTTOOLS_GPOS_COMPACT_MODE"] = str(mode)
+    with set_env(**env):
+        fb = FontBuilder(1000)
+        fb.setupGlyphOrder(glyphs)
+        addOpenTypeFeaturesFromString(fb.font, features)
+        assert expected_subtables == count_pairpos_subtables(fb.font)
+        assert expected_bytes == count_pairpos_bytes(fb.font)
diff --git a/Tests/pens/__init__.py b/Tests/pens/__init__.py
new file mode 100644
index 0000000..187b981
--- /dev/null
+++ b/Tests/pens/__init__.py
@@ -0,0 +1,13 @@
+import os
+from fontTools.ufoLib.glifLib import GlyphSet
+import pkg_resources
+
+DATADIR = os.path.join(os.path.dirname(__file__), 'data')
+CUBIC_GLYPHS = GlyphSet(os.path.join(DATADIR, 'cubic'))
+QUAD_GLYPHS = GlyphSet(os.path.join(DATADIR, 'quadratic'))
+
+import unittest
+# 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
diff --git a/Tests/pens/areaPen_test.py b/Tests/pens/areaPen_test.py
index ae915df..c3f3f80 100644
--- a/Tests/pens/areaPen_test.py
+++ b/Tests/pens/areaPen_test.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.pens.areaPen import AreaPen
 import unittest
 
diff --git a/Tests/pens/basePen_test.py b/Tests/pens/basePen_test.py
index 2a7e43c..db57e80 100644
--- a/Tests/pens/basePen_test.py
+++ b/Tests/pens/basePen_test.py
@@ -1,7 +1,6 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.pens.basePen import \
-    BasePen, decomposeSuperBezierSegment, decomposeQuadraticSegment
+    AbstractPen, BasePen, decomposeSuperBezierSegment, decomposeQuadraticSegment
+from fontTools.pens.pointPen import AbstractPointPen
 from fontTools.misc.loggingTools import CapturingLogHandler
 import unittest
 
diff --git a/Tests/pens/boundsPen_test.py b/Tests/pens/boundsPen_test.py
index 533c575..c0c5610 100644
--- a/Tests/pens/boundsPen_test.py
+++ b/Tests/pens/boundsPen_test.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.pens.boundsPen import BoundsPen, ControlBoundsPen
 import unittest
 
diff --git a/Tests/pens/cocoaPen_test.py b/Tests/pens/cocoaPen_test.py
new file mode 100644
index 0000000..11077c0
--- /dev/null
+++ b/Tests/pens/cocoaPen_test.py
@@ -0,0 +1,58 @@
+import unittest
+
+try:
+    from fontTools.pens.cocoaPen import CocoaPen
+    from AppKit import NSBezierPathElementMoveTo, NSBezierPathElementLineTo
+    from AppKit import NSBezierPathElementCurveTo, NSBezierPathElementClosePath
+
+    PATH_ELEMENTS = {
+        # NSBezierPathElement key      desc
+        NSBezierPathElementMoveTo:    'moveto',
+        NSBezierPathElementLineTo:    'lineto',
+        NSBezierPathElementCurveTo:   'curveto',
+        NSBezierPathElementClosePath: 'close',
+    }
+
+    PYOBJC_AVAILABLE = True
+except ImportError:
+    PYOBJC_AVAILABLE = False
+
+
+def draw(pen):
+    pen.moveTo((50, 0))
+    pen.lineTo((50, 500))
+    pen.lineTo((200, 500))
+    pen.curveTo((350, 500), (450, 400), (450, 250))
+    pen.curveTo((450, 100), (350, 0), (200, 0))
+    pen.closePath()
+
+
+def cocoaPathToString(path):
+    num_elements = path.elementCount()
+    output = []
+    for i in range(num_elements - 1):
+        elem_type, elem_points = path.elementAtIndex_associatedPoints_(i)
+        elem_type = PATH_ELEMENTS[elem_type]
+        path_points = " ".join([f"{p.x} {p.y}" for p in elem_points])
+        output.append(f"{elem_type} {path_points}")
+    return " ".join(output)
+
+
+@unittest.skipUnless(PYOBJC_AVAILABLE, "pyobjc not installed")
+class CocoaPenTest(unittest.TestCase):
+    def test_draw(self):
+        pen = CocoaPen(None)
+        draw(pen)
+        self.assertEqual(
+            "moveto 50.0 0.0 lineto 50.0 500.0 lineto 200.0 500.0 curveto 350.0 500.0 450.0 400.0 450.0 250.0 curveto 450.0 100.0 350.0 0.0 200.0 0.0 close ",
+            cocoaPathToString(pen.path)
+        )
+
+    def test_empty(self):
+        pen = CocoaPen(None)
+        self.assertEqual("", cocoaPathToString(pen.path))
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/pens/cu2quPen_test.py b/Tests/pens/cu2quPen_test.py
new file mode 100644
index 0000000..db51787
--- /dev/null
+++ b/Tests/pens/cu2quPen_test.py
@@ -0,0 +1,390 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+import unittest
+
+from fontTools.pens.cu2quPen import Cu2QuPen, Cu2QuPointPen
+from . import CUBIC_GLYPHS, QUAD_GLYPHS
+from .utils import DummyGlyph, DummyPointGlyph
+from .utils import DummyPen, DummyPointPen
+from fontTools.misc.loggingTools import CapturingLogHandler
+from textwrap import dedent
+import logging
+
+
+MAX_ERR = 1.0
+
+
+class _TestPenMixin(object):
+    """Collection of tests that are shared by both the SegmentPen and the
+    PointPen test cases, plus some helper methods.
+    """
+
+    maxDiff = None
+
+    def diff(self, expected, actual):
+        import difflib
+        expected = str(self.Glyph(expected)).splitlines(True)
+        actual = str(self.Glyph(actual)).splitlines(True)
+        diff = difflib.unified_diff(
+            expected, actual, fromfile='expected', tofile='actual')
+        return "".join(diff)
+
+    def convert_glyph(self, glyph, **kwargs):
+        # draw source glyph onto a new glyph using a Cu2Qu pen and return it
+        converted = self.Glyph()
+        pen = getattr(converted, self.pen_getter_name)()
+        quadpen = self.Cu2QuPen(pen, MAX_ERR, **kwargs)
+        getattr(glyph, self.draw_method_name)(quadpen)
+        return converted
+
+    def expect_glyph(self, source, expected):
+        converted = self.convert_glyph(source)
+        self.assertNotEqual(converted, source)
+        if not converted.approx(expected):
+            print(self.diff(expected, converted))
+            self.fail("converted glyph is different from expected")
+
+    def test_convert_simple_glyph(self):
+        self.expect_glyph(CUBIC_GLYPHS['a'], QUAD_GLYPHS['a'])
+        self.expect_glyph(CUBIC_GLYPHS['A'], QUAD_GLYPHS['A'])
+
+    def test_convert_composite_glyph(self):
+        source = CUBIC_GLYPHS['Aacute']
+        converted = self.convert_glyph(source)
+        # components don't change after quadratic conversion
+        self.assertEqual(converted, source)
+
+    def test_convert_mixed_glyph(self):
+        # this contains a mix of contours and components
+        self.expect_glyph(CUBIC_GLYPHS['Eacute'], QUAD_GLYPHS['Eacute'])
+
+    def test_reverse_direction(self):
+        for name in ('a', 'A', 'Eacute'):
+            source = CUBIC_GLYPHS[name]
+            normal_glyph = self.convert_glyph(source)
+            reversed_glyph = self.convert_glyph(source, reverse_direction=True)
+
+            # the number of commands is the same, just their order is iverted
+            self.assertTrue(
+                len(normal_glyph.outline), len(reversed_glyph.outline))
+            self.assertNotEqual(normal_glyph, reversed_glyph)
+
+    def test_stats(self):
+        stats = {}
+        for name in CUBIC_GLYPHS.keys():
+            source = CUBIC_GLYPHS[name]
+            self.convert_glyph(source, stats=stats)
+
+        self.assertTrue(stats)
+        self.assertTrue('1' in stats)
+        self.assertEqual(type(stats['1']), int)
+
+    def test_addComponent(self):
+        pen = self.Pen()
+        quadpen = self.Cu2QuPen(pen, MAX_ERR)
+        quadpen.addComponent("a", (1, 2, 3, 4, 5.0, 6.0))
+
+        # components are passed through without changes
+        self.assertEqual(str(pen).splitlines(), [
+            "pen.addComponent('a', (1, 2, 3, 4, 5.0, 6.0))",
+        ])
+
+
+class TestCu2QuPen(unittest.TestCase, _TestPenMixin):
+
+    def __init__(self, *args, **kwargs):
+        super(TestCu2QuPen, self).__init__(*args, **kwargs)
+        self.Glyph = DummyGlyph
+        self.Pen = DummyPen
+        self.Cu2QuPen = Cu2QuPen
+        self.pen_getter_name = 'getPen'
+        self.draw_method_name = 'draw'
+
+    def test__check_contour_is_open(self):
+        msg = "moveTo is required"
+        quadpen = Cu2QuPen(DummyPen(), MAX_ERR)
+
+        with self.assertRaisesRegex(AssertionError, msg):
+            quadpen.lineTo((0, 0))
+        with self.assertRaisesRegex(AssertionError, msg):
+            quadpen.qCurveTo((0, 0), (1, 1))
+        with self.assertRaisesRegex(AssertionError, msg):
+            quadpen.curveTo((0, 0), (1, 1), (2, 2))
+        with self.assertRaisesRegex(AssertionError, msg):
+            quadpen.closePath()
+        with self.assertRaisesRegex(AssertionError, msg):
+            quadpen.endPath()
+
+        quadpen.moveTo((0, 0))  # now it works
+        quadpen.lineTo((1, 1))
+        quadpen.qCurveTo((2, 2), (3, 3))
+        quadpen.curveTo((4, 4), (5, 5), (6, 6))
+        quadpen.closePath()
+
+    def test__check_contour_closed(self):
+        msg = "closePath or endPath is required"
+        quadpen = Cu2QuPen(DummyPen(), MAX_ERR)
+        quadpen.moveTo((0, 0))
+
+        with self.assertRaisesRegex(AssertionError, msg):
+            quadpen.moveTo((1, 1))
+        with self.assertRaisesRegex(AssertionError, msg):
+            quadpen.addComponent("a", (1, 0, 0, 1, 0, 0))
+
+        # it works if contour is closed
+        quadpen.closePath()
+        quadpen.moveTo((1, 1))
+        quadpen.endPath()
+        quadpen.addComponent("a", (1, 0, 0, 1, 0, 0))
+
+    def test_qCurveTo_no_points(self):
+        quadpen = Cu2QuPen(DummyPen(), MAX_ERR)
+        quadpen.moveTo((0, 0))
+
+        with self.assertRaisesRegex(
+                AssertionError, "illegal qcurve segment point count: 0"):
+            quadpen.qCurveTo()
+
+    def test_qCurveTo_1_point(self):
+        pen = DummyPen()
+        quadpen = Cu2QuPen(pen, MAX_ERR)
+        quadpen.moveTo((0, 0))
+        quadpen.qCurveTo((1, 1))
+
+        self.assertEqual(str(pen).splitlines(), [
+            "pen.moveTo((0, 0))",
+            "pen.lineTo((1, 1))",
+        ])
+
+    def test_qCurveTo_more_than_1_point(self):
+        pen = DummyPen()
+        quadpen = Cu2QuPen(pen, MAX_ERR)
+        quadpen.moveTo((0, 0))
+        quadpen.qCurveTo((1, 1), (2, 2))
+
+        self.assertEqual(str(pen).splitlines(), [
+            "pen.moveTo((0, 0))",
+            "pen.qCurveTo((1, 1), (2, 2))",
+        ])
+
+    def test_curveTo_no_points(self):
+        quadpen = Cu2QuPen(DummyPen(), MAX_ERR)
+        quadpen.moveTo((0, 0))
+
+        with self.assertRaisesRegex(
+                AssertionError, "illegal curve segment point count: 0"):
+            quadpen.curveTo()
+
+    def test_curveTo_1_point(self):
+        pen = DummyPen()
+        quadpen = Cu2QuPen(pen, MAX_ERR)
+        quadpen.moveTo((0, 0))
+        quadpen.curveTo((1, 1))
+
+        self.assertEqual(str(pen).splitlines(), [
+            "pen.moveTo((0, 0))",
+            "pen.lineTo((1, 1))",
+        ])
+
+    def test_curveTo_2_points(self):
+        pen = DummyPen()
+        quadpen = Cu2QuPen(pen, MAX_ERR)
+        quadpen.moveTo((0, 0))
+        quadpen.curveTo((1, 1), (2, 2))
+
+        self.assertEqual(str(pen).splitlines(), [
+            "pen.moveTo((0, 0))",
+            "pen.qCurveTo((1, 1), (2, 2))",
+        ])
+
+    def test_curveTo_3_points(self):
+        pen = DummyPen()
+        quadpen = Cu2QuPen(pen, MAX_ERR)
+        quadpen.moveTo((0, 0))
+        quadpen.curveTo((1, 1), (2, 2), (3, 3))
+
+        self.assertEqual(str(pen).splitlines(), [
+            "pen.moveTo((0, 0))",
+            "pen.qCurveTo((0.75, 0.75), (2.25, 2.25), (3, 3))",
+        ])
+
+    def test_curveTo_more_than_3_points(self):
+        # a 'SuperBezier' as described in fontTools.basePen.AbstractPen
+        pen = DummyPen()
+        quadpen = Cu2QuPen(pen, MAX_ERR)
+        quadpen.moveTo((0, 0))
+        quadpen.curveTo((1, 1), (2, 2), (3, 3), (4, 4))
+
+        self.assertEqual(str(pen).splitlines(), [
+            "pen.moveTo((0, 0))",
+            "pen.qCurveTo((0.75, 0.75), (1.625, 1.625), (2, 2))",
+            "pen.qCurveTo((2.375, 2.375), (3.25, 3.25), (4, 4))",
+        ])
+
+    def test_addComponent(self):
+        pen = DummyPen()
+        quadpen = Cu2QuPen(pen, MAX_ERR)
+        quadpen.addComponent("a", (1, 2, 3, 4, 5.0, 6.0))
+
+        # components are passed through without changes
+        self.assertEqual(str(pen).splitlines(), [
+            "pen.addComponent('a', (1, 2, 3, 4, 5.0, 6.0))",
+        ])
+
+    def test_ignore_single_points(self):
+        pen = DummyPen()
+        try:
+            logging.captureWarnings(True)
+            with CapturingLogHandler("py.warnings", level="WARNING") as log:
+                quadpen = Cu2QuPen(pen, MAX_ERR, ignore_single_points=True)
+        finally:
+            logging.captureWarnings(False)
+        quadpen.moveTo((0, 0))
+        quadpen.endPath()
+        quadpen.moveTo((1, 1))
+        quadpen.closePath()
+
+        self.assertGreaterEqual(len(log.records), 1)
+        self.assertIn("ignore_single_points is deprecated",
+                      log.records[0].args[0])
+
+        # single-point contours were ignored, so the pen commands are empty
+        self.assertFalse(pen.commands)
+
+        # redraw without ignoring single points
+        quadpen.ignore_single_points = False
+        quadpen.moveTo((0, 0))
+        quadpen.endPath()
+        quadpen.moveTo((1, 1))
+        quadpen.closePath()
+
+        self.assertTrue(pen.commands)
+        self.assertEqual(str(pen).splitlines(), [
+            "pen.moveTo((0, 0))",
+            "pen.endPath()",
+            "pen.moveTo((1, 1))",
+            "pen.closePath()"
+        ])
+
+
+class TestCu2QuPointPen(unittest.TestCase, _TestPenMixin):
+
+    def __init__(self, *args, **kwargs):
+        super(TestCu2QuPointPen, self).__init__(*args, **kwargs)
+        self.Glyph = DummyPointGlyph
+        self.Pen = DummyPointPen
+        self.Cu2QuPen = Cu2QuPointPen
+        self.pen_getter_name = 'getPointPen'
+        self.draw_method_name = 'drawPoints'
+
+    def test_super_bezier_curve(self):
+        pen = DummyPointPen()
+        quadpen = Cu2QuPointPen(pen, MAX_ERR)
+        quadpen.beginPath()
+        quadpen.addPoint((0, 0), segmentType="move")
+        quadpen.addPoint((1, 1))
+        quadpen.addPoint((2, 2))
+        quadpen.addPoint((3, 3))
+        quadpen.addPoint(
+            (4, 4), segmentType="curve", smooth=False, name="up", selected=1)
+        quadpen.endPath()
+
+        self.assertEqual(str(pen).splitlines(), """\
+pen.beginPath()
+pen.addPoint((0, 0), name=None, segmentType='move', smooth=False)
+pen.addPoint((0.75, 0.75), name=None, segmentType=None, smooth=False)
+pen.addPoint((1.625, 1.625), name=None, segmentType=None, smooth=False)
+pen.addPoint((2, 2), name=None, segmentType='qcurve', smooth=True)
+pen.addPoint((2.375, 2.375), name=None, segmentType=None, smooth=False)
+pen.addPoint((3.25, 3.25), name=None, segmentType=None, smooth=False)
+pen.addPoint((4, 4), name='up', segmentType='qcurve', selected=1, smooth=False)
+pen.endPath()""".splitlines())
+
+    def test__flushContour_restore_starting_point(self):
+        pen = DummyPointPen()
+        quadpen = Cu2QuPointPen(pen, MAX_ERR)
+
+        # collect the output of _flushContour before it's sent to _drawPoints
+        new_segments = []
+        def _drawPoints(segments):
+            new_segments.extend(segments)
+            Cu2QuPointPen._drawPoints(quadpen, segments)
+        quadpen._drawPoints = _drawPoints
+
+        # a closed path (ie. no "move" segmentType)
+        quadpen._flushContour([
+            ("curve", [
+                ((2, 2), False, None, {}),
+                ((1, 1), False, None, {}),
+                ((0, 0), False, None, {}),
+            ]),
+            ("curve", [
+                ((1, 1), False, None, {}),
+                ((2, 2), False, None, {}),
+                ((3, 3), False, None, {}),
+            ]),
+        ])
+
+        # the original starting point is restored: the last segment has become
+        # the first
+        self.assertEqual(new_segments[0][1][-1][0], (3, 3))
+        self.assertEqual(new_segments[-1][1][-1][0], (0, 0))
+
+        new_segments = []
+        # an open path (ie. starting with "move")
+        quadpen._flushContour([
+            ("move", [
+                ((0, 0), False, None, {}),
+            ]),
+            ("curve", [
+                ((1, 1), False, None, {}),
+                ((2, 2), False, None, {}),
+                ((3, 3), False, None, {}),
+            ]),
+        ])
+
+        # the segment order stays the same before and after _flushContour
+        self.assertEqual(new_segments[0][1][-1][0], (0, 0))
+        self.assertEqual(new_segments[-1][1][-1][0], (3, 3))
+
+    def test_quad_no_oncurve(self):
+        """When passed a contour which has no on-curve points, the
+        Cu2QuPointPen will treat it as a special quadratic contour whose
+        first point has 'None' coordinates.
+        """
+        self.maxDiff = None
+        pen = DummyPointPen()
+        quadpen = Cu2QuPointPen(pen, MAX_ERR)
+        quadpen.beginPath()
+        quadpen.addPoint((1, 1))
+        quadpen.addPoint((2, 2))
+        quadpen.addPoint((3, 3))
+        quadpen.endPath()
+
+        self.assertEqual(
+            str(pen),
+            dedent(
+                """\
+                pen.beginPath()
+                pen.addPoint((1, 1), name=None, segmentType=None, smooth=False)
+                pen.addPoint((2, 2), name=None, segmentType=None, smooth=False)
+                pen.addPoint((3, 3), name=None, segmentType=None, smooth=False)
+                pen.endPath()"""
+            )
+        )
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/Tests/pens/data/cubic/A_.glif b/Tests/pens/data/cubic/A_.glif
new file mode 100755
index 0000000..bee6cd7
--- /dev/null
+++ b/Tests/pens/data/cubic/A_.glif
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="1">
+	<unicode hex="0041"/>
+	<advance width="668"/>
+	<outline>
+		<contour>
+			<point x="501" y="347" type="line"/>
+			<point x="412" y="393"/>
+			<point x="359" y="401"/>
+			<point x="282" y="401" type="curve" smooth="yes"/>
+			<point x="192" y="401"/>
+			<point x="131" y="390"/>
+			<point x="114" y="347" type="curve"/>
+			<point x="119" y="347"/>
+			<point x="127" y="330"/>
+			<point x="127" y="316" type="curve" smooth="yes"/>
+			<point x="127" y="292"/>
+			<point x="102" y="277"/>
+			<point x="71" y="277" type="curve" smooth="yes"/>
+			<point x="37" y="277"/>
+			<point x="9" y="296"/>
+			<point x="9" y="335" type="curve" smooth="yes"/>
+			<point x="9" y="403"/>
+			<point x="95" y="458"/>
+			<point x="213" y="458" type="curve" smooth="yes"/>
+			<point x="278" y="458"/>
+			<point x="428" y="439"/>
+			<point x="526" y="385" type="curve"/>
+		</contour>
+		<contour>
+			<point x="629" y="678" type="line"/>
+			<point x="615" y="685"/>
+			<point x="596" y="692"/>
+			<point x="578" y="692" type="curve" smooth="yes"/>
+			<point x="421" y="692"/>
+			<point x="204" y="149"/>
+			<point x="204" y="-58" type="curve" smooth="yes"/>
+			<point x="204" y="-90"/>
+			<point x="212" y="-105"/>
+			<point x="218" y="-114" type="curve"/>
+			<point x="164" y="-114"/>
+			<point x="84" y="-112"/>
+			<point x="84" y="-8" type="curve" smooth="yes"/>
+			<point x="84" y="191"/>
+			<point x="374" y="750"/>
+			<point x="613" y="750" type="curve" smooth="yes"/>
+			<point x="636" y="750"/>
+			<point x="668" y="746"/>
+			<point x="692" y="736" type="curve"/>
+		</contour>
+		<contour>
+			<point x="391" y="0" type="line"/>
+			<point x="547" y="736" type="line"/>
+			<point x="692" y="736" type="line"/>
+			<point x="535" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
\ No newline at end of file
diff --git a/Tests/pens/data/cubic/A_acute.glif b/Tests/pens/data/cubic/A_acute.glif
new file mode 100755
index 0000000..72a8855
--- /dev/null
+++ b/Tests/pens/data/cubic/A_acute.glif
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="Aacute" format="1">
+	<unicode hex="00C1"/>
+	<advance width="668"/>
+	<outline>
+		<component base="A"/>
+		<component base="acute" xOffset="366" yOffset="250"/>
+	</outline>
+</glyph>
\ No newline at end of file
diff --git a/Tests/pens/data/cubic/E_acute.glif b/Tests/pens/data/cubic/E_acute.glif
new file mode 100755
index 0000000..fa57e34
--- /dev/null
+++ b/Tests/pens/data/cubic/E_acute.glif
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="E" format="1">
+	<unicode hex="0045"/>
+	<advance width="459"/>
+	<outline>
+		<contour>
+			<point x="461" y="180" type="line"/>
+			<point x="425" y="114"/>
+			<point x="337" y="78"/>
+			<point x="276" y="78" type="curve" smooth="yes"/>
+			<point x="208" y="78"/>
+			<point x="181" y="123"/>
+			<point x="181" y="179" type="curve" smooth="yes"/>
+			<point x="181" y="271"/>
+			<point x="253" y="394"/>
+			<point x="360" y="418" type="curve"/>
+			<point x="288" y="437"/>
+			<point x="259" y="494"/>
+			<point x="259" y="552" type="curve" smooth="yes"/>
+			<point x="259" y="622"/>
+			<point x="302" y="693"/>
+			<point x="361" y="693" type="curve" smooth="yes"/>
+			<point x="388" y="693"/>
+			<point x="416" y="678"/>
+			<point x="416" y="637" type="curve" smooth="yes"/>
+			<point x="416" y="603"/>
+			<point x="397" y="558"/>
+			<point x="372" y="550" type="curve"/>
+			<point x="386" y="531"/>
+			<point x="402" y="523"/>
+			<point x="419" y="523" type="curve" smooth="yes"/>
+			<point x="459" y="523"/>
+			<point x="493" y="567"/>
+			<point x="493" y="627" type="curve" smooth="yes"/>
+			<point x="493" y="709"/>
+			<point x="429" y="752"/>
+			<point x="350" y="752" type="curve" smooth="yes"/>
+			<point x="222" y="752"/>
+			<point x="131" y="640"/>
+			<point x="131" y="537" type="curve" smooth="yes"/>
+			<point x="131" y="492"/>
+			<point x="149" y="448"/>
+			<point x="192" y="417" type="curve"/>
+			<point x="77" y="389"/>
+			<point x="7" y="263"/>
+			<point x="7" y="158" type="curve" smooth="yes"/>
+			<point x="7" y="64"/>
+			<point x="63" y="-18"/>
+			<point x="191" y="-18" type="curve" smooth="yes"/>
+			<point x="310" y="-18"/>
+			<point x="432" y="52"/>
+			<point x="484" y="170" type="curve"/>
+		</contour>
+		<component base="acute" xOffset="210" yOffset="250"/>
+	</outline>
+</glyph>
\ No newline at end of file
diff --git a/Tests/pens/data/cubic/a.glif b/Tests/pens/data/cubic/a.glif
new file mode 100644
index 0000000..1fc63b4
--- /dev/null
+++ b/Tests/pens/data/cubic/a.glif
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="1">
+	<unicode hex="0061"/>
+	<advance width="1118"/>
+	<outline>
+		<contour>
+			<point x="771" y="183" type="line"/>
+			<point x="771" y="123"/>
+			<point x="782" y="41"/>
+			<point x="802" y="0" type="curve"/>
+			<point x="1010" y="0" type="line"/>
+			<point x="1010" y="17" type="line"/>
+			<point x="984" y="76"/>
+			<point x="971" y="166"/>
+			<point x="971" y="238" type="curve" smooth="yes"/>
+			<point x="971" y="738" type="line"/>
+			<point x="971" y="981"/>
+			<point x="803" y="1102"/>
+			<point x="566" y="1102" type="curve" smooth="yes"/>
+			<point x="301" y="1102"/>
+			<point x="133" y="935"/>
+			<point x="133" y="782" type="curve"/>
+			<point x="333" y="782" type="line"/>
+			<point x="333" y="867"/>
+			<point x="421" y="945"/>
+			<point x="554" y="945" type="curve" smooth="yes"/>
+			<point x="697" y="945"/>
+			<point x="771" y="864"/>
+			<point x="771" y="740" type="curve"/>
+		</contour>
+		<contour>
+			<point x="804" y="661" type="line"/>
+			<point x="596" y="661" type="line"/>
+			<point x="301" y="661"/>
+			<point x="111" y="539"/>
+			<point x="111" y="303" type="curve" smooth="yes"/>
+			<point x="111" y="123"/>
+			<point x="257" y="-20"/>
+			<point x="480" y="-20" type="curve" smooth="yes"/>
+			<point x="711" y="-20"/>
+			<point x="857" y="170"/>
+			<point x="874" y="277" type="curve"/>
+			<point x="789" y="370" type="line"/>
+			<point x="789" y="279"/>
+			<point x="673" y="151"/>
+			<point x="510" y="151" type="curve" smooth="yes"/>
+			<point x="377" y="151"/>
+			<point x="311" y="230"/>
+			<point x="311" y="331" type="curve" smooth="yes"/>
+			<point x="311" y="462"/>
+			<point x="425" y="524"/>
+			<point x="629" y="524" type="curve"/>
+			<point x="806" y="524" type="line"/>
+		</contour>
+		<contour>
+			<point name="top" x="582" y="1290" type="move"/>
+		</contour>
+		<contour>
+			<point name="bottom" x="501" y="0" type="move"/>
+		</contour>
+		<contour>
+			<point name="ogonek" x="974" y="0" type="move"/>
+		</contour>
+		<contour>
+			<point name="rhalfring" x="700" y="1290" type="move"/>
+		</contour>
+		<contour>
+			<point name="top_dd" x="1118" y="1600" type="move"/>
+		</contour>
+		<contour>
+			<point name="bottom_dd" x="1118" y="-407" type="move"/>
+		</contour>
+		<contour>
+			<point name="rhotichook" x="879" y="654" type="move"/>
+		</contour>
+		<contour>
+			<point name="top0315" x="1118" y="1290" type="move"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/pens/data/cubic/acute.glif b/Tests/pens/data/cubic/acute.glif
new file mode 100755
index 0000000..2d39251
--- /dev/null
+++ b/Tests/pens/data/cubic/acute.glif
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="acute" format="1">
+	<unicode hex="00B4"/>
+	<advance width="316"/>
+	<outline>
+		<contour>
+			<point x="120" y="561" type="line"/>
+			<point x="195" y="561" type="line"/>
+			<point x="316" y="750" type="line"/>
+			<point x="211" y="750" type="line"/>
+		</contour>
+	</outline>
+</glyph>
\ No newline at end of file
diff --git a/Tests/pens/data/cubic/contents.plist b/Tests/pens/data/cubic/contents.plist
new file mode 100644
index 0000000..3baa4e7
--- /dev/null
+++ b/Tests/pens/data/cubic/contents.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>A</key>
+	<string>A_.glif</string>
+	<key>Aacute</key>
+	<string>A_acute.glif</string>
+	<key>Eacute</key>
+	<string>E_acute.glif</string>
+	<key>a</key>
+	<string>a.glif</string>
+	<key>acute</key>
+	<string>acute.glif</string>
+</dict>
+</plist>
diff --git a/Tests/pens/data/quadratic/A_.glif b/Tests/pens/data/quadratic/A_.glif
new file mode 100644
index 0000000..dbc10e9
--- /dev/null
+++ b/Tests/pens/data/quadratic/A_.glif
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="1">
+	<unicode hex="0041"/>
+	<advance width="668"/>
+	<outline>
+		<contour>
+			<point x="501" y="347" type="line"/>
+			<point x="456.5" y="370"/>
+			<point x="386.5" y="393.16666666666663"/>
+			<point x="320.5" y="401"/>
+			<point x="282" y="401" type="qcurve" smooth="yes"/>
+			<point x="214.5" y="401"/>
+			<point x="126.75" y="379.25"/>
+			<point x="114" y="347" type="qcurve"/>
+			<point x="117.75" y="347"/>
+			<point x="127" y="326.5"/>
+			<point x="127" y="316" type="qcurve" smooth="yes"/>
+			<point x="127" y="298"/>
+			<point x="94.25" y="277"/>
+			<point x="71" y="277" type="qcurve" smooth="yes"/>
+			<point x="45.5" y="277"/>
+			<point x="9" y="305.75"/>
+			<point x="9" y="335" type="qcurve" smooth="yes"/>
+			<point x="9" y="369"/>
+			<point x="61.83333333333332" y="424.8333333333333"/>
+			<point x="154" y="458"/>
+			<point x="213" y="458" type="qcurve" smooth="yes"/>
+			<point x="237.375" y="458"/>
+			<point x="313.0052083333333" y="450.2916666666667"/>
+			<point x="401.2447916666667" y="433.2083333333333"/>
+			<point x="489.25" y="405.25"/>
+			<point x="526" y="385" type="qcurve"/>
+		</contour>
+		<contour>
+			<point x="629" y="678" type="line"/>
+			<point x="618.5" y="683.25"/>
+			<point x="591.5" y="692"/>
+			<point x="578" y="692" type="qcurve" smooth="yes"/>
+			<point x="544.3571428571429" y="692"/>
+			<point x="471.67614188532553" y="631.7033527696792"/>
+			<point x="398.9164237123421" y="527.9810495626824"/>
+			<point x="330.9234693877551" y="396.2091836734695"/>
+			<point x="272.54275996112733" y="251.76384839650154"/>
+			<point x="228.61977648202145" y="110.0211370262391"/>
+			<point x="204.00000000000006" y="-13.64285714285704"/>
+			<point x="204" y="-58" type="qcurve" smooth="yes"/>
+			<point x="204" y="-82"/>
+			<point x="213.5" y="-107.25"/>
+			<point x="218" y="-114" type="qcurve"/>
+			<point x="197.75" y="-114"/>
+			<point x="151.36458333333334" y="-109.60416666666667"/>
+			<point x="110.13541666666667" y="-90.39583333333333"/>
+			<point x="84" y="-47"/>
+			<point x="84" y="-8" type="qcurve" smooth="yes"/>
+			<point x="84" y="34.64285714285714"/>
+			<point x="117.10762876579204" y="157.5352283770651"/>
+			<point x="176.7779397473275" y="300.3955296404276"/>
+			<point x="257.04591836734687" y="447.1479591836734"/>
+			<point x="351.94655004859084" y="581.7167152575314"/>
+			<point x="455.5148202137998" y="688.0259961127308"/>
+			<point x="561.7857142857142" y="750"/>
+			<point x="613" y="750" type="qcurve" smooth="yes"/>
+			<point x="630.25" y="750"/>
+			<point x="674" y="743.5"/>
+			<point x="692" y="736" type="qcurve"/>
+		</contour>
+		<contour>
+			<point x="391" y="0" type="line"/>
+			<point x="547" y="736" type="line"/>
+			<point x="692" y="736" type="line"/>
+			<point x="535" y="0" type="line"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/pens/data/quadratic/E_acute.glif b/Tests/pens/data/quadratic/E_acute.glif
new file mode 100644
index 0000000..89b4a5a
--- /dev/null
+++ b/Tests/pens/data/quadratic/E_acute.glif
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="E" format="1">
+	<unicode hex="0045"/>
+	<advance width="459"/>
+	<outline>
+		<contour>
+			<point x="461" y="180" type="line"/>
+			<point x="443" y="147"/>
+			<point x="378.91666666666663" y="101.49999999999999"/>
+			<point x="306.5" y="78"/>
+			<point x="276" y="78" type="qcurve" smooth="yes"/>
+			<point x="225" y="78"/>
+			<point x="181" y="137"/>
+			<point x="181" y="179" type="qcurve" smooth="yes"/>
+			<point x="181" y="213.5"/>
+			<point x="206.65104166666666" y="289.3854166666667"/>
+			<point x="254.09895833333334" y="358.6145833333333"/>
+			<point x="319.875" y="409"/>
+			<point x="360" y="418" type="qcurve"/>
+			<point x="306" y="432.25"/>
+			<point x="259" y="508.5"/>
+			<point x="259" y="552" type="qcurve" smooth="yes"/>
+			<point x="259" y="587"/>
+			<point x="285.41666666666663" y="651.6666666666667"/>
+			<point x="331.5" y="693"/>
+			<point x="361" y="693" type="qcurve" smooth="yes"/>
+			<point x="381.25" y="693"/>
+			<point x="416" y="667.75"/>
+			<point x="416" y="637" type="qcurve" smooth="yes"/>
+			<point x="416" y="611.5"/>
+			<point x="390.75" y="556"/>
+			<point x="372" y="550" type="qcurve"/>
+			<point x="391.89473684210526" y="523"/>
+			<point x="419" y="523" type="qcurve" smooth="yes"/>
+			<point x="449" y="523"/>
+			<point x="493" y="582"/>
+			<point x="493" y="627" type="qcurve" smooth="yes"/>
+			<point x="493" y="688.5"/>
+			<point x="409.25" y="752"/>
+			<point x="350" y="752" type="qcurve" smooth="yes"/>
+			<point x="302" y="752"/>
+			<point x="221.84375" y="714.4114583333334"/>
+			<point x="163.15625" y="651.8385416666666"/>
+			<point x="131" y="575.625"/>
+			<point x="131" y="537" type="qcurve" smooth="yes"/>
+			<point x="131" y="503.25"/>
+			<point x="159.75" y="440.25"/>
+			<point x="192" y="417" type="qcurve"/>
+			<point x="134.5" y="403"/>
+			<point x="51.58333333333333" y="319.58333333333326"/>
+			<point x="7" y="210.5"/>
+			<point x="7" y="158" type="qcurve" smooth="yes"/>
+			<point x="7" y="111"/>
+			<point x="45.66666666666667" y="30.83333333333333"/>
+			<point x="127.00000000000001" y="-18"/>
+			<point x="191" y="-18" type="qcurve" smooth="yes"/>
+			<point x="250.5" y="-18"/>
+			<point x="365.4166666666667" y="26.833333333333336"/>
+			<point x="458" y="110.99999999999999"/>
+			<point x="484" y="170" type="qcurve"/>
+		</contour>
+		<component base="acute" xOffset="210" yOffset="250"/>
+	</outline>
+</glyph>
diff --git a/Tests/pens/data/quadratic/a.glif b/Tests/pens/data/quadratic/a.glif
new file mode 100644
index 0000000..dc776e1
--- /dev/null
+++ b/Tests/pens/data/quadratic/a.glif
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="1">
+	<unicode hex="0061"/>
+	<advance width="1118"/>
+	<outline>
+		<contour>
+			<point x="771" y="183" type="line"/>
+			<point x="771" y="153"/>
+			<point x="778.1666666666665" y="83.58333333333333"/>
+			<point x="792" y="20.5"/>
+			<point x="802" y="0" type="qcurve"/>
+			<point x="1010" y="0" type="line"/>
+			<point x="1010" y="17" type="line"/>
+			<point x="990.5" y="61.25"/>
+			<point x="971" y="184"/>
+			<point x="971" y="238" type="qcurve" smooth="yes"/>
+			<point x="971" y="738" type="line"/>
+			<point x="971" y="859.5"/>
+			<point x="867.25" y="1021.25"/>
+			<point x="684.5" y="1102"/>
+			<point x="566" y="1102" type="qcurve" smooth="yes"/>
+			<point x="466.625" y="1102"/>
+			<point x="306.8385416666667" y="1045.9739583333333"/>
+			<point x="193.41145833333334" y="952.7760416666666"/>
+			<point x="133" y="839.375"/>
+			<point x="133" y="782" type="qcurve"/>
+			<point x="333" y="782" type="line"/>
+			<point x="333" y="824.5"/>
+			<point x="388.0833333333333" y="898.9166666666665"/>
+			<point x="487.5" y="945"/>
+			<point x="554" y="945" type="qcurve" smooth="yes"/>
+			<point x="661.25" y="945"/>
+			<point x="771" y="833"/>
+			<point x="771" y="740" type="qcurve"/>
+		</contour>
+		<contour>
+			<point x="804" y="661" type="line"/>
+			<point x="596" y="661" type="line"/>
+			<point x="448.5" y="661"/>
+			<point x="230.58333333333331" y="580.3333333333334"/>
+			<point x="111" y="421"/>
+			<point x="111" y="303" type="qcurve" smooth="yes"/>
+			<point x="111" y="213"/>
+			<point x="202.58333333333334" y="66.5"/>
+			<point x="368.5" y="-20"/>
+			<point x="480" y="-20" type="qcurve" smooth="yes"/>
+			<point x="549.3000000000001" y="-20"/>
+			<point x="666.664" y="20.413000000000004"/>
+			<point x="760.4599999999999" y="86.77000000000001"/>
+			<point x="828.576" y="165.967"/>
+			<point x="868.8999999999999" y="244.90000000000003"/>
+			<point x="874" y="277" type="qcurve"/>
+			<point x="789" y="370" type="line"/>
+			<point x="789" y="335.875"/>
+			<point x="748.015625" y="259.765625"/>
+			<point x="673.234375" y="192.984375"/>
+			<point x="571.125" y="151"/>
+			<point x="510" y="151" type="qcurve" smooth="yes"/>
+			<point x="443.5" y="151"/>
+			<point x="355.08333333333326" y="198.916666666666666"/>
+			<point x="311" y="280.5"/>
+			<point x="311" y="331" type="qcurve" smooth="yes"/>
+			<point x="311" y="429.25"/>
+			<point x="476" y="524"/>
+			<point x="629" y="524" type="qcurve"/>
+			<point x="806" y="524" type="line"/>
+		</contour>
+		<contour>
+			<point name="top" x="582" y="1290" type="move"/>
+		</contour>
+		<contour>
+			<point name="bottom" x="501" y="0" type="move"/>
+		</contour>
+		<contour>
+			<point name="ogonek" x="974" y="0" type="move"/>
+		</contour>
+		<contour>
+			<point name="rhalfring" x="700" y="1290" type="move"/>
+		</contour>
+		<contour>
+			<point name="top_dd" x="1118" y="1600" type="move"/>
+		</contour>
+		<contour>
+			<point name="bottom_dd" x="1118" y="-407" type="move"/>
+		</contour>
+		<contour>
+			<point name="rhotichook" x="879" y="654" type="move"/>
+		</contour>
+		<contour>
+			<point name="top0315" x="1118" y="1290" type="move"/>
+		</contour>
+	</outline>
+</glyph>
diff --git a/Tests/pens/data/quadratic/acute.glif b/Tests/pens/data/quadratic/acute.glif
new file mode 100755
index 0000000..2d39251
--- /dev/null
+++ b/Tests/pens/data/quadratic/acute.glif
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="acute" format="1">
+	<unicode hex="00B4"/>
+	<advance width="316"/>
+	<outline>
+		<contour>
+			<point x="120" y="561" type="line"/>
+			<point x="195" y="561" type="line"/>
+			<point x="316" y="750" type="line"/>
+			<point x="211" y="750" type="line"/>
+		</contour>
+	</outline>
+</glyph>
\ No newline at end of file
diff --git a/Tests/pens/data/quadratic/contents.plist b/Tests/pens/data/quadratic/contents.plist
new file mode 100644
index 0000000..5ed74ed
--- /dev/null
+++ b/Tests/pens/data/quadratic/contents.plist
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>A</key>
+	<string>A_.glif</string>
+	<key>Eacute</key>
+	<string>E_acute.glif</string>
+	<key>a</key>
+	<string>a.glif</string>
+	<key>acute</key>
+	<string>acute.glif</string>
+</dict>
+</plist>
diff --git a/Tests/pens/hashPointPen_test.py b/Tests/pens/hashPointPen_test.py
new file mode 100644
index 0000000..6b744e6
--- /dev/null
+++ b/Tests/pens/hashPointPen_test.py
@@ -0,0 +1,138 @@
+from fontTools.misc.transform import Identity
+from fontTools.pens.hashPointPen import HashPointPen
+import pytest
+
+
+class _TestGlyph(object):
+    width = 500
+
+    def drawPoints(self, pen):
+        pen.beginPath(identifier="abc")
+        pen.addPoint((0.0, 0.0), "line", False, "start", identifier="0000")
+        pen.addPoint((10, 110), "line", False, None, identifier="0001")
+        pen.addPoint((50.0, 75.0), None, False, None, identifier="0002")
+        pen.addPoint((60.0, 50.0), None, False, None, identifier="0003")
+        pen.addPoint((50.0, 0.0), "curve", True, "last", identifier="0004")
+        pen.endPath()
+
+
+class _TestGlyph2(_TestGlyph):
+    def drawPoints(self, pen):
+        pen.beginPath(identifier="abc")
+        pen.addPoint((0.0, 0.0), "line", False, "start", identifier="0000")
+        # Minor difference to _TestGlyph() is in the next line:
+        pen.addPoint((101, 10), "line", False, None, identifier="0001")
+        pen.addPoint((50.0, 75.0), None, False, None, identifier="0002")
+        pen.addPoint((60.0, 50.0), None, False, None, identifier="0003")
+        pen.addPoint((50.0, 0.0), "curve", True, "last", identifier="0004")
+        pen.endPath()
+
+
+class _TestGlyph3(_TestGlyph):
+    def drawPoints(self, pen):
+        pen.beginPath(identifier="abc")
+        pen.addPoint((0.0, 0.0), "line", False, "start", identifier="0000")
+        pen.addPoint((10, 110), "line", False, None, identifier="0001")
+        pen.endPath()
+        # Same segment, but in a different path:
+        pen.beginPath(identifier="pth2")
+        pen.addPoint((50.0, 75.0), None, False, None, identifier="0002")
+        pen.addPoint((60.0, 50.0), None, False, None, identifier="0003")
+        pen.addPoint((50.0, 0.0), "curve", True, "last", identifier="0004")
+        pen.endPath()
+
+
+class _TestGlyph4(_TestGlyph):
+    def drawPoints(self, pen):
+        pen.beginPath(identifier="abc")
+        pen.addPoint((0.0, 0.0), "move", False, "start", identifier="0000")
+        pen.addPoint((10, 110), "line", False, None, identifier="0001")
+        pen.addPoint((50.0, 75.0), None, False, None, identifier="0002")
+        pen.addPoint((60.0, 50.0), None, False, None, identifier="0003")
+        pen.addPoint((50.0, 0.0), "curve", True, "last", identifier="0004")
+        pen.endPath()
+
+
+class _TestGlyph5(_TestGlyph):
+    def drawPoints(self, pen):
+        pen.addComponent("b", Identity)
+
+
+class HashPointPenTest(object):
+    def test_addComponent(self):
+        pen = HashPointPen(_TestGlyph().width, {"a": _TestGlyph()})
+        pen.addComponent("a", (2, 0, 0, 3, -10, 5))
+        assert pen.hash == "w500[l0+0l10+110o50+75o60+50c50+0|(+2+0+0+3-10+5)]"
+
+    def test_NestedComponents(self):
+        pen = HashPointPen(
+            _TestGlyph().width, {"a": _TestGlyph5(), "b": _TestGlyph()}
+        )  # "a" contains "b" as a component
+        pen.addComponent("a", (2, 0, 0, 3, -10, 5))
+
+        assert (
+            pen.hash
+            == "w500[[l0+0l10+110o50+75o60+50c50+0|(+1+0+0+1+0+0)](+2+0+0+3-10+5)]"
+        )
+
+    def test_outlineAndComponent(self):
+        pen = HashPointPen(_TestGlyph().width, {"a": _TestGlyph()})
+        glyph = _TestGlyph()
+        glyph.drawPoints(pen)
+        pen.addComponent("a", (2, 0, 0, 2, -10, 5))
+
+        assert (
+            pen.hash
+            == "w500l0+0l10+110o50+75o60+50c50+0|[l0+0l10+110o50+75o60+50c50+0|(+2+0+0+2-10+5)]"
+        )
+
+    def test_addComponent_missing_raises(self):
+        pen = HashPointPen(_TestGlyph().width, dict())
+        with pytest.raises(KeyError) as excinfo:
+            pen.addComponent("a", Identity)
+        assert excinfo.value.args[0] == "a"
+
+    def test_similarGlyphs(self):
+        pen = HashPointPen(_TestGlyph().width)
+        glyph = _TestGlyph()
+        glyph.drawPoints(pen)
+
+        pen2 = HashPointPen(_TestGlyph2().width)
+        glyph = _TestGlyph2()
+        glyph.drawPoints(pen2)
+
+        assert pen.hash != pen2.hash
+
+    def test_similarGlyphs2(self):
+        pen = HashPointPen(_TestGlyph().width)
+        glyph = _TestGlyph()
+        glyph.drawPoints(pen)
+
+        pen2 = HashPointPen(_TestGlyph3().width)
+        glyph = _TestGlyph3()
+        glyph.drawPoints(pen2)
+
+        assert pen.hash != pen2.hash
+
+    def test_similarGlyphs3(self):
+        pen = HashPointPen(_TestGlyph().width)
+        glyph = _TestGlyph()
+        glyph.drawPoints(pen)
+
+        pen2 = HashPointPen(_TestGlyph4().width)
+        glyph = _TestGlyph4()
+        glyph.drawPoints(pen2)
+
+        assert pen.hash != pen2.hash
+
+    def test_glyphVsComposite(self):
+        # If a glyph contains a component, the decomposed glyph should still
+        # compare false
+        pen = HashPointPen(_TestGlyph().width, {"a": _TestGlyph()})
+        pen.addComponent("a", Identity)
+
+        pen2 = HashPointPen(_TestGlyph().width)
+        glyph = _TestGlyph()
+        glyph.drawPoints(pen2)
+
+        assert pen.hash != pen2.hash
diff --git a/Tests/pens/perimeterPen_test.py b/Tests/pens/perimeterPen_test.py
index aa73c3d..1b64534 100644
--- a/Tests/pens/perimeterPen_test.py
+++ b/Tests/pens/perimeterPen_test.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.pens.perimeterPen import PerimeterPen
 import unittest
 
diff --git a/Tests/pens/pointInsidePen_test.py b/Tests/pens/pointInsidePen_test.py
index 3696ece..b561c43 100644
--- a/Tests/pens/pointInsidePen_test.py
+++ b/Tests/pens/pointInsidePen_test.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+from io import StringIO
 from fontTools.pens.pointInsidePen import PointInsidePen
 import unittest
 
@@ -73,16 +72,16 @@
 
     @staticmethod
     def render(draw_function, even_odd):
-        result = BytesIO()
+        result = StringIO()
         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"*")
+                    result.write("*")
                 else:
-                    result.write(b" ")
-        return tounicode(result.getvalue())
+                    result.write(" ")
+        return result.getvalue()
 
 
     def test_contour_no_solutions(self):
diff --git a/Tests/pens/pointPen_test.py b/Tests/pens/pointPen_test.py
new file mode 100644
index 0000000..a920178
--- /dev/null
+++ b/Tests/pens/pointPen_test.py
@@ -0,0 +1,490 @@
+import unittest
+
+from fontTools.pens.basePen import AbstractPen
+from fontTools.pens.pointPen import AbstractPointPen, PointToSegmentPen, \
+    SegmentToPointPen, GuessSmoothPointPen, ReverseContourPointPen
+
+
+class _TestSegmentPen(AbstractPen):
+
+    def __init__(self):
+        self._commands = []
+
+    def __repr__(self):
+        return " ".join(self._commands)
+
+    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 curveTo(self, *pts):
+        pts = ["%s %s" % pt for pt in pts]
+        self._commands.append("%s curveto" % " ".join(pts))
+
+    def qCurveTo(self, *pts):
+        pts = ["%s %s" % pt if pt is not None else "None" for pt in pts]
+        self._commands.append("%s qcurveto" % " ".join(pts))
+
+    def closePath(self):
+        self._commands.append("closepath")
+
+    def endPath(self):
+        self._commands.append("endpath")
+
+    def addComponent(self, glyphName, transformation):
+        self._commands.append("'%s' %s addcomponent" % (glyphName, transformation))
+
+
+def _reprKwargs(kwargs):
+    items = []
+    for key in sorted(kwargs):
+        value = kwargs[key]
+        if isinstance(value, str):
+            items.append("%s='%s'" % (key, value))
+        else:
+            items.append("%s=%s" % (key, value))
+    return items
+
+
+class _TestPointPen(AbstractPointPen):
+
+    def __init__(self):
+        self._commands = []
+
+    def __repr__(self):
+        return " ".join(self._commands)
+
+    def beginPath(self, identifier=None, **kwargs):
+        items = []
+        if identifier is not None:
+            items.append("identifier='%s'" % identifier)
+        items.extend(_reprKwargs(kwargs))
+        self._commands.append("beginPath(%s)" % ", ".join(items))
+
+    def addPoint(self, pt, segmentType=None, smooth=False, name=None,
+                 identifier=None, **kwargs):
+        items = ["%s" % (pt,)]
+        if segmentType is not None:
+            items.append("segmentType='%s'" % segmentType)
+        if smooth:
+            items.append("smooth=True")
+        if name is not None:
+            items.append("name='%s'" % name)
+        if identifier is not None:
+            items.append("identifier='%s'" % identifier)
+        items.extend(_reprKwargs(kwargs))
+        self._commands.append("addPoint(%s)" % ", ".join(items))
+
+    def endPath(self):
+        self._commands.append("endPath()")
+
+    def addComponent(self, glyphName, transform, identifier=None, **kwargs):
+        items = ["'%s'" % glyphName, "%s" % transform]
+        if identifier is not None:
+            items.append("identifier='%s'" % identifier)
+        items.extend(_reprKwargs(kwargs))
+        self._commands.append("addComponent(%s)" % ", ".join(items))
+
+
+class PointToSegmentPenTest(unittest.TestCase):
+
+    def test_open(self):
+        pen = _TestSegmentPen()
+        ppen = PointToSegmentPen(pen)
+        ppen.beginPath()
+        ppen.addPoint((10, 10), "move")
+        ppen.addPoint((10, 20), "line")
+        ppen.endPath()
+        self.assertEqual("10 10 moveto 10 20 lineto endpath", repr(pen))
+
+    def test_closed(self):
+        pen = _TestSegmentPen()
+        ppen = PointToSegmentPen(pen)
+        ppen.beginPath()
+        ppen.addPoint((10, 10), "line")
+        ppen.addPoint((10, 20), "line")
+        ppen.addPoint((20, 20), "line")
+        ppen.endPath()
+        self.assertEqual("10 10 moveto 10 20 lineto 20 20 lineto closepath", repr(pen))
+
+    def test_cubic(self):
+        pen = _TestSegmentPen()
+        ppen = PointToSegmentPen(pen)
+        ppen.beginPath()
+        ppen.addPoint((10, 10), "line")
+        ppen.addPoint((10, 20))
+        ppen.addPoint((20, 20))
+        ppen.addPoint((20, 40), "curve")
+        ppen.endPath()
+        self.assertEqual("10 10 moveto 10 20 20 20 20 40 curveto closepath", repr(pen))
+
+    def test_quad(self):
+        pen = _TestSegmentPen()
+        ppen = PointToSegmentPen(pen)
+        ppen.beginPath(identifier='foo')
+        ppen.addPoint((10, 10), "line")
+        ppen.addPoint((10, 40))
+        ppen.addPoint((40, 40))
+        ppen.addPoint((10, 40), "qcurve")
+        ppen.endPath()
+        self.assertEqual("10 10 moveto 10 40 40 40 10 40 qcurveto closepath", repr(pen))
+
+    def test_quad_onlyOffCurvePoints(self):
+        pen = _TestSegmentPen()
+        ppen = PointToSegmentPen(pen)
+        ppen.beginPath()
+        ppen.addPoint((10, 10))
+        ppen.addPoint((10, 40))
+        ppen.addPoint((40, 40))
+        ppen.endPath()
+        self.assertEqual("10 10 10 40 40 40 None qcurveto closepath", repr(pen))
+
+    def test_roundTrip1(self):
+        tpen = _TestPointPen()
+        ppen = PointToSegmentPen(SegmentToPointPen(tpen))
+        ppen.beginPath()
+        ppen.addPoint((10, 10), "line")
+        ppen.addPoint((10, 20))
+        ppen.addPoint((20, 20))
+        ppen.addPoint((20, 40), "curve")
+        ppen.endPath()
+        self.assertEqual("beginPath() addPoint((10, 10), segmentType='line') addPoint((10, 20)) "
+                         "addPoint((20, 20)) addPoint((20, 40), segmentType='curve') endPath()",
+                         repr(tpen))
+
+    def test_closed_outputImpliedClosingLine(self):
+        tpen = _TestSegmentPen()
+        ppen = PointToSegmentPen(tpen, outputImpliedClosingLine=True)
+        ppen.beginPath()
+        ppen.addPoint((10, 10), "line")
+        ppen.addPoint((10, 20), "line")
+        ppen.addPoint((20, 20), "line")
+        ppen.endPath()
+        self.assertEqual(
+            "10 10 moveto "
+            "10 20 lineto "
+            "20 20 lineto "
+            "10 10 lineto "  # explicit closing line
+            "closepath",
+            repr(tpen)
+        )
+
+    def test_closed_line_overlapping_start_end_points(self):
+        # Test case from https://github.com/googlefonts/fontmake/issues/572.
+        tpen = _TestSegmentPen()
+        ppen = PointToSegmentPen(tpen, outputImpliedClosingLine=False)
+        # The last oncurve point on this closed contour is a "line" segment and has
+        # same coordinates as the starting point.
+        ppen.beginPath()
+        ppen.addPoint((0, 651), segmentType="line")
+        ppen.addPoint((0, 101), segmentType="line")
+        ppen.addPoint((0, 101), segmentType="line")
+        ppen.addPoint((0, 651), segmentType="line")
+        ppen.endPath()
+        # Check that we always output an explicit 'lineTo' segment at the end,
+        # regardless of the value of 'outputImpliedClosingLine', to disambiguate
+        # the duplicate point from the implied closing line.
+        self.assertEqual(
+            "0 651 moveto "
+            "0 101 lineto "
+            "0 101 lineto "
+            "0 651 lineto "
+            "0 651 lineto "
+            "closepath",
+            repr(tpen)
+        )
+
+    def test_roundTrip2(self):
+        tpen = _TestPointPen()
+        ppen = PointToSegmentPen(SegmentToPointPen(tpen))
+        ppen.beginPath()
+        ppen.addPoint((0, 651), segmentType="line")
+        ppen.addPoint((0, 101), segmentType="line")
+        ppen.addPoint((0, 101), segmentType="line")
+        ppen.addPoint((0, 651), segmentType="line")
+        ppen.endPath()
+        self.assertEqual(
+            "beginPath() "
+            "addPoint((0, 651), segmentType='line') "
+            "addPoint((0, 101), segmentType='line') "
+            "addPoint((0, 101), segmentType='line') "
+            "addPoint((0, 651), segmentType='line') "
+            "endPath()",
+            repr(tpen)
+        )
+
+
+class TestSegmentToPointPen(unittest.TestCase):
+
+    def test_move(self):
+        tpen = _TestPointPen()
+        pen = SegmentToPointPen(tpen)
+        pen.moveTo((10, 10))
+        pen.endPath()
+        self.assertEqual("beginPath() addPoint((10, 10), segmentType='move') endPath()",
+                         repr(tpen))
+
+    def test_poly(self):
+        tpen = _TestPointPen()
+        pen = SegmentToPointPen(tpen)
+        pen.moveTo((10, 10))
+        pen.lineTo((10, 20))
+        pen.lineTo((20, 20))
+        pen.closePath()
+        self.assertEqual("beginPath() addPoint((10, 10), segmentType='line') "
+                         "addPoint((10, 20), segmentType='line') "
+                         "addPoint((20, 20), segmentType='line') endPath()",
+                         repr(tpen))
+
+    def test_cubic(self):
+        tpen = _TestPointPen()
+        pen = SegmentToPointPen(tpen)
+        pen.moveTo((10, 10))
+        pen.curveTo((10, 20), (20, 20), (20, 10))
+        pen.closePath()
+        self.assertEqual("beginPath() addPoint((10, 10), segmentType='line') "
+                         "addPoint((10, 20)) addPoint((20, 20)) addPoint((20, 10), "
+                         "segmentType='curve') endPath()", repr(tpen))
+
+    def test_quad(self):
+        tpen = _TestPointPen()
+        pen = SegmentToPointPen(tpen)
+        pen.moveTo((10, 10))
+        pen.qCurveTo((10, 20), (20, 20), (20, 10))
+        pen.closePath()
+        self.assertEqual("beginPath() addPoint((10, 10), segmentType='line') "
+                         "addPoint((10, 20)) addPoint((20, 20)) "
+                         "addPoint((20, 10), segmentType='qcurve') endPath()",
+                         repr(tpen))
+
+    def test_quad2(self):
+        tpen = _TestPointPen()
+        pen = SegmentToPointPen(tpen)
+        pen.qCurveTo((10, 20), (20, 20), (20, 10), (10, 10), None)
+        pen.closePath()
+        self.assertEqual("beginPath() addPoint((10, 20)) addPoint((20, 20)) "
+                         "addPoint((20, 10)) addPoint((10, 10)) endPath()",
+                         repr(tpen))
+
+    def test_roundTrip1(self):
+        spen = _TestSegmentPen()
+        pen = SegmentToPointPen(PointToSegmentPen(spen))
+        pen.moveTo((10, 10))
+        pen.lineTo((10, 20))
+        pen.lineTo((20, 20))
+        pen.closePath()
+        self.assertEqual("10 10 moveto 10 20 lineto 20 20 lineto closepath", repr(spen))
+
+    def test_roundTrip2(self):
+        spen = _TestSegmentPen()
+        pen = SegmentToPointPen(PointToSegmentPen(spen))
+        pen.qCurveTo((10, 20), (20, 20), (20, 10), (10, 10), None)
+        pen.closePath()
+        pen.addComponent('base', [1, 0, 0, 1, 0, 0])
+        self.assertEqual("10 20 20 20 20 10 10 10 None qcurveto closepath "
+                         "'base' [1, 0, 0, 1, 0, 0] addcomponent",
+                         repr(spen))
+
+
+class TestGuessSmoothPointPen(unittest.TestCase):
+
+    def test_guessSmooth_exact(self):
+        tpen = _TestPointPen()
+        pen = GuessSmoothPointPen(tpen)
+        pen.beginPath(identifier="foo")
+        pen.addPoint((0, 100), segmentType="curve")
+        pen.addPoint((0, 200))
+        pen.addPoint((400, 200), identifier='bar')
+        pen.addPoint((400, 100), segmentType="curve")
+        pen.addPoint((400, 0))
+        pen.addPoint((0, 0))
+        pen.endPath()
+        self.assertEqual("beginPath(identifier='foo') "
+                         "addPoint((0, 100), segmentType='curve', smooth=True) "
+                         "addPoint((0, 200)) addPoint((400, 200), identifier='bar') "
+                         "addPoint((400, 100), segmentType='curve', smooth=True) "
+                         "addPoint((400, 0)) addPoint((0, 0)) endPath()",
+                         repr(tpen))
+
+    def test_guessSmooth_almost(self):
+        tpen = _TestPointPen()
+        pen = GuessSmoothPointPen(tpen)
+        pen.beginPath()
+        pen.addPoint((0, 100), segmentType="curve")
+        pen.addPoint((1, 200))
+        pen.addPoint((395, 200))
+        pen.addPoint((400, 100), segmentType="curve")
+        pen.addPoint((400, 0))
+        pen.addPoint((0, 0))
+        pen.endPath()
+        self.assertEqual("beginPath() addPoint((0, 100), segmentType='curve', smooth=True) "
+                         "addPoint((1, 200)) addPoint((395, 200)) "
+                         "addPoint((400, 100), segmentType='curve', smooth=True) "
+                         "addPoint((400, 0)) addPoint((0, 0)) endPath()",
+                         repr(tpen))
+
+    def test_guessSmooth_tangent(self):
+        tpen = _TestPointPen()
+        pen = GuessSmoothPointPen(tpen)
+        pen.beginPath()
+        pen.addPoint((0, 0), segmentType="move")
+        pen.addPoint((0, 100), segmentType="line")
+        pen.addPoint((3, 200))
+        pen.addPoint((300, 200))
+        pen.addPoint((400, 200), segmentType="curve")
+        pen.endPath()
+        self.assertEqual("beginPath() addPoint((0, 0), segmentType='move') "
+                         "addPoint((0, 100), segmentType='line', smooth=True) "
+                         "addPoint((3, 200)) addPoint((300, 200)) "
+                         "addPoint((400, 200), segmentType='curve') endPath()",
+                         repr(tpen))
+
+class TestReverseContourPointPen(unittest.TestCase):
+
+    def test_singlePoint(self):
+        tpen = _TestPointPen()
+        pen = ReverseContourPointPen(tpen)
+        pen.beginPath()
+        pen.addPoint((0, 0), segmentType="move")
+        pen.endPath()
+        self.assertEqual("beginPath() "
+                         "addPoint((0, 0), segmentType='move') "
+                         "endPath()",
+                         repr(tpen))
+
+    def test_line(self):
+        tpen = _TestPointPen()
+        pen = ReverseContourPointPen(tpen)
+        pen.beginPath()
+        pen.addPoint((0, 0), segmentType="move")
+        pen.addPoint((0, 100), segmentType="line")
+        pen.endPath()
+        self.assertEqual("beginPath() "
+                         "addPoint((0, 100), segmentType='move') "
+                         "addPoint((0, 0), segmentType='line') "
+                         "endPath()",
+                         repr(tpen))
+
+    def test_triangle(self):
+        tpen = _TestPointPen()
+        pen = ReverseContourPointPen(tpen)
+        pen.beginPath()
+        pen.addPoint((0, 0), segmentType="line")
+        pen.addPoint((0, 100), segmentType="line")
+        pen.addPoint((100, 100), segmentType="line")
+        pen.endPath()
+        self.assertEqual("beginPath() "
+                         "addPoint((0, 0), segmentType='line') "
+                         "addPoint((100, 100), segmentType='line') "
+                         "addPoint((0, 100), segmentType='line') "
+                         "endPath()",
+                         repr(tpen))
+
+    def test_cubicOpen(self):
+        tpen = _TestPointPen()
+        pen = ReverseContourPointPen(tpen)
+        pen.beginPath()
+        pen.addPoint((0, 0), segmentType="move")
+        pen.addPoint((0, 100))
+        pen.addPoint((100, 200))
+        pen.addPoint((200, 200), segmentType="curve")
+        pen.endPath()
+        self.assertEqual("beginPath() "
+                         "addPoint((200, 200), segmentType='move') "
+                         "addPoint((100, 200)) "
+                         "addPoint((0, 100)) "
+                         "addPoint((0, 0), segmentType='curve') "
+                         "endPath()",
+                         repr(tpen))
+
+    def test_quadOpen(self):
+        tpen = _TestPointPen()
+        pen = ReverseContourPointPen(tpen)
+        pen.beginPath()
+        pen.addPoint((0, 0), segmentType="move")
+        pen.addPoint((0, 100))
+        pen.addPoint((100, 200))
+        pen.addPoint((200, 200), segmentType="qcurve")
+        pen.endPath()
+        self.assertEqual("beginPath() "
+                         "addPoint((200, 200), segmentType='move') "
+                         "addPoint((100, 200)) "
+                         "addPoint((0, 100)) "
+                         "addPoint((0, 0), segmentType='qcurve') "
+                         "endPath()",
+                         repr(tpen))
+
+    def test_cubicClosed(self):
+        tpen = _TestPointPen()
+        pen = ReverseContourPointPen(tpen)
+        pen.beginPath()
+        pen.addPoint((0, 0), segmentType="line")
+        pen.addPoint((0, 100))
+        pen.addPoint((100, 200))
+        pen.addPoint((200, 200), segmentType="curve")
+        pen.endPath()
+        self.assertEqual("beginPath() "
+                         "addPoint((0, 0), segmentType='curve') "
+                         "addPoint((200, 200), segmentType='line') "
+                         "addPoint((100, 200)) "
+                         "addPoint((0, 100)) "
+                         "endPath()",
+                         repr(tpen))
+
+    def test_quadClosedOffCurveStart(self):
+        tpen = _TestPointPen()
+        pen = ReverseContourPointPen(tpen)
+        pen.beginPath()
+        pen.addPoint((100, 200))
+        pen.addPoint((200, 200), segmentType="qcurve")
+        pen.addPoint((0, 0), segmentType="line")
+        pen.addPoint((0, 100))
+        pen.endPath()
+        self.assertEqual("beginPath() "
+                         "addPoint((100, 200)) "
+                         "addPoint((0, 100)) "
+                         "addPoint((0, 0), segmentType='qcurve') "
+                         "addPoint((200, 200), segmentType='line') "
+                         "endPath()",
+                         repr(tpen))
+
+    def test_quadNoOnCurve(self):
+        tpen = _TestPointPen()
+        pen = ReverseContourPointPen(tpen)
+        pen.beginPath(identifier='bar')
+        pen.addPoint((0, 0))
+        pen.addPoint((0, 100), identifier='foo', arbitrary='foo')
+        pen.addPoint((100, 200), arbitrary=123)
+        pen.addPoint((200, 200))
+        pen.endPath()
+        pen.addComponent("base", [1, 0, 0, 1, 0, 0], identifier='foo')
+        self.assertEqual("beginPath(identifier='bar') "
+                         "addPoint((0, 0)) "
+                         "addPoint((200, 200)) "
+                         "addPoint((100, 200), arbitrary=123) "
+                         "addPoint((0, 100), identifier='foo', arbitrary='foo') "
+                         "endPath() "
+                         "addComponent('base', [1, 0, 0, 1, 0, 0], identifier='foo')",
+                         repr(tpen))
+
+    def test_closed_line_overlapping_start_end_points(self):
+        # Test case from https://github.com/googlefonts/fontmake/issues/572
+        tpen = _TestPointPen()
+        pen = ReverseContourPointPen(tpen)
+        pen.beginPath()
+        pen.addPoint((0, 651), segmentType="line")
+        pen.addPoint((0, 101), segmentType="line")
+        pen.addPoint((0, 101), segmentType="line")
+        pen.addPoint((0, 651), segmentType="line")
+        pen.endPath()
+        self.assertEqual(
+            "beginPath() "
+            "addPoint((0, 651), segmentType='line') "
+            "addPoint((0, 651), segmentType='line') "
+            "addPoint((0, 101), segmentType='line') "
+            "addPoint((0, 101), segmentType='line') "
+            "endPath()",
+            repr(tpen)
+        )
diff --git a/Tests/pens/quartzPen_test.py b/Tests/pens/quartzPen_test.py
new file mode 100644
index 0000000..3a81d97
--- /dev/null
+++ b/Tests/pens/quartzPen_test.py
@@ -0,0 +1,78 @@
+import unittest
+
+try:
+    from fontTools.pens.quartzPen import QuartzPen
+
+    from Quartz.CoreGraphics import CGPathApply
+    from Quartz.CoreGraphics import kCGPathElementMoveToPoint
+    from Quartz.CoreGraphics import kCGPathElementAddLineToPoint
+    from Quartz.CoreGraphics import kCGPathElementAddQuadCurveToPoint
+    from Quartz.CoreGraphics import kCGPathElementAddCurveToPoint
+    from Quartz.CoreGraphics import kCGPathElementCloseSubpath
+
+    PATH_ELEMENTS = {
+        # CG constant key                    desc       num_points
+        kCGPathElementMoveToPoint:         ('moveto',   1),
+        kCGPathElementAddLineToPoint:      ('lineto',   1),
+        kCGPathElementAddCurveToPoint:     ('curveto',  3),
+        kCGPathElementAddQuadCurveToPoint: ('qcurveto', 2),
+        kCGPathElementCloseSubpath:        ('close',    0),
+    }
+
+    PYOBJC_AVAILABLE = True
+except ImportError:
+    PYOBJC_AVAILABLE = False
+
+
+def draw(pen):
+    pen.moveTo((50, 0))
+    pen.lineTo((50, 500))
+    pen.lineTo((200, 500))
+    pen.curveTo((350, 500), (450, 400), (450, 250))
+    pen.curveTo((450, 100), (350, 0), (200, 0))
+    pen.closePath()
+
+
+def quartzPathApplier(elements, element):
+    num_points = 0
+    elem_type = None
+    if element.type in PATH_ELEMENTS:
+        num_points = PATH_ELEMENTS[element.type][1]
+        elem_type = PATH_ELEMENTS[element.type][0]
+    elements.append((elem_type, element.points.as_tuple(num_points)))
+
+
+def quartzPathElements(path):
+    elements = []
+    CGPathApply(path, elements, quartzPathApplier)
+    return elements
+
+
+def quartzPathToString(path):
+    elements = quartzPathElements(path)
+    output = []
+    for element in elements:
+        elem_type, elem_points = element
+        path_points = " ".join([f"{p.x} {p.y}" for p in elem_points])
+        output.append(f"{elem_type} {path_points}")
+    return " ".join(output)
+
+
+@unittest.skipUnless(PYOBJC_AVAILABLE, "pyobjc not installed")
+class QuartzPenTest(unittest.TestCase):
+    def test_draw(self):
+        pen = QuartzPen(None)
+        draw(pen)
+        self.assertEqual(
+            "moveto 50.0 0.0 lineto 50.0 500.0 lineto 200.0 500.0 curveto 350.0 500.0 450.0 400.0 450.0 250.0 curveto 450.0 100.0 350.0 0.0 200.0 0.0 close ",
+            quartzPathToString(pen.path)
+        )
+
+    def test_empty(self):
+        pen = QuartzPen(None)
+        self.assertEqual("", quartzPathToString(pen.path))
+
+
+if __name__ == '__main__':
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/pens/recordingPen_test.py b/Tests/pens/recordingPen_test.py
index fdc5d06..6977b93 100644
--- a/Tests/pens/recordingPen_test.py
+++ b/Tests/pens/recordingPen_test.py
@@ -1,20 +1,29 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.pens.recordingPen import RecordingPen, DecomposingRecordingPen
+from fontTools.pens.recordingPen import (
+    RecordingPen,
+    DecomposingRecordingPen,
+    RecordingPointPen,
+)
 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()
 
+    def drawPoints(self, pen):
+        pen.beginPath(identifier="abc")
+        pen.addPoint((0.0, 0.0), "line", False, "start", identifier="0000")
+        pen.addPoint((0.0, 100.0), "line", False, None, identifier="0001")
+        pen.addPoint((50.0, 75.0), None, False, None, identifier="0002")
+        pen.addPoint((60.0, 50.0), None, False, None, identifier="0003")
+        pen.addPoint((50.0, 0.0), "curve", True, "last", identifier="0004")
+        pen.endPath()
+
 
 class RecordingPenTest(object):
-
     def test_addComponent(self):
         pen = RecordingPen()
         pen.addComponent("a", (2, 0, 0, 3, -10, 5))
@@ -22,18 +31,42 @@
 
 
 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', ())]
+            ("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"
+
+
+class RecordingPointPenTest:
+    def test_record_and_replay(self):
+        pen = RecordingPointPen()
+        glyph = _TestGlyph()
+        glyph.drawPoints(pen)
+        pen.addComponent("a", (2, 0, 0, 2, -10, 5))
+
+        assert pen.value == [
+            ("beginPath", (), {"identifier": "abc"}),
+            ("addPoint", ((0.0, 0.0), "line", False, "start"), {"identifier": "0000"}),
+            ("addPoint", ((0.0, 100.0), "line", False, None), {"identifier": "0001"}),
+            ("addPoint", ((50.0, 75.0), None, False, None), {"identifier": "0002"}),
+            ("addPoint", ((60.0, 50.0), None, False, None), {"identifier": "0003"}),
+            ("addPoint", ((50.0, 0.0), "curve", True, "last"), {"identifier": "0004"}),
+            ("endPath", (), {}),
+            ("addComponent", ("a", (2, 0, 0, 2, -10, 5)), {}),
+        ]
+
+        pen2 = RecordingPointPen()
+        pen.replay(pen2)
+
+        assert pen2.value == pen.value
diff --git a/Tests/pens/reverseContourPen_test.py b/Tests/pens/reverseContourPen_test.py
index bace806..9c71540 100644
--- a/Tests/pens/reverseContourPen_test.py
+++ b/Tests/pens/reverseContourPen_test.py
@@ -278,6 +278,26 @@
             ('lineTo', ((848, 348),)),  # the duplicate point is kept
             ('closePath', ())
         ]
+    ),
+    # Test case from https://github.com/googlefonts/fontmake/issues/572
+    # An additional closing lineTo is required to disambiguate a duplicate
+    # point at the end of a contour from the implied closing line.
+    (
+        [
+            ('moveTo', ((0, 651),)),
+            ('lineTo', ((0, 101),)),
+            ('lineTo', ((0, 101),)),
+            ('lineTo', ((0, 651),)),
+            ('lineTo', ((0, 651),)),
+            ('closePath', ())
+        ],
+        [
+            ('moveTo', ((0, 651),)),
+            ('lineTo', ((0, 651),)),
+            ('lineTo', ((0, 101),)),
+            ('lineTo', ((0, 101),)),
+            ('closePath', ())
+        ]
     )
 ]
 
@@ -293,11 +313,8 @@
 
 @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")
+    from fontTools.ufoLib.pointPen import (
+        ReverseContourPointPen, PointToSegmentPen, SegmentToPointPen)
 
     recpen = RecordingPen()
     pt2seg = PointToSegmentPen(recpen, outputImpliedClosingLine=True)
diff --git a/Tests/pens/t2CharStringPen_test.py b/Tests/pens/t2CharStringPen_test.py
index 7225fc4..b710df5 100644
--- a/Tests/pens/t2CharStringPen_test.py
+++ b/Tests/pens/t2CharStringPen_test.py
@@ -1,5 +1,3 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.pens.t2CharStringPen import T2CharStringPen
 import unittest
 
@@ -8,16 +6,12 @@
 
     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)
+            if isinstance(i1, str):
+                self.assertIsInstance(i2, str)
                 self.assertEqual(i1, i2)
             else:
                 self.assertAlmostEqual(i1, i2)
@@ -142,14 +136,14 @@
         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.49), (9.9, 39.9), (0.1, 40.1))
+        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, 10, -10, 10, -10, 'hvcurveto',
+             10, 10, 10, 10, 11, -10, 9, -10, 'hvcurveto',
              'endchar'],
             charstring.program)
 
diff --git a/Tests/pens/ttGlyphPen_test.py b/Tests/pens/ttGlyphPen_test.py
index 328773e..96d75a1 100644
--- a/Tests/pens/ttGlyphPen_test.py
+++ b/Tests/pens/ttGlyphPen_test.py
@@ -1,57 +1,63 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-
 import os
-import unittest
+import pytest
+import struct
 
 from fontTools import ttLib
-from fontTools.pens.ttGlyphPen import TTGlyphPen
+from fontTools.pens.basePen import PenError
+from fontTools.pens.ttGlyphPen import TTGlyphPen, TTGlyphPointPen, MAX_F2DOT14
 
 
-class TTGlyphPenTest(unittest.TestCase):
-
+class TTGlyphPenTestBase:
     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)
+            "..",
+            "ttLib",
+            "data",
+            filename,
+        )
         font.importXML(ttx_path)
 
         glyphSet = font.getGlyphSet()
-        glyfTable = font['glyf']
-        pen = TTGlyphPen(font.getGlyphSet())
+        glyfTable = font["glyf"]
+        pen = self.penClass(font.getGlyphSet())
 
         for name in font.getGlyphOrder():
             oldGlyph = glyphSet[name]
-            oldGlyph.draw(pen)
+            getattr(oldGlyph, self.drawMethod)(pen)
             oldGlyph = oldGlyph._glyph
             newGlyph = pen.glyph()
 
-            if hasattr(oldGlyph, 'program'):
+            if hasattr(oldGlyph, "program"):
                 newGlyph.program = oldGlyph.program
 
-            self.assertEqual(
-                oldGlyph.compile(glyfTable), newGlyph.compile(glyfTable))
+            assert oldGlyph.compile(glyfTable) == newGlyph.compile(glyfTable)
 
     def test_e2e_linesAndSimpleComponents(self):
-        self.runEndToEnd('TestTTF-Regular.ttx')
+        self.runEndToEnd("TestTTF-Regular.ttx")
 
     def test_e2e_curvesAndComponentTransforms(self):
-        self.runEndToEnd('TestTTFComplex-Regular.ttx')
+        self.runEndToEnd("TestTTFComplex-Regular.ttx")
+
+
+class TTGlyphPenTest(TTGlyphPenTestBase):
+    penClass = TTGlyphPen
+    drawMethod = "draw"
 
     def test_moveTo_errorWithinContour(self):
         pen = TTGlyphPen(None)
         pen.moveTo((0, 0))
-        with self.assertRaises(AssertionError):
+        with pytest.raises(PenError):
             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)
+        assert not pen.points
+        assert not pen.types
+        assert not pen.endPts
 
     def test_endPath_sameAsClosePath(self):
         pen = TTGlyphPen(None)
@@ -68,16 +74,16 @@
         pen.endPath()
         endPathGlyph = pen.glyph()
 
-        self.assertEqual(closePathGlyph, endPathGlyph)
+        assert closePathGlyph == endPathGlyph
 
     def test_glyph_errorOnUnendedContour(self):
         pen = TTGlyphPen(None)
         pen.moveTo((0, 0))
-        with self.assertRaises(AssertionError):
+        with pytest.raises(PenError):
             pen.glyph()
 
     def test_glyph_decomposes(self):
-        componentName = 'a'
+        componentName = "a"
         glyphSet = {}
         pen = TTGlyphPen(glyphSet)
 
@@ -92,6 +98,7 @@
         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))
@@ -104,7 +111,7 @@
         pen.closePath()
         plainGlyph = pen.glyph()
 
-        self.assertEqual(plainGlyph, compositeGlyph)
+        assert plainGlyph == compositeGlyph
 
     def test_remove_extra_move_points(self):
         pen = TTGlyphPen(None)
@@ -112,8 +119,8 @@
         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))
+        assert len(pen.points) == 4
+        assert pen.points[0] == (0, 0)
 
     def test_keep_move_point(self):
         pen = TTGlyphPen(None)
@@ -122,8 +129,8 @@
         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))
+        assert len(pen.points) == 5
+        assert pen.points[0] == (0, 0)
 
     def test_keep_duplicate_end_point(self):
         pen = TTGlyphPen(None)
@@ -132,8 +139,441 @@
         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))
+        assert len(pen.points) == 5
+        assert 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()
+
+        assert 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()
+
+        assert 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()
+
+        assert 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()
+
+        assert compositeGlyph.components[0].transform == ((3, 0), (0, 1))
+
+        with pytest.raises(struct.error):
+            compositeGlyph.compile({"a": baseGlyph})
+
+    def assertGlyphBoundsEqual(self, glyph, bounds):
+        assert (glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax) == bounds
+
+    def test_round_float_coordinates_and_component_offsets(self):
+        glyphSet = {}
+        pen = TTGlyphPen(glyphSet)
+
+        pen.moveTo((0, 0))
+        pen.lineTo((0, 1))
+        pen.lineTo((367.6, 0))
+        pen.closePath()
+        simpleGlyph = pen.glyph()
+
+        simpleGlyph.recalcBounds(glyphSet)
+        self.assertGlyphBoundsEqual(simpleGlyph, (0, 0, 368, 1))
+
+        componentName = "a"
+        glyphSet[componentName] = simpleGlyph
+
+        pen.addComponent(componentName, (1, 0, 0, 1, -86.4, 0))
+        compositeGlyph = pen.glyph()
+
+        compositeGlyph.recalcBounds(glyphSet)
+        self.assertGlyphBoundsEqual(compositeGlyph, (-86, 0, 282, 1))
+
+    def test_scaled_component_bounds(self):
+        glyphSet = {}
+
+        pen = TTGlyphPen(glyphSet)
+        pen.moveTo((-231, 939))
+        pen.lineTo((-55, 939))
+        pen.lineTo((-55, 745))
+        pen.lineTo((-231, 745))
+        pen.closePath()
+        glyphSet["gravecomb"] = pen.glyph()
+
+        pen = TTGlyphPen(glyphSet)
+        pen.moveTo((-278, 939))
+        pen.lineTo((8, 939))
+        pen.lineTo((8, 745))
+        pen.lineTo((-278, 745))
+        pen.closePath()
+        glyphSet["circumflexcomb"] = pen.glyph()
+
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("circumflexcomb", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("gravecomb", (0.9, 0, 0, 0.9, 198, 180))
+        glyphSet["uni0302_uni0300"] = uni0302_uni0300 = pen.glyph()
+
+        uni0302_uni0300.recalcBounds(glyphSet)
+        self.assertGlyphBoundsEqual(uni0302_uni0300, (-278, 745, 148, 1025))
+
+
+class TTGlyphPointPenTest(TTGlyphPenTestBase):
+    penClass = TTGlyphPointPen
+    drawMethod = "drawPoints"
+
+    def test_glyph_simple(self):
+        pen = TTGlyphPointPen(None)
+        pen.beginPath()
+        pen.addPoint((50, 0), "line")
+        pen.addPoint((450, 0), "line")
+        pen.addPoint((450, 700), "line")
+        pen.addPoint((50, 700), "line")
+        pen.endPath()
+        glyph = pen.glyph()
+        assert glyph.numberOfContours == 1
+        assert glyph.endPtsOfContours == [3]
+
+    def test_addPoint_errorOnCurve(self):
+        pen = TTGlyphPointPen(None)
+        pen.beginPath()
+        with pytest.raises(NotImplementedError):
+            pen.addPoint((0, 0), "curve")
+
+    def test_beginPath_beginPathOnOpenPath(self):
+        pen = TTGlyphPointPen(None)
+        pen.beginPath()
+        pen.addPoint((0, 0))
+        with pytest.raises(PenError):
+            pen.beginPath()
+
+    def test_glyph_errorOnUnendedContour(self):
+        pen = TTGlyphPointPen(None)
+        pen.beginPath()
+        pen.addPoint((0, 0))
+        with pytest.raises(PenError):
+            pen.glyph()
+
+    def test_glyph_errorOnEmptyContour(self):
+        pen = TTGlyphPointPen(None)
+        pen.beginPath()
+        with pytest.raises(PenError):
+            pen.endPath()
+
+    def test_glyph_decomposes(self):
+        componentName = "a"
+        glyphSet = {}
+        pen = TTGlyphPointPen(glyphSet)
+
+        pen.beginPath()
+        pen.addPoint((0, 0), "line")
+        pen.addPoint((0, 1), "line")
+        pen.addPoint((1, 0), "line")
+        pen.endPath()
+        glyphSet[componentName] = _TestGlyph(pen.glyph())
+
+        pen.beginPath()
+        pen.addPoint((0, 0), "line")
+        pen.addPoint((0, 1), "line")
+        pen.addPoint((1, 0), "line")
+        pen.endPath()
+        pen.addComponent(componentName, (1, 0, 0, 1, 2, 0))
+        pen.addComponent("missing", (1, 0, 0, 1, 0, 0))  # skipped
+        compositeGlyph = pen.glyph()
+
+        pen.beginPath()
+        pen.addPoint((0, 0), "line")
+        pen.addPoint((0, 1), "line")
+        pen.addPoint((1, 0), "line")
+        pen.endPath()
+        pen.beginPath()
+        pen.addPoint((2, 0), "line")
+        pen.addPoint((2, 1), "line")
+        pen.addPoint((3, 0), "line")
+        pen.endPath()
+        plainGlyph = pen.glyph()
+
+        assert plainGlyph == compositeGlyph
+
+    def test_keep_duplicate_end_point(self):
+        pen = TTGlyphPointPen(None)
+        pen.beginPath()
+        pen.addPoint((0, 0), "line")
+        pen.addPoint((100, 0), "line")
+        pen.addPoint((100, 50))
+        pen.addPoint((50, 100))
+        pen.addPoint((0, 0), "qcurve")
+        pen.addPoint((0, 0), "line")  # the duplicate point is not removed
+        pen.endPath()
+        assert len(pen.points) == 6
+        assert pen.points[0] == (0, 0)
+
+    def test_within_range_component_transform(self):
+        componentName = "a"
+        glyphSet = {}
+        pen = TTGlyphPointPen(glyphSet)
+
+        pen.beginPath()
+        pen.addPoint((0, 0), "line")
+        pen.addPoint((0, 1), "line")
+        pen.addPoint((1, 0), "line")
+        pen.endPath()
+        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()
+
+        assert expectedGlyph == compositeGlyph
+
+    def test_clamp_to_almost_2_component_transform(self):
+        componentName = "a"
+        glyphSet = {}
+        pen = TTGlyphPointPen(glyphSet)
+
+        pen.beginPath()
+        pen.addPoint((0, 0), "line")
+        pen.addPoint((0, 1), "line")
+        pen.addPoint((1, 0), "line")
+        pen.endPath()
+        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()
+
+        assert expectedGlyph == compositeGlyph
+
+    def test_out_of_range_transform_decomposed(self):
+        componentName = "a"
+        glyphSet = {}
+        pen = TTGlyphPointPen(glyphSet)
+
+        pen.beginPath()
+        pen.addPoint((0, 0), "line")
+        pen.addPoint((0, 1), "line")
+        pen.addPoint((1, 0), "line")
+        pen.endPath()
+        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.beginPath()
+        pen.addPoint((0, 0), "line")
+        pen.addPoint((0, 2), "line")
+        pen.addPoint((3, 0), "line")
+        pen.endPath()
+        pen.beginPath()
+        pen.addPoint((-1, 2), "line")
+        pen.addPoint((-1, 3), "line")
+        pen.addPoint((0, 2), "line")
+        pen.endPath()
+        pen.beginPath()
+        pen.addPoint((0, 0), "line")
+        pen.addPoint((0, -3), "line")
+        pen.addPoint((2, 0), "line")
+        pen.endPath()
+        expectedGlyph = pen.glyph()
+
+        assert expectedGlyph == compositeGlyph
+
+    def test_no_handle_overflowing_transform(self):
+        componentName = "a"
+        glyphSet = {}
+        pen = TTGlyphPointPen(glyphSet, handleOverflowingTransforms=False)
+
+        pen.beginPath()
+        pen.addPoint((0, 0), "line")
+        pen.addPoint((0, 1), "line")
+        pen.addPoint((1, 0), "line")
+        pen.endPath()
+        baseGlyph = pen.glyph()
+        glyphSet[componentName] = _TestGlyph(baseGlyph)
+
+        pen.addComponent(componentName, (3, 0, 0, 1, 0, 0))
+        compositeGlyph = pen.glyph()
+
+        assert compositeGlyph.components[0].transform == ((3, 0), (0, 1))
+
+        with pytest.raises(struct.error):
+            compositeGlyph.compile({"a": baseGlyph})
+
+    def assertGlyphBoundsEqual(self, glyph, bounds):
+        assert (glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax) == bounds
+
+    def test_round_float_coordinates_and_component_offsets(self):
+        glyphSet = {}
+        pen = TTGlyphPointPen(glyphSet)
+
+        pen.beginPath()
+        pen.addPoint((0, 0), "line")
+        pen.addPoint((0, 1), "line")
+        pen.addPoint((367.6, 0), "line")
+        pen.endPath()
+        simpleGlyph = pen.glyph()
+
+        simpleGlyph.recalcBounds(glyphSet)
+        self.assertGlyphBoundsEqual(simpleGlyph, (0, 0, 368, 1))
+
+        componentName = "a"
+        glyphSet[componentName] = simpleGlyph
+
+        pen.addComponent(componentName, (1, 0, 0, 1, -86.4, 0))
+        compositeGlyph = pen.glyph()
+
+        compositeGlyph.recalcBounds(glyphSet)
+        self.assertGlyphBoundsEqual(compositeGlyph, (-86, 0, 282, 1))
+
+    def test_scaled_component_bounds(self):
+        glyphSet = {}
+
+        pen = TTGlyphPointPen(glyphSet)
+        pen.beginPath()
+        pen.addPoint((-231, 939), "line")
+        pen.addPoint((-55, 939), "line")
+        pen.addPoint((-55, 745), "line")
+        pen.addPoint((-231, 745), "line")
+        pen.endPath()
+        glyphSet["gravecomb"] = pen.glyph()
+
+        pen = TTGlyphPointPen(glyphSet)
+        pen.beginPath()
+        pen.addPoint((-278, 939), "line")
+        pen.addPoint((8, 939), "line")
+        pen.addPoint((8, 745), "line")
+        pen.addPoint((-278, 745), "line")
+        pen.endPath()
+        glyphSet["circumflexcomb"] = pen.glyph()
+
+        pen = TTGlyphPointPen(glyphSet)
+        pen.addComponent("circumflexcomb", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("gravecomb", (0.9, 0, 0, 0.9, 198, 180))
+        glyphSet["uni0302_uni0300"] = uni0302_uni0300 = pen.glyph()
+
+        uni0302_uni0300.recalcBounds(glyphSet)
+        self.assertGlyphBoundsEqual(uni0302_uni0300, (-278, 745, 148, 1025))
+
+    def test_open_path_starting_with_move(self):
+        # when a contour starts with a 'move' point, it signifies the beginnig
+        # of an open contour.
+        # https://unifiedfontobject.org/versions/ufo3/glyphs/glif/#point-types
+        pen1 = TTGlyphPointPen(None)
+        pen1.beginPath()
+        pen1.addPoint((0, 0), "move")  # contour is open
+        pen1.addPoint((10, 10), "line")
+        pen1.addPoint((20, 20))
+        pen1.addPoint((20, 0), "qcurve")
+        pen1.endPath()
+
+        pen2 = TTGlyphPointPen(None)
+        pen2.beginPath()
+        pen2.addPoint((0, 0), "line")  # contour is closed
+        pen2.addPoint((10, 10), "line")
+        pen2.addPoint((20, 20))
+        pen2.addPoint((20, 0), "qcurve")
+        pen2.endPath()
+
+        # Since TrueType contours are always implicitly closed, the pen will
+        # interpret both these paths as equivalent
+        assert pen1.points == pen2.points == [(0, 0), (10, 10), (20, 20), (20, 0)]
+        assert pen1.types == pen2.types == [1, 1, 0, 1]
+
 
 
 class _TestGlyph(object):
@@ -146,7 +586,8 @@
             pen.lineTo(point)
         pen.closePath()
 
-
-if __name__ == '__main__':
-    import sys
-    sys.exit(unittest.main())
+    def drawPoints(self, pen):
+        pen.beginPath()
+        for point in self.coordinates:
+            pen.addPoint(point, "line")
+        pen.endPath()
diff --git a/Tests/pens/utils.py b/Tests/pens/utils.py
new file mode 100644
index 0000000..dced3c1
--- /dev/null
+++ b/Tests/pens/utils.py
@@ -0,0 +1,281 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+from . import CUBIC_GLYPHS
+from fontTools.pens.pointPen import PointToSegmentPen, SegmentToPointPen
+from math import isclose
+import unittest
+
+
+class BaseDummyPen(object):
+    """Base class for pens that record the commands they are called with."""
+
+    def __init__(self, *args, **kwargs):
+        self.commands = []
+
+    def __str__(self):
+        """Return the pen commands as a string of python code."""
+        return _repr_pen_commands(self.commands)
+
+    def addComponent(self, glyphName, transformation, **kwargs):
+        self.commands.append(('addComponent', (glyphName, transformation), kwargs))
+
+
+class DummyPen(BaseDummyPen):
+    """A SegmentPen that records the commands it's called with."""
+
+    def moveTo(self, pt):
+        self.commands.append(('moveTo', (pt,), {}))
+
+    def lineTo(self, pt):
+        self.commands.append(('lineTo', (pt,), {}))
+
+    def curveTo(self, *points):
+        self.commands.append(('curveTo', points, {}))
+
+    def qCurveTo(self, *points):
+        self.commands.append(('qCurveTo', points, {}))
+
+    def closePath(self):
+        self.commands.append(('closePath', tuple(), {}))
+
+    def endPath(self):
+        self.commands.append(('endPath', tuple(), {}))
+
+
+class DummyPointPen(BaseDummyPen):
+    """A PointPen that records the commands it's called with."""
+
+    def beginPath(self, **kwargs):
+        self.commands.append(('beginPath', tuple(), kwargs))
+
+    def endPath(self):
+        self.commands.append(('endPath', tuple(), {}))
+
+    def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
+        kwargs['segmentType'] = str(segmentType) if segmentType else None
+        kwargs['smooth'] = smooth
+        kwargs['name'] = name
+        self.commands.append(('addPoint', (pt,), kwargs))
+
+
+class DummyGlyph(object):
+    """Provides a minimal interface for storing a glyph's outline data in a
+    SegmentPen-oriented way. The glyph's outline consists in the list of
+    SegmentPen commands required to draw it.
+    """
+
+    # the SegmentPen class used to draw on this glyph type
+    DrawingPen = DummyPen
+
+    def __init__(self, glyph=None):
+        """If another glyph (i.e. any object having a 'draw' method) is given,
+        its outline data is copied to self.
+        """
+        self._pen = self.DrawingPen()
+        self.outline = self._pen.commands
+        if glyph:
+            self.appendGlyph(glyph)
+
+    def appendGlyph(self, glyph):
+        """Copy another glyph's outline onto self."""
+        glyph.draw(self._pen)
+
+    def getPen(self):
+        """Return the SegmentPen that can 'draw' on this glyph."""
+        return self._pen
+
+    def getPointPen(self):
+        """Return a PointPen adapter that can 'draw' on this glyph."""
+        return PointToSegmentPen(self._pen)
+
+    def draw(self, pen):
+        """Use another SegmentPen to replay the glyph's outline commands."""
+        if self.outline:
+            for cmd, args, kwargs in self.outline:
+                getattr(pen, cmd)(*args, **kwargs)
+
+    def drawPoints(self, pointPen):
+        """Use another PointPen to replay the glyph's outline commands,
+        indirectly through an adapter.
+        """
+        pen = SegmentToPointPen(pointPen)
+        self.draw(pen)
+
+    def __eq__(self, other):
+        """Return True if 'other' glyph's outline is the same as self."""
+        if hasattr(other, 'outline'):
+            return self.outline == other.outline
+        elif hasattr(other, 'draw'):
+            return self.outline == self.__class__(other).outline
+        return NotImplemented
+
+    def __ne__(self, other):
+        """Return True if 'other' glyph's outline is different from self."""
+        return not (self == other)
+
+    def approx(self, other, rel_tol=1e-12):
+        if hasattr(other, 'outline'):
+            outline2 == other.outline
+        elif hasattr(other, 'draw'):
+            outline2 = self.__class__(other).outline
+        else:
+            raise TypeError(type(other).__name__)
+        outline1 = self.outline
+        if len(outline1) != len(outline2):
+            return False
+        for (cmd1, arg1, kwd1), (cmd2, arg2, kwd2) in zip(outline1, outline2):
+            if cmd1 != cmd2:
+                return False
+            if kwd1 != kwd2:
+                return False
+            if arg1:
+                if isinstance(arg1[0], tuple):
+                    if not arg2 or not isinstance(arg2[0], tuple):
+                        return False
+                    for (x1, y1), (x2, y2) in zip(arg1, arg2):
+                        if (
+                            not isclose(x1, x2, rel_tol=rel_tol) or
+                            not isclose(y1, y2, rel_tol=rel_tol)
+                        ):
+                            return False
+                elif arg1 != arg2:
+                    return False
+            elif arg2:
+                return False
+        return True
+
+    def __str__(self):
+        """Return commands making up the glyph's outline as a string."""
+        return str(self._pen)
+
+
+class DummyPointGlyph(DummyGlyph):
+    """Provides a minimal interface for storing a glyph's outline data in a
+    PointPen-oriented way. The glyph's outline consists in the list of
+    PointPen commands required to draw it.
+    """
+
+    # the PointPen class used to draw on this glyph type
+    DrawingPen = DummyPointPen
+
+    def appendGlyph(self, glyph):
+        """Copy another glyph's outline onto self."""
+        glyph.drawPoints(self._pen)
+
+    def getPen(self):
+        """Return a SegmentPen adapter that can 'draw' on this glyph."""
+        return SegmentToPointPen(self._pen)
+
+    def getPointPen(self):
+        """Return the PointPen that can 'draw' on this glyph."""
+        return self._pen
+
+    def draw(self, pen):
+        """Use another SegmentPen to replay the glyph's outline commands,
+        indirectly through an adapter.
+        """
+        pointPen = PointToSegmentPen(pen)
+        self.drawPoints(pointPen)
+
+    def drawPoints(self, pointPen):
+        """Use another PointPen to replay the glyph's outline commands."""
+        if self.outline:
+            for cmd, args, kwargs in self.outline:
+                getattr(pointPen, cmd)(*args, **kwargs)
+
+
+def _repr_pen_commands(commands):
+    """
+    >>> print(_repr_pen_commands([
+    ...     ('moveTo', tuple(), {}),
+    ...     ('lineTo', ((1.0, 0.1),), {}),
+    ...     ('curveTo', ((1.0, 0.1), (2.0, 0.2), (3.0, 0.3)), {})
+    ... ]))
+    pen.moveTo()
+    pen.lineTo((1, 0.1))
+    pen.curveTo((1, 0.1), (2, 0.2), (3, 0.3))
+
+    >>> print(_repr_pen_commands([
+    ...     ('beginPath', tuple(), {}),
+    ...     ('addPoint', ((1.0, 0.1),),
+    ...      {"segmentType":"line", "smooth":True, "name":"test", "z":1}),
+    ... ]))
+    pen.beginPath()
+    pen.addPoint((1, 0.1), name='test', segmentType='line', smooth=True, z=1)
+
+    >>> print(_repr_pen_commands([
+    ...    ('addComponent', ('A', (1, 0, 0, 1, 0, 0)), {})
+    ... ]))
+    pen.addComponent('A', (1, 0, 0, 1, 0, 0))
+    """
+    s = []
+    for cmd, args, kwargs in commands:
+        if args:
+            if isinstance(args[0], tuple):
+                # cast float to int if there're no digits after decimal point,
+                # and round floats to 12 decimal digits (more than enough)
+                args = [
+                    tuple((int(v) if int(v) == v else round(v, 12)) for v in pt)
+                    for pt in args
+                ]
+            args = ", ".join(repr(a) for a in args)
+        if kwargs:
+            kwargs = ", ".join("%s=%r" % (k, v)
+                               for k, v in sorted(kwargs.items()))
+        if args and kwargs:
+            s.append("pen.%s(%s, %s)" % (cmd, args, kwargs))
+        elif args:
+            s.append("pen.%s(%s)" % (cmd, args))
+        elif kwargs:
+            s.append("pen.%s(%s)" % (cmd, kwargs))
+        else:
+            s.append("pen.%s()" % cmd)
+    return "\n".join(s)
+
+
+class TestDummyGlyph(unittest.TestCase):
+
+    def test_equal(self):
+        # verify that the copy and the copy of the copy are equal to
+        # the source glyph's outline, as well as to each other
+        source = CUBIC_GLYPHS['a']
+        copy = DummyGlyph(source)
+        copy2 = DummyGlyph(copy)
+        self.assertEqual(source, copy)
+        self.assertEqual(source, copy2)
+        self.assertEqual(copy, copy2)
+        # assert equality doesn't hold any more after modification
+        copy.outline.pop()
+        self.assertNotEqual(source, copy)
+        self.assertNotEqual(copy, copy2)
+
+
+class TestDummyPointGlyph(unittest.TestCase):
+
+    def test_equal(self):
+        # same as above but using the PointPen protocol
+        source = CUBIC_GLYPHS['a']
+        copy = DummyPointGlyph(source)
+        copy2 = DummyPointGlyph(copy)
+        self.assertEqual(source, copy)
+        self.assertEqual(source, copy2)
+        self.assertEqual(copy, copy2)
+        copy.outline.pop()
+        self.assertNotEqual(source, copy)
+        self.assertNotEqual(copy, copy2)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/Tests/subset/data/CmapSubsetTest.subset.ttx b/Tests/subset/data/CmapSubsetTest.subset.ttx
new file mode 100644
index 0000000..10b94a3
--- /dev/null
+++ b/Tests/subset/data/CmapSubsetTest.subset.ttx
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.18">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/subset/data/CmapSubsetTest.ttx b/Tests/subset/data/CmapSubsetTest.ttx
new file mode 100644
index 0000000..ffbfae7
--- /dev/null
+++ b/Tests/subset/data/CmapSubsetTest.ttx
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.18">
+
+  <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="basket"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="0.0"/>
+    <checkSumAdjustment value="0xc643119c"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Jan 12 16:39:39 2021"/>
+    <modified value="Tue Jan 12 16:39:39 2021"/>
+    <xMin value="50"/>
+    <yMin value="-200"/>
+    <xMax value="450"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="942"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <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="3"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="3"/>
+    <maxPoints value="8"/>
+    <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="660"/>
+    <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="300"/>
+    <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="00000010 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="97"/>
+    <usLastCharIndex value="65535"/>
+    <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="500" lsb="50"/>
+    <mtx name="a" width="538" lsb="0"/>
+    <mtx name="basket" width="942" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x1f9fa" name="basket"/><!-- BASKET -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x1f9fa" name="basket"/><!-- BASKET -->
+    </cmap_format_12>
+  </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a"/><!-- contains no outline data -->
+
+    <TTGlyph name="basket"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      New Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      0.000;NONE;NewFont-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      New Font Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 0.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NewFont-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="basket"/>
+    </extraNames>
+  </post>
+
+</ttFont>
diff --git a/Tests/subset/data/GPOS_PairPos_Format2_ClassDef1_useClass0.subset.ttx b/Tests/subset/data/GPOS_PairPos_Format2_ClassDef1_useClass0.subset.ttx
new file mode 100644
index 0000000..3df9aa8
--- /dev/null
+++ b/Tests/subset/data/GPOS_PairPos_Format2_ClassDef1_useClass0.subset.ttx
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.21">
+
+  <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>
+            <Glyph value="g33"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="g33" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XPlacement="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XPlacement="-100"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/subset/data/GPOS_PairPos_Format2_ClassDef2_useClass0.subset.ttx b/Tests/subset/data/GPOS_PairPos_Format2_ClassDef2_useClass0.subset.ttx
new file mode 100644
index 0000000..dc599f1
--- /dev/null
+++ b/Tests/subset/data/GPOS_PairPos_Format2_ClassDef2_useClass0.subset.ttx
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.21">
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=0 -->
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/subset/data/GPOS_PairPos_Format2_PR_2221.ttx b/Tests/subset/data/GPOS_PairPos_Format2_PR_2221.ttx
new file mode 100644
index 0000000..d5132d1
--- /dev/null
+++ b/Tests/subset/data/GPOS_PairPos_Format2_PR_2221.ttx
@@ -0,0 +1,322 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.21">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="g33"/>
+    <GlyphID id="2" name="g35"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x3d6ba467"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000001"/>
+    <unitsPerEm value="1500"/>
+    <created value="Thu Jan  1 00:00:00 1970"/>
+    <modified value="Mon Mar 29 14:18:07 2021"/>
+    <xMin value="24"/>
+    <yMin value="-31"/>
+    <xMax value="1000"/>
+    <yMax value="689"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="2500"/>
+    <descent value="0"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="1500"/>
+    <minLeftSideBearing value="300"/>
+    <minRightSideBearing value="224"/>
+    <xMaxExtent value="1276"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="3"/>
+  </maxp>
+
+  <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="33"/>
+    <usLastCharIndex value="35"/>
+    <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>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      gpos2_2_font5
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      gpos2_2_font5
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      gpos2_2_font5
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version1.0
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      gpos2_2_font5
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x21" name="g33"/><!-- EXCLAMATION MARK -->
+      <map code="0x23" name="g35"/><!-- NUMBER SIGN -->
+    </cmap_format_4>
+  </cmap>
+
+  <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>
+
+  <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="24 -31 1000 689"/>
+      <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">
+            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="1">
+            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>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          endchar
+        </CharString>
+        <CharString name="g33">
+          -107 callsubr
+          -107 callsubr
+          endchar
+        </CharString>
+        <CharString name="g35">
+          -107 callsubr
+          -106 callsubr
+          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="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="g33"/>
+            <Glyph value="g35"/>
+          </Coverage>
+          <ValueFormat1 value="1"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1 Format="1">
+            <ClassDef glyph="g33" class="1"/>
+            <ClassDef glyph="g35" class="2"/>
+          </ClassDef1>
+          <ClassDef2 Format="1">
+            <ClassDef glyph="g33" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=3 -->
+          <!-- 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>
+          <Class1Record index="2">
+            <Class2Record index="0">
+              <Value1 XPlacement="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XPlacement="-100"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <hmtx>
+    <mtx name=".notdef" width="1500" lsb="300"/>
+    <mtx name="g33" width="1500" lsb="300"/>
+    <mtx name="g35" width="1500" lsb="300"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/subset/data/GPOS_SinglePos_no_value_issue_2312.subset.ttx b/Tests/subset/data/GPOS_SinglePos_no_value_issue_2312.subset.ttx
new file mode 100644
index 0000000..7eee95f
--- /dev/null
+++ b/Tests/subset/data/GPOS_SinglePos_no_value_issue_2312.subset.ttx
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.24">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="ra-sidd"/>
+    <GlyphID id="2" name="ra-sidd.ini"/>
+    <GlyphID id="3" name="ra-sidd.iniThird"/>
+    <GlyphID id="4" name="r-sidd.med.ra"/>
+  </GlyphOrder>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=3 -->
+            <FeatureIndex index="0" value="1"/>
+            <FeatureIndex index="1" value="3"/>
+            <FeatureIndex index="2" value="4"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="sidd"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=4 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="2"/>
+            <FeatureIndex index="2" value="3"/>
+            <FeatureIndex index="3" value="4"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=5 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="dist"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="2"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <FeatureTag value="mark"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="4">
+        <FeatureTag value="mkmk"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="ra-sidd.ini"/>
+            <Glyph value="ra-sidd.iniThird"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="r-sidd.med.ra"/>
+          </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="ra-sidd.ini"/>
+            <Glyph value="ra-sidd.iniThird"/>
+          </Coverage>
+          <ValueFormat value="0"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="ra-sidd"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="r-sidd.med.ra" class="1"/>
+            <ClassDef glyph="ra-sidd" class="1"/>
+            <ClassDef glyph="ra-sidd.ini" class="1"/>
+            <ClassDef glyph="ra-sidd.iniThird" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="r-sidd.med.ra"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="ra-sidd"/>
+            <Glyph value="ra-sidd.ini"/>
+            <Glyph value="ra-sidd.iniThird"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="258"/>
+                <YCoordinate value="435"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=3 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="257"/>
+                <YCoordinate value="-53"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="1">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="258"/>
+                <YCoordinate value="435"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="2">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="258"/>
+                <YCoordinate value="435"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="16"/><!-- useMarkFilteringSet -->
+        <!-- SubTableCount=1 -->
+        <MarkMarkPos index="0" Format="1">
+          <Mark1Coverage>
+            <Glyph value="r-sidd.med.ra"/>
+          </Mark1Coverage>
+          <Mark2Coverage>
+            <Glyph value="r-sidd.med.ra"/>
+          </Mark2Coverage>
+          <!-- ClassCount=1 -->
+          <Mark1Array>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="258"/>
+                <YCoordinate value="435"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </Mark1Array>
+          <Mark2Array>
+            <!-- Mark2Count=1 -->
+            <Mark2Record index="0">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="258"/>
+                <YCoordinate value="235"/>
+              </Mark2Anchor>
+            </Mark2Record>
+          </Mark2Array>
+        </MarkMarkPos>
+        <MarkFilteringSet value="0"/>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/subset/data/GPOS_SinglePos_no_value_issue_2312.ttx b/Tests/subset/data/GPOS_SinglePos_no_value_issue_2312.ttx
new file mode 100644
index 0000000..9149637
--- /dev/null
+++ b/Tests/subset/data/GPOS_SinglePos_no_value_issue_2312.ttx
@@ -0,0 +1,689 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.24">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="ra-sidd"/>
+    <GlyphID id="2" name="ra-sidd.ini"/>
+    <GlyphID id="3" name="ra-sidd.iniThird"/>
+    <GlyphID id="4" name="r-sidd.med.ra"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.001"/>
+    <checkSumAdjustment value="0x78efacfb"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Mon Feb 11 05:00:43 2019"/>
+    <modified value="Fri Dec 25 13:11:40 2020"/>
+    <xMin value="-220"/>
+    <yMin value="-1025"/>
+    <xMax value="1053"/>
+    <yMax value="995"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-1030"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1083"/>
+    <minLeftSideBearing value="-220"/>
+    <minRightSideBearing value="-842"/>
+    <xMaxExtent value="1053"/>
+    <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="288"/>
+    <maxContours value="22"/>
+    <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="625"/>
+    <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="410"/>
+    <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="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange2 value="00000010 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="65535"/>
+    <usLastCharIndex value="65535"/>
+    <sTypoAscender value="1000"/>
+    <sTypoDescender value="-1030"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="1030"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="683"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="9"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="94"/>
+    <mtx name="r-sidd.med.ra" width="0" lsb="41"/>
+    <mtx name="ra-sidd" width="501" lsb="38"/>
+    <mtx name="ra-sidd.ini" width="522" lsb="41"/>
+    <mtx name="ra-sidd.iniThird" width="522" lsb="41"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="28" language="0" nGroups="1">
+      <map code="0x115a8" name="ra-sidd"/><!-- SIDDHAM LETTER RA -->
+    </cmap_format_12>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="28" language="0" nGroups="1">
+      <map code="0x115a8" name="ra-sidd"/><!-- SIDDHAM LETTER RA -->
+    </cmap_format_12>
+  </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="r-sidd.med.ra" xMin="41" yMin="205" xMax="482" yMax="483">
+      <contour>
+        <pt x="213" y="252" on="1"/>
+        <pt x="213" y="291" on="1"/>
+        <pt x="140" y="338" on="0"/>
+        <pt x="66" y="395" on="0"/>
+        <pt x="41" y="430" on="0"/>
+        <pt x="41" y="443" on="1"/>
+        <pt x="41" y="457" on="0"/>
+        <pt x="53" y="464" on="1"/>
+        <pt x="74" y="451" on="0"/>
+        <pt x="98" y="442" on="1"/>
+        <pt x="116" y="471" on="0"/>
+        <pt x="151" y="471" on="1"/>
+        <pt x="181" y="471" on="0"/>
+        <pt x="233" y="450" on="0"/>
+        <pt x="269" y="450" on="1"/>
+        <pt x="302" y="450" on="0"/>
+        <pt x="367" y="467" on="0"/>
+        <pt x="392" y="483" on="1"/>
+        <pt x="482" y="420" on="1"/>
+        <pt x="466" y="398" on="0"/>
+        <pt x="407" y="367" on="0"/>
+        <pt x="346" y="339" on="0"/>
+        <pt x="303" y="304" on="0"/>
+        <pt x="303" y="276" on="1"/>
+        <pt x="303" y="218" on="1"/>
+        <pt x="303" y="205" on="0"/>
+        <pt x="293" y="205" on="1"/>
+        <pt x="282" y="205" on="0"/>
+        <pt x="243" y="221" on="0"/>
+        <pt x="213" y="243" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="ra-sidd" xMin="38" yMin="-103" xMax="481" yMax="683">
+      <contour>
+        <pt x="40" y="643" on="1"/>
+        <pt x="40" y="657" on="0"/>
+        <pt x="52" y="664" on="1"/>
+        <pt x="73" y="651" on="0"/>
+        <pt x="97" y="642" on="1"/>
+        <pt x="115" y="671" on="0"/>
+        <pt x="150" y="671" on="1"/>
+        <pt x="180" y="671" on="0"/>
+        <pt x="232" y="650" on="0"/>
+        <pt x="268" y="650" on="1"/>
+        <pt x="301" y="650" on="0"/>
+        <pt x="366" y="667" on="0"/>
+        <pt x="391" y="683" on="1"/>
+        <pt x="481" y="620" on="1"/>
+        <pt x="465" y="598" on="0"/>
+        <pt x="406" y="567" on="0"/>
+        <pt x="345" y="539" on="0"/>
+        <pt x="302" y="504" on="0"/>
+        <pt x="302" y="476" on="1"/>
+        <pt x="302" y="142" on="1"/>
+        <pt x="302" y="78" on="0"/>
+        <pt x="277" y="50" on="1"/>
+        <pt x="458" y="-74" on="1"/>
+        <pt x="439" y="-103" on="1"/>
+        <pt x="245" y="29" on="1"/>
+        <pt x="232" y="25" on="0"/>
+        <pt x="215" y="25" on="1"/>
+        <pt x="186" y="25" on="0"/>
+        <pt x="123" y="53" on="0"/>
+        <pt x="83" y="86" on="1"/>
+        <pt x="58" y="108" on="0"/>
+        <pt x="38" y="138" on="0"/>
+        <pt x="38" y="151" on="1"/>
+        <pt x="38" y="173" on="0"/>
+        <pt x="64" y="173" on="1"/>
+        <pt x="81" y="173" on="0"/>
+        <pt x="128" y="152" on="0"/>
+        <pt x="170" y="123" on="1"/>
+        <pt x="183" y="114" on="1"/>
+        <pt x="198" y="115" on="0"/>
+        <pt x="212" y="134" on="0"/>
+        <pt x="212" y="159" on="1"/>
+        <pt x="212" y="491" on="1"/>
+        <pt x="139" y="538" on="0"/>
+        <pt x="65" y="595" on="0"/>
+        <pt x="40" y="630" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="ra-sidd.ini" xMin="41" yMin="405" xMax="482" yMax="683">
+      <contour>
+        <pt x="213" y="452" on="1"/>
+        <pt x="213" y="491" on="1"/>
+        <pt x="140" y="538" on="0"/>
+        <pt x="66" y="595" on="0"/>
+        <pt x="41" y="630" on="0"/>
+        <pt x="41" y="643" on="1"/>
+        <pt x="41" y="657" on="0"/>
+        <pt x="53" y="664" on="1"/>
+        <pt x="74" y="651" on="0"/>
+        <pt x="98" y="642" on="1"/>
+        <pt x="116" y="671" on="0"/>
+        <pt x="151" y="671" on="1"/>
+        <pt x="181" y="671" on="0"/>
+        <pt x="233" y="650" on="0"/>
+        <pt x="269" y="650" on="1"/>
+        <pt x="302" y="650" on="0"/>
+        <pt x="367" y="667" on="0"/>
+        <pt x="392" y="683" on="1"/>
+        <pt x="482" y="620" on="1"/>
+        <pt x="466" y="598" on="0"/>
+        <pt x="407" y="567" on="0"/>
+        <pt x="346" y="539" on="0"/>
+        <pt x="303" y="504" on="0"/>
+        <pt x="303" y="476" on="1"/>
+        <pt x="303" y="418" on="1"/>
+        <pt x="303" y="405" on="0"/>
+        <pt x="293" y="405" on="1"/>
+        <pt x="282" y="405" on="0"/>
+        <pt x="243" y="421" on="0"/>
+        <pt x="213" y="443" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="ra-sidd.iniThird" xMin="41" yMin="405" xMax="482" yMax="683">
+      <contour>
+        <pt x="213" y="452" on="1"/>
+        <pt x="213" y="491" on="1"/>
+        <pt x="140" y="538" on="0"/>
+        <pt x="66" y="595" on="0"/>
+        <pt x="41" y="630" on="0"/>
+        <pt x="41" y="643" on="1"/>
+        <pt x="41" y="657" on="0"/>
+        <pt x="53" y="664" on="1"/>
+        <pt x="74" y="651" on="0"/>
+        <pt x="98" y="642" on="1"/>
+        <pt x="116" y="671" on="0"/>
+        <pt x="151" y="671" on="1"/>
+        <pt x="181" y="671" on="0"/>
+        <pt x="233" y="650" on="0"/>
+        <pt x="269" y="650" on="1"/>
+        <pt x="302" y="650" on="0"/>
+        <pt x="367" y="667" on="0"/>
+        <pt x="392" y="683" on="1"/>
+        <pt x="482" y="620" on="1"/>
+        <pt x="466" y="598" on="0"/>
+        <pt x="407" y="567" on="0"/>
+        <pt x="346" y="539" on="0"/>
+        <pt x="303" y="504" on="0"/>
+        <pt x="303" y="476" on="1"/>
+        <pt x="303" y="418" on="1"/>
+        <pt x="303" y="405" on="0"/>
+        <pt x="293" y="405" on="1"/>
+        <pt x="282" y="405" on="0"/>
+        <pt x="243" y="421" on="0"/>
+        <pt x="213" y="443" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2019 Google Inc. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Noto Sans Siddham
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      2.001;GOOG;NotoSansSiddham-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Noto Sans Siddham Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NotoSansSiddham-Regular
+    </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 -->
+      <psName name="ra-sidd"/>
+      <psName name="ra-sidd.ini"/>
+      <psName name="ra-sidd.iniThird"/>
+      <psName name="r-sidd.med.ra"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010002"/>
+    <GlyphClassDef>
+      <ClassDef glyph="r-sidd.med.ra" class="3"/>
+      <ClassDef glyph="ra-sidd" class="1"/>
+      <ClassDef glyph="ra-sidd.ini" class="1"/>
+      <ClassDef glyph="ra-sidd.iniThird" class="1"/>
+    </GlyphClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=3 -->
+      <Coverage index="0">
+        <Glyph value="r-sidd.med.ra"/>
+      </Coverage>
+      <Coverage index="1">
+      </Coverage>
+      <Coverage index="2">
+        <Glyph value="r-sidd.med.ra"/>
+      </Coverage>
+    </MarkGlyphSetsDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=3 -->
+            <FeatureIndex index="0" value="1"/>
+            <FeatureIndex index="1" value="3"/>
+            <FeatureIndex index="2" value="4"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="sidd"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=4 -->
+            <FeatureIndex index="0" value="0"/>
+            <FeatureIndex index="1" value="2"/>
+            <FeatureIndex index="2" value="3"/>
+            <FeatureIndex index="3" value="4"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=5 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="dist"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="2"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <FeatureTag value="mark"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="4">
+        <FeatureTag value="mkmk"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="ra-sidd.ini"/>
+            <Glyph value="ra-sidd.iniThird"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="r-sidd.med.ra"/>
+          </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="ra-sidd.ini"/>
+            <Glyph value="ra-sidd.iniThird"/>
+          </Coverage>
+          <ValueFormat value="0"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="ra-sidd"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="r-sidd.med.ra" class="1"/>
+            <ClassDef glyph="ra-sidd" class="1"/>
+            <ClassDef glyph="ra-sidd.ini" class="1"/>
+            <ClassDef glyph="ra-sidd.iniThird" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="r-sidd.med.ra"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="ra-sidd"/>
+            <Glyph value="ra-sidd.ini"/>
+            <Glyph value="ra-sidd.iniThird"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="258"/>
+                <YCoordinate value="435"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=3 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="257"/>
+                <YCoordinate value="-53"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="1">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="258"/>
+                <YCoordinate value="435"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="2">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="258"/>
+                <YCoordinate value="435"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="6"/>
+        <LookupFlag value="16"/><!-- useMarkFilteringSet -->
+        <!-- SubTableCount=1 -->
+        <MarkMarkPos index="0" Format="1">
+          <Mark1Coverage>
+            <Glyph value="r-sidd.med.ra"/>
+          </Mark1Coverage>
+          <Mark2Coverage>
+            <Glyph value="r-sidd.med.ra"/>
+          </Mark2Coverage>
+          <!-- ClassCount=1 -->
+          <Mark1Array>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="258"/>
+                <YCoordinate value="435"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </Mark1Array>
+          <Mark2Array>
+            <!-- Mark2Count=1 -->
+            <Mark2Record index="0">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="258"/>
+                <YCoordinate value="235"/>
+              </Mark2Anchor>
+            </Mark2Record>
+          </Mark2Array>
+        </MarkMarkPos>
+        <MarkFilteringSet value="0"/>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="sidd"/>
+        <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="pref"/>
+        <Feature>
+          <!-- LookupCount=0 -->
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="psts"/>
+        <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=1 -->
+          <InputCoverage index="0">
+            <Glyph value="ra-sidd.ini"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="r-sidd.med.ra"/>
+          </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="ra-sidd.ini" out="ra-sidd.iniThird"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/subset/data/Lobster.subset.otf b/Tests/subset/data/Lobster.subset.otf
new file mode 100644
index 0000000..8147542
--- /dev/null
+++ b/Tests/subset/data/Lobster.subset.otf
Binary files differ
diff --git a/Tests/subset/data/Lobster.subset.ttx b/Tests/subset/data/Lobster.subset.ttx
index c35e570..8089b24 100644
--- a/Tests/subset/data/Lobster.subset.ttx
+++ b/Tests/subset/data/Lobster.subset.ttx
@@ -490,7 +490,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="2">
+          <Coverage>
             <Glyph value="one"/>
             <Glyph value="three"/>
             <Glyph value="two"/>
@@ -616,7 +616,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="A" out="A.salt"/>
           <Substitution in="B" out="B.salt"/>
         </SingleSubst>
@@ -625,7 +625,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="I">
             <Ligature components="J" glyph="IJ"/>
           </LigatureSet>
@@ -635,7 +635,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="A" out="A.salt"/>
           <Substitution in="B" out="B.salt"/>
         </SingleSubst>
diff --git a/Tests/subset/data/TestContextSubstFormat3.ttx b/Tests/subset/data/TestContextSubstFormat3.ttx
new file mode 100644
index 0000000..0ed43ee
--- /dev/null
+++ b/Tests/subset/data/TestContextSubstFormat3.ttx
@@ -0,0 +1,604 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.9">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="plus"/>
+    <GlyphID id="2" name="glyph00002"/>
+    <GlyphID id="3" name="glyph00003"/>
+    <GlyphID id="4" name="glyph00004"/>
+    <GlyphID id="5" name="glyph00005"/>
+    <GlyphID id="6" name="glyph00006"/>
+    <GlyphID id="7" name="glyph00007"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xa6bcdc24"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001111"/>
+    <unitsPerEm value="1000"/>
+    <created value="Mon Nov 21 06:10:39 2016"/>
+    <modified value="Fri Apr 24 05:31:23 2020"/>
+    <xMin value="-1000"/>
+    <yMin value="-509"/>
+    <xMax value="1135"/>
+    <yMax value="1194"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="8"/>
+    <fontDirectionHint value="0"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="977"/>
+    <descent value="-205"/>
+    <lineGap value="67"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="-1000"/>
+    <minRightSideBearing value="-1000"/>
+    <xMaxExtent value="1135"/>
+    <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>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="8"/>
+    <maxPoints value="240"/>
+    <maxContours value="41"/>
+    <maxCompositePoints value="163"/>
+    <maxCompositeContours value="12"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="4"/>
+    <maxComponentDepth value="3"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="500"/>
+    <usWeightClass value="500"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000000"/>
+    <ySubscriptXSize value="665"/>
+    <ySubscriptYSize value="716"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="143"/>
+    <ySuperscriptXSize value="0"/>
+    <ySuperscriptYSize value="0"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="0"/>
+    <yStrikeoutSize value="51"/>
+    <yStrikeoutPosition value="265"/>
+    <sFamilyClass value="2057"/>
+    <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="BE5N"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="43"/>
+    <usLastCharIndex value="43"/>
+    <sTypoAscender value="977"/>
+    <sTypoDescender value="-272"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="977"/>
+    <usWinDescent value="272"/>
+    <ulCodePageRange1 value="00100000 00000000 00000001 00011111"/>
+    <ulCodePageRange2 value="11000100 00000000 00000000 00000000"/>
+    <sxHeight value="530"/>
+    <sCapHeight value="735"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="8"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="57"/>
+    <mtx name="glyph00002" width="500" lsb="57"/>
+    <mtx name="glyph00003" width="500" lsb="57"/>
+    <mtx name="glyph00004" width="500" lsb="-8"/>
+    <mtx name="glyph00005" width="500" lsb="-8"/>
+    <mtx name="glyph00006" width="500" lsb="-8"/>
+    <mtx name="glyph00007" width="500" lsb="-65"/>
+    <mtx name="plus" width="500" lsb="57"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x2b" name="plus"/><!-- PLUS SIGN -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x2b" name="plus"/><!-- PLUS 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"/><!-- contains no outline data -->
+
+    <TTGlyph name="glyph00002" xMin="57" yMin="139" xMax="508" yMax="541">
+      <contour>
+        <pt x="203" y="139" on="1"/>
+        <pt x="203" y="298" on="1"/>
+        <pt x="57" y="298" on="1"/>
+        <pt x="57" y="382" on="1"/>
+        <pt x="203" y="382" on="1"/>
+        <pt x="203" y="541" on="1"/>
+        <pt x="297" y="541" on="1"/>
+        <pt x="297" y="382" on="1"/>
+        <pt x="508" y="382" on="1"/>
+        <pt x="508" y="298" on="1"/>
+        <pt x="297" y="298" on="1"/>
+        <pt x="297" y="139" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="glyph00003" xMin="57" yMin="139" xMax="508" yMax="541">
+      <contour>
+        <pt x="260" y="139" on="1"/>
+        <pt x="260" y="298" on="1"/>
+        <pt x="57" y="298" on="1"/>
+        <pt x="57" y="382" on="1"/>
+        <pt x="260" y="382" on="1"/>
+        <pt x="260" y="541" on="1"/>
+        <pt x="354" y="541" on="1"/>
+        <pt x="354" y="382" on="1"/>
+        <pt x="508" y="382" on="1"/>
+        <pt x="508" y="298" on="1"/>
+        <pt x="354" y="298" on="1"/>
+        <pt x="354" y="139" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="glyph00004" xMin="-8" yMin="139" xMax="508" yMax="541">
+      <contour>
+        <pt x="203" y="139" on="1"/>
+        <pt x="203" y="298" on="1"/>
+        <pt x="-8" y="298" on="1"/>
+        <pt x="-8" y="382" on="1"/>
+        <pt x="203" y="382" on="1"/>
+        <pt x="203" y="541" on="1"/>
+        <pt x="297" y="541" on="1"/>
+        <pt x="297" y="382" on="1"/>
+        <pt x="508" y="382" on="1"/>
+        <pt x="508" y="298" on="1"/>
+        <pt x="297" y="298" on="1"/>
+        <pt x="297" y="139" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="glyph00005" xMin="-8" yMin="139" xMax="443" yMax="541">
+      <contour>
+        <pt x="203" y="139" on="1"/>
+        <pt x="203" y="298" on="1"/>
+        <pt x="-8" y="298" on="1"/>
+        <pt x="-8" y="382" on="1"/>
+        <pt x="203" y="382" on="1"/>
+        <pt x="203" y="541" on="1"/>
+        <pt x="297" y="541" on="1"/>
+        <pt x="297" y="382" on="1"/>
+        <pt x="443" y="382" on="1"/>
+        <pt x="443" y="298" on="1"/>
+        <pt x="297" y="298" on="1"/>
+        <pt x="297" y="139" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="glyph00006" xMin="-8" yMin="139" xMax="443" yMax="541">
+      <contour>
+        <pt x="146" y="139" on="1"/>
+        <pt x="146" y="298" on="1"/>
+        <pt x="-8" y="298" on="1"/>
+        <pt x="-8" y="382" on="1"/>
+        <pt x="146" y="382" on="1"/>
+        <pt x="146" y="541" on="1"/>
+        <pt x="240" y="541" on="1"/>
+        <pt x="240" y="382" on="1"/>
+        <pt x="443" y="382" on="1"/>
+        <pt x="443" y="298" on="1"/>
+        <pt x="240" y="298" on="1"/>
+        <pt x="240" y="139" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="glyph00007" xMin="-65" yMin="139" xMax="443" yMax="541">
+      <contour>
+        <pt x="203" y="139" on="1"/>
+        <pt x="203" y="298" on="1"/>
+        <pt x="-65" y="298" on="1"/>
+        <pt x="-65" y="382" on="1"/>
+        <pt x="203" y="382" on="1"/>
+        <pt x="203" y="541" on="1"/>
+        <pt x="297" y="541" on="1"/>
+        <pt x="297" y="382" on="1"/>
+        <pt x="443" y="382" on="1"/>
+        <pt x="443" y="298" on="1"/>
+        <pt x="297" y="298" on="1"/>
+        <pt x="297" y="139" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="plus" xMin="57" yMin="139" xMax="443" yMax="541">
+      <contour>
+        <pt x="203" y="139" on="1"/>
+        <pt x="203" y="298" on="1"/>
+        <pt x="57" y="298" on="1"/>
+        <pt x="57" y="382" on="1"/>
+        <pt x="203" y="382" on="1"/>
+        <pt x="203" y="541" on="1"/>
+        <pt x="297" y="541" on="1"/>
+        <pt x="297" y="382" on="1"/>
+        <pt x="443" y="382" on="1"/>
+        <pt x="443" y="298" on="1"/>
+        <pt x="297" y="298" on="1"/>
+        <pt x="297" y="139" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (c) 2015-2019 Belleve Invis.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Iosevka Medium
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Iosevka Medium Version 3.0.0-rc.8
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Iosevka Medium
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 3.0.0-rc.8; ttfautohint (v1.8.3)
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      Iosevka-Medium
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-50"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="6380"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="1"/>
+  </post>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph=".notdef" class="1"/>
+      <ClassDef glyph="glyph00002" class="1"/>
+      <ClassDef glyph="glyph00003" class="1"/>
+      <ClassDef glyph="glyph00004" class="1"/>
+      <ClassDef glyph="glyph00005" class="1"/>
+      <ClassDef glyph="glyph00006" class="1"/>
+      <ClassDef glyph="glyph00007" class="1"/>
+      <ClassDef glyph="plus" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <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="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <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="calt"/>
+        <Feature>
+          <!-- LookupCount=2 -->
+          <LookupListIndex index="0" value="0"/>
+          <LookupListIndex index="1" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=6 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ChainContextSubst index="0" Format="2">
+          <Coverage>
+            <Glyph value="plus"/>
+          </Coverage>
+          <BacktrackClassDef>
+            <ClassDef glyph="glyph00005" class="1"/>
+            <ClassDef glyph="glyph00007" class="1"/>
+          </BacktrackClassDef>
+          <InputClassDef>
+            <ClassDef glyph="plus" class="1"/>
+          </InputClassDef>
+          <LookAheadClassDef>
+          </LookAheadClassDef>
+          <!-- ChainSubClassSetCount=2 -->
+          <ChainSubClassSet index="0" empty="1"/>
+          <ChainSubClassSet index="1">
+            <!-- ChainSubClassRuleCount=4 -->
+            <ChainSubClassRule index="0">
+              <!-- BacktrackGlyphCount=1 -->
+              <Backtrack index="0" value="1"/>
+              <!-- InputGlyphCount=1 -->
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=1 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="5"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+            <ChainSubClassRule index="1">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=4 -->
+              <Input index="0" value="1"/>
+              <Input index="1" value="1"/>
+              <Input index="2" value="1"/>
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=4 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="4"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="3"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="2">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="3"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="3">
+                <SequenceIndex value="3"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+            <ChainSubClassRule index="2">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=3 -->
+              <Input index="0" value="1"/>
+              <Input index="1" value="1"/>
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=3 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="4"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="3"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="2">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+            <ChainSubClassRule index="3">
+              <!-- BacktrackGlyphCount=0 -->
+              <!-- InputGlyphCount=2 -->
+              <Input index="0" value="1"/>
+              <!-- LookAheadGlyphCount=0 -->
+              <!-- SubstCount=2 -->
+              <SubstLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="4"/>
+              </SubstLookupRecord>
+              <SubstLookupRecord index="1">
+                <SequenceIndex value="1"/>
+                <LookupListIndex value="2"/>
+              </SubstLookupRecord>
+            </ChainSubClassRule>
+          </ChainSubClassSet>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="5"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <ContextSubst index="0" Format="3">
+          <!-- GlyphCount=3 -->
+          <!-- SubstCount=2 -->
+          <Coverage index="0">
+            <Glyph value="glyph00002"/>
+          </Coverage>
+          <Coverage index="1">
+            <Glyph value="glyph00004"/>
+          </Coverage>
+          <Coverage index="2">
+            <Glyph value="glyph00005"/>
+          </Coverage>
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="5"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="1">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="5"/>
+          </SubstLookupRecord>
+        </ContextSubst>
+        <ContextSubst index="1" Format="3">
+          <!-- GlyphCount=2 -->
+          <!-- SubstCount=2 -->
+          <Coverage index="0">
+            <Glyph value="glyph00002"/>
+          </Coverage>
+          <Coverage index="1">
+            <Glyph value="glyph00005"/>
+          </Coverage>
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="5"/>
+          </SubstLookupRecord>
+          <SubstLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="5"/>
+          </SubstLookupRecord>
+        </ContextSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="plus" out="glyph00005"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="plus" out="glyph00004"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="plus" out="glyph00002"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="5">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="glyph00002" out="glyph00003"/>
+          <Substitution in="glyph00005" out="glyph00006"/>
+          <Substitution in="plus" out="glyph00007"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/subset/data/TestHVVAR.ttx b/Tests/subset/data/TestHVVAR.ttx
new file mode 100644
index 0000000..3e74652
--- /dev/null
+++ b/Tests/subset/data/TestHVVAR.ttx
@@ -0,0 +1,443 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.40">
+
+  <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="D"/>
+    <GlyphID id="5" name="E"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xddab44f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000001"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Apr 23 17:22:52 2019"/>
+    <modified value="Tue Apr 23 17:22:52 2019"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="0"/>
+    <yMax value="0"/>
+    <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="580"/>
+    <xMaxExtent value="80"/>
+    <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="1"/>
+    <maxContours value="1"/>
+    <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="A" width="700" lsb="0"/>
+    <mtx name="B" width="625" lsb="40"/>
+    <mtx name="C" width="660" lsb="80"/>
+    <mtx name="D" width="660" lsb="80"/>
+    <mtx name="E" width="660" lsb="80"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x41" name="A"/><!-- SPACE -->
+      <map code="0x42" name="B"/><!-- DIGIT ZERO -->
+      <map code="0x43" name="C"/><!-- EQUALS SIGN -->
+      <map code="0x44" name="D"/><!-- MINUS SIGN -->
+      <map code="0x45" name="E"/><!-- PLUS 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="0" yMin="0" xMax="0" yMax="0">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A"/><!-- contains no outline data -->
+
+    <TTGlyph name="B" xMin="0" yMin="0" xMax="0" yMax="0">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="C" xMin="0" yMin="0" xMax="0" yMax="0">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="D" xMin="0" yMin="0" xMax="0" yMax="0">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="E" xMin="0" yMin="0" xMax="0" yMax="0">
+      <contour>
+        <pt x="0" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestHVVAR
+    </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;TestHVVAR-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestHVVAR
+    </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">
+      TestHVVAR-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      TestHVVAR
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;UKWN;TestHVVAR-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      TestHVVAR-Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestHVVAR-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>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=2 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.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>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=2 -->
+      <VarData index="0">
+        <!-- ItemCount=6 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="1"/>
+        <Item index="0" value="[1]"/>
+        <Item index="1" value="[2]"/>
+        <Item index="2" value="[3]"/>
+        <Item index="3" value="[4]"/>
+        <Item index="4" value="[5]"/>
+        <Item index="5" value="[6]"/>
+      </VarData>
+      <VarData index="1">
+        <!-- ItemCount=6 -->
+        <NumShorts value="1"/>
+        <!-- VarRegionCount=2 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+        <Item index="0" value="[10, 21]"/>
+        <Item index="1" value="[11, 22]"/>
+        <Item index="2" value="[126, 23]"/>
+        <Item index="3" value="[127, 24]"/>
+        <Item index="4" value="[-128, 25]"/>
+        <Item index="5" value="[-129, 26]"/>
+      </VarData>
+    </VarStore>
+    <LsbMap>
+      <Map glyph=".notdef" outer="0" inner="2"/>
+      <Map glyph="A" outer="0" inner="5"/>
+      <Map glyph="B" outer="1" inner="1"/>
+      <Map glyph="C" outer="1" inner="4"/>
+      <Map glyph="D" outer="0" inner="3"/>
+      <Map glyph="E" outer="1" inner="0"/>
+    </LsbMap>
+    <RsbMap>
+      <Map glyph=".notdef" outer="0" inner="1"/>
+      <Map glyph="A" outer="0" inner="4"/>
+      <Map glyph="B" outer="1" inner="5"/>
+      <Map glyph="C" outer="0" inner="0"/>
+      <Map glyph="D" outer="1" inner="2"/>
+      <Map glyph="E" outer="1" inner="3"/>
+    </RsbMap>
+  </HVAR>
+
+  <VVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=4 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.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>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-0.5"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="3">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.5"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=2 -->
+      <VarData index="0">
+        <!-- ItemCount=4 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="1"/>
+        <Item index="0" value="[32]"/>
+        <Item index="1" value="[17]"/>
+        <Item index="2" value="[0]"/>
+        <Item index="3" value="[14]"/>
+        <Item index="4" value="[5]"/>
+        <Item index="5" value="[6]"/>
+      </VarData>
+      <VarData index="1">
+        <!-- ItemCount=6 -->
+        <NumShorts value="3"/>
+        <!-- VarRegionCount=4 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+        <VarRegionIndex index="2" value="2"/>
+        <VarRegionIndex index="3" value="3"/>
+        <Item index="0" value="[0, 1, 11, 21]"/>
+        <Item index="1" value="[0, 2, 12, 22]"/>
+        <Item index="2" value="[128, 130, 13, 23]"/>
+        <Item index="3" value="[0, 4, 14, 24]"/>
+        <Item index="4" value="[0, 5, -129, 25]"/>
+        <Item index="5" value="[14, 6, 16, 26]"/>
+      </VarData>
+    </VarStore>
+    <AdvHeightMap>
+      <Map glyph=".notdef" outer="1" inner="0"/>
+      <Map glyph="A" outer="1" inner="2"/>
+      <Map glyph="B" outer="0" inner="2"/>
+      <Map glyph="C" outer="0" inner="0"/>
+      <Map glyph="D" outer="1" inner="1"/>
+      <Map glyph="E" outer="0" inner="1"/>
+    </AdvHeightMap>
+    <VOrgMap>
+      <Map glyph=".notdef" outer="1" inner="3"/>
+      <Map glyph="A" outer="1" inner="5"/>
+      <Map glyph="B" outer="0" inner="5"/>
+      <Map glyph="C" outer="0" inner="3"/>
+      <Map glyph="D" outer="1" inner="4"/>
+      <Map glyph="E" outer="0" inner="4"/>
+    </VOrgMap>
+  </VVAR>
+
+  <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>
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>100.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+    <NamedInstance flags="0x0" subfamilyNameID="258">
+      <coord axis="wght" value="100.0"/>
+    </NamedInstance>
+    <NamedInstance flags="0x0" subfamilyNameID="259">
+      <coord axis="wght" value="300.0"/>
+    </NamedInstance>
+    <NamedInstance flags="0x0" subfamilyNameID="260">
+      <coord axis="wght" value="400.0"/>
+    </NamedInstance>
+    <NamedInstance flags="0x0" subfamilyNameID="261">
+      <coord axis="wght" value="700.0"/>
+    </NamedInstance>
+    <NamedInstance flags="0x0" subfamilyNameID="262">
+      <coord axis="wght" value="900.0"/>
+    </NamedInstance>
+  </fvar>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_HVVAR.ttx b/Tests/subset/data/expect_HVVAR.ttx
new file mode 100644
index 0000000..5fbc177
--- /dev/null
+++ b/Tests/subset/data/expect_HVVAR.ttx
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.40">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="B"/>
+    <GlyphID id="2" name="D"/>
+  </GlyphOrder>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=2 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.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>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=2 -->
+      <VarData index="0">
+        <!-- ItemCount=5 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="1"/>
+        <Item index="0" value="[1]"/>
+        <Item index="1" value="[3]"/>
+        <Item index="2" value="[5]"/>
+        <Item index="3" value="[2]"/>
+        <Item index="4" value="[4]"/>
+      </VarData>
+      <VarData index="1">
+        <!-- ItemCount=3 -->
+        <NumShorts value="1"/>
+        <!-- VarRegionCount=2 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+        <Item index="0" value="[11, 22]"/>
+        <Item index="1" value="[126, 23]"/>
+        <Item index="2" value="[-129, 26]"/>
+      </VarData>
+    </VarStore>
+    <LsbMap>
+      <Map glyph=".notdef" outer="0" inner="1"/>
+      <Map glyph="B" outer="1" inner="0"/>
+      <Map glyph="D" outer="0" inner="4"/>
+    </LsbMap>
+    <RsbMap>
+      <Map glyph=".notdef" outer="0" inner="3"/>
+      <Map glyph="B" outer="1" inner="2"/>
+      <Map glyph="D" outer="1" inner="1"/>
+    </RsbMap>
+  </HVAR>
+
+  <VVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=3 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <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="-0.5"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.5"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=2 -->
+      <VarData index="0">
+        <!-- ItemCount=2 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="0"/>
+        <Item index="0" value="[0]"/>
+        <Item index="1" value="[6]"/>
+      </VarData>
+      <VarData index="1">
+        <!-- ItemCount=4 -->
+        <NumShorts value="1"/>
+        <!-- VarRegionCount=3 -->
+        <VarRegionIndex index="0" value="1"/>
+        <VarRegionIndex index="1" value="0"/>
+        <VarRegionIndex index="2" value="2"/>
+        <Item index="0" value="[11, 1, 21]"/>
+        <Item index="1" value="[12, 2, 22]"/>
+        <Item index="2" value="[14, 4, 24]"/>
+        <Item index="3" value="[-129, 5, 25]"/>
+      </VarData>
+    </VarStore>
+    <AdvHeightMap>
+      <Map glyph=".notdef" outer="1" inner="0"/>
+      <Map glyph="B" outer="0" inner="0"/>
+      <Map glyph="D" outer="1" inner="1"/>
+    </AdvHeightMap>
+    <VOrgMap>
+      <Map glyph=".notdef" outer="1" inner="2"/>
+      <Map glyph="B" outer="0" inner="1"/>
+      <Map glyph="D" outer="1" inner="3"/>
+    </VOrgMap>
+  </VVAR>
+
+  <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>
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>100.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+    <NamedInstance flags="0x0" subfamilyNameID="258">
+      <coord axis="wght" value="100.0"/>
+    </NamedInstance>
+    <NamedInstance flags="0x0" subfamilyNameID="259">
+      <coord axis="wght" value="300.0"/>
+    </NamedInstance>
+    <NamedInstance flags="0x0" subfamilyNameID="260">
+      <coord axis="wght" value="400.0"/>
+    </NamedInstance>
+    <NamedInstance flags="0x0" subfamilyNameID="261">
+      <coord axis="wght" value="700.0"/>
+    </NamedInstance>
+    <NamedInstance flags="0x0" subfamilyNameID="262">
+      <coord axis="wght" value="900.0"/>
+    </NamedInstance>
+  </fvar>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_HVVAR_retain_gids.ttx b/Tests/subset/data/expect_HVVAR_retain_gids.ttx
new file mode 100644
index 0000000..8e51ca7
--- /dev/null
+++ b/Tests/subset/data/expect_HVVAR_retain_gids.ttx
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.40">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="glyph00001"/>
+    <GlyphID id="2" name="B"/>
+    <GlyphID id="3" name="glyph00003"/>
+    <GlyphID id="4" name="D"/>
+  </GlyphOrder>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=2 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.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>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=2 -->
+      <VarData index="0">
+        <!-- ItemCount=6 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="1"/>
+        <Item index="0" value="[1]"/>
+        <Item index="1" value="[2]"/>
+        <Item index="2" value="[3]"/>
+        <Item index="3" value="[4]"/>
+        <Item index="4" value="[5]"/>
+        <Item index="5" value="[0]"/>
+      </VarData>
+      <VarData index="1">
+        <!-- ItemCount=3 -->
+        <NumShorts value="1"/>
+        <!-- VarRegionCount=2 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+        <Item index="0" value="[11, 22]"/>
+        <Item index="1" value="[126, 23]"/>
+        <Item index="2" value="[-129, 26]"/>
+      </VarData>
+    </VarStore>
+    <LsbMap>
+      <Map glyph=".notdef" outer="0" inner="2"/>
+      <Map glyph="B" outer="1" inner="0"/>
+      <Map glyph="D" outer="0" inner="3"/>
+      <Map glyph="glyph00001" outer="0" inner="0"/>
+      <Map glyph="glyph00003" outer="0" inner="0"/>
+    </LsbMap>
+    <RsbMap>
+      <Map glyph=".notdef" outer="0" inner="1"/>
+      <Map glyph="B" outer="1" inner="2"/>
+      <Map glyph="D" outer="1" inner="1"/>
+      <Map glyph="glyph00001" outer="0" inner="0"/>
+      <Map glyph="glyph00003" outer="0" inner="0"/>
+    </RsbMap>
+  </HVAR>
+
+  <VVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=3 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <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="-0.5"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.5"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=2 -->
+      <VarData index="0">
+        <!-- ItemCount=2 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="0"/>
+        <Item index="0" value="[0]"/>
+        <Item index="1" value="[6]"/>
+      </VarData>
+      <VarData index="1">
+        <!-- ItemCount=4 -->
+        <NumShorts value="1"/>
+        <!-- VarRegionCount=3 -->
+        <VarRegionIndex index="0" value="1"/>
+        <VarRegionIndex index="1" value="0"/>
+        <VarRegionIndex index="2" value="2"/>
+        <Item index="0" value="[11, 1, 21]"/>
+        <Item index="1" value="[12, 2, 22]"/>
+        <Item index="2" value="[14, 4, 24]"/>
+        <Item index="3" value="[-129, 5, 25]"/>
+      </VarData>
+    </VarStore>
+    <AdvHeightMap>
+      <Map glyph=".notdef" outer="1" inner="0"/>
+      <Map glyph="B" outer="0" inner="0"/>
+      <Map glyph="D" outer="1" inner="1"/>
+      <Map glyph="glyph00001" outer="0" inner="0"/>
+      <Map glyph="glyph00003" outer="0" inner="0"/>
+    </AdvHeightMap>
+    <VOrgMap>
+      <Map glyph=".notdef" outer="1" inner="2"/>
+      <Map glyph="B" outer="0" inner="1"/>
+      <Map glyph="D" outer="1" inner="3"/>
+      <Map glyph="glyph00001" outer="0" inner="0"/>
+      <Map glyph="glyph00003" outer="0" inner="0"/>
+    </VOrgMap>
+  </VVAR>
+
+  <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>
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>100.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+    <NamedInstance flags="0x0" subfamilyNameID="258">
+      <coord axis="wght" value="100.0"/>
+    </NamedInstance>
+    <NamedInstance flags="0x0" subfamilyNameID="259">
+      <coord axis="wght" value="300.0"/>
+    </NamedInstance>
+    <NamedInstance flags="0x0" subfamilyNameID="260">
+      <coord axis="wght" value="400.0"/>
+    </NamedInstance>
+    <NamedInstance flags="0x0" subfamilyNameID="261">
+      <coord axis="wght" value="700.0"/>
+    </NamedInstance>
+    <NamedInstance flags="0x0" subfamilyNameID="262">
+      <coord axis="wght" value="900.0"/>
+    </NamedInstance>
+  </fvar>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_keep_gvar.ttx b/Tests/subset/data/expect_keep_gvar.ttx
index 92bc3b0..43e4a34 100644
--- a/Tests/subset/data/expect_keep_gvar.ttx
+++ b/Tests/subset/data/expect_keep_gvar.ttx
@@ -131,6 +131,18 @@
     <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>
diff --git a/Tests/subset/data/expect_keep_gvar_notdef_outline.ttx b/Tests/subset/data/expect_keep_gvar_notdef_outline.ttx
index 94ead91..d4cc79f 100644
--- a/Tests/subset/data/expect_keep_gvar_notdef_outline.ttx
+++ b/Tests/subset/data/expect_keep_gvar_notdef_outline.ttx
@@ -178,6 +178,18 @@
     <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>
diff --git a/Tests/subset/data/expect_keep_math.ttx b/Tests/subset/data/expect_keep_math.ttx
index c734dd9..f2bc41d 100644
--- a/Tests/subset/data/expect_keep_math.ttx
+++ b/Tests/subset/data/expect_keep_math.ttx
@@ -417,7 +417,7 @@
     </MathConstants>
     <MathGlyphInfo>
       <MathItalicsCorrectionInfo>
-        <Coverage Format="1">
+        <Coverage>
           <Glyph value="u1D435"/>
         </Coverage>
         <!-- ItalicsCorrectionCount=1 -->
@@ -426,7 +426,7 @@
         </ItalicsCorrection>
       </MathItalicsCorrectionInfo>
       <MathTopAccentAttachment>
-        <TopAccentCoverage Format="1">
+        <TopAccentCoverage>
           <Glyph value="A"/>
           <Glyph value="uni0302"/>
           <Glyph value="u1D400"/>
@@ -466,14 +466,14 @@
           <Value value="1164"/>
         </TopAccentAttachment>
       </MathTopAccentAttachment>
-      <ExtendedShapeCoverage Format="1">
+      <ExtendedShapeCoverage>
         <Glyph value="parenleft.size1"/>
         <Glyph value="parenleft.size2"/>
         <Glyph value="parenleft.size3"/>
         <Glyph value="parenleft.size4"/>
       </ExtendedShapeCoverage>
       <MathKernInfo>
-        <MathKernCoverage Format="1">
+        <MathKernCoverage>
           <Glyph value="A"/>
           <Glyph value="u1D400"/>
         </MathKernCoverage>
@@ -522,10 +522,10 @@
     </MathGlyphInfo>
     <MathVariants>
       <MinConnectorOverlap value="50"/>
-      <VertGlyphCoverage Format="1">
+      <VertGlyphCoverage>
         <Glyph value="parenleft"/>
       </VertGlyphCoverage>
-      <HorizGlyphCoverage Format="1">
+      <HorizGlyphCoverage>
         <Glyph value="uni0302"/>
       </HorizGlyphCoverage>
       <!-- VertGlyphCount=1 -->
diff --git a/Tests/subset/data/expect_layout_scripts.ttx b/Tests/subset/data/expect_layout_scripts.ttx
new file mode 100644
index 0000000..bd35083
--- /dev/null
+++ b/Tests/subset/data/expect_layout_scripts.ttx
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=0 -->
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="arab"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="2"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=1 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="URD "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=2 -->
+              <FeatureIndex index="0" value="1"/>
+              <FeatureIndex index="1" value="2"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="2"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=1 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="TRK "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=2 -->
+              <FeatureIndex index="0" value="0"/>
+              <FeatureIndex index="1" value="2"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=3 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="locl"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="locl"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="numr"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni06F4" out="uni06F4.urd"/>
+          <Substitution in="uni06F6" out="uni06F6.urd"/>
+          <Substitution in="uni06F7" out="uni06F7.urd"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni06F0" out="uni06F0.numr"/>
+          <Substitution in="uni06F1" out="uni06F1.numr"/>
+          <Substitution in="uni06F2" out="uni06F2.numr"/>
+          <Substitution in="uni06F3" out="uni06F3.numr"/>
+          <Substitution in="uni06F4" out="uni06F4.numr"/>
+          <Substitution in="uni06F4.urd" out="uni06F4.urd.numr"/>
+          <Substitution in="uni06F5" out="uni06F5.numr"/>
+          <Substitution in="uni06F6" out="uni06F6.numr"/>
+          <Substitution in="uni06F6.urd" out="uni06F6.urd.numr"/>
+          <Substitution in="uni06F7" out="uni06F7.numr"/>
+          <Substitution in="uni06F7.urd" out="uni06F7.urd.numr"/>
+          <Substitution in="uni06F8" out="uni06F8.numr"/>
+          <Substitution in="uni06F9" out="uni06F9.numr"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="i" out="i.TRK"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_math_partial.ttx b/Tests/subset/data/expect_math_partial.ttx
new file mode 100644
index 0000000..34136e9
--- /dev/null
+++ b/Tests/subset/data/expect_math_partial.ttx
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <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>
+  </MATH>
+
+</ttFont>
diff --git a/Tests/subset/data/expect_no_hinting_desubroutinize_CFF.ttx b/Tests/subset/data/expect_no_hinting_desubroutinize_CFF.ttx
index 7f44c90..7896626 100644
--- a/Tests/subset/data/expect_no_hinting_desubroutinize_CFF.ttx
+++ b/Tests/subset/data/expect_no_hinting_desubroutinize_CFF.ttx
@@ -1,16 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<ttFont sfntVersion="OTTO" ttLibVersion="3.5">
+<ttFont sfntVersion="OTTO" ttLibVersion="3.37">
 
   <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"/>
+    <CFFFont name="SourceSerifPro-Regular">
+      <version value="1.0"/>
+      <Notice value="Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries."/>
+      <Copyright value="Copyright 2014 Adobe Systems Incorporated. All Rights Reserved."/>
+      <FamilyName value="Source Serif Pro"/>
       <isFixedPitch value="0"/>
       <ItalicAngle value="0"/>
       <UnderlinePosition value="-100"/>
@@ -18,7 +16,7 @@
       <PaintType value="0"/>
       <CharstringType value="2"/>
       <FontMatrix value="0.001 0 0 0.001 0 0"/>
-      <FontBBox value="-209 -250 1186 1000"/>
+      <FontBBox value="0 -249 560 758"/>
       <StrokeWidth value="0"/>
       <!-- charset is dumped separately as the 'GlyphOrder' element -->
       <Encoding name="StandardEncoding"/>
@@ -30,165 +28,62 @@
         <LanguageGroup value="0"/>
         <ExpansionFactor value="0.06"/>
         <initialRandomSeed value="0"/>
-        <defaultWidthX value="267"/>
-        <nominalWidthX value="448"/>
+        <defaultWidthX value="0"/>
+        <nominalWidthX value="604"/>
       </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
+          36 80 hmoveto
+          480 669 -480 hlineto
+          240 -286 rmoveto
+          -148 236 rlineto
+          296 hlineto
+          32 -523 rmoveto
+          -149 239 149 238 rlineto
+          -360 -477 rmoveto
+          477 vlineto
+          150 -238 rlineto
+          -118 -285 rmoveto
+          148 236 148 -236 rlineto
           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
+        <CharString name="y">
+          -92 92 -249 rmoveto
+          82 56 75 177 67 hvcurveto
+          161 425 54 11 rlineto
+          36 -195 vlineto
+          -36 vlineto
+          87 -12 -123 -340 rlineto
+          -125 341 88 10 rlineto
+          37 -240 vlineto
+          -36 vlineto
+          55 -8 181 -457 -4 -12 -19 -54 -29 -54 -42 -36 rlinecurve
+          -5 5 rlineto
+          28 -27 -21 10 -26 hhcurveto
+          -31 -29 -15 -29 -7 hvcurveto
+          -39 42 -27 50 vhcurveto
           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
+        <CharString name="yacute">
+          -92 92 -249 rmoveto
+          82 56 75 177 67 hvcurveto
+          161 425 54 11 rlineto
+          36 -195 vlineto
+          -36 vlineto
+          87 -12 -123 -340 rlineto
+          -125 341 88 10 rlineto
+          37 -240 vlineto
+          -36 vlineto
+          55 -8 181 -457 -4 -12 -19 -54 -29 -54 -42 -36 rlinecurve
+          -5 5 rlineto
+          28 -27 -21 10 -26 hhcurveto
+          -31 -29 -15 -29 -7 hvcurveto
+          -39 42 -27 50 vhcurveto
+          155 825 rmoveto
+          26 -19 41 36 39 35 41 37 rlinecurve
+          28 26 6 15 14 vvcurveto
+          26 -19 12 -19 -18 -17 -10 -31 -19 vhcurveto
+          -32 -48 -29 -46 -28 -47 rrcurveto
           endchar
         </CharString>
       </CharStrings>
diff --git a/Tests/subset/data/expect_sbix.ttx b/Tests/subset/data/expect_sbix.ttx
new file mode 100644
index 0000000..e7d39b6
--- /dev/null
+++ b/Tests/subset/data/expect_sbix.ttx
@@ -0,0 +1,7709 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.43">
+
+  <sbix>
+    <version value="1"/>
+    <flags value="00000000 00000001"/>
+    <strike>
+      <ppem value="20"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000011 00000011 08060000 003b6d47
+          fa000000 85494441 5478da63 bc1fe17f
+          ecd7f397 324c2c8c ff194804 fffefc67
+          6493147f c2f2fbd9 136589fa 8e387679
+          85fba41a f2f3e103 c5978d95 8b581859
+          d97e70a8 aadd6493 957b40aa 218c6c6c
+          bf185959 7f308138 ff7ffd62 632003c0
+          f4313150 018c1a32 6a087d0d 01e50172
+          34c3f4b1 fcfffd8b e3c7ed5b eae4e41f
+          502e06e9 67a44679 0200f1c6 3ba42d08
+          36000000 00004945 4e44ae42 6082
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000000d 0000000f 08060000 003f2345
+          77000002 65494441 5478da4d 924b4c53
+          511086e7 9c7bdbdb 162a9447 2d854269
+          6e79a81b 5de80a4d 24d1b060 c5c2950b
+          89efb0d6 95c68531 d1b53181 1817269a
+          1837ac48 30113151 b006049b 622d6221
+          8437a9d8 2269ef73 3c73ab49 4f7273ce
+          3fff3727 33e70e4b f59c9c2e 2dadc498
+          8c606b0c eafacf8f c59e3ebf 04ffd6f2
+          d0b527b9 d7a3035c 41409381 478d6665
+          ed47a6db d8cad770 0f805502 2865b31d
+          50b1b4e5 6c5cdfd9 09cac2b7 85cf5053
+          38935d3a 5700aa22 61074243 972a9384
+          9619947d e21c9e57 57efdb06 0909649f
+          047f66e6 8fff1e1f 3b470985 c9b767f6
+          a712a728 4e3e71dc ef2ff0c6 c12bc396
+          2dcadafd 05be9690 e8cb70af 0c5d1fc9
+          bd7a7961 f9c6e567 d681e6f5 b51c767c
+          e2828357 8745b621 a5fb7ac7 3f884a92
+          6d0df8b5 b50ea7c4 f9ff37df 5c8bc968
+          23929fee 3bfb8678 4044d037 d643f3dd
+          6a665ac8 543c2cc0 00ce04bc 3817aa71
+          f4b40c38 d7155bd4 d6d79a88 e754bbab
+          29bce589 45b3b625 5e50d3c0 1da80197
+          ff1028f5 b560e93a d8268057 5597dce1
+          e6cdf2eb 88ccf587 f76f7f14 c7d97a1f
+          a63ac4cd 1c9034ed 29b5c989 93de78f4
+          e016f190 7fffaee7 93221909 8f84df8e
+          b462c22b 61a24a32 3203fda3 9ffdb24e
+          7aa13b82 e413979f 9c382d5d 5c597c51
+          fcb91af5 b787a094 db03bda0 43e4ee9d
+          7bed8f47 6e8a42ac bdf1895e 0013bcc1
+          7a28ee16 b89efdde 095f228d db0937e0
+          4257c479 ade489a3 49db3425 2a43ec9c
+          34c5173a 5b90b8d9 48c33607 c618885f
+          8ea6e9f4 a8b44557 99245974 16bb4dda
+          e9dd1221 c131c619 475d73db 1ac0c1da
+          268816c1 2e16bd95 63449ae2 e41347bc
+          acc43bd3 c8ca53ce 359ae278 a632c9a3
+          aa197730 75ac72ca ff02b8ca 3d01d4f8
+          daf30000 00004945 4e44ae42 6082
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="32"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000001a 0000001a 08060000 00a94a4c
+          ce000000 9d494441 5478daed d6310a02
+          311404d0 f9c1282c 7b8f2862 61612108
+          1ec44eb1 f45e165e 62c1de4a 30368b77
+          58029a25 dfe40efb b7fa03d3 ce6b87da
+          e3e116bd dff32f54 200c1b06 685a05eb
+          5c43efed aa83b5dd 6cb9bec3 0c0c25e0
+          fb7cec10 fb1a7eb3 089fcbf9 cacc9068
+          d92e8601 1123a509 a452b6b3 61305214
+          52482185 14524821 19889960 4c2f27e4
+          ed6c8c74 b7624ded 291fc897 f0819cbb
+          e60f5bd3 81f73472 f64f0000 00004945
+          4e44ae42 6082  
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000014 00000017 08060000 000b1d6f
+          a3000004 7c494441 5478da6d 555b4c1c
+          65143ef3 cfcc327b 19765b60 29502217
+          a1146c4d 6b9188f6 8190d434 356aa26f
+          9a16052f 0f7dc418 1f4c888f 4ddf8cd6
+          62d24a7c 3031a689 315e1a23 4d0cb045
+          9416435a 2e61b72d 7459a034 0b7b6197
+          b9fc9ef3 0fbb6c17 ff647667 ce7fce77
+          ce7fbe6f ce48b36f bc722d31 f6f70bac
+          0476970d 20b9dcc9 a6ef7f78 d37becf8
+          54c10e64 e6669e9e 7dfdd59f ac642220
+          c9052159 00bdf344 48498c8e 7419b178
+          40520038 0149b88b 97698ae0 b63d8091
+          85e6d4dd f9167907 8c737467 f88ffe84
+          a5304dcb 50264905 905d2ad8 8629bc98
+          4936d584 a22529aa c5c0f107 2681ac2a
+          606f1b18 838f88c5 728edc02 286dac17
+          a0b4e99c c3b68b01 c1264f67 912fc5e4
+          2d54089e d1ca954e 199d333b 2bf957a8
+          a3182f39 1eeac8e5 0386e132 73629d67
+          8ba915c1 582e83b1 990477b0 028f8dd9
+          91a4d817 5f9d4f84 46f2a0a9 db9347a3
+          172f7ecc 5c207cdc c1728c49 e44fa856
+          1e88b2f2 b7ce7e43 e762484a f27e145c
+          fbfc50b2 df27b2da 5b592df2 7eef156b
+          73d36ba7 d35ab8f7 dc90b591 d2893457
+          c08b971f 121843b1 8451f176 cf55c9ce
+          66d599d3 ddd7e3c3 235d241d c5ad41a0
+          ad09d66f dd412f0b 1b0ee03d 7a781a64
+          d94a4d4e 3fcb342a 8741d9b1 5688cf2e
+          8099da12 920974bf 74a3e5d7 e15312c7
+          52b2f722 b5d39dcf 4f18ab8f 2ab1abe0
+          a92a07ad 623f3c9e 9a03861c d9999d16
+          69ce5103 6d0da23d a9a55590 b034a5a2
+          6cf599d0 447b495d fd03c132 de2c360c
+          7efdaec8 8d9674f4 11061aa0 d7558195
+          21a93897 859578aa cb50770c 528babc4
+          89684dc3 e5c13e02 13cc0f0c 0c88ecee
+          e6967949 b2ed8d3f feeca2a6 67d6e2a0
+          373e056a a907148f 06aadf07 5af93e41
+          447c7a1e 41b968c7 c14f3ff9 acf2c3f3
+          9773c429 4f88d6e5 ca163e73 94a18432
+          226938a2 964549c2 bea360b9 b474e309
+          0cbe23a2 8ddf7feb 9e3973e6 bac46c19
+          b980c0e1 7a30d35b 9008c7c4 7145027c
+          6f7c7541 507d5e88 df8d0023 7c908d96
+          9f7f79d9 df7dea86 e833fd6c 3f5caa5a
+          e87b6788 5b086613 29654886 82328a81
+          4cac924e 118ceea9 7712bec8 d44bf2e5
+          86a5867b 7b86b6a3 0f0f0840 6e592cfc
+          5ecf95ec 83588d84 bd533c25 e0adad86
+          f84cd8d1 1711d1da 34eb3972 e80edd93
+          6d633602 de9a2ae1 4bef74e6 fe726db8
+          efdc556e 9a32ac0c 7ed93b8a 89c635e0
+          374b243e dfd1ca27 833abfe9 021e42fb
+          d49143d3 663caee3 b872ff7b bced16d9
+          686f32e8 13be1433 ee064e18 b14b9f7f
+          20f7acc7 2e198bcb 0739aa5f afab16d3
+          26bdb426 9acedc5a baf9da8f af690d8d
+          f79030d3 77a27d7c fdbb6fcf 72cb54cd
+          d436283e 0d5cba07 b28f9342 8fe67aac
+          926d2f47 6b7283d2 e5f7c3d6 caaa2366
+          9444f547 fd177ced 1dffe418 f43ed77e
+          bbaabfff 02ed91cf 566c0d65 55eab08b
+          18c64aac 9a616705 8762c888 17d89958
+          f4a8779e 1c2d9e36 64937675 2502f303
+          cab66556 38ae78f1 f8c3f9b9 671e16db
+          0a63102b 3f60a9e4 cd850858 347d6177
+          60ee5905 36f2a598 c26f8b62 67329a98
+          87886366 8d7cc1b6 9871d69e 0ac9467b
+          92c8cbc1 cc18ce37 c5a22192 d114fdc5
+          93c389b1 89cec2af 1e6d6aba 1ed7ea1b
+          e78a01c9 e66d6d9a b112fff7 d56b1ffb
+          0f34f801 89b13b7e 89000000 0049454e
+          44ae4260 82  
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="40"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000021 00000021 08060000 0057e4c2
+          6f000000 ec494441 5478da63 7c909f39
+          e9fbf193 414c6cff 5918fe33 d00f3032
+          30fcfbc5 f887d3d2 7c1de375 13adb79c
+          dcec42df de7c6260 62a19f1b fefd6160
+          e012e163 f8fef5e7 3b16260e 8edfdfde
+          bc6510eb 9ce2cca9 acfc905e 8ef87ef7
+          aefcabf2 9cbdcc82 c2bf8151 f01f1802
+          2c0c5c9a 9ab73995 941fd32d 36d8d87e
+          81ec05db 0f13fcff eb171b1d 53048a7d
+          4c0c8300 8c3a62d4 11a38e18 75c4a823
+          461d31ea 8851478c 3a62d411 a38e1819
+          8e00f588 e86931b2 7d2c0c8c 8cc0cee9
+          1f866fd7 afabd2b3 1706ea8b 82ec6506
+          da3f387a e59cd60e 4bbf1f3f 11c42cc8
+          47d7f109 66466068 7c67fcc3 69e3b00e
+          0043ca56 20f696f0 16000000 0049454e
+          44ae4260 82  
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000019 0000001d 08060000 005f78c7
+          75000005 e1494441 5478da95 567b6c53
+          5518ffce ede3b65d bb76dde8 c6185933
+          d846c718 303ae710 4d0c7fa9 a022687c
+          f0580463 4c345188 2224240a 269049f0
+          0fe33f46 88046690 0445343e 628c068c
+          636c13b6 754fe6d8 9864838e aeebdade
+          3e6eeff5 3be7f6b1 3e66f424 cb7acf39
+          dfebf77d bfef3b64 f4b5dd9f cc5cfae1
+          29a29539 c8587214 647d4d95 abf2cb8b
+          cfaa2d16 5fe6b924 08fcc88e e75afded
+          5d4d4403 244b3e42 a482cd8f 7d433a17
+          5bee4727 bd56a282 ec25e145 19a0ee7a
+          57bd614d fdf5cce3 f0d8a8fd c68a65b7
+          e430402e 793906a0 595ce051 733a7d98
+          a8bc40b4 a983a400 1a61ee11 02b91721
+          9c9e0f4b 72984fc8 a4c94700 389d2e9c
+          0591ce56 80a22453 97bc808d 8c4fc2e4
+          33579a11 0a8db97a 397a82ae 50b5a843
+          a69089b1 9c36e498 28cba248 58b83230
+          392a2fcb d9466269 3988c552 1ee23f29
+          0a200cf6 adc86524 343cb45c 0ac6b4c9
+          8038824e 494ccfbc f0444e6d b6786529
+          85a7148d 8226dfc4 7eb33b18 d49d23ef
+          bf277a3c e6b49a08 04741387 0e1e4b3a
+          83f7b5f9 f95891d1 a42cd58b fa67b83c
+          67435b22 3ca206f0 8d8c83a5 7a99120d
+          ee735810 c1c1d1ea f17d6f7c 34dfc8ed
+          03fb8ecd 75f6ace3 f8547af2 ab2ac077
+          738ce961 8ea0bcb1 b1e90f12 e8e976b8
+          9a1afe94 23111d3d 94b022cc 0e3ba80d
+          7a98ee1c 0015afe0 1d0b012c 3ff5e9ae
+          a25dbbcf 78ce7fb1 ede68b3b ce530728
+          e01296b0 756d1543 c1db778b 39268b68
+          98e783ab dabbd672 86557503 4b0f1f3e
+          108bc661 c50bb303 63a0d6f1 60b297b0
+          9c5038a8 c7b75e7f f594cb59 db33fa4a
+          732b924f 3180ca0c 6585a031 19c1dbaf
+          18a08bea 43bd07f5 352b8789 1cc76a78
+          ebe60bf7 bffaee19 955ea932 4ea586e2
+          0d4e705f eb063128 00e194c2 a091524e
+          d16f764f ad01dbfa 7a705fbd 01b17098
+          edc70480 c22d4f7c 5d85fa18 94092351
+          f7bd4257 93f35a78 74a282d3 2b55a55f
+          64018ba3 12a6ae74 e46434bd 636b5a0d
+          81894908 fc7d8f45 21a101de 5e3656db
+          d6d9a029 2e9e4ee3 896691ed feb293a7
+          77125e13 a6787298 1fe1ae17 04f73414
+          d4558328 285124fe e8777ee5 52cc5518
+          8da0018d 4206a251 452b3e3b d59c3090
+          1649624d 1e3ffad6 d8db074f 50d818b6
+          98d49247 eab12c25 88ce0520 413c4ea7
+          054d9e01 ee5eee48 561385a9 fce8e1fd
+          a5ef1e6a 99af539d d5f4266e 9727c985
+          ca54bc8a b50bc1ed c1086842 6485dd02
+          1ad1eba8 e70a4988 52c6e2f4 745156f7
+          991f89fb f4c9ed23 cd7bcea8 747182a1
+          ce450fae 0261ca0d 73a3530c c278b761
+          55652cb7 81614931 b8db7b15 b8e2a55e
+          79f6f317 8a5eda75 2ecb48d0 d5e3e87b
+          78fd55c9 1fc8a7e5 490d50cc b56613f2
+          a53f599a 6989c73b 85f52b20 ea0f806f
+          788295b9 4c3bafc9 385bfb7b 5ba3bea6
+          76289978 29e0d7ff d5bcfd8c e845035a
+          a5bdf056 23e4952d 064ff7a0 e2a532c4
+          18ee5411 13c67d4f cf10184a 4b802f34
+          29044479 71c66f46 7d67b1f5 e893466e
+          efdfdb32 d7d5bb8e 259bce10 8e03eb9a
+          1a34d0af 343c4ef1 5a5b5672 c7b6f3f9
+          56debe64 9c7e3369 44c2d33d 00d63a07
+          93a3f254 8fafa3db 39fece9b 1f32b866
+          7ffd6543 ffc68d57 b8048351 b8a8a106
+          22b3730a 048916a1 e58595bf 5d7e28cf
+          f9c0f560 6f774ddf 86f5ed92 1034265a
+          1183d682 d076c4a1 8d13d7f1 f34f8faa
+          5e0ecdb5 045d83b5 0965794b 6dc01758
+          c0736358 b92c2b97 edc75bf6 5ab76cbb
+          c438555c e2569b4d 1ecfb73f 6ea2c540
+          0d85dc3e 30da4b99 f6e86c90 91979255
+          0efa4caa 3d443c22 ba3d45f4 22cd8515
+          89e71b1a 417e4458 8ba0cd0f 5bc4c5f2
+          131fef9b 9f746343 6397d0db b93ae8ba
+          e948f044 0c04c054 6147724e 312304f5
+          c9445471 31bfdf44 b8d4eca0 b88a42bc
+          57c9cc4b b9f4c0a1 23b98616 92ee037a
+          ceeed19e 150a31f9 640be2d8 dc3172f4
+          2d90620d 28899653 43873368 22b4e5e4
+          3242f7e9 7962e825 4944d2df 1a590f09
+          36dfd336 b8acb740 daf826dc bfcb670a
+          d3b6308b f950e63c fcbf4594 f70195cf
+          7cec6479 18ba3703 994df3bf 2e2a47e5
+          33975a0a 857836f8 23e92fbf e4c38284
+          e30338b7 5e4908f3 ac03a816 900f8574
+          6aebd35b cfcd5cfa 7ec1b7b0 b1d1d986
+          bc98cc99 78dc2fd8 b4f982bf bd73e1b7
+          f0938f5f fc07df31 a8fddaee 55570000
+          00004945 4e44ae42 6082 
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="72"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000003a 0000003a 08060000 00e1bb4a
+          28000001 90494441 5478daed dbcf4a02
+          511406f0 739db18c fc13b6b0 8c264633
+          28c88816 1906f500 41b8b0e7 30a8a728
+          b05ea320 e8092a48 7230a274 61658cd6
+          a26891e4 9f087366 6e2aa88b 5e202edf
+          b73bdcd5 6f71eeea 7cac9e49 878b89ed
+          fd46499f 26896c24 524c6e0d 28ea937a
+          78b0c372 91c5334b d7d7bd91 0522c314
+          ca49b244 e5f41dd9 02817396 f18f3e8f
+          2fcd2992 cb49955c 8178f35b 0823b30f
+          91673e44 66ad4e6f 37f91799 c932e7a6
+          459fd93c d5deabaf eee88a26 02b49aba
+          5ae63cef 772a13c4 2489cbbd 97e60f79
+          d6565333 c7a75b22 401fe39b 4756ee3a
+          de9dfb9f 0f63c40d 5316653d 3b9696e9
+          2f54f000 0a28a080 020a28a0 80020a28
+          a080020a 28a08002 0a28a080 020a28a0
+          80020a28 a080020a 28a08002 0a28a080
+          020a28a0 8002fa3f a19c1393 25431458
+          c7d23275 d33f44b6 0f50e5e2 32fa10db
+          381101da 3e3577f9 dc7d2837 0dc6241b
+          8d846789 e50a7e7e 7f1b1301 ea1ef3f6
+          ca036da3 ec50a6f4 0f2dabb4 eb20c393
+          3ea1f6d2 a87f5159 cb922318 d4593da3
+          858b89c4 5ea3a487 c42bf890 35a8060b
+          6a32b9fb 0bf4417e f7b34c45 e8000000
+          0049454e 44ae4260 82 
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000002c 00000034 08060000 00f101da
+          f700000a 8d494441 5478dad5 5a0b5054
+          d719feef 63597659 96e5b1e0 f25a1745
+          d010a515 a291f808 44698c8f 68e338b6
+          46a2f551 5fd13658 6b9a69c7 ce04db26
+          b593264d 0405ab6d 8d93b1d1 26526c06
+          7c2022d8 448d2f2c f2461ecb 6b97dd65
+          59f6716f cfb948a7 8673965d 9d7126d7
+          b9c3ba7b cef9bffb dfefffff ef3ff732
+          d6aacad4 e65db9ef ba3ada26 000b0cfa
+          37f62100 b04ab531 66f7cff6 85af5a7d
+          0afc382c e7ca66b5 bcf9e63e 778f31de
+          277ba264 4f948d8b aed7bff3 6e2e736b
+          d6f40bfd 955fcde1 b8073ffa 72202382
+          074016a1 ea49ad69 48e423b4 665fa689
+          4e277733 2de54bdb cd7ba9fe daf3207b
+          211969e5 bcd3d869 e058f45d 00f87570
+          c8cb82cd 16e1eaea d2f90ad8 63b3aa5d
+          3d3d711c 8fecc9fc b43704e0 ecec30b0
+          0cc789f0 a807cb88 c0308ccf e3d1d8c7
+          b187e7b2 f02d3bbc 0216116f 44f79303
+          836d619b 8f04184f 54c64482 4aaff30e
+          da0f4648 6329e3b1 0d6c0bdb f4069a0a
+          58400b04 1be2413d 2941fa4c 4b6fa2cb
+          e53b60b7 1b017303 2995611b d816b629
+          b8bd0316 e9a09101 8f404d35 a24b6404
+          bbcde7fc 22381cbc e81894d1 722fb625
+          babd7202 059d48bf e1224a7e 0cc77acd
+          c5432dcd e37d05ec 6c6fd379 ac0e3543
+          599295f1 684d2fee 45585936 50611745
+          32dddc76 07f04a85 574a761f 295aeb2b
+          e0ee2385 afa1db4d ad6e7c50 10b23948
+          a439c6c8 2a94032c af8de826 91824195
+          c8d1d50d f2500d30 3cc52372 005371e9
+          92ae4307 568f05d6 5c727a9e b1a0701b
+          27a77917 401ea601 87b147b2 4d2ad132
+          adb68b55 2426dd15 810c78d0 689678ac
+          491a0f9e 21322d58 74312dbb 77bde7b8
+          f71f3d0d acabcba8 69dcb2e9 1008224f
+          0a73bc76 489201fd 1d926c92 00638c8a
+          e429b7d9 9017b2cf 78abe1ed 6515a0cb
+          7c0e642a 3931dde0 12ebeab3 85356e5e
+          7f00739e 7434edd8 f29ea3a9 7d024bf0
+          ae88629a 5706806e de2c682f adf05a19
+          42b217fe 93d5bcf8 d217cac9 863b02c1
+          83d87b03 2d3dd07b ed16c42f 990f0225
+          837188e6 a6b28aef 75ecffed f651bc2d
+          ca5fd573 fcd3355c 20257338 01e21765
+          42dfad1a 64ab5bb2 393abb00 044d4bba
+          16323ffb 2ccb06a9 8662f7e6 fd1c5f29
+          89cb188c b1e2baf4 39eab954 f00c5240
+          a3e4767f efaff759 2f5e481b f96ee0fa
+          d5e4a6dc dc0f2410 8440f220 201169c9
+          c005cac1 587e55b2 45e2020e b8b8bd79
+          7bd8c040 37233e48 11753ff8 fe9fbb3f
+          fe348734 09538193 cb2171ed 0a683ef5
+          2fb0b777 4b41326a 1cba039c 46d51bb6
+          74d94986 97794cc5 9f2d7676 f444b301
+          e4ca16a0 09868455 4ba1fe6f 27c1651d
+          2072173b 28f2d595 45138e1e ff9144c1
+          11c0ee9e ee905bb3 d2ab1d75 cd492ce1
+          f661ca04 4f8c8598 f9b3a1b6 e813c43d
+          94a35932 9011eab0 3419290e 57b6c49c
+          e5d073f5 06f45daf 03126530 150227ea
+          6b532aff fd0c92b0 fd0f9566 fc45c2c1
+          a21c8667 9da4e0c2 01d35f7b 1f2c758d
+          10f7d2f3 40e2bce4 017e9846 f8a4695e
+          4c05ddbc 74709afb a1ef1a19 2cc6c0c8
+          58574241 61ce08d8 515a423d 37b33af6
+          57bfdce3 7152123b 5ab8bdb4 0a0242d4
+          887b4992 617f0f1c 64c18628 08993411
+          5a4b2e00 4bc9cb18 43ec5bbf 784b3d2f
+          abea2187 88df2c73 82007717 2df887a9
+          a46c0991 cfe856ca d42a98b8 7a39d41f
+          3f05437d 16626413 2b2b0a6c 060d4e5c
+          fb0ab49d 390fb6e6 4e20f11b f356337f
+          6e497249 d94286e3 c6506b2c 0b86fcc2
+          8d01d1da 56c149be e5437d36 e8387f09
+          f44bb387 ebac8f3d 04a651ec 8b73c052
+          db00d606 325811d9 0c880aeb 4c2838bc
+          fe9b60a9 f2521ea7 3726e417 ac434004
+          208835cc b9beafeb 611095ee 980519e0
+          f6811ab8 9a854d9b 80324308 bad82b44
+          de625b58 50193ef8 70837cbc a1dd2f3d
+          1cbae8e5 d2e8dc9f bceda104 173678bf
+          a41c82e2 6220744a 3cb80687 838974ba
+          d16f016a 0544cd9e 01ad9f97 0e8b1b86
+          7c51ba6d 1b7f1ff6 cacad3d4 e22b8a22
+          78939735 9919a596 8aea2c52 70e0f425
+          0f57a3f4 b4024c35 f56831b2 76163c0c
+          a8514a34 965741df 8d7a720a c36973e6
+          f48b53ce 5eca64e4 72aac6f4 1a2e1eab
+          25c86db1 6aa8821b e1534445 c050af09
+          29bb3e60 290db1e0 66c0ad0b a36beb91
+          86c0e3e1 d0c97a6b babc7ab8 ee872b0e
+          771f3bf1 1a2d5be0 4a6558b9 04ea8f9d
+          44816807 9a30c7dc e4830350 665906cd
+          27cf80a3 b79f9859 7076885a bfe6c384
+          8347b6fa ddd319f3 ff942381 0df452df
+          17bf00c6 8b55e034 d981c7c5 424e39d1
+          6fee0127 0ab64a54 74b286b7 3244725c
+          180f1ddd 8284fe4a bf3c8c45 cbed3919
+          d5a203b5 33044fe0 208a5d30 1338d48d
+          b49c3a47 162d40f6 60ecc20c e96aef97
+          5412e761 3dc2aa54 a6948acb cf289e4a
+          a91bd3c3 c2c080bc 617dce51 c14a068b
+          73734862 0c044fd0 a3e45f0e 6ca01fdb
+          4d52a5ac 0465ac0e 4292e388 4d012ee7
+          6eb32db4 7eddab47 8441bb6c 4cc0cdbb
+          76fcc6fa d5ad7456 410e324e 110031d9
+          73a1b5b8 0c0593c7 af6d0929 b004e4dd
+          e2b3109d 456f0ab0 e72d57ae cf6ad9fd
+          d33caf80 7b3ffecb cb9d1f15 eee4bd89
+          edc55960 ba512309 7bd6cf0d bd91decd
+          de6e929a 82b84559 d4a68047 dcef783f
+          3fb7f793 634b8880 1d75b5f1 4d3b5e2f
+          a08a6dc4 3fedcc14 a915efac b846e52d
+          9684782c 3e85417a 87d275f9 a6f439f2
+          d9a7c94d 013b2c4f 9bb66e3d e8b8571b
+          ff306054 201a376f c877769b b5244988
+          bda0880e 056d7a2a b47c564a 153b18a0
+          2a7d5af5 f8fdbfdb 69f8e3fe d7d57366
+          9ca381c6 9e6e3d7d 16c2a74f 05654c18
+          d1d3188b b3c71cd9 b069ed21 d1ed66fe
+          9725ba0a 3e5a53bf 69cb116a 8b223052
+          b761acb8 02e69a26 20b5eab8 52c9f5b1
+          f529555f a6c922a3 a4fd628f c512783b
+          23bdca7e bb761a29 3871d069 92e2216a
+          ee4cb857 7402e571 81787771 564a78ff
+          0f1bc76d db799071 9b4d4137 5227df74
+          b6761a18 8ad48b5b 3c47fadc f279b994
+          6f49c188 0a8930b9 b8380b75 b6e7ffff
+          376b4579 da9dace7 2fa14b0a a0b54071
+          8b664bbb 32ad687d 5aaa9345 45dc9ffa
+          75cd53dc f694294b bb8afeba 91d816a1
+          20d34cd6 43d8d3c9 d074a204 3819bd99
+          8c7963c7 bea8cddb 8b4629bf 787dbbe8
+          1c14cde7 2e659282 14d3cb52 d70ae3e6
+          cc40e06d a8c4f78f eaedf0ff 5d7d76b5
+          3279d26d eec71ad5 8e816b37 a693239e
+          01fdb26c 68fbe202 aa6636e2 0e10f690
+          3a7d6a35 6a127318 998ca87e d419b32b
+          2da5c599 8ea6cef8 517698e1 7668a8b7
+          1bb54d19 481cd550 7b455ea3 ea671d75
+          f7263194 fd5a658c 56ca9bb6 6623596c
+          7bb04650 580d0587 d7b10a05 75df15a9
+          2f4fc281 c20d68ac 85d82fa2 8bb03577
+          49795da9 0b276fd8 e04c565f 379175f5
+          f6684957 843a2508 d4862351 63068a6a
+          942813bd 7bd7db41 a9dfbd33 56fe554e
+          fbce5d34 364f70d2 959fd36c 06790405
+          30c2e8ee eb0d6745 a753417b 3e86f722
+          dc763b55 81710ad6 15b67ce5 df7d2d1a
+          782c9e03 024d6b0c a135e540 1390a2cb
+          15c8a2e8 a46a6206 2953da06 335e9453
+          29ad7c68 a8c957c0 782c9e43 0584fa23
+          96e5bc3d 72e0c77e 8a445b1d a74c79a0
+          80f8e9f3 632c3c16 cfa17958 929ccc63
+          3c45fad6 3df6c26d 36c3734f 0c0cb645
+          6aed7dea e97042b7 36b6a03e 8df379a3
+          e4b13c87 0b486d03 6a583d5e edd1030e
+          5da8bdad 4be215c3 3f09efe2 5cdc2171
+          98e11e01 f008e827 79f8e298 6f5fd089
+          1e0ff3a8 931fccf5 e7e9bcf8 b8f6f800
+          9daec15e df16c73b c1ef172e 14dac856
+          4e156cf1 b9094563 6568ce50 87295cda
+          73f1c39e 1be56e95 4ed7c858 ab2fa736
+          e7bef1ce 83576858 5f5f69e1 b5e39ae3
+          f3f2f6a8 e7655ef6 c74b96f3 679f6dd9
+          b307bf42 a3f7e315 1a41161d 8b5fa1d9
+          f55fbebb 98e434cf e5b40000 00004945
+          4e44ae42 6082  
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="96"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000004d 0000004d 08060000 00e309e9
+          b0000002 16494441 5478daed dc3d6b53
+          511cc7f1 ffb939e7 e64ab114 75698574
+          68e31411 4ac17751 2d4e1d3b baaa8b52
+          548af501 b5a38e8e ba15f46d a8a80d0e
+          a2432ed5 a9884442 6feec339 9e9b2660
+          93a6e8dc ef774cee f4e19c33 fe54b1b7
+          a77fdc5f bffef3f5 9b659b74 ce88121a
+          ce89a8f0 c4eea9a5 a5adb36b 779eeaef
+          1b77afc5 f71e3d0c 239120d4 008dc966
+          f95cbcf1 f9a23f54 4e7d68d4 dfa5dfbe
+          2e346e5c 15a95444 8a02a1e1 fa2ecd27
+          cf259caf bfd72e4d a7025311 e5fff8f2
+          e2a564ed 8e489e02 35488762 2627e4dc
+          ea8a944e aedb9dd2 1204ae77 6dbd64d6
+          fe2d7937 cfa2d95a 4b148f9b 3827492b
+          9e95b635 6e7003bd d7c1472c cfc583c5
+          e7df362f 2863b263 6f966566 7bb1f129
+          df89e70e 1cbe912f 95724114 255ed41e
+          7734ff64 15a5c7f0 efc1a1c2 4551e16e
+          8e7708a0 f9ff4003 0d34d040 038d4003
+          0d34d040 23d04003 0d34d008 34d04003
+          0d34020d 34d04003 8d40030d 34d04023
+          d040030d 34d00834 d040030d 34020d34
+          d040038d 40030d34 d04023d0 40030d34
+          d00834d0 40030d34 fa17b4de 6e188d75
+          181d9d73 4ed92489 58eadb5f ea2b3d8e
+          46d3badc 40ac6d2f 363eb209 29834dc8
+          9aae0e31 89b5aa7f 14c54c9e 1469774c
+          be13cf23 d607aaee af8f963e bdbc9756
+          d5ea2f9b 15bdf5d1 faea0a3b b787e5c1
+          4a9fd249 7b2f7d7a f9caabd6 fa8385e6
+          e3672c2a 1f914d73 49139169 efa5676e
+          ddde1427 eaafed6e 1eb391b7 4d9c8926
+          76a72f5f da9ab9b9 b6f9070e f8b1b035
+          80f7da00 00000049 454e44ae 426082
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000003c 00000046 08060000 009045f7
+          d500000e 2a494441 5478daed 5c095454
+          47167dff ffa69b86 6e686804 44765044
+          dc125722 eeca8989 6bd4d144 31711d8d
+          a368a263 e2926db2 9b28064d 84889ab8
+          448f1a27 1aa363dc e296c115 513683ec
+          ab2c4d43 d31bddff 4fd56f4d cc8ca68a
+          eef61cce 9c94a7cf c1c3afea f7eabdfb
+          de7dafea c3588d06 aee2c3f7 966a0e1d
+          9ec81b75 3ec0a07f f60c0180 91c8349e
+          23461c09 7cf39d0f 390f4f23 3830049e
+          87aaa44f fe5abb67 ef345ea7 6d0facdd
+          72098cd4 add67bdc 980301af af592fa9
+          f8e0ddc4 e2b7df5b cbe15fb2 e0d8404a
+          376666f7 e69b75ee 619bd396 3bb2d4dd
+          944dd30b 5e5db1d9 2972f110 d99471b3
+          3f3289c0 dce81175 459f99d7 8b7505a7
+          0cde0c20 0bf42dee 71eb4e14 ab5098ec
+          5d277b78 dc51eda9 0b4f7372 27c985fc
+          cdad67f4 5556309b bdec7496 870e06ad
+          25b49895 bcd1a870 c8594c46 b513c502
+          aca36034 7ab1c0b2 02387b30 ac4d7347
+          06cb3a5d 2caceb63 58b56d8f 3f157e24
+          a6acb64f 5b1bad95 8b4a6181 479157ed
+          09522fa5 f8739b51 16c98265 c2b2d1ca
+          45a5308f 924bc088 41e01bdb 5bfc992a
+          543b1ab4 28e66359 b04c5836 dee4740c
+          a3602e50 04742427 6f3000af 6f66ec77
+          532bf03a 1d50a54b 5126a155 2e4df734
+          a27a0c4d aac0f9ce 6496f126 83cc6eeb
+          5aac0caf d7cb6914 1665e2a9 1516d312
+          15e4798b 15188ea3 4ac1bcc1 e2d6527d
+          d7df6e7d 1bea3d2d 75b5fe0c 47613117
+          0992cd42 a9ae6045 949ca122 f956a309
+          38b98cce a5d1de68 8f1f1d66 afc24d17
+          cff76ea9 d5f93012 f2b31237 375136aa
+          b0c0490c ac44e5a5 059eecf8 264d0348
+          559e54a8 675d00aa 5352124d 25c5bead
+          e6bc06bd a4fcfd77 dea47267 e401526f
+          1598ea1b c872211d 392f2f0d ebe2e75f
+          29502cdc 5c5a0eae 6a6f90b8 4b882900
+          5bc65c55 d7a1e8e5 b99ba802 dd03a374
+          f58a35ba 2b37e358 19392549 14529021
+          85b16c24 f7c75248 fddb97b3 ae9da272
+          8816438b e9cb6ac0 62308277 f72e60a5
+          f0205ce5 d41df971 5255f2ba 59b4ca36
+          1cf96e50 65d2c655 1c0572b0 0cea1e31
+          60d1e941 5f5e23ca 481af2ce d139aca2
+          5fec45aa c88b76b4 fcf86908 1c350c64
+          2a390814 718273c1 165bbd4e 9f793d92
+          f46c4b65 85aae0e5 f95bf134 927b6266
+          25f57485 80f8c150 76ec94cd e32820a0
+          1830f03c eb3978d8 4559a04f b1602658
+          590a507f 3d1f1ab2 6f43c4b4 09383011
+          131a766d 4b93d1b3 60deec2d a85cfc43
+          350a17ce 4b369554 45b014d6 e55b00c2
+          9f1f0bda dc7ca8cf c8176523 d5e8f2c8
+          c0dbcaa7 06a6b39c 4ad5ec3b 73ce462b
+          4572c2c2 141d3886 7657091d e263c162
+          a473eda6 f4eb83cb df59b3e2 51cf546f
+          4a9a5177 f0c8748e a2096131 00b41fd2
+          0be4ed7c 902c4781 6a83906e be73e727
+          710a8589 115050b1 68eadd6f f6ea76dd
+          545cd191 b400de5d b99f37c4 2c9e0d79
+          5b7643e3 9d722062 8ec76ec8 98a38f1f
+          1fe83174 c4a5077f a5bf7eb5 d3adc171
+          e9a83857 91d210b6 945b7b35 44ff6d26
+          e4a6ec80 e6926ab2 7511d6e5 1d43b3ba
+          5eceeccd 29953637 93787937 87ac5db7
+          58c48240 4e39cde5 f5507ce8 5f10317d
+          228ada52 72b5c2e2 9c2f48f3 13a6edd5
+          1e3f168b 58948c37 1a5c7497 2fc5dc7e
+          7ef27e5e 475616cb c6700c44 244c84ca
+          53e7a1a9 90acaca8 0b0f42f0 27498bb1
+          b222cc84 07d246c1 ec84a4aa ad3b1325
+          147d242b 72adc897 c621eb4a 212f759f
+          cdca0c89 728acaf3 b2b0e022 86617853
+          5959206f b0ba8afd 3481ecca e1934782
+          ab9f2fe4 7ebecba6 2c43e1fe f367ad0f
+          fb22ed95 5fc57850 616b63a3 2c6b50ff
+          73fa1b39 7d5839c5 8eb312e8 b66c1e54
+          5fbc0415 27af00cd 4689ee6d b9a7a08b
+          8d8a9259 1e8057b7 70089f32 0e6e7e9a
+          8202a11e 88ee8f94 75eb117d 39e6dcbf
+          07714a0f e343ab25 cec3c314 b17d6702
+          ab70d592 d28ec899 cd16c8ff 7a1f04c6
+          0f056598 9f88319a 728541d6 616474ca
+          da52901c c2268f85 c2fd87c1 ac212b8b
+          6567dd65 8d115bbf 7ef14165 1f5a1eba
+          f77c322f e4e38f16 e2e044c4 3312bca9
+          b8064a8f 9e84c884 4928624a 9cdb1511
+          eea7a071 d090950b 75282d12 23f9bd39
+          216b3f5e e4fe64ef 1caa7ad8 6fc1e25d
+          eda6ff25 8526ed60 37aefae9 1ae82baa
+          207cea18 3a2bd356 4de8fbdb 0fed8d38
+          bc07141d 3c0e540c 0ccdf199 3a611bd2
+          e1eb5635 00c29237 bfe21ed3 31036381
+          a65828d8 731814c1 81e037b0 a7182c1c
+          1d78e314 c13e1030 7230dcd9 f52d7253
+          9e0801dc 6c974785 65856d4c 4d6c75c7
+          835379e9 23b66c4f 60e42e3a 229e118f
+          b5eacd90 bf733f04 8d1e098a 20b54396
+          1603a284 458cee39 a8f8f10c e84a6a89
+          29084389 9172c6f0 2fb7ce90 a87d9aec
+          6af128fa 3f752be4 83f71753 e119b95b
+          637e258a d6672172 c6646475 ceee861f
+          2e0c42c6 8f44014a 0b95a7af 52457f2b
+          dae0a0b7 df5ae631 70c83587 7a5afe89
+          cbb6f94c 99b0dd4a 83671450 2a7e4c17
+          6be7d0c9 a3a8aaaa 876150dd 33125431
+          5150f0cd 77225c68 38817a74 fcbe80e5
+          2b3739a5 8917b629 7591bc53 68164f52
+          9ab1e1f9 cece83e0 d929027c fbc78878
+          c696a6f9 604f92aa dc2074e2 b350b80f
+          a5a04623 b1cec5d4 5116e457 1096b275
+          3ecdf10c 95c20813 ba882fb7 25704a77
+          0da90386 056cd199 c44013f4 cc085086
+          faa1e82a 03891bf9 c3c95d11 b91883d2
+          cf2dd0dc 2c24a720 1e4349aa 0f4f4d7b
+          491ad0a1 9eaacd23 b4a22351 3067c6fa
+          eab41d4b 688e305b 9065bbcc 9f02cace
+          9160aed5 50b5a919 dc334364 26e3dd64
+          915c90e6 6057f69b 35edb3f0 b49d89b4
+          3a48681f 6c46554d fde12353 8884fd1e
+          0edb3dd1 115c7dbc 21fbd354 e4aa66b2
+          2f09b602 3ae285b1 e0d3ab0b d4a46703
+          6963317c b4274f3f 6bfce5f6 5ad78e9d
+          ca9c6661 ab4e27cd 8aeb7b16 71ec7e24
+          8ecda314 26f55040 ccd23950 b0fb2034
+          641703cd 26dd2f3d 1508029d 664e859c
+          cfb783b1 564b0c5a d8ca1e03 fa9c8c3e
+          792e9e95 c978a760 b8f8d545 1f355328
+          7baf1c43 65e304a8 bd9c011a a42cb612
+          c635cd07 6316977d 55677f16 bb2a8c78
+          ba4e6e30 682f5c1e 5efafab2 b7694f1e
+          fe70d47c 95f65c75 eaf62554 dd08e4ca
+          81a30602 cb71507a e48c98a6 5a3bc4d4
+          76ea1258 f406081a 3394aaab 2241d0af
+          5cbf7155 ddbe3da3 1c52587f 2b33ac28
+          31319595 906b4f9c 73559d83 c037b60f
+          625cdfda 9eb7e774 09a736f4 7d05df1c
+          02ef1e5d c1bb5b18 1039002b 7a0853b8
+          70fe5684 e740bb14 c60df182 392f7e65
+          d136ab19 1732ad73 7197a1aa 663c14ff
+          f328186b 1a45a1ed 1dd8bdcd 5a0314ed
+          ff1e4227 8d11cb43 22bd4571 a2a546eb
+          7f67e6f4 edbce9d1 0dc347fe a2e4ef4b
+          df6d4ccf 18489382 c4120e45 d7c6db05
+          50733907 3827dc08 c26bd4df 2c004d66
+          365a7b1c 5597f437 3c2f7fab 550ad7ee
+          de31ba72 63ea0a09 453976bf 8be8eaa3
+          163b9a9c 0c9c36f0 5a25874f 828bc21d
+          0286f76b 0d9e57d7 eddff334 95c286bc
+          dca0a245 0bb7882e 492ac750 7a5586b4
+          830ef143 217fc701 64692b55 1783dab5
+          71f38f17 1055fd16 d5c503c0 23dc9f5c
+          85ddc7f3 cb0bb63d 0ccfecef eb492387
+          70bbada5 bec98f88 5b4ceb50 45149130
+          493c91d0 95d4d0e5 5bc1d682 115a6c29
+          8ca6d6d6 5768a0ec 871362aa 12bb2a3c
+          0d9e1bfc efcc4af8 1f3cffee 3f656fbc
+          b64a7bfe d2701adc e2a88c2b 22634d2d
+          549eb94a 4e4138a5 9a6d014e e2a3aa75
+          f153d720 ce63150b 12868ccd eaf399d0
+          5c510561 939fa1ba de20e219 e952ba72
+          f91b0f55 b8f1a753 7d2bd77f b686aa8d
+          8270ebdb af0b7876 0c476cea 908d0d31
+          e46e842c 3428aff3 b113b1dd 6fe475ea
+          9e9917d5 e5f4b95e 6e3d62d2 793d85a5
+          91d50af7 7e0fcab0 606817db 5594810a
+          cf491b57 6b8f1f8d fb1db5e4 0d0649d6
+          805e179a afe7f425 5247e48a ae6a4f88
+          5932177e f96a2f68 6f971203 95d8d863
+          25869853 a76315b1 71371efc 9d312f27
+          f866ff3e d7f86694 fe2464af f288ec20
+          52cfac0d 69887a36 10a9a7d8 f6898ec8
+          ec7629a3 2febae30 8916bebb 65f30b4d
+          14ca8ab4 1b593272 c644b117 dd905b4a
+          159571a0 097af3cd 15ffad2c 1eae51d1
+          25211faf 5d44d355 c1dfa5bd 5d8edc3b
+          1d221326 daee7592 3a31086a cdd977ba
+          576dda20 1edbb2bc d120a9fe 2279194b
+          d9100f1e 334cbcef 51f6c359 2aea885d
+          4f153fe4 70fbe52b 931ff58c efdc05df
+          a89f7b76 076d57a5 ecd87924 430b048f
+          1d0e3473 f0d594ea 94cf97a2 2248c6e2
+          fb1486dc c26e3447 8e5e5d82 c1a7cf13
+          284d1cb0 a19f805b 1ca4a47e 5e95e129
+          69f318ee 8f773434 3965892c d0b78898
+          76185be4 c1a94add ab872813 690e8eda
+          c6828a8e 4de7cef4 679b2e9c 8bbbefaa
+          a4fc1634 3a1e2a4e 9c05438d 8e4c1d05
+          db3165e8 868df364 a1e15524 2be08e45
+          58f2a6b9 08ef0229 5de1efc6 32605902
+          470da7e6 ec4d3f5f 8c630dd9 595d8918
+          4479d33d b09dd810 afbb9649 855bcc8a
+          fce6bdb4 413de585 ef698986 d7f84927
+          fc17cc5e 67a1493b 4886daab 37c0c543
+          096e016a aa1b09c6 dcec2eac b9a23c88
+          b4417831 45481098 ea34d0d2 682237c4
+          7113bd5b 5446c8da a4d75bcb ae823ffc
+          74359a7b 9de8a648 8696a616 5126f7e0
+          0e36ae4d 4082b9b2 2280b56a 342a9af6
+          8bcccb76 3d88eaec 083d13f8 d63f5671
+          1e9ead3e 83c02f87 a0b9aba9 aecb21d7
+          37373480 abb717d0 5cbdb234 683c59c1
+          6a71a3ab 5e64a828 273304ec 0d2efe9e
+          351e8386 9cb79743 e3b9780d 1a37c537
+          8b70b793 ee484390 e39b7854 952bbe76
+          c89b5bc8 6b62eae8 adaee03c 558d7657
+          49682e5e 83c69b04 ca2b91f7 b31a4572
+          79b060a0 60fbc8fd 39a5d2dc 0a211eba
+          b9780d9a 6baf02ed a5d7dfa0 efe4815f
+          d892c91c 7b4903cd 15d770fe eb278fe9
+          9d074168 1b6b38a4 30036d77 308fc5c2
+          4eb8d6ff 58946d5d 7b942a42 e3b35f4c
+          e3708060 656d4757 2ccbdd9f af88418b
+          562eba94 84ef4bd7 697f6da1 b619e322
+          b9cc9aa6 56c945dd 3d6e4b8a 3a22d79f
+          6fa6fdff 2bcc3f86 57cd9cb1 e663928b
+          655c5d1b 9cc968f0 52acdcad ce614ba0
+          359c4a3d 300344ba b2dee327 edb60ab6
+          cb98b8df ebd0c760 abd2bc27 4ed9e5a8
+          7c780dbc 96b3e4c2 3a625d25 1d5e5bb5
+          01d138e6 de1f3d68 67f71f3d e081e794
+          aa2af5f3 5377facd 5bb0d351 85f11abc
+          bed9ad76 cfdee9bc 4eeb8fc0 c7da2997
+          c0ba296a bcc78d3b 10b062e5 86ff0068
+          224493d5 98cd8100 00000049 454e44ae
+          426082   
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="128"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000067 00000067 08060000 001d415c
+          f9000002 c3494441 5478daed d6bd4f13
+          7118c0f1 e7771cb4 d4221243 a089c441
+          6b0c0353 c55772c6 18fe1057 4a60d281
+          8585c5d8 8481aefc 21c481f2 92882d0b
+          03c41406 44539118 a9a52d87 f4cedf1d
+          1a0956c2 d2a3c3f7 9b5cd26b ef96e793
+          e7aeca75 5d39dcca c70aa954 b2bc9a1d
+          72ecea75 a5440905 9a667095 d9f62d3a
+          f8602136 3e9e0edf b95b50d5 0f1bb1f5
+          6756a6fa f96b9c11 3547edb1 ee7cfffc
+          826516a6 df8c7a30 919ea844 6ff68952
+          2ccde56d 8f2b07db 3b5229ec c50bd3a9
+          51f360e5 9de5fdd0 a1616e8f bc10e77b
+          d1df311e 6c41aae8 432f85d1 d5299be9
+          59a9ec6e 4839f7de 32a556eb f22fd03f
+          3afb4559 7b3d23b5 caa188c1 cc02cb11
+          69898465 e055d277 f0bd8eec 2e534e3f
+          c6f4c61c 572a725c 3e12f9e9 df430dce
+          df81566f 799c9327 d69f9421 e6d98b95
+          61f86bd6 f13891eb 18b2328c afb19516
+          33566925 97f0e77e 26b3ee96 1d8b5c7d
+          3e3cd737 3935c1f8 1adbcee4 c4547139
+          97f8ef56 d57d47d9 7688d105 f05fe09c
+          39f3da6f f6f71181 43e08043 e08043e0
+          1038e010 38e01038 040e3804 0e38040e
+          81030e81 030e2300 87c00187 c02170c0
+          2170c021 70081c70 081c7008 1c02071c
+          02071c02 87c00187 c00187c0 2170c021
+          70c02170 081c7008 1c70081c 02071c02
+          071c0287 c00187c0 0187c021 70c02170
+          c0217008 1c70081c 70081c02 071c0207
+          1c0287c0 0187c001 87c0a10b e1a850c8
+          663c8def bc399b75 c5f4b73f dece0d7f
+          b4ed10e3 6b6ca5c5 8c659817 c4711d47
+          73ea9b96 7389a23e 185f008f aed6df73
+          ff07c775 dcbf3ba6 c48c44b4 8dc1db28
+          c8b44b4b 24eccfff d496b8a6 6a0bed9f
+          9cb8625c eb948197 49ffb328 661658ee
+          c96278f3 f767ef9d 6a17f3ca bdc1f962
+          76ed5169 7b47f2e9 597d0d2a 9766a461
+          0eb48397 76c9a8ea 663eb6fe f449a6fa
+          6937ce78 9aa3f61b 3df9fef9 254b795a
+          875bf9de 422a3552 5ecd0e39 76b55b2f
+          0feb13f8 d6886b84 daf7a2f7 1f2ef58e
+          8dcd846f c5bffc02 2061d186 71d4b857
+          00000000 49454e44 ae426082 
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000050 0000005d 08060000 00328fe1
+          73000013 58494441 5478daed 5d077454
+          d5d6def7 4e6fe9dd f4820192 50420945
+          29820541 419a0a08 218841a5 a8bffadb
+          9ef04091 278a204d 11835204 41418380
+          413a5244 14880442 92499bf4 5ea6cfdc
+          fbce3983 ed7f8f72 0e93acdf 65ce6216
+          b29c7bee d9dfddfb dbe5ec7b 86134511
+          acfa82c0 f277973e 6dfcf1cc 9d82d5e4
+          c701a03f ee1ba200 66794868 aedfa449
+          9bfc264f dd036d38 5a4e1e4f ac5ab53a
+          dd7cf962 b2e8b47b b8531011 fde1a48a
+          5a6ddf94 23c173e7 ad56c6dd 5ec999af
+          e406e50c 1d74c462 a8ecc4e3 6f716d23
+          98289205 40f8eb2f cf0e9dff c6cab6b8
+          47e3de6f 5272c78c fed66975 78b4992c
+          480801fd a5bc2d20 b7cba163 8325694e
+          cbff361e 383a46aa 42f793a1 8fb46d3e
+          bccc258f f1ec0ffd fc273fb6 4ee2e969
+          76ab604e 2714a44e 5a6f292a ebd2a6b2
+          c85cb2d8 ea8d7e20 58ad7ceb e9938379
+          0eda65e0 9b3b9aad dea6f33f 7773f7dc
+          b6ca0a2f f3a59c64 2c5c7b0c 8c99f1cc
+          e9c13c7a 72ded0ce 433099fc dd6e5976
+          bb270882 06b8f693 43b4597d 78e038ae
+          bd0104be 0d741ecb d1dea270 3cf0d031
+          6e4d173a 20e800b0 03c0bf1f 80289874
+          5a4986f1 971f5806 2c0b89f2 db054011
+          079252f0 498a0599 4efd9706 11af1dcb
+          8065c132 b180c8b3 dc54aa51 c3edb3a6
+          82363c04 440753c8 d1062105 7d068fd7
+          8e65c0b2 60995894 81d18445 1444dad1
+          0dd9f45e b058dcaf 4d562b02 4460d042
+          972c2459 6f2f0efc fd561cd3 7ded9515
+          6e4fb81c f57512c1 6ce3399e 4a0f7eab
+          38305220 015060d1 40fce124 8c0aecb0
+          7bb95d03 9d0e1d42 81fac110 19aecac3
+          624c1801 07fd6205 a4fa0270 522993b0
+          56bd3eda dd005a0b 0ac20427 52274a1e
+          e4657297 193b99bc a1834709 b895ee91
+          21d81d0e 72438942 4eadfb98 eb5bcf9c
+          ea87eeeb 56005b4e 7e3f8085 8ba46a25
+          02d04964 a22e4408 8285e775 ba565a00
+          04bb1dc5 4e5690eb 74d400f2 0a00e385
+          4bbd9abe fbb6b7bb c0b3d754 abeb77ed
+          9cc04be9 01947b79 80d36225 32d10407
+          d8e2255e decdbcd4 d7af96ca fc318056
+          116c0d8d a00a0e60 0837c8c2 a5252ffe
+          cf72674b 8b5b9c49 c90bcf2c b255d445
+          7132fab5 a88303c1 5adf4864 a2d54089
+          8f4f2d2f 0f0e29a3 e7406432 8525a08b
+          0a075e09 4c5ad872 2ea75fe9 2b2fbc76
+          abe0d56e cc1851bd 61cb3312 25433cab
+          e6411b11 06ad85c5 4426daa1 b82dac84
+          57c676ba 42ad44c8 54ea2fe4 80dcdf17
+          7411a1e0 b4d1df5c 8a40ac58 b9f6e586
+          cc5d0359 c1b3e45d 092a7a66 de3a62ba
+          94da23a0 d04f171d 01526f2f a8cfbe44
+          64a21dca b84eb9bc 3a29e902 2d77e2b2
+          798bbe1c 5a0b8a20 74e43060 0aa47852
+          8f94143e 3d6b3d8a 0b3d1842 21d0cf4c
+          5d6baf6b 0ea1365d d1c5e5b7 dd37145a
+          f3f54416 2afe14c9 da41d3a3 e7795ed3
+          23f9822c c8ab5cb4 d32fa268 c76ef088
+          8f8390a1 bdc1c190 5cf0c889 5b4b2a3b
+          15ce497f 87f6dab2 45f36737 1e3ef1a0
+          44c5107b a0b5060d 4a065d5c 3491813c
+          7c1a0782 b0528405 e8d5dd7b 5ee4a57e
+          01ad9e77 0dff5a70 d00bdf5c 500925db
+          332162e2 68f08809 72553528 07e6aeba
+          ed5fcfa8 5eb766ec cd5ed37c f840b7b2
+          37172f91 c819225f 1bce7ffd 2072e218
+          28fdf21b 684132f0 94f3a0a8 07bcee1b
+          b543a2f3 b0925422 6046fa6a a4920e5a
+          339422e1 cbbe3b05 8de72f42 5cdaa388
+          9465f464 ccb928a1 f885e757 992f5fba
+          ed86da53 57abd2cf 4cdb203a 441527a1
+          771cbc4c 0271d31f 81e6dc7c 28db7f02
+          689d0fce db783967 0e9cf9e4 87bfe5c2
+          1e838766 fb8cbe7f b3d3c220 3c12227f
+          e3976852 19443ff2 002167ea e01af18f
+          a3d11888 386d8de8 b8be2914 cd7bea2d
+          735e7177 5ec9a07d c84222c6 dc0d72e4
+          38f23fdd f17b5845 31b095f9 3d3cf123
+          758f9e05 7f2a2684 2d5cf29a 44ab68a0
+          2d4f61e1 edcd16c8 fb680bf8 f6ed85b8
+          a527131f 622e6b3a 767a54d9 9b0b665f
+          eb3bd8cc 6b367d3e 47c2001e 560e9fee
+          b110386c 10147cb2 0dacb5ad 40bb878c
+          b191f968 ab4317bc b9e8b775 cf9f3f9f
+          fc87cc3f a099e3c5 c686ac43 236927c6
+          1ecc54d9 4cd83962 fc03d098 9d8d026d
+          13d09a18 fe7ef3a1 a377493d 3d0b35c9
+          bdb3ff58 aca8dff1 f9507dfa cc2d1c08
+          4ada7931 bf2b3cd5 d065eee3 5073fc14
+          94679d02 16e74334 78c9e227 bdee1971
+          e2b7358b 7f484344 94ce5c1a 76e7574d
+          474f3d40 7d03d115 5b759efd 18287cbd
+          217bf12a 925fb2f0 948816aa e999705c
+          d5a58b1e 172c70f3 53cba9d3 77e10c86
+          9332aeeb e929c474 b397ac26 b92f4759
+          48729a91 e3b87bd0 179df71e 1c07fcef
+          17ff0940 129ce65f 09fca57f ca19677d
+          431827a7 7fd232ad 1ababd36 0f69610e
+          e46dd805 2ce64684 b6fe5e61 e2ae662f
+          2cd54b07 123cf4ee 14081f3b 122ebcb9
+          1c4c861a 6aaf2b22 cf2df1f5 2e4d3cf5
+          632f4554 74f5750b aa2833a9 8a5eb336
+          15698293 b652884d d9d66882 bcf59bc1
+          6f605f08 1c90449e 1c4bbe8c 9d04b602
+          fce1556c e0e187e0 111d08e1 e34641d1
+          b69dd05a 4c0f1ec6 00612144 af5e3bfd
+          ff8207d7 5a96cfd8 0907829f 9dbdc0c1
+          18d7355c 2c86b2af f641f4e4 f1a009f3
+          25b1577b 0f12b228 a51097fa 08349cff
+          052a0e9f 256117b5 06230c82 e6cc5a88
+          30f9ee1a 09d57f1f c82b2ff2 48e9b18f
+          45833088 a57b8e40 739e9ec4 87bc5cd2
+          eebb7758 fb22c7de 0b12b512 f49b77ba
+          b898a3e7 3d84c181 f0c54bff 799d8cf4
+          1aff43a5 12633236 a7c97c3d ca68d3bc
+          5feb6af9 1bb623e2 f684c871 2340b0b6
+          1f785870 bfdef110 30a83fe4 676c055b
+          9305686b 855866a9 b7ae1a61 90caabd4
+          02358078 a8e23b97 47ad5c3d 5dc4390a
+          2d1fa250 c85adf0a f91f6f81 802103c0
+          3fa52b1b 1fd26a1e 125ce9af 83982913
+          a07ccf01 a8ffa588 de91892e 8718b96c
+          d90c8441 e90d6a22 d71fbe0f 4fca0a9a
+          3b6b2173 9e7b5e0f e5bbf743 cc631340
+          1de2ddb6 7c78d56b c74e1d07 e6ca2a28
+          dd7d1024 0ab6a03b e889d4b7 fda7a665
+          de4451e9 c623fcad 7716687b 77fb4e60
+          e1432440 69e64130 1697223e 7c04652e
+          7c9bf121 ce8042ef 1908dae8 08c8cbd8
+          46368b68 e33d2ca3 a65bfc89 f0a52b5e
+          b9c9aadc 4d7c49a9 12623efe 3455e2a5
+          ada4e643 b26b2812 8194017e 10f9d03d
+          6dc287d8 423ce342 20f4c1fb 90d3f802
+          4c158d4c a99a44a7 6c88c9d8 3455a2d5
+          dadd0620 1eea8424 43d4fbef a7098c7c
+          68a96e21 841e387c 10f8f58a 772b1fe2
+          0a905425 235596ba 1fce42f5 f7d92065
+          cca4c297 2c99a5e9 919c4f51 17bef9e1
+          3779da9e a0593316 33c58748 a0da9fae
+          40e5be83 10336d22 a8023d5d 951b3739
+          8ea88923 499ca2df 9a491f2c 5f357fff
+          49e3d704 ce9ab38d 4a39686f 14b1f4bd
+          7f78f4e9 7ed8c9c8 87c5bbf6 83b9bc12
+          69cbc3c8 bcb95be6 43bc0eff 9404f04b
+          e98568e2 337098ec d4f9b780 c0d3c447
+          ff14b9f2 83e768ef 4f0d20af d63863d6
+          7f3a4dea a1aea62e 7df1ae66 9ebc8fb7
+          823a3418 c21f1ce6 ea3374b0 7db0b754
+          057942cc a4b160f8 6a2f345d 29a3f6ba
+          d8fc7985 ac35faa3 8cc7a45e ded46af1
+          1fc5849b 1d3519eb 1ed03f31 eb734ee2
+          54b044f8 38d08d9d fe28147c fa39b414
+          18a8099f bc772570 283c7a08 81e9849c
+          151b807a 77ce55b4 1023dffd 575ad0bc
+          e7335870 6006108f 9ca1fd77 361f3e39
+          9a670854 b1f624bd 3413994e 2c88cd46
+          fab60abc 33269390 169373f3 df4641bb
+          917e6f03 adc1e38e 3ebbbb1c 393d8a15
+          0329eb85 8d7b32fb b59e3937 9895b043
+          8624832a 3810f256 6680bdc5 046cfb1b
+          3264be63 9057ef0e a57bbfa7 e623bc1d
+          6acac9eb d9fac3c9 786d9f7e 97db4d03
+          adc5453e 17fbf739 6dabaa89 a57eea28
+          13d18405 40e24bb3 a168eb2e 283b7416
+          2412b687 e844fce5 df230e3a a53f0639
+          cb3e401c 68a0e640 ac85aadb a37fea7a
+          fc878152 5f5f6a0e a46ff145 ab2e4c9f
+          bed65a4e 0f9e8bb0 a5d00965 24f53fbb
+          4a4c32a5 6b8b94e5 23c3a1d1 cf795079
+          e87b52b6 926915d4 bb82987e 4c97f53d
+          0b9f7a7c 19cb43a4 06b0ec8d f9b3ebf7
+          1d1acfb4 a780b42f 6ac20804 a202f45b
+          76b91cc7 2db64b93 d0686716 388c2688
+          797434d3 ae20ced9 6bb7ed7c a272f93b
+          53da14c0 c6acbd7d 0c0bdf78 9b65431b
+          7bde807e 09e03fa0 2fe4addf 02f6562b
+          35ef5d33 34720824 34f24eea 02c18393
+          e97705af ee4d97bc f4d2aa96 13c7bbb6
+          09803643 a9977e46 ea461044 0575a08a
+          344f15ec 05d193c7 a1786d1f 34e61a98
+          aa24d714 023dd056 431d146e dd099113
+          1e046d84 3f75d507 6f560916 bbae2075
+          ca26475d 8ddabd00 2247a39f 396db5b5
+          b4aa134f 1ba80a78 bb922399 478bbe08
+          0cfb8e31 95d66f18 4e204aa9 3c760eea
+          cf654327 9413932a 38031f9a af14752f
+          9cf5f872 b70258f6 d6c2271b f61e7c84
+          85f770a6 11317a38 a9c4e47f c2d60d40
+          53b4d06f f91a719a 0222c7dd c7547b24
+          7cb8fdab 1915cbfe 35cd2d00 361fdc9f
+          6c787dc1 5296780f 07cbbedd 6321e8ee
+          c150b061 2b58ebe8 bb01a8cc 10518bdd
+          68457cf8 1904dc91 027e7d3a d3577d38
+          172594be fccafb2d c78f24de 1280f68a
+          725d415a eaa728da 57317503 f86a2136
+          f561a8dc 7f04851b f96c7bc4 0c5e1973
+          ac21f35b 8899321e 54811ed4 551f2cab
+          6075680b a64fdde8 a8add130 01885f63
+          d0cf4c5d 850f71a0 e53d1246 88b8b43e
+          1eacb575 a402e34e a771433e 440fcab0
+          f718188b 0de8014e 245a455b f5217c98
+          57dc4d9f 9eb68209 c0f2258b 1eafdb9d
+          3585b581 31f4de3b 40171b49 c20b1c66
+          70edf962 edafbb82 9f6c0775 4810848f
+          1ccadebb f845e6f4 8aa56f4d a702b0f9
+          e8a16e86 050bdf93 30f29e57 7c18848e
+          be0ff49b 7680a9ac 81a9c0e9 0e8762a9
+          6921d59e 90fb8781 4f422430 b5efa1b5
+          97bcfaea 0a8449d2 4d0168af ae42b63f
+          6da36873 a8a9137c dc1ba353 90cdf4da
+          1367a0fa 44365317 14295559 5d1b3ce4
+          c3f83e2f a982ff98 0bd528d5 8b45a99e
+          dc4b05b4 9db81803 d1e6d420 4c36616c
+          6e0860f1 bc27df36 179424b2 94a870eb
+          6bcce431 e4a595c2 6dbb993c 2e8edd70
+          f8a18c8d c8f618d2 ffa8e7f0 3b8faabb
+          c4fe24da 5108cfb0 0580f9bb e8cb7de0
+          6835a2b5 3d844c84 fe61602c 2c0893c2
+          5933965d 17c0baad 9b46d47c f6653a53
+          0f092951 f506ef1e 89245573 98e94beb
+          24f0e524 e6e80f3f 1897782e a77b9783
+          df0fea9c 756450e2 d98bc971 db770ce1
+          75da6a96 5d41c1e6 842b28b4 f14ae80c
+          21c3fab2 3580623e fc72f78c ea8f3f7c
+          e8bf0268 afa9d615 3fffdc0a 961e126c
+          62baa840 d26c5efc f9d7a4f9 9c69431b
+          695ef8a2 85b303d2 667ef1c7 760a4e2e
+          079f3163 8f46affd 600ad915 a4d5209c
+          ea15d790 0eadf071 0f908678 eaad55ce
+          d57d56fa d28bcb6d a5253eff 0160d9c2
+          7f3c6b35 54c7b094 a8244a57 890af704
+          961f38cd 94aae180 d7e7fee1 db829f7d
+          71fdb5be e33be1d1 acc01953 de713268
+          105e132e 9f35a054 2f6efaa3 881fe91b
+          e27101d6 56dd186a f8e7abaf fea9a06a
+          c9bd1c7c 21b95b8e 68b779d1 9a1d163c
+          36753478 23f338bf f03d9409 98e91b79
+          90e64903 fd8a124f 9ded250f 0bafbbee
+          fd9a1a95 d97d7a9c b0e415f5 a0e569b2
+          71ae5642 77dc009a 930b7919 3b99baf4
+          45913725 9c389da0 49ee5548 34b07add
+          9a348791 0dbc80fe 89e03fb0 2f499f6c
+          4df4e091 05394188 5ab926ed 46e0112e
+          f2f4b244 af5d379d 937216da e0f8b786
+          78b456bf 017d20a0 5f227daa 47385550
+          23ccd2c9 3f059351 5a9fb9eb 619eb687
+          0491396e 16c225aa f2cc2c68 b858c494
+          aa9106c6 d93317fb 8c1977f0 66aff118
+          32ec5cc8 f3cfbdc2 1a1c37e4 1443195a
+          335e3b69 78a27dc9 08295ae3 be6fc663
+          6be04d17 cedd6e2d 28e94c1d ec22228f
+          1c3f122c 95d550ba e73093d3 c0319eae
+          47d713e1 8bdf5d40 7b6de8eb 8b9679a4
+          f4cc62dd e037a035 9b2baa20 62ec08ea
+          430f3017 5a4baaa2 8ce77eea ca9bce9f
+          eb8ee237 9ec6f362 edd34606 81676267
+          28dd9d85 fe4ddf05 85f988d7 289aa2d7
+          65a4f21a 0d7584c7 c91522ba 76a644a7
+          ac63d9e0 c76b2efd fa5b12da 68c2fce9
+          0a0e9cab 01de74e1 7c37de7c 39a73375
+          b0ebc065 aa0444e8 2dd09457 082cda4b
+          1a79de78 639e26b9 f715601c ea84a4e2
+          88c56fcd 61da0741 5a88d76e 6f6c029f
+          a42e4ce7 df98732f c5f396e2 a208eaa7
+          8fc81817 0a5a8b4a c06174d0 bf7381df
+          1a7af0de cd41739f db70ab39 6fe05373
+          b7a0b936 b2e4b94e b300adf8 c5f19828
+          a6fd199b a1348c77 d6d7539d 2649ce0a
+          50f0a0f0 f1066379 15357fe0 272d0ff4
+          a9885af9 e15c7715 0ed05ccf e039a9b5
+          089b6179 25287dbd 5d5644a1 c5b80fdc
+          5157e7c7 3b1b1b3c 38ca7e12 fc62213e
+          b1c386d4 9fe54d71 9f87c66c 918786d5
+          b90b403c 179a7333 75bb1c5a bbada919
+          05d50a22 136d8f81 d0d2ace5 81e755b4
+          4f8d974a 91d9f2e0 3499e9f7 37d0f73d
+          870c3b08 6e1e644e 86b5e013 3b388984
+          c844ffd6 3dafc2ec 455f33c1 47a022b5
+          75daecd4 26837017 e5e11125 ee0610cd
+          598ae616 684120ef f32112e7 d8fa4ba4
+          3c307427 fc7a3c10 6ef3a0be 56c2d938
+          99cce86e 00f19c68 6eea7088 9c587455
+          2158eab6 b7586817 e90eabc1 6b552b44
+          a9af9fe8 6e00f19c 786e9af4 ceb576f1
+          576b661a 1d4780de e2e800b0 03c00e00
+          ff8600e2 53d751e0 c9f1dc5f 1e002c03
+          9685f55c 5729fd0d 813433e6 aef9048c
+          8672a633 a7fedf80 87d6de5a 524e64c1
+          32b16cfe d38b4fce 0f7440fd 857cd76f
+          84f07f65 ed03d2e0 8e65214d 045c7b00
+          7815c4f6 ec75696b 106f4596 0e27d2e1
+          853b00ec 00f0ef0d 20eb79ee ccf507d1
+          f50abbfb e715416c 5f513076 3c275734
+          b4df0d71 ec256de5 958a26b7 6b029a13
+          cd6d8476 c41063c7 6b7aa71c 6e2f1d24
+          4792c477 3e2b0b08 6c74f7dc 784e34f7
+          8fee7a0b fe86b220 cc30769c 459f1f74
+          71f0c0c3 9692cadb dbfad700 250a6973
+          7c66e6dd 9ec3ef3d dd164235 eddfd7f7
+          f2a85159 edf2cb86 e141b95d 0f1f1fcc
+          fde1b735 9f32fe78 e60ec16a f2e7dce8
+          5cc8f9ae 125993aa 6bd2d9a0 f4f4b5da
+          7e037e69 4bcd6839 793ca16a d5ea27cc
+          972ff612 9d764f37 ffb6a6c0 2bd435da
+          947ec782 e7cc5ba5 888ea9fa 37e89256
+          f9b676e1 3a000000 0049454e 44ae4260
+          82   
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="256"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          000000cd 000000cd 08060000 001bbcf4
+          69000005 c5494441 5478daed da4d6c14
+          e71dc0e1 77766777 4dfd9108 303484f4
+          900a5b88 4252f502 9444e294 e6d2268a
+          c4a1892a 55b98473 1c12a997 a455a434
+          ee014e3d 57552eb9 54e9a1c4 9736288a
+          7b8a12be 64d92848 016452d9 8e8cb11d
+          efe7e41d 2f348622 1c2c5169 d9e71123
+          a3dd592c bd9a9fe6 ffce9264 5916d6aa
+          5dbe5499 1ffbf0e7 cbe7ce1e 58999c18
+          0e853018 5f2e05e8 0ef5d00a 33a5ed8f
+          4cf51f7a ea93879f f9c578f9 b11f55d7
+          9e90dc8c 26ab55c3 f4bbeffc e6ea89e3
+          c76ab3f3 7b92fc4d 0b4897ca 6e1ce5c1
+          87cf6ffb edcba33b dffac35f 0a3d9bbe
+          8ba6b5b8 182ebc74 6474ee83 9323c534
+          bee8be02 ed78ea21 341a216c fde5b3a3
+          bbde7bff 58a1af2f 0e5fd195 b7dffcf5
+          6c0c26dd 2418b865 148b3d94 6217b37f
+          3ff95aec e4c5d5d7 aa572ef7 9c7962cf
+          e9e6c2c2 5092b64f 6cc5bab2 86f98cee
+          9ecff21e 0a376e22 790fc581 81c97da7
+          cf3f995e 1b3bb9bf 3eb73054 dcf45d30
+          db9ffa59 e81f7a3c 845ac3e2 d19dca69
+          b83e7531 fce7e34f 57c3c903 8a9d0c5f
+          1bfbc7fe 74e1d43f 0fdd1258 33848776
+          fd386c7e fa4008d5 6accab68 01e92ecd
+          1841a512 0a5912be fae8d35b 9e1d2f9d
+          feec505a fbeaeaae dba7b056 3dde6e56
+          5642b6fc 4da82f5c 0fb73d95 8607770f
+          1363280d f4e74fc8 da1dac7d 2f1edf4c
+          4e0ca549 9a6ebde3 a7d334d4 6230e7ff
+          f4e7d08c 635a52b0 a03ce0db 98561cac
+          e258b667 e468a8c4 70ee1855 31dd9a6f
+          fd2b77db 0c356bb5 78b44443 574413e7
+          acf61734 77ddedac 77bb2a14 e2211aba
+          643c2bac 7fa1a71b 2ab219d6 ab113aa0
+          907cdcba f78fdd7b 344992a5 03bd4bf9
+          4fab4e67 cf6359d2 5c5eeacd 7fdeb768
+          f22f78d2 877a9776 ff6bfc60 697070c6
+          aad3c9ea 33338313 870f8e37 ae2df625
+          e97d8ae6 e69da6bc 63c774ba 79cb9c65
+          a7a3a7b3 52a9be91 896943db fbac5ef7
+          3fd4e8fc e96c83d7 b1676220 1a100d88
+          064403a2 014403a2 01d18068 40348068
+          4034201a 100d8806 44038806 4403a201
+          d1806800 d1806840 34201a10 0d880610
+          0d880644 03a201d1 00a201d1 80684034
+          201a100d 201a100d 88064403 a2014403
+          a201d180 68403420 1a403420 1a100d88
+          06440388 064403a2 01d18068 40348068
+          4034201a 100d8806 100d8806 4403a201
+          d1806800 d1806840 34201a10 0d201a10
+          0d880644 03a201d1 00a201d1 80684034
+          201a4034 201a100d 88064403 a2014403
+          a201d180 68403480 68403420 1a100d88
+          06440388 064403a2 01d18068 00d18068
+          4034201a 100d8806 100d8806 4403a201
+          d100a201 d1806840 34201a10 0d201a10
+          0d880644 03a20144 03a201d1 80684034
+          201a4034 201a100d 88064403 88064403
+          a201d180 68403480 68403420 1a100d88
+          06100d88 064403a2 01d18068 00d18068
+          4034201a 100d201a 100d8806 4403a201
+          d100a201 d18068a0 6ba2494a a5baa5a3
+          d36df43a 4eeff913 5996d4a6 a77764f5
+          7ac9b2d3 c9ea3333 83f9f57c 5fa349e2
+          d9cde5a5 de89c307 c7439264 969d8e16
+          83c9afe7 24bd8fd1 dcfc458d 85c5be20
+          193a7e3e 8b7f8aff 8ff12c6c ec17c183
+          62dd68b2 562b1e16 8a2e98d6 5aedebfd
+          fb44b372 b7db57b1 5c8e7f69 84c4c369
+          ba209a62 395dbdee efa29a66 cdc6d777
+          7cabd108 e581fef0 9391a371 1b6341e9
+          926d4e0c a614affb fcfabf63 58cdc66c
+          ba6978f7 e4fcd8a9 5bde2894 4a21f4f4
+          c47f2059 0d07ba4a b31942a5 d2ee606d
+          30f188bd 4ca5bd4f fcf493db 37f9d72e
+          7c115af9 13e55ac3 02d29de2 9876fdc2
+          c5ff79e8 95f79254 af5cee39 b36ff7e7
+          cdeb8bc3 379f57b7 eab1aa46 586fb683
+          077883d3 fe5eb270 e36693f7 50ecef9b
+          dc7766e2 c9b4fce8 ce95475e 1df9fd97
+          bf7bf3bd b4d80e65 f544dff7 c37f036a
+          c61bc9ce 5747deca 7b49b2b8 cbcf6ad5
+          3075e4f9 3fce7d70 f258317f 78201868
+          f7526b6f 71b6fcea d97787de ffdbeb49
+          b91256a3 597db35a 0dd3a3ef bc74f5c4
+          f163b5d9 f9bd89e9 8ceebeb9 b4b7368f
+          6e3ffbc3 578e8eee 78ed8dbf 26954a7b
+          df9fddf6 3cb976f9 52797eec c303cbe7
+          ce1e5c99 9c180a85 b02dffac 86e89256
+          aaa11566 7a86774f fd60efde 7f6f7eee
+          85f174cb 96dada93 be0565e8 7e36c905
+          66d60000 00004945 4e44ae42 6082
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000009e 000000b8 08060000 00127055
+          50000029 6c494441 5478daed 5d079894
+          45d2aef9 26efec6c ce39b084 254a1049
+          8ae88a07 92410ffd 55104441 451445d1
+          33073087 f30c8044 0f044150 90a8a2e4
+          bc80bbc0 c2e69cf3 e4f477f5 b770a8e7
+          89b05bdf ce32e533 8feecac3 7477bd5d
+          f5767575 95cce572 c1c562c9 cfd3d46e
+          db36c094 76b29f29 e34c0799 1c82d8af
+          95d0bac5 c1fea911 f4fa6cfd c0eb0ffa
+          a5dcb25b 9bdcb91a da80388d 06a8ddbe
+          ad9be158 ea4063ea a16e2e87 231264a0
+          6de5c3b6 b91c50a1 0c0b3bab 1f70fd3e
+          bfa143f7 aa63622d 17ff01d9 79e0b92c
+          66287a63 debda51f 7e38c75a 559b2cc3
+          ffe9664a 72357d14 5e8ac280 b1e33f8d
+          796dfe9b aa98589b bb82ae7a c3bade05
+          cf3ef3aa f1d4d99b d9bce4ee a693f3fa
+          5005f99d 0a993cf9 ada8975e 5d2a68bd
+          fe033c67 63039cbb f3f677aa 366e7d5c
+          ae60bf54 bab79560 bb0d1c56 005de7f6
+          9b3a6eda 7abb3a2e dee46e73 287e7bde
+          88bc279f 59c580a6 13b46eae 0f3b809d
+          6dffa0db 86be9bb4 eaabd982 b75e045e
+          fe538fdf 5df0e67b cb956e3e c1df8a9d
+          c12d60e8 e0773b6e f97136c8 dcc756d4
+          fffc636c fae09b52 0515f833 aad366c4
+          c6f4113d e7b17b62 de787785 cc5290af
+          3dd9bdf3 09474343 924cd1b6 808776de
+          690573f2 0f3f74f6 193c24db 3dc6ec82
+          8c11b77c 58fdddf7 8fc8db98 2140cb27
+          d7ebcf75 3b91de5d a8dbb6f9 3a5b751b
+          041d8884 c8e9044d d5dad5a3 dc65c8ec
+          70a7abdf b56b34b3 766d4f1d 0c6388b5
+          ba6d5bfa 09753fef 1c086d58 04063e53
+          fa2f835c 0ebb5b8c b7f1c0be 8ef6066b
+          745b72b1 bf15c389 6303045b 596992ac
+          0d034f26 302b929b 13e7b258 04b7e041
+          e5a5096d d91020d6 4c1967da 0b328522
+          10dab8c8 e4727f76 b850b9c7 58af027d
+          30cca115 5043db17 0c10096e 3256d555
+          a00f9500 1ef18814 dcdbb304 1ef100cf
+          231ee079 c4231ee0 79c4033c 8f78c403
+          3c8f7880 e7118f78 80e7110f f03ce211
+          0ff03c72 1503ef7c 22be475a 8748a40f
+          817a922e a7f8f148 2bc1dd79 7d10838f
+          2cefd869 0108eadb 05c287de 082e9319
+          3257ac01 73651d08 728ff2a5 10a70340
+          13e40bed eebe1d64 5a0d946c db099507
+          d34050b7 31e0e1ae 52f9ea41 97140f60
+          3082a054 7a5caec4 2e1675e0 9d180ba0
+          f302d581 23a49e88 d4d5ba9c 6c66561b
+          b8d8075c 1ed4490f 3e97a80b d4899396
+          ff5c1da7 5a7cda28 b8c75465 f2ab837b
+          b479e0f1 974d6515 326b4eb6 5bccd570
+          3c55f000 af054c3b 07033742 321a8e87
+          4f1c0d16 b9a3bede 2d1e70da 2a2b68c6
+          e9127520 fb8d6e28 8147f78d 4ed7afdd
+          1fdd2c55 ccd56adc c4d5eae9 d645f89d
+          51a03241 f8cd0e9a 15c5b9b9 c409e24e
+          a3e5325e a40abd32 e005127e 976800f0
+          90814681 ce1638e8 808730b7 3b44fb8a
+          c0a322fb 325ec447 6d3a7d2a a8d51f32
+          6d363067 9e8b9251 2c0de24c 21175fbc
+          bb9a7403 b4c0b392 795abbfd 3f160f27
+          4d64ddf1 2bada525 89ad1d78 0e43a3cc
+          5a541427 23da9372 8ca5e297 61c5303b
+          69a5052b 4ed14c65 799c56ab 880266ed
+          307849c9 2a0cc78f f568edc0 b3646785
+          d9ca2ae3 29ead870 35a818f0 e4028fee
+          3b99b525 74b56632 e0219570 60b01203
+          950c7872 8d9acce2 e1b55ce3 bebd831c
+          8d0dad1a 788d07f6 f574dac1 8704002e
+          2c60a915 0f188cdf 392c16ca f39e4970
+          391c34da 40e0994c e22521db 650aad96
+          0c785868 d29c55d8 a371cfae 8ead1978
+          d5ebd68c a13cec2b bc754d16 c1c17463
+          26b3782e bbbd5650 8684d451 01cfce26
+          e744abc7 26abf4f1 263c2ab2 c9ca4055
+          f2c1bb0f b456d035 ecda195e bf6bff38
+          caf2646a 5f1fae0b d409ea86 02f4686b
+          b4ed3bd4 08baaedd ab48e2b8 cca2db0d
+          4670184d 7cb2ea00 7f52c5ca 9967afdd
+          fee3fd75 dbb7746a 7da70a07 14bef6f2
+          8b4ebbd3 8f2aa48f 65d0d441 62e4c661
+          3673dd50 7db73a36 ae5a705a 2c15541c
+          cf6eb480 b55634b0 dad02076 c000baf0
+          b5b89b75 b98f3ef2 99bda2bc 55dd6214
+          cd7b7964 edf69fa6 915500c5 4ade1a39
+          688245e0 596a6ab9 6ea8dcbc cb6a2d13
+          34098925 3222c53b d9a1d658 52ce7fd4
+          04073177 eb459b8a c3ac9ef1 4cd6a0dc
+          47a7cf6d 45078a88 a279f33f 15080b9e
+          b3030cf3 387ea0f6 f713993e d309eaa6
+          c5399e18 4903afee d7140baa d8f832b4
+          b65496a7 21379f7d 9b13148c 5fe8a2c2
+          f92290ba 5c0d40c5 aaafff51 b5fadffd
+          24f7b00d f59035f9 eecf9c26 6b386529
+          60ac8aef 1d170d32 9d964719 1a72f209
+          bf1c2c72 3fff3241 1914542a f7d1d602
+          81e511d8 e23664e7 82b3b111 40a900bf
+          ce1de993 41657c1c aa9c9933 179933ce
+          784b09bc fc271e9d 653c937d 1bf92d32
+          e3727ec9 edc58345 a381e924 8feb8602
+          f0ca207d 25e37865 822a2aa6 5a19e89b
+          4fe1f270 579b4a6b a1914d14 25a04b47
+          50f9aaf9 8048b1a7 c292afd5 c93933a7
+          bf215542 6ad59a95 3d4b172c 7d5d4e0c
+          3aacbcae 0df101df 0eedf82e 6c64d6ce
+          545e0324 1697614c 111c98ad 080c6c14
+          04add6a5 6ed7e10c 95cb4390 551c4ee5
+          265e1912 0441bdbb f36628b4 abcf5cae
+          164fb93f cd28f9f0 9d11d4a0 339fcbf0
+          ca9d3973 31b3325a ea563d0e 1b40709f
+          6b408ea1 14666d2a 0ea57230 92704b7c
+          e7d12139 1dbbfb08 18b9d675 bfe60899
+          9567d6a6 fac41930 e717f118 4bf89041
+          8ce46ac9 26ffdbb1 e43ffdf4 278d870e
+          84d281de 05398f3c f886b5ac aa3b7555
+          66342eda 606f0819 d8976f3e d441f589
+          d340193b f4ee73ed 81266f8f a78c1efb
+          c9dc1cc6 f38c7628 dcbe936f 01555830
+          c48c4ce1 bb819aef 612ccb69 7144e63e
+          34ed23a7 c542f29d 85af3c3f ba66db4f
+          0ff3d009 e57c5da2 9b8d1e7e 13e35901
+          dcdaa10e 501764d9 3032b0e9 075cff1f
+          e0f9de94 728291be 3c2aab83 c1dccac3
+          e9507df8 381f4d30 db81a1fd bb81dd0c
+          e4821cab e1c82fe3 0be6ce9e dae227fa
+          037b238b e7cdfb44 2e41796d 075bdbe0
+          be9d21a8 ffb57ccd 6b8e1ce7 3a90133d
+          6774d930 701c9ea6 eb71cdb9 0bc05386
+          4718f583 066d7152 f5396c6a 4398bbf6
+          3bb01497 32edcb21 76dc08f0 8e09a2e7
+          7b20c6f7 8adfffd7 dbb55b37 b76f31c5
+          d7d743d6 a4ff5bc0 2c6c1875 f314d4ab
+          36dc0fe2 c6333aab 9083b5b4 1c72d66e
+          bea0072a 7ee77b73 ca06b9af 9ff302f0
+          5002c7dd be82d2f8 e3f1dd52 6d80ec95
+          ebc165b6 70b29b78 d758b603 15f4a75c
+          811b01df dc993316 daabab5a e47c97f7
+          e4a38f9b 32728751 874e305a 21136490
+          f0f751a0 08f4e74f 1973566f 004b5523
+          4908e582 9b15c01c 78c75d2b 2f8ae888
+          e23f72ec 7e4d4cc8 01176177 577473b5
+          e9b950b4 f97b3e3a 5dfb4488 1d95024e
+          093acca2 d5339dcb bb3ee7c1 294f37f7
+          df5db972 79afb205 4b5f934b f0eac3c1
+          a86be42d 03c1a76b 270e8092 ef7f86ea
+          e3594039 16ac22a1 efdf67b3 ef909b33
+          7f073cb9 5eef0aba 7bd2bbd4 2dbf50e1
+          45db7743 5dea2f7c 61426f1c c8b84832
+          3824e830 8bcaa8fc ea9be72a ffbdecba
+          660b9d9c 3da3cb9b fdf81266 5d34e4a1
+          13c6ebfc 93632072 d8cddca4 37669c83
+          82ef7692 95a9b8e8 200fc1f7 4e79e7e2
+          c745bf3a cf843dfc d80675a8 df514aab
+          27131360 21fbcb6f c1565ec1 3948dc84
+          91e015e9 2fde1f92 fa5cf156 236ff6ec
+          45e68cd3 decdb1e2 3933a6bd 692dadea
+          4add7c1a 0f8a4a1f 0dc4df31 1a641a0d
+          386aeb21 7bd506b6 a60e9011 3e6a7532
+          f07b758c df1474d7 3dfb7e65 702efe41
+          1916668f 7af195e7 a9ef4ff1 82dc5cd9
+          c0c0b781 73104580 3f24de39 0604959c
+          bcb21402 c45a56d5 3967c6fd f35d8e2b
+          239b85af 3c37b6f6 87dd33c8 fbceba44
+          321f37f6 56d0c444 f21ff2d7 7f0786fc
+          4ad2985d d389c11e fdfa9b2f 08da5f2f
+          c2efb01f 3c69ea66 5db70e9b 9c16dab5
+          4237577d 3c138ab7 fec82d85 77720788
+          b96d0850 8f838f05 6f357edc fb50d987
+          ef5ef6ad 46e3fe3d d145afcd ff588abe
+          b318960a edd74d0c 9d30a93a 7004caf6
+          1c076a8e 89aede67 c8c0a501 63c61ffb
+          9db1f9dd 2f98598e 79ebfd27 99a53152
+          077431a6 54b0e527 a8ffe5b4 e8fa6fbe
+          01827a77 9084efa1 152e78f1 c58f8d27
+          52c3fef2 82d7d6c8 b326ddbd c4657384
+          92874e18 3dd14506 40ccb8e1 8cb60860
+          292c86dc 755b78b0 9c926362 6442d008
+          15b16f7f f0dc7f5d dffff64b bf5b6e3d
+          1372efc4 d71cd401 5de47b0e 17e722b6
+          8a2a9ec1 1277c728 d086f991 9f74f1d2
+          dcd1608c ca1879db 0a6b41fe 25d37197
+          c502676f 1ff796f1 6cee4d92 844e18d8
+          78e8c4df 0f5c260b 5f4b6bad 09a83bb0
+          633c36ec a1879ed5 f5e8597a c9c04389
+          79f3fdb7 35b16187 a9093e5a 1a53691d
+          8f3581cd 06cac000 4898388a ed58819c
+          ef21702c f9c53767 8c1eb9ca 70fc58c0
+          9f2e7603 23f0d3a7 3d53bb63 e763e4bc
+          ae297412 75eb20d0 77eec0e9 0ad296da
+          5379e42e d6c93c94 77d70edf 47bf346f
+          d11f7ab7 175f7cf1 bfff0f9d cea18e8d
+          3b54f9e5 57f7b293 9e92d24c 23f80cf9
+          55a0d0ca 419f9400 ead06090 b1635a4d
+          5a2e08c4 a743bcc8 b7e49775 aafe6ad5
+          187b7545 a32220a8 92ed00b3 e0e52503
+          a75370d4 d50ae6ac 2cbf867d bb7be73c
+          30f5ddea 4d5b1f56 48013a0c 9d748e85
+          f88963d9 0149090d 6967206b d54666e9
+          5ca4656a b8711084 baa4556b 466992da
+          57ffe1ba bafe241f 2d6bf25d b3ca96ae
+          7c8f7a31 c588bb1c 3a3d7c2f e83b7500
+          9795b9b0 4f9741f5 892c9022 108be109
+          74f70cf8 f50a7f9f 4a654888 53b4720d
+          602d2dd3 bb2cc033 5c040940 875108a5
+          4e0b9d67 4f034d54 04d8ab6a 20fdddcf
+          789e1df5 46b5336b 17f5c4cc 69316f7d
+          b0f07f6e e83f039e a3be0ed2 075eb7de
+          f8cb99d1 d48b8a6e 5e1b1600 9d1f9fc6
+          398bb5bc 12d2def9 0cac350d e40b7a71
+          880089f3 f96b3db4 269c3f11 de7bfe76
+          3cc8a7da 4f1e0b81 98ee64b7 43e6e295
+          50be3f1d a88d051e 027dfaf7 59db69fb
+          8f13049d f79fd1f9 3f3969fa f842e2b2
+          2f1e1474 ea3cea9c 390c4518 8aaa21f7
+          ab6fd982 3a40c55c 6ee2c491 fcee51b2
+          caf14d40 c3e83f7e 784e9d20 11e89a5c
+          6ce8c01e 10d8af37 ffb962f7 01a838c8
+          40274166 b39c6124 fed3850f fd19e82e
+          097828ba 6b7a95c5 bdf7de64 e66a1ce4
+          65e9f171 cec15350 b6733727 ccbe3dba
+          42e4d081 9c485fed c2432731 41103b66
+          38cff031 e5e643de 86ede2e5 3fe54670
+          711ae260 18b9cfab 6bf7f24b 0c605c9a
+          84dc3f7d 67c8e4bb 5e200fb1 c8c4c346
+          de37df83 e16c16ff 05de3d06 74890387
+          f9ea051d 5a7c4129 e7a113b9 9f0fb80c
+          46310cd5 6801ead8 21ea8161 e3258691
+          1fff42e4 ecd225ee bd8f5ed7 75ebb4d9
+          491cd015 3385ed90 b5723d38 eaea40a6
+          5643c2c4 31a00ef0 922465be 55583b66
+          f1a387dd 00de1d93 b82728dc b41deacf
+          16912576 5e1c3ad1 754fde81 d8f84b34
+          eaaffc61 b9af9f2b 71e98aa9 72bdb650
+          0abed798 5f09796b 37f177b9 aaf03088
+          bf7da4f8 48ec2aeb 5cc04327 dd1220fc
+          96c1fc74 83993dc5 3fec273f ede3014b
+          a65294c6 7ff4f114 860d478b 01af89ef
+          9544bdf8 c214e6d3 9d52f0bd b2fd27a1
+          629798e8 e0dfbb3b 44dedc4f 929479c9
+          5c2c5601 f0f782f8 3b46b1ff 50838d9d
+          f4b3576f 14372035 af631c33 e6f557ee
+          d70fbca1 e02f1b92 cbf9cef0 c7e66c0f
+          1c3be215 49f81e23 ceb9ebb7 83313b97
+          e754458d 180a7e1d a3af0ebe d7d40b2e
+          6edc3050 4784f34c 9edc35df 80b9bc9e
+          3cbc84eb cd30f056 f8e34f6d ba2c0f76
+          79009041 c282cf5f d6b68bf9 9e3a7b04
+          f99ec368 85ac7f7f 0dce8646 de872bf1
+          ceb1a0f2 d3b679be 87ca0e1b d41302fa
+          f6e2282c dbb9072a 8f9d05ea eb39d439
+          d3fd5e86 817f5cee b5c865a7 042a0283
+          9d894b56 4c962985 12ea3712 183f6bc8
+          2983bcaf 37f187e1 eae80888 9f30bc4d
+          f33d746b deb1c110 3d7a182f 6c693897
+          0df91b7f 20b7749c d729846a d43dc3c0
+          65dfe45f 512eaa7e e0f58531 afcfbb9f
+          2d8a4b0a be57ba3b 152af71e e260432b
+          103ef8da 36e97279 8a910a43 27a3d901
+          4f0fcefa 06c85ec5 4ef8661b 7de88441
+          2de2a939 3398eecf 5d91f1b8 d281843f
+          f6e47781 e347ce93 8cefaddb 02a6bc7c
+          eefea347 dd0a3e49 116d2eb8 8c77c4d1
+          c38780ae 633b6ee1 0bbedd0a 8d39e540
+          9d648a57 62feb7dc f049f44b afafbe62
+          af75e500 607cefd3 c52f6893 627e7212
+          830f773b 064cf189 a4135b91 eabc20e1
+          ce31a0f4 5693973f 6b29c113 7b408f76
+          109e723d ffb9fa50 2a94fc7c 843ed589
+          6d667554 706ae2e7 2b9e688e 74976679
+          f6a1080c b4272ef9 62924c25 9453f33d
+          0c98d69f 2b86826f b6f040aa 362e869d
+          fafe265e e2bb39df c3cda309 d441fced
+          a300542a b0169742 ee57df91 2724f07b
+          71b9d090 b070c9bd aaa86863 b3f0f4e6
+          1a9c7ec0 a0bc98f9 f3a74951 0900777f
+          c9cec350 7de028ff 3968c0b5 10767d4f
+          b78eef9d 3f28c58f bf0d5461 a1001666
+          d9f12176 8d81ee21 f679178b 6f739f9e
+          33cbefd6 e1bf34db 01b13907 18fee813
+          df040cbd f16df237 12327126 39cc1a98
+          0b8a7863 8bd831c3 409f10ea b67c8f87
+          4e6ee803 fe7d7a70 04966cff 096a4ee6
+          d0ba5899 c8eb7c07 f45e1139 f7f9c5cd
+          1a9968de 8132bef7 f9f267d5 51217ba9
+          e37b6805 acf526b1 2486c904 82de9bc7
+          f7145e2a f29218cd c1a7f4f1 a110c30e
+          4bb8891a 4f6540c1 e69fe91f 62b37128
+          83fd4f27 2e5ef1c8 6f9f27b6 2ee03151
+          45465913 162d9904 82ac8a3a 670ef95e
+          dd990228 d8b88dfb 2aaf8438 881b33d4
+          ad0e1ae2 eb2c05cf 3a117cf4 e0a8a915
+          1f62db68 1f6263f5 4eb684e6 f88ffe35
+          49d3be63 b3f74269 91a9f80d 1d9619f5
+          8f671e94 e44d2c73 45c5dfef e765b8d0
+          55045fdf 8f974093 e289e4e5 864e6246
+          dcc4ebc8 603671de faefa0b1 b09a3c74
+          6267ba0b 9b3eede9 c0db271e 6a110fd5
+          52038f7a eea5b5fe 7f1bf281 247c8f7d
+          72d66ce2 a7408cf2 c78ebb8d 274c3aad
+          ad1b74fc fef39a24 081b3288 4fa272df
+          6128db7b 823c9b18 539df4d7 74f93afa
+          d5373e68 316ad472 a44b0e09 8b963fad
+          8e0a3de8 9280eff1 1268abd6 f377ae72
+          5f5f5e12 438a1268 7fc5d269 82f462e8
+          44ad0273 7e210f8e 53671363 dd1cb9af
+          776ee2e2 650fcafd fcc0fd80 877c2f22
+          d29cf8f9 927b5d32 19493b83 dfbadcea
+          b45c28fa aea9045a 87768cac 4b5302ed
+          cfb52dfe 2bfef6db 40191622 66137fb9
+          1e6cf566 da2b3117 8f1ddae3 de7d67b2
+          578f9e2d daf1a9c5 e9aaef2d 7fcb887a
+          e11f33a4 086b28b0 04dab63d 62093490
+          b604da9f b9d88821 d7815fcf eefc4aac
+          68eb0f50 7bba4092 5a27a153 ef7921f8
+          be693fb5 b857a298 50e4332f acf21f76
+          d327e40a e7cda75d bc049ab5 ec7c09b4
+          51e01515 d06af81e 6e489f76 e13caf10
+          f968fdc9 5350b47d 0f7d0a3b 2f2796b0
+          2df69d7f ce23520d 01df9733 beb770d9
+          13eae8d0 a3e4f1bd a6126839 c8f7ac56
+          fe3e3771 e268fe50 46b22792 17854e14
+          5e4a9e75 22e875bc 5e4cf697 dff0fa31
+          94a1131e c2512b4a 123e5f36 45eee3e3
+          6a33c06b e27bc6b8 f73eb897 fd67bd4b
+          0abe7722 0b8ab7ee e444c6bb 537b881e
+          314472ab 877c3376 640a6813 e3793671
+          deda8dbc 6e0c7936 b1155c31 f3e74fd5
+          f71f5844 e88ce824 60dc1de9 e18f3d32
+          530abe87 51ffc22d 3f379540 9341f8cd
+          374060af 0ebce402 0698a93f f8bd41bd
+          3b42c8e0 01fcd45a be6b1f54 1c3a45ce
+          eb701cc1 7f1f3b3f ecd1d99b 49a35e2e
+          e25e5e2e 9b0d4edd 346861fd ee8353c9
+          53b679c8 c2173a3f fe002803 fdc15659
+          0d673f5f 09b60623 e9e99137 93f3f682
+          f65326f2 53ac3133 07d2df5f c43b6553
+          8e83a7b0 7748dc9d bc6bff10 6550b0bd
+          4d030fc5 929be375 eac6813b ac45c5fd
+          c9ebb699 c4206dd2 b4bb4186 79650e07
+          03023dd9 c3b26b58 ffcfde68 80331f2f
+          8586ec12 d203052f 8aa4d414 74dcb869
+          b0cfe09b b2c9230e 52701b75 5c9c5115
+          199161c9 a7071e8a 9211790e 3aac42ae
+          51314f27 a3cdddc3 8030d657 669bbefe
+          4c26d467 969017d8 71317eeb 737d9ff5
+          52804e32 e0e5cf7d e29efabd 47264bf1
+          3a4a9f10 06b1d8e1 46a50243 c639a83c
+          7a8abc5a 26be86f3 8e0b83c0 ebfa805f
+          f7ce10d4 f33854a5 9e23e577 c879eb7f
+          da3ba972 e5f29541 77de7390 dce253bb
+          da9a6fd6 25678c1d bf9f9ddc 7c803a64
+          c0c0d679 d614f06a 170f76c6 efd2def9
+          140c25b5 b4272c10 8d2bf633 4b9e791f
+          e8bb7502 6b7119a4 bff31958 88cbafe1
+          f598a0f7 3ed765ef 81ebb49d 3a57931e
+          f628bfcc 9c9da9cd 9efee072 e66a7ca8
+          b5cd4317 a35318e8 e2c487d0 5f7d0b26
+          063aa556 acf24ef9 41b78a1b 01db2be0
+          06508587 42c21d23 c4a70c84 76005b2b
+          d86b1a93 b226fddf a70eec9a de1681e7
+          349b2167 eaa4b7ad 2595bda8 131af140
+          11725d17 08b9a13f 576cf94f 7bc5d085
+          04d53b2f 2c3cb378 c6c26ac8 fffa3b5e
+          ebd9b757 778848e9 4ffe3c13 d7a0fed0
+          f109054f 3dfe689b 045ef11b af4eacd9
+          b9770675 9f560c12 7b450688 bc4ea9e4
+          a18bbc6f 76485751 f462a563 2d987d27
+          a172cf41 7ed0891a 7e0bf876 8c224fd7
+          c7b4ab92 8f17be51 f9c5d27e 6d0a7875
+          df6f6b5f 34efcd7f 51f769bd 5043eece
+          d1bc5b10 7f088da5 ce4cf40f a1ffe874
+          cbdf067f bd95d190 3c9079eb 78f935a5
+          5e439bbe 258e439d f3c8c3cb 4ca7d202
+          db04f06c 2545eaec fba72c77 d96cfee4
+          cd46b086 dcf0c1a0 4fee20b6 55dab085
+          97bea076 f5ff53e7 e7df0633 bee76a30
+          80262e9a b782e2e9 fad47caf d6909439
+          e9ff3e71 12f0bd96 051e6686 4cbbef0d
+          736e515f 725e870f a1af6927 d690635b
+          baeac051 2895e021 f425b95c b636b519
+          8550f8dd 76367017 040de8cb eb1adb25
+          e07b8d87 4f4cc89b f3d82cb7 065ed1eb
+          2f8dabde b4fd51f2 57efe7b3 79ef180d
+          a05283b9 b0887703 97b248f6 a5800fdf
+          8ad4a59e e4371ab1 6387f322 3dd4ef94
+          5157a59f 2c7aa372 c592fe6e 09bcba1d
+          5b130b5f 7ef533fe 48853275 bba94061
+          fc1d2341 151ac29f 3a624a14 b6551214
+          d06a4576 3e7770cd 46b09596 839ca76f
+          4990ae2f d69c5621 df33a6ff 12e856c0
+          b316152a b3a64d5d eab23b02 a528041d
+          993200fc 7a76e5a7 0becfe5d 732abf55
+          bad8df29 03db6995 d543ce9a 6f01cc16
+          d0754ce4 2fce24e9 e356676c 87f13da7
+          c1e026c0 733a21e7 c1fb5eb3 e4160da4
+          6e228761 08bf4ed1 10353c85 9b90ba13
+          e99264f3 5e516883 f1acaa63 1950fae3
+          6e6e7ec2 6e1a0441 bde83b58 62339d86
+          2327c7e7 cd9ef998 5b008ff1 ba51559b
+          763c491d afc3fb4f 958f9687 23645e5a
+          b05554f2 94779793 369bb7b9 2c1f165d
+          346464f2 3ac771b7 63074b5f 72cbc76b
+          107eb678 7ec5f2c5 fd5b35f0 1a0eec8d
+          2d7ce5d5 05d4f1ba 0bb581c7 0f034d74
+          14bf09e0 b581cbea 5a45a0f8 72422c0e
+          8b9d8758 1cb575a0 0c09629c 7504ef68
+          44fa5a4f 8cefa972 673ebcdc 98d6bc7c
+          afd98067 afa95664 4fbe7b89 cbee0c91
+          82d7855d dfaba9ad 920b4a7f d80d9547
+          3224bd12 bbe2d325 b657c829 87820d5b
+          783b2dec 6814750b 7d47231e dfab3325
+          664dbaf3 33a7c9d8 fa809733 63ea4bc6
+          33393752 c7ebc454 a7508819 338c3f22
+          47f7c46b 03abc0ed 85873676 1d85aa83
+          477841a4 88613783 5fe75849 ee731b8f
+          a68dcb9d f5d0e3ad 0a78a51f bc33acf2
+          cbf573a5 68f021d7 2a39afc3 ea50d8f5
+          8717b861 6eaa555c 893583ab 437e9abb
+          76336ff1 8edc155f a44951e1 9edf2b2f
+          583aaf62 d9e7035a 05f01af6 ed89ca7f
+          e6d94582 524ce4a5 e47562aa d32d3cbf
+          0e337af3 d76f8686 bc8a3661 ed2e0e6d
+          600c925f a999cca0 8e8e84f8 f1c3c5a7
+          99c459d3 18dfcb7d f491e5c6 5f4e0649
+          0a3c7b55 a59075df 3d8b9d26 4b38f9db
+          09e66e82 afeb0c21 83c50317 16b8c12a
+          f00a0db4 39416b53 9b96cb5b bd63843c
+          e0ba5e10 76432f72 978b3a66 7c2f21eb
+          5ec6f7ae 30be7745 c0cb7968 da73a68c
+          9c14ea78 9d98eae4 cf4eb123 d9995f01
+          e6bca602 377268b5 5762576c 21f079e6
+          567c9e79 8ab7088d 19350cbc 25a878ca
+          f95e6afa 58c6f766 4b02bcd2 f7df4aa9
+          5cbdfe79 725ec753 9d04ce75 14017ee0
+          32987855 285b8319 640a68b3 c2afd4ec
+          2e5e6900 b396055f 3de7b60a c671c90b
+          9e23df5b b46c5ec5 92850349 81d7b077
+          5778feb3 cf2d663e 5fa0b630 788a8d1a
+          3618f49d 3bb21f5c 3ca30333 3bdce976
+          e2b295c5 b8aba9b8 16f2d66d e2b14a5d
+          bb049ece cf03cbf4 7c4f99fb e8ac65c6
+          93c78349 80672b2f 97654d99 bc88f1ba
+          2829dec4 06f4486c 6a97c978 4fea4928
+          fa7e9f5b dcc336a7 b5293f98 0615bbf6
+          73b485dc 300082af 4d9684ef 391a8c09
+          9993fe6f c1e5e4ef fd65e0e5 3ff5d833
+          c68cec61 8204a94e ea206f88 bf630c6f
+          948cd59f 72567fcb 77ba4c76 f500ef42
+          d6f286ed 60cac903 5029216e c248d046
+          f8915fa9 e17dae21 357d74ee ac194fb6
+          28f0ca17 7f36b87c e9ca1715 12f03a5c
+          f0044c75 0a6396dd 6a85dcd5 1bc05cd9
+          e8965762 578c3d76 88b21bac 90bd7203
+          ef60a908 0ce0c5ba b13a8114 0591ca3e
+          5ff11ac3 c6a01601 9ef1f8b1 90bcc766
+          2f618a56 50f33ade e0e3e6fe e0d7ab1b
+          b770253b 7e86aae3 9957958b fd9dc2f1
+          41766631 146edcce 77a64f97 6488fadb
+          f5405ef0 bc89ef31 6c2c6318 096e56e0
+          a10fcf9c 7cf70247 bd214e26 4157196c
+          841c75db 2ddca736 9e3e0b05 9b76b6a9
+          20f19558 9be29d07 a0f6e809 0e80c8a1
+          43c0bf6b bc347caf de10cf30 b2f052f9
+          de25018f f9f05986 e3a74609 d4f53dec
+          c05f5c9d 4f7572d4 d4f1083e 79cf87ff
+          3ab88b3e 12f23dfc 7ecc5ab6 96560068
+          d410fff7 d1a00ed0 915fa971 bec730c2
+          b0f244b3 00af7add 9a9ecc87 bf4eeed6
+          9a529de2 31d52926 926768e4 aedb048d
+          05559258 3b54249e aa793d3d 9358baf5
+          fc077f8f 1f970485 bd91e35a 18d7cd59
+          f30dfb0f 2ba823c2 246b1add c4f75e2d
+          5ff4e99f becffd9f b5536ce5 659a5ffa
+          f4d8632d 2c95e4f5 7ff88d3d 21eeae09
+          bc3670c5 cebd90f9 c5b73c5d 883c7668
+          c20e95de f9fea346 af52c726 1cd42627
+          d72983ff 43670ca9 473596dc fc4ed56b
+          564db094 54f59522 1d0b3744 fcf81408
+          1f7e338f 6fe67df9 3514ef38 449e1ac6
+          db15f878 9fee72e8 585f4d62 52c36501
+          2ff39e3b 5ead58b1 e659f2aa 4ed8063d
+          26049267 4de36d95 4c39f990 fede22c6
+          f72cf41d a99942bd 7bf7589b b864d974
+          af2edd2a ffd79fb5 1615ca0b 9e79faa9
+          f2e5ff7e 8d5ce158 ef4e9043 f2ccc9e0
+          dd3189f1 72039cfe 6021afbb 2785d1c0
+          62eb1d37 ed98f147 b1ae3f74 b5556b57
+          f762a09b 2349aa93 a629d589 810e1b20
+          f32b3103 3de8d0d2 e9ba256f ebb869cb
+          c43f031d 8a2a32ca 91b874c5 eb21f7dc
+          f912f51b 09e4bc4e ab83a785 39eaea41
+          68aa4aa0 d0d13711 c44d57b3 f987e965
+          0b3e4ef9 4b1ccf51 5fa7c87f 62d63fd9
+          6494e46e 0d539d46 a5805752 c28536e8
+          75e78ac9 afc490d3 c97dbdf3 13172fbd
+          47191a76 e9549ded f0b87f7e fca2778f
+          ce1ba93b 9623f735 e45742fe d79b799a
+          98362116 62cf3711 24e67bc8 3d99f5ff
+          a7253757 77c9c02b 7cf9b929 e6bcd27e
+          d4269a17 82eedb59 2c48cd04 1be195fc
+          78509278 9d836d80 e8575f7e 40d7ab4f
+          f95fdef1 3ebe90b0 70f18382 46592ac5
+          057ee99e 549e2686 123ca81f 84f4eb4a
+          5e950043 2cb6eac6 0e05cf3d 35e79280
+          67387634 b0ecd305 2f509f1c d1d2e922
+          fc206ec2 089eea64 2d29e38d f09a8c08
+          3947099e 30eabdd0 e933b75e eedfa1eb
+          7d6d71c4 d3731e21 ef582e13 6f36f2be
+          de22368d 56c8c526 82d85486 f8d48d5e
+          aa72e59a d90dfbf6 b4fb53e0 15be3077
+          b6c36009 27ad82ee 148b5127 4c1c0d0a
+          7f7f7059 c52236d8 088ffa4a 0c6b03ab
+          23838fc6 7df8c933 d818e64a 2472ee0b
+          6b0386de f839f99b 58cc5aae 3373be87
+          59cbd854 26418aa6 3202d7ad 2e7fce63
+          cf63b5ff 3f045ec3 dedd9135 5b77cca0
+          e653783b 113dec06 31d509db a06fdb09
+          3527b3e9 5dacd81c d890b068 c97dcab0
+          f02b764e 32a592f1 bd4f662b fcbdcf4a
+          f146a2f6 743e146d f99e4f4a dfa9035b
+          e3c1e457 6a388efa bd472656 af5bdded
+          0f8157fc e6eb33d8 02f95296 89e53d5a
+          bb2742c4 d01bb99f 68483f23 491b74ce
+          31b139f0 23d3e7f8 dd3afc64 73fd9d9a
+          a40e7531 afbd763f e38c0ef2 806e5313
+          c1fa13e9 dc0587a7 0c86801e ed68afd4
+          649c2a29 4ade7ffb 093c2cfe 0e78e673
+          677deb7f dc3999f2 31364f75 0af4e6d7
+          3c58d5c9 5e5dc3dc c337ec44 e924bf12
+          4377e8d3 b7c7fad8 37dfffb8 b9ffee90
+          071ede15 3461d47c f23b54ee ea5c90bd
+          fa5bb055 56f1142a 7ea516e4 4dcaf7d0
+          88188e9d 1cdb7060 6fccef80 57b96ac5
+          487ba325 1ca8b85d 5355272c 3aad0a0b
+          619ab7f3 5e5ec6e2 1af22b31 74830a5f
+          5d61fcc7 0ba7cb54 aa160080 00f11f7d
+          f6b23a3a f4a0144d 04b13f5a de571bf9
+          b5a32a34 18126e27 2ef48d05 106c2e5d
+          d5aa2f26 fe1a784c e9b59bbe fd3be5e9
+          915775ba a99fd8a3 9517c4de 07e507d3
+          2579fdcf dca02b66 febc69ba 9ebdcb5a
+          ea3b9421 a1d6d877 de9fc2e6 6a2077b9
+          6c4d2b0e 9fe645c7 f1bb71cd c3d9da53
+          5a607c88 55bb7deb 38a7d128 5c009ee1
+          c4f110e3 c9f40154 9606cdbc 777c2844
+          f2aa4e00 c6ec5cc8 fb66bb24 499d3c74
+          327ee4bb a10f3cbc a5a5bf2b 70c2dfd3
+          c367ce78 9a3aa676 def2e57d bb83af35
+          ae79d4b0 14f08e0b 2673b958 0ac39c99
+          df9d1d60 132f00af 61f7ae3e 0e8b83e6
+          50d1e462 6346a4f0 6b1d97d9 0cb9ccc5
+          da8df405 b1d1eda9 a3428fc4 fd6be13f
+          a8828551 2fcffb48 d7296193 93fa4a0d
+          b396d91a e77cf52d 0fb1607f dc684c28
+          a0ca6291 f1d08aca 70f860bf 8b2c5e6a
+          5f32655b 31b1330e 7c3174c2 8ef95587
+          8f439d04 afc4c44b 755963c2 c2cf272b
+          4342c86c 90dcc707 12162c9e 2e78a9cb
+          c86f3530 6b39a308 2a0e1ce5 3ffb75ed
+          043eed22 490f1a86 e3c7aee5 c0c3aed5
+          a6b493d7 509d2271 7385f4ef c37b4ee0
+          ce2bdd7d 4092a44e 070f9dcc 78d2efd6
+          e169d4df ad1f7843 61d4b373 e96f3540
+          bcca2afd 791f381b 0cbcf65e f075bdc9
+          82ca6875 4da7d3ba 39cd2610 1c4683d2
+          5a549044 a17cdce1 da101ff0 ed98c4ad
+          5d634e3e 34e697d2 77a4666e ce7740ef
+          7531f3de fe142492 f027e67e e53ba0cf
+          62f25b0d b6d6c6a2 2a66f932 45abd7a9
+          3da8fc35 24e0438c d9ca2be2 1cb5b53a
+          c172ee5c 90adbc8e a4f6099a 749f7671
+          ccdde8b9 cfaf493b c3afa848 8b7363e8
+          c4475710 f7cf4f67 c8d4d2bd 16c2b04d
+          e2d22f1e 5706f99c a3ce5c46 90556350
+          d9e90465 a01f78c7 4693b85b ded3a3a2
+          3ed89293 1d2ad8aa 2a235c76 a79ecccd
+          24c6f150 b6cb6c81 faac1cda b2136285
+          2957ecdb 6fddafbb a6573948 2c9a76ed
+          eb625e9f 7fbfd30e 76d2662a 6ccd1b98
+          b7c16451 5028c127 21962ea6 e7020df3
+          b0e182f1 f8d1307c 13dde256 c725925b
+          afc870fe a3adb60e cce595a4 27598c5b
+          054d18f9 76c8d407 b7412b11 36969fd9
+          98dea48c a9e19a5b 6aeac052 55cdbd8d
+          5754b868 005a1a7c 4dfda84d 674e8709
+          32b93c8c cabc2bbc bd40ede7 cb7f3657
+          56892114 22378ba7 69754c04 864e9e6b
+          55a50730 71f45f0b 5f62633b ecb4927d
+          25db844e 30958949 d59aa000 50681440
+          d5ba58d0 6842046b 69490809 f0d8a494
+          3a2f906b 357ce6e6 8a6af109 1e050670
+          419de088 7befbd87 95c12116 6865c2c6
+          6465639b c9c66827 74794c07 55fcdf0a
+          a617fc50 14f64675 9b4e9f0a 128ce969
+          be24fb9f 4d4aeea5 e5a94288 426b6d2d
+          9962799d e47ebdb6 048c9d70 105aa9b0
+          b11d6063 dc467997 6badabe7 ae48ae56
+          83c24b43 66f12c85 057ee86a 7da92c9e
+          02ad9d20 c66d6c0d 06b205c6 ef668afd
+          a275576d 94e11857 b8080f19 76a3913f
+          85c42c65 b9564b76 c0902914 7a44818e
+          2c868499 1f9c6030 9f623291 b959b94a
+          68f4bd29 650fb472 6163dccd c6da4075
+          85e5305b 7948058d 815ca306 42d0eb10
+          7834f920 2e0c5e32 37cb9b84 38c18993
+          26001ec6 c8941121 673549ed 8b5b3bf0
+          708c3856 8ab81eee 7fa7b509 78ec07ae
+          1b3a51d3 010f2d9e 42d13463 c6f46d36
+          1a83c7d6 559bdc25 53d07ab9 5a3bf0d8
+          1871ac59 24575818 dab0db45 1e721e78
+          742ba441 e0d144d2 707e7259 d38c5dc0
+          1f7f5058 3ca430be be45e7b9 65eb469e
+          80632da4 72794ebb e302f0ae f461d35f
+          14391df0 9a1656ac 70e41227 4d75b870
+          382ac14d 848db58a ecbbd0cd 3655bd94
+          c90572e0 d19b02dc 65844c96 ed6693bb
+          008f74ac 1c78d0d4 414846e9 6a25f43f
+          4eba079e ba3e7d1d ee023cb2 b1cac48e
+          e0ae8b4f 1b94ac42 1277d2f4 a10aab09
+          5af769e3 e84e6375 3be0b565 eb7a558d
+          d5033c8f 7880e711 8f7880e7 110ff03c
+          e2110ff0 3ce2019e 473cc0f3 88473cc0
+          f3489b16 d2ce6458 ae0b6bb4 c96ccaab
+          acd7672b 15cc4a61 fae03a21 be3d55d0
+          810e73fc 1bc0702e 8797ae70 12a54579
+          e48f1402 5c078d59 7920d36a b86e284b
+          8990010f ab4256a7 a641d551 b15409be
+          ed14e41e fd4bc6b1 d8da5b6b ea20fdbd
+          85170c03 65f95f05 f52e9379 5865eb32
+          7cc27f74 d366399e 1413f448 ebd487c7
+          fe78c403 3c8f7880 e7118f78 80e7110f
+          f03ce211 0ff03ce2 019e473c e2019e47
+          3cc0f388 473cc0f3 8807781e f1000f2c
+          57c13cad 6e34d6ab 411f16c1 65b757b7
+          f559ba1c f62a371a 6b4d9bd7 87dd5e25
+          683b74cc 70b5e149 62da8f26 a94396bb
+          8c978d35 938db9cd aa0427c6 307756d0
+          75efb9b7 ed5a3a00 b94e95af edd4f98c
+          bb8c998d 35838db9 d0e568bb c6003127
+          f80efddb 7ea5bfee 2cef39d1 c6041b96
+          e8070dda a08e8935 bacb98d9 580d6ccc
+          eb9dd636 680818c6 106b8839 4115156d
+          0e7f7cf6 2b0e5bdb 9ba4a016 6aa29e7f
+          e55db77a 58c4c68a 63c6b1b7 35638018
+          63587b19 31c7c329 114fcefd 2270c4ad
+          efd84d62 95747727 11d8fdda 690343cc
+          bc79777b f7ed97e7 6e53c031 e3d8d91c
+          1a9da636 600498f5 466c318c bdcdb0f6
+          6fbebf5c 4d25619d 163314bf 31ef9ed2
+          0f3f7cd2 5a55dba5 a94cb65b 9156fc08
+          32707875 4dde1ef5 d2cbcf05 8c1e77d4
+          9d1556bd 615dcf82 679f79d5 78ea6c0a
+          d6107747 9da0a822 42d2421f 78f0ad88
+          a7e62e17 9a5ab55e 00de8500 4b7e9eaa
+          76dbb6fe a6b493fd 4d196792 6472c05e
+          67ea563c 3f279ba1 51269717 7bf5b8f6
+          177dff7e bb7d5352 d2640a25 b405711a
+          0d50bb7d 5b17c3b1 d441c6d4 435d5c0e
+          4714431f 960d155a 31deacec 70548111
+          13afaedd 0e048c19 bb571118 f42bd6fa
+          ffd9bc4b 463e5e72 82000000 0049454e
+          44ae4260 82  
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="512"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000019a 0000019a 08060000 00360685
+          63000014 12494441 5478daed dd5f8c5c
+          d57d07f0 7bc677ff 186305b0 89b18509
+          50e38616 92f2d03f 69aa2452 ab88974a
+          7d208112 a24aad40 4d28aa12 aa84b40f
+          7da89aa8 5240499a 9034a895 22112884
+          87483c44 a5a91a35 6ad2340f a8a1943f
+          819662e2 35c69850 dbe0fde3 3dbd7777
+          d63b9ecc ec9ebbde 333bb3fe 7cc4ee1d
+          76cefddd 7b7f473e dfbdbbb3 bb21c658
+          a49a3b76 ac08214c fcf4d16f ee3af9fc
+          0b6f0f65 b1b3fa70 fdb6ad7a db52bd85
+          620d62b1 7c12a13a 42b18e96 6ae7aaab
+          b65e9b47 bddea4bd ae6bcc54 6fd3d5db
+          e1eaede5 38573cbf e3a61ba7 c676ed9e
+          d9b2fdfc e44261d5 a0393557 1c79e0fe
+          8b8ffde0 87d71f7d e4a17757 e1f29ed9
+          a9572f3d 355f6c5f afa684ae 2b5b4f21
+          735db5f5 da3ceaf5 39d2ebba d489891d
+          db5e2cca c9ef5e74 c38ddfdd feabbffc
+          ad9d37df 72b4d852 ae3d688e dcffb52b
+          a7eeb9fb 23c71f7f e243d5ff ee6e8d2d
+          1e2a946b bd7759f1 027af568 3d6be7aa
+          abb65e9b 47bd3e67 7a5dddd5 2c989f5d
+          d81c3cff ba6bbfbe fbe377fe cdce9b3f
+          fc7cd16a a507cdc9 1f3f534c 7dfe0bb7
+          4d7de9de bfa8767b 6b6bf2f4 a94613e9
+          1f8d5e9b 47bdd6eb 85bad5a3 f9930b1f
+          38bced97 aefdf3fd 0f7fe32b 9357fdfc
+          ea4173f2 b967c79e 7aff6fdd f3c6ff1c
+          f8a3b1c9 334ed144 fa47a3d7 e651aff5
+          fa676b57 1f395505 cee4157b bf78f563
+          dfbe7372 dffe99ce 1dceb8cf 39f9ccd3
+          e1a9f7ff e6e7a6eb 90392fc3 e901b0f9
+          5459b1a5 ca8c3a3b aa0cb9a7 ca923e77
+          34a74e15 4f5cf70b b71e7fe2 d9afd63b
+          f4f80e92 cf187c76 a6d7e651 aff5ba7f
+          edeaff4e bd5114e7 5fbbffb6 6b1fffaf
+          fb8a2d5b cebca339 f2f0837b 8f3ff9ec
+          5f6e992c d6ff2510 006c7e55 76d41952
+          65c9a7ab 4c79dbd2 87178226 9e9a2ba6
+          3efbe93b 422c2ef6 e53200d6 2cd45f2a
+          2b765699 f2d13a5b 163e547f e9ecc843
+          0f5cf0ec cd1f7aba 1c2f76f5 0b9a385f
+          fd37ebd6 d49701f4 da3ceab5 5e577729
+          6355e156 9fdad588 b999e2e0 fe07be7e
+          cdce1b6f 7eadac13 e7d8bf7c e7fa627e
+          2164629f 9029caf3 b7c66d97 ee29ea60
+          ca70d631 634762ce ba6aebb5 79d4eb73
+          add72184 e2c44b07 8bb9e36f d661d3f3
+          aea6ca94 3d75b6ec b8e1830f 865327df
+          2c1ebf6c d757e78e fedfad61 ac77d0cc
+          4d17c545 efd817df 7ee7478a 627ada67
+          0c3e3bd3 6bf3a8d7 e772af27 268aa7ef
+          fe7271f4 47cf8572 a2cfa0d9 226cb970
+          fb7dd7fd f74f6e2b 670e1c18 8b31fc7a
+          bfbb99d3 67393f5f c42a64e2 f48c59f4
+          2f46afcd a35e9fc3 bd5eca84 556ac7d6
+          e4c4bb5b 131365f9 eadfdfbf 7bfa95d7
+          df36b675 f5f309cb db6c2f19 c8557b14
+          cf59afd5 368f7a3d 8cb5434a 8e954531
+          f3caeb7b 8f3ef2f0 256528c7 2eaf469e
+          b7b6a355 7b96e57a 9e7491b1 21235157
+          6dfd308f 6a0f5ddd b9b9a288 cd7eeea5
+          8e87b937 67b79dfc f1b3fbea 94d85374
+          fd8680d4 2af3b3b3 c5f4a1c3 6e4dd5d6
+          6bf3a8d7 9bb8d713 3b2e2c5a 6363cdc3
+          a6ca9630 3ebebb0e 9a1d6b3a 72752753
+          87cc7f7c e64bbd5f 7500c0c8 ab5f75fc
+          ce4fdd5e 6cbdb4ba 27999d5d 4b898beb
+          a039ef6c 4ea20e19 4103401f e375448c
+          e9030099 4cd64153 ea030099 2c7cd1cb
+          afd00420 1bdf5d01 40d00020 6800a0a7
+          c1bd1060 7ec567fd f0addafa 611ed51e
+          74dd01dd 6a0c2c68 c2c4d84a 3fe9e387
+          6f37576d fd308f7a 3d02bd8e b3b303f9
+          f196fc41 53ddc984 f172ee17 bff3fd77
+          8dedbae4 50cf8bed f8b506f5 df3958d7
+          6eb76be7 aaabb65e 9b47bd1e c55ecfbe
+          7ce89227 dffb6bff 1667e6ca dc773683
+          b9a30921 8eefbdec c572e7c5 af14006c
+          b8303e3e 53afcd9b e38e6629 616766c6
+          7d76e6b3 33bd368f 6a0f47af 575a93d7
+          9b579d01 20680010 34002068 0018bca5
+          1703a4bc f260e1db 4b9d031b bc5c6171
+          df3e7f9d 2dc67c2f 7cc8557b 14cf59af
+          d5368f7a ddb57ec7 94a5bc73 505cc32f
+          626e0dec 573747bf 241a6068 0c684dae
+          8f523678 515d38fd aef30389 fbaef4f2
+          bdf57e69 df206a8f e239ebb5 dae651af
+          bbc6249d 40d7badf e8a4ebc1 6583cc88
+          a19d4e61 8dbf2ea1 efebb9fd 4cc0a6aa
+          ad1fe651 af47aad7 8dc226b6 b3a0c9be
+          5e0c0040 56820600 410380a0 01004103
+          80a00140 d00080a0 0140d000 20680040
+          d0002068 00103400 20680010 34002068
+          00103400 081a0010 34006cac b2fd97a5
+          63c2d885 bf42dd39 30a61f27 d67fc3ba
+          f3ef5877 3d99ed02 73d51ec5 73d66bb5
+          cda35e77 8d89294b 79e7a0d8 68e95f1c
+          ec8e0680 bc773461 711b5206 87e56d48
+          dea9bd4b a57e9732 30cb85e6 aaabb67e
+          9847b547 f19cdb1f 0f294b79 f7a0d0f3
+          61ff7ddd d1009095 a00140d0 00206800
+          40d00020 68001034 00206800 10340008
+          1a001034 00081a00 040d0008 1a00040d
+          00081a00 040d0082 0600040d 001bab6c
+          6f63c2d8 181307f6 dd37c63e 4fc66c17
+          98abf628 9eb35eab 6d1ef5ba 3873d14f
+          5ed6e3f2 b6f149b7 cee63263 b32b17eb
+          00c3a2c1 9a7cb639 d10a83ba a8104c2c
+          c0b018d0 9a5c1fa5 ec789cb4 437b1b92
+          77eab8a4 90706121 d3c5878c 4d555b3f
+          cca3daa3 76ce6179 b3eac1bb 07859e0f
+          fbf36200 00b21234 00081a00 040d0008
+          1a00040d 00820600 040d0082 06004103
+          00820600 410380a0 01004103 80a00100
+          410380a0 0140d000 80a00160 6395ed6d
+          4c181b63 d7c0987e 9cc57d63 ecf364cc
+          7681b96a 8fe239eb b5dae651 afbbd6ef
+          98b29477 0e8a8d96 fef61d4d be16f68a
+          1a0086c2 80d6e4fa 28ad30a8 8b0ac1c4
+          020c8b01 adc9f551 ca8ec749 3bb4b721
+          79a78e4b 0a091716 325d7cc8 d854b5f5
+          c33caa3d 6ae71c96 37ab1ebc 7b50e8f9
+          b03f2f06 00202b41 0380a001 40d00080
+          a00140d0 00206800 40d00020 68001034
+          00206800 10340008 1a001034 00081a00
+          10340008 1a00040d 00081a00 3656d9de
+          c684b131 760d8ce9 c759dc37 c63e4fc6
+          6c1798ab f6289eb3 5eab6d1e f5ba6bfd
+          8e294b79 e7a0d868 e96fdfd1 e46b61af
+          a8016028 0c684dae 8fd20a67 51a0d1be
+          21985880 61d1604d 3edb9c28 9bd409cb
+          dbb0964b 0a091716 320552c8 18746aeb
+          8779547b d4ce392c 6f1aadff 5d8f93f6
+          f5620000 b2123400 081a0004 0d00081a
+          00040d00 82060004 0d008206 00410300
+          82060041 0380a001 00410380 a0010041
+          0380a001 40d00080 a0016063 9571711b
+          13c6c6d8 3530a61f 2756ea77 fd9ecc76
+          81b96a8f e239ebb5 dae651af bbc6c494
+          a5bc7350 6cb4f42f 0e764703 40de3b9a
+          b0b80d29 83c3f236 24efd4de a552bf4b
+          1998e542 73d5555b 3fcca3da a378ceed
+          8f8794a5 bc7b50e8 f9b0ffbe ee6800c8
+          4ad00020 68001034 00206800 10340008
+          1a001034 00081a00 040d0008 1a00040d
+          00820600 040d0082 0600040d 00820600
+          41030082 06808d55 b6b73165 705cdec6
+          b51c2cc6 b82e6372 1d5b6dfd 505b3fce
+          c15e375a ffbb1e27 ed5b36b8 bcb8f42e
+          2cfcd728 6de24a17 dbf95c08 214b9373
+          d5555baf cda35e8f 62afdb63 d61232b1
+          a36248d9 b7b5be97 b78230b0 23013024
+          6b727d94 3235953a 072dddd1 84869794
+          92da21d3 c5878c4d 555b3fcc a3daa376
+          ce6179b3 eac1bb07 35b9a3a9 79310000
+          59091a00 040d0082 0600040d 00820600
+          41030082 06004103 80a00100 410380a0
+          0140d000 80a00140 d00080a0 0140d000
+          20680040 d000b0b1 caf63626 8c8db16b
+          604c3fce e2be31f6 793266bb c05cb547
+          f19cf55a 6df3a8d7 5deb774c 59ca3b07
+          c5464b7f fb8e265f 0b7b450d 00436140
+          6b727d94 5618d445 85606201 86c580d6
+          e4fa2865 c7e3a41d dadb90bc 53c72585
+          840b0b99 2e3e646c aadafa61 1ed51eb5
+          730ecb9b 550fde3d 28f47cd8 9f170300
+          9095a001 40d00020 680040d0 00206800
+          10340020 68001034 00081a00 10340008
+          1a00040d 00081a00 040d0008 1a00040d
+          00820600 040d001b ab6c6f63 c2d818bb
+          06c6f4e3 2cee1b63 9f2763b6 0bcc557b
+          14cf59af d5368f7a ddb57ec7 94a5bc73
+          506cb4f4 b7ef68f2 b5b057d4 00301406
+          b426d747 6985415d 54082616 60580c68
+          4dae8f52 763c4eda a1bd0dc9 3b755c52
+          48b8b090 e9e243c6 a6aaad1f e651ed51
+          3be7b0bc 59f5e0dd 8342cf87 fd793100
+          0059091a 00040d00 82060004 0d008206
+          00410300 82060041 0380a001 00410380
+          a00140d0 0080a001 40d00080 a00140d0
+          00206800 40d000b0 b1caf636 268c8db1
+          6b604c3f cee2be31 f6793266 bbc05cb5
+          47f19cf5 5a6df3a8 d75deb77 4c59ca3b
+          07c5464b 7ffb8e26 5f0b7b45 0d004361
+          406b727d 9432a48f 0fa7df75 7e2071df
+          ca4a4f66 bbc85cb5 47f19cf5 5a6df3a8
+          d75d6392 4ea06bdd 6f74d2f5 e0b24166
+          c4d04ea7 a6075aed e23b6ff3 d6bbf14b
+          b573d555 5bafcda3 5e8f78af 1b854d6c
+          6741937d bd180080 ac040d00 82060041
+          03008206 00410380 a0010041 0380a001
+          40d00080 a00140d0 00206800 40d00020
+          680040d0 00206800 10340020 6800d858
+          65fb2f4b c784b10b 7f85ba73 604c3f4e
+          acff8675 e7dfb1ee 7a32db05 e6aa3d8a
+          e7acd76a 9b47bdee 1a135396 f2ce41b1
+          d1d2bf38 d81d0d00 79ef68c2 e236a40c
+          0ecbdb90 bc537b97 4afd2e65 60960bcd
+          55576dfd 308f6a8f e239b73f 1e5296f2
+          ee41a1e7 c3fefbba a301202b 410380a0
+          0140d000 80a00140 d0002068 0040d000
+          20680010 34002068 00103400 081a0010
+          3400081a 00103400 081a0004 0d00081a
+          003656d9 dec684b1 31260eec bb6f8c7d
+          9e8cd92e 3057ed51 3c67bd56 db3cea75
+          71e6a29f bcacc7e5 6de3936e 9dcd65c6
+          66572ed6 01864583 35f96c73 a2150675
+          51219858 806131a0 35b93e4a d9f13869
+          87f63624 efd47149 21e1c242 a68b0f19
+          9baab67e 9847b547 ed9cc3f2 66d58377
+          0f0a3d1f f6e7c500 00642568 00103400
+          081a0010 3400081a 00040d00 081a0004
+          0d008206 00040d00 82060041 03008206
+          00410300 82060041 0380a001 004103c0
+          c62adbdb 983036c6 ae8131fd 388bfbc6
+          d8e7c998 ed0273d5 1ec573d6 6bb5cda3
+          5e77addf 316529ef 1c141b2d fded3b9a
+          7c2dec15 35000c85 01adc9f5 515a6150
+          17158289 05181603 5a93eba3 941d8f93
+          76686f43 f24e1d97 14122e2c 64baf890
+          b1a96aeb 8779547b d4ce392c 6f563d78
+          f7a0d0f3 617f5e0c 00405682 06004103
+          80a00100 410380a0 0140d000 80a00140
+          d0002068 0040d000 20680010 34002068
+          00103400 20680010 3400081a 00103400
+          6cacb2bd 8d096363 ec1a18d3 8fb3b86f
+          8c7d9e8c d92e3057 ed513c67 bd56db3c
+          ea75d7fa 1d5396f2 ce41b1d1 d2dfbea3
+          c9d7c25e 5103c050 18d09a5c 1fa515ce
+          a240a37d 4330b100 c3a2c19a 7cb63951
+          36a91396 b7612d97 14122e2c 640aa490
+          31e8d4d6 0ff3a8f6 a89d7358 de345aff
+          bb1e27ed ebc50000 64256800 10340008
+          1a001034 00081a00 040d0008 1a00040d
+          00820600 040d0082 06004103 00820600
+          41030082 06004103 80a00100 4103c0c6
+          2ae3e236 268c8db1 6b604c3f 4eacd4ef
+          fa3d99ed 0273d51e c573d66b b5cda35e
+          778d8929 4b79e7a0 d868e95f 1cec8e06
+          80bc7734 61711b52 0687e56d 48dea9bd
+          4ba57e97 3230cb85 e6aaabb6 7e9847b5
+          47f19cdb 1f0f294b 79f7a0d0 f361ff7d
+          ddd10090 95a00140 d0002068 0040d000
+          20680010 34002068 00103400 081a0010
+          3400081a 00040d00 081a0004 0d00081a
+          00040d00 82060004 0d001bab 6c6f63ca
+          e0b8bc8d 6b39588c 715dc6e4 3ab6dafa
+          a1b67e9c 83bd6eb4 fe773d4e dab76c70
+          7971e95d 58f8af51 dac4952e b6f3b910
+          429626e7 aaabb65e 9b47bd1e c55eb7c7
+          ac256462 47c590b2 6f6b7d2f 6f056160
+          47026048 d6e4fa28 656a2a75 0e5abaa3
+          090d2f29 25b543a6 8b0f199b aab67e98
+          47b547ed 9cc3f266 d583770f 6a724753
+          f3620000 b2123400 081a0004 0d00f454
+          0eea4061 7c7ca6ef 73be79a7 b67e9847
+          b5075a77 a5357934 8326c630 73e0c5cb
+          e2cccc78 efa7fd4c c066aaad 1fe651af
+          87bfd7b3 2f1fbaa4 5e9b3747 d0b4aa8b
+          9e9b2b9f 7cdfbbbe bf525f3a 8376bd63
+          2e735db5 f5da3cea f548f6ba 5e9b07f1
+          0d94817d e92c4ecf 8e15000c 8f017d97
+          be1c920b f2d999cf cef4da3c eaf5689c
+          f3b0e619 006e9c00 40d00020 6800a02b
+          68e6b501 804ce6eb a099d607 003299ad
+          5fde7ce2 6c2a44f7 43009bd6 3aacf127
+          eaa0796d 4dbbcecd 15133b2e 2cdef9a9
+          dbcffe42 3a1e7b91 ba1f08d0 6bf3a8d7
+          c375cef5 5a5faff9 6b74a48c 3333afc4
+          c5f36a76 3e3116ad b1b162eb a57b443e
+          c0665687 4c8c6b09 bc5865cc e172f2aa
+          fdcf6c99 1c3b1ee3 ecf6c6bf d7ad3ef0
+          ecaccf18 d4d66bf3 a8d7e748 af9bc443
+          9d2d75c6 9417ddf0 c143e39f fcd80bb3
+          5347ae29 c656ddaf 88394e28 4fd9acb5
+          47f19cf5 5a6df3a8 d703ab5d dd048def
+          7ecb8b55 c61c2ee7 a7a7e7e6 4f4ed7bf
+          59f9da7e 39b2f0c1 56ab0813 1359d231
+          67e28611 ababb67e 9847b587 fe9cab2c
+          a8336195 c40955b6 7cafce98 b2b5756b
+          b1e30337 fdf3d4bd f7dd56f6 b9a3d952
+          7dfcc481 9f144f7f f6de85bf 73e0d6d4
+          9701f4da 3ceaf5b9 dbebfa6f dc9c78e9
+          e04236f4 73aabaa3 79eb076e fa769d31
+          65d85216 dbdff3be 6f4d7de5 be835585
+          3dbdce2a b4aabba0 e36f16af 3df1bc90
+          575b3fcc a3daceb9 688d2d64 43e89b46
+          ade25095 2d8fd519 13ea3b94 5845cf7f
+          feca3b3e 73e2f1a7 ee6a4dfa a4c12768
+          7aadb67e e8f5da6b cf9f2c8a 6dd75dfd
+          57d7fcfb 8feeaa83 66e1779d d50f76ff
+          c99fde3b 1f8b23f9 be8504c0 a6576548
+          9d2555a6 7cb1ce96 85bb9fa5 e776feee
+          2d07f6fc e1efdf35 7bb21882 3f9303c0
+          c8a9b2a3 ce902a4b 3e5965ca 4b4b1f3e
+          e3b737ef fec49ffd ed79575c faf9f937
+          840d00cd 42a6ce8e 2a43feba ca92bfeb
+          7cea8ca0 99b8e2ca e2eac7fe e9131357
+          ecfdc2ec 1b45e1cb 6800acaa fed9fd37
+          ea0cd9fb b92a43ee acb3a46f d0d426f7
+          ed9fb9fa 1ffef18f f7dcfed1 3f983b59
+          4ccdbf79 3a70dce3 0070fa1e 26ce5577
+          31554654 5971b0ca 8cdbaaec f8589521
+          b33f3b70 85df5f73 e4feaf5d 3e75cfdd
+          b71e7ffc 890f57ff bbb77e39 5b1d3aa1
+          5cf7d8f1 aa8ecd55 5b3fcca3 5e6fc65e
+          d7bf1660 6eb1e27c 1527636f d9f6bf13
+          575ef9e0 ee8fdff9 e59db7fc de8bfd13
+          69b55f94 766aae38 f2c0fd17 1cfbc10f
+          af3ffac8 43bf5185 cc7b67a7 5ebdecd4
+          7c717eb1 4e7fa133 f4e9ce7a d6ce5557
+          6dbd368f 7a7d8ef4 7a7e4bab 383eb67b
+          c70b55d8 fceb8537 dcf8bddd 77dcf1e8
+          d67dfb5e 2fdaaf2e 5b7bd074 983b76ac
+          fe89d0f1 9f3efacd 1d279f7f e1aa2a74
+          76551fbe b87a3baf 7aabef77 cab585e4
+          f2498422 ac6bb22f d5ce5557 6dbd368f
+          7abd497b 5ddfbbcc 546ff577 ec5fadc2
+          e5e5c99f bbfcb90b 7efb770e 57b9315b
+          6edf9e5c e8ff01eb 6a0a1a60 4668ca00
+          00000049 454e44ae 426082 
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000013c 00000171 08060000 00eec314
+          b4000064 c1494441 5478daec 5d079c14
+          c5d2af99 d9bc9773 ce702059 11242339
+          67447d9f fa4411cc 59101fa0 82113020
+          0aa8600e 04c93908 92511189 771ce172
+          ce69f3ce 7cdd3d7b b7770292 ee8edde9
+          a9f79b27 1ee7ec76 a87f5755 57d59f11
+          0401ae55 6c9595c0 308cba74 ddea1073
+          6a4622a3 8040f463 7ff4e8d1 c3a18781
+          9b10a1ce 97419f73 53efbada 67b8e0fb
+          ad8ea71c 3d79820d b274ad5b 5ef0ea37
+          b092d3e9 0058567c bff82135 73048d34
+          06f9fdd7 f0fe4b3e 83e7c16e 30005f59
+          a12ffc76 5902f010 833402eb 871f7ad4
+          e86125b0 4f6f852e e377591c 4f3e7a0a
+          907e5cf0 bf677cae 3234dccc 797a5cf3
+          8b98ab01 9e60b741 d1f7df05 551df97d
+          50c9ea95 dd10c8f5 b4e41447 f0027834
+          f42431ff 18616308 e33eef37 734a2851
+          f8f91fd5 b66cbed7 67f0c8ad fef74c38
+          a98e89a9 7d3fd348 639001ef fade6fce
+          cc80e29f 7f6a57be 75c320c3 e9e4aee8
+          dcea68c9 aff01744 9093fa3e bd15ba8c
+          5f59adf2 d765320a ed3ebf31 e3f77974
+          eeb425e0 ff1e2866 38c58d03 5ed1775f
+          c7e77c30 fff1eabf 4ffd07fd 6b08ab14
+          3f8a5134 9ab60957 98b3c6f8 0cd77f3f
+          8f5e861f 2bf9a351 e5e7b52b 70e2a485
+          214f3eb5 ad06f864 40ba05ef 772cae35
+          3f0f72e6 7fd0af70 d9e74f5b 8acb0720
+          f34dc328 1d8bafa0 689fde22 5d46569e
+          a82656f2 8f5c7dfb d63f86bd f0e2e280
+          ff3c701e 58eeda01 cf94920c b91f7d3c
+          2577d1e2 37d12206 b29adaaf ecf693e4
+          ce1b092f b01d2dae d2c7fbc7 8859ff7b
+          39f4b997 7264406a faf76329 5db73a34
+          6bf6ec77 2b8efefd a042d920 002703de
+          cd8c01fd 8937911f 14eadbb5 7ebdf98a
+          959f699a b7b83ae0 21b0539d 19d8ff43
+          635ad613 4acd255f 5506bc5b ff7e102c
+          08f8ec70 32f6c3b9 f723d03b 250352d3
+          be3f6fe1 472d2e3e fdfccfc8 1868c7ea
+          1acd2f94 01ef46c6 807e6247 c0a78e89
+          f8ecb66d 3b5e40a0 67be22e0 19939398
+          a4c1fd3f b3a4654f b9c242ca 80e722ef
+          17ecc0d8 2d908a40 af2f02bd 5419909a
+          e6fd08ec a210d8ed e25490c0 708d1b4a
+          9501ef06 c780fe8d 3700a862 c217b5dc
+          b2e3496d 8b96c2a5 80874c86 131d5a4c
+          ae3e797e 3177e553 4b063c17 da480ed0
+          dbd36ae7 f641de7d fb9b6540 6adcf757
+          fcb64775 aaf7dd9b 11d8f575 809dbc4f
+          5d5597d1 4fec08f4 f46d12a6 b43d96bc
+          043831a6 577b4d5e b4e2c7c8 ead3e7e7
+          701a68bc ab1b591a 5490d2e1 75ed9df3
+          fe5b93e5 d9687c41 f3fc309a 6f0c76b2
+          b8ba200c c3588630 ed2d846d 31353f26
+          8027d86c 903bf79d 67180102 1a2ff224
+          4b6308a7 0628dbbd fff9f25f 77f8cab3
+          d17852b1 778f77e9 ce3daf70 ea4bac17
+          595cd622 c02e2cf8 236c7b02 635c2de0
+          15af5aee 53f977d2 03ac5a9e 23b713bc
+          82567b0c b23e86cb 93d1a8d6 dd10b0d8
+          e2ae3d75 58169750 0f846908 dbfe0f61
+          1c4efe06 0546beca bd7b06a3 332b58bc
+          dcbd66bf bcb1fd7e 77fd8c26 7f3fc302
+          6338937c bf2537f7 5b6548c8 cd7f80d0
+          b84370b7 f75b0bf2 c170facc 7d8cb3d8
+          45dea7ee a2cb0cf9 ad508471 83fcc74d
+          f891c580 57f2cb8a bb3905c8 e2bea798
+          60caccbf a36cdbe6 5079361a 5e90b204
+          19d3723a e1799667 c3fd0463 1bb2f0ee
+          e68d4660 2d99194a 4160ba5d 8375278b
+          6b8b3fe7 e1d1419e 86861773 66463b64
+          2804ca33 e1b622b0 6a7557f4 2814c53f
+          7f1f6a2e 2c8f566a afebbaa2 29ae3618
+          f9fdd7fe 192cfa53 e1b75f25 22b37d73
+          83bd9c69 dc21b8cb fb0b972e 69c95eb9
+          f05fdea7 ae3e0664 e1598aca 238b7f59
+          1ec2324a 652cfa6d ad7c08b8 b7e01537
+          269f4994 67a211e6 56a94c90 67c18dd7
+          0fe7e499 ac7af3b9 94047c6a e1b88f7c
+          f7240161 55aa0879 161a4522 e529707b
+          83806554 aa500c74 fef27448 4682e529
+          681491e3 77125947 0c783a79 1e242372
+          2665e388 5e9e0249 880a039e 4a9e07c9
+          881c8b6d 244591a7 4012a2c1 80275706
+          4a47e4c2 c0c61139 4b5522fa 81014fce
+          bf934596 7f175947 2422f2ed ac2cb2c8
+          22039e2c b2c8228b 0c78b2c8 228b2c32
+          e0c9228b 2cb2c880 278b2cb2 c822039e
+          2cb2c822 8b0c78b2 c8228b2c 32e0c922
+          8b2cb2c8 80278b2c b2c82203 9e2cb2c8
+          228b0c78 b2c8228b 0c78b2c8 228b2c52
+          959a2e10 d75a1c2d d334baf8 fb1b82a2
+          50a669bc eabccbfb d44d7599 95db40c8
+          228b2c34 08c63a56 6ea0268b 2cb2d020
+          4c1d9796 b981ffb6 29be9ffc feebfc8c
+          86a43e94 691aaf69 3de57dea 4663902f
+          2d649145 166a4406 3c596491 45063c59
+          64914516 19f06491 45165964 c0934516
+          59649101 4f165964 9145063c 59649145
+          1619f064 91451659 1a4124cd a86e37c1
+          25d576ac da413f2e 8b2c948b c003f0e6
+          7ffc9001 e03432e0 b9a584f5 ed040a0f
+          3d5a555e fc01c741 e1e1a360 2e2e0786
+          9337bc2c 14839d1d 40edef0d 8177dd81
+          2c03bbc3 1a60c156 550df907 7e9701cf
+          bd5653fc 4778ff5e a0080f05 b05ac51f
+          a8d55071 2e158c05 e518fb64 91855ae1
+          09e0f941 f8f81100 668799a7 54822d3b
+          17f2f7ff 5e6bedc9 80e74662 339a4061
+          3020c0b3 397e6047 663c2fef 76596421
+          2e2dd285 6aa41f16 8b03f0ac 4467a42c
+          72344b16 5964a146 64c09345 165964c0
+          93451659 6491014f 16596491 45063c59
+          6eb530ac bc9c8d32 af9c3caf 32e0c9e2
+          722b692d af047346 badcb5bf 01c59299
+          09d6920a 00398d49 063c5797 cbb25731
+          d2c40356 0560cac8 63cbb66e 52c8dbba
+          e1a46ce7 36d69896 cde20a1d e999aecc
+          b5e98c84 44d2348d 78f1844b d698111a
+          7914b76c 8ed0f655 b02a950a 8ddbdae0
+          0745631f 442efa7e 349f1c83 a7569018
+          4d231a0f e6fc101a 6fee649a c6263fc0
+          6ed12adc 4a430f8d 4f29db65 0daa5178
+          3e395a14 8491f65a 129a46c9 62c0e5aa
+          2a186913 532ad0e8 34204b43 62029e4f
+          691e2297 b9e49272 2512c63a ecd2f220
+          419a466c 99e3c563 fef11ad6 59442b45
+          fa3bac9c 5ec855c9 6b9097cb 348d583c
+          1df32a39 9a467cab cfd47b3f 531b0662
+          dc4b17ae f5fd3c86 78bb642d 3cfba5c6
+          abc4530c d40e0595 a5e1c403 246a35b3
+          0aee323a c34b39ee 63671d16 9e346313
+          027fd953 4dca80c7 28143e32 4635e056
+          5228fcd0 3f749204 3cee3217 fa352ead
+          34233f52 b7f0ec97 2c1c2bed be508cb5
+          203f4886 a986136b 3e994f49 9e92ac92
+          bbc45010 ec76292f a784014f b8c2a585
+          845d5a6c bc162cf9 2c14e416 580d26f9
+          4b168649 f59e8b51 5e7a1723 e0467912
+          77696d92 8573d2f8 93b9ea22 4b6b1733
+          f1324c35 e429c224 4875689c 4af5cfcd
+          03bc4dd2 169e1503 9e45aa16 1e6fbd14
+          cb598584 0b110462 c1b605b9 a6b6610e
+          4c830104 abada554 93d358d5 652c3c8b
+          55ca169e 096b8651 a2ba0f3c eee4ca5c
+          7d91a5e3 a2009873 8be3aa0e edd7cb70
+          75f352b2 ea67bd21 25a38524 cbca8885
+          a7fca781 0776a433 124ece27 169e59aa
+          d68edd6c b9c4a5e5 d42ae902 1e87ccf5
+          d28a40e3 99d36d65 b86a80f9 54a91241
+          100225eb d2ea7497 bab4d2b6 f08cd275
+          6901039e f9120b8f d3a825ad a41c03ca
+          8adf76df 09122f02 6f0a29df b1ed4e86
+          916ee58a 52afbbd4 c23399a5 bca4d275
+          69717621 6fb25cd2 1182d36a 25ada4f8
+          46b17ce7 f6debcd1 08b2dc84 83800e4b
+          04783d24 7b438b3b 4de8b597 58783693
+          49f2165e 25483035 05af99ed 324aafd4
+          4b3cbc85 0c58737e 79afd2d5 2b4365d8
+          ba71295d b7dacf94 53dc8f91 a043808d
+          7f56cd00 ab54413d 4f005b78 06e3e5f2
+          f5a52038 65a31c03 5e154834 35c58629
+          e8f8fa5d 7d38ad1a 58a9778c e379bff2
+          5ddb07c9 b07513ee ecce6d03 c0ce074b
+          d5fb5120 3d506835 f5010fe9 8a5dba34
+          8d5634d6 721699ee 150ef493 9cc94e38
+          36712e5e 8d5b8b16 9753a981 d328a57a
+          8a896e3b 3ab80b57 acfcaff1 e471393f
+          e546fc9e a4d34ce1 8f3f4fe6 247aa18f
+          318ed368 c9e15fab 08584790 ae589185
+          c74873d7 58198e2b 63f577dc 59c9e935
+          76c955d4 62f31c01 9e60a913 c7432798
+          42a7458f 46da317d 0ee79059 7be6bcff
+          563f19be ae5f72df 7fbb87bd dadc4bb2
+          9d050531 7e476e69 6b3c20dc 0c14e90a
+          b1f02416 b71490ff aa0ef135 073c38d1
+          c0fa0c1a 6a52f979 540b128b e2e153ca
+          5a5525de 3ad5b5f0 f41ac742 4b5b69b1
+          9557bc7a cd4ce3c9 137243d0 eb1053f2
+          19b668e5 aaff21eb 8e916af0 1e1b752a
+          6f2f60d4 75627848 47701a97 b5ba5a7a
+          161e4ec8 5770e50a 6f1f1bcb 9b4c1681
+          17f225e7 d262c0ab a8162f2e 6a2d3c1e
+          5804762a 6f4f49bb b435569e cd60eb96
+          f3deec49 122f086f 50c97e77 ce445bb5
+          a53f4839 ce8bd441 171a52ff 369665d0
+          7e311222 2849bab4 825080f4 c052d33c
+          a0448a8b 6ab7d8c1 54542ae6 6ad4116d
+          70a0b47b 593b44a1 01c8fb61 d59cc2a5
+          4b5aca50 7675c99c f64262c1 373fbdab
+          907acf68 04749ae0 807fe80b 03e6d232
+          a433bc54 75031b75 a45b8ae0 f817492e
+          aca9a010 ea1d59e8 67ba8830 c9935bd4
+          803ea700 dfacb7df 5a683879 5c766dff
+          450c274f a8723efc 7031ab04 7f291f86
+          e2850507 9aa0807f a4a4b060 c8cd97ac
+          5e08164b 111e2f8b ff4fb059 0ba51aab
+          a8cec9ab 7f62a1f1 620b8fd3 b0541423
+          2005064b 7a4e9f94 5143a691 0b1c592e
+          dd27361b a48c1bfe aa6083de 8cc45396
+          70ac5eed eb096a1f 6fe402d9 eb1d8e06
+          a42b520c f5603dd7 24b6c803 8502397b
+          3a1d04fc df7f73d0 d80587b5 2799071d
+          5a8231bf 50e0ab0d 82c0b284 e6022db2
+          a0f6f321 8f20c131 5fee61b5 c8d24dcd
+          999e3973 da5d84b3 407e6a1f 3c4559af
+          4fef653a 9f319d55 d5f800d2 7d10a009
+          da902041 e1ed85fe cc139dc0 bac15755
+          0bc6bc42 a233521b 33c675ff 7beecf66
+          b55a0478 1c07eaa8 e81c3472 c919f2b8
+          98de5c54 4202b135 2d937053 50b4d8a0
+          8b0c059e 9e58bec0 28419333 f7c34586
+          137f7b30 0c03b288 243f055f 2cf6cf7c
+          6bee1246 052ae9de cbd6b776 3c1362d1
+          295867b0 48372c48 478c0545 44672418
+          d911788b 39131f70 2c39e9ac d63c4682
+          891a3874 67a93443 5566b613 f01c8bee
+          dd3c1e68 527bb491 f1e9dd3e 65ecf0b7
+          ed4623d0 0e7a78fc c6b34990 fdde3bf3
+          39052432 94801da7 62c0332e 06ea75c5
+          46ba518d 74c45e6d 95e40dad 801ba458
+          ade49e82 c50bcf9b cd5938be 2f82a1c4
+          1e1e988a b3171822 62820ac3 f03ce315
+          1fc3283d 558cc04b 70cc5778 70bf3cd3
+          85ec2773 de9a35b4 46e9697d b064bf31
+          e341e385 cc871865 8d2120ed 47b001a3
+          0b0960b4 c1810c63 e79dfa80 50a0e25c
+          2ae3f0f2 a4356634 264ea534 2ac32332
+          f1ba133c f71b3d36 5b1de85d 2148b0a2
+          169be895 a9e960af 74bab560 b3813a38
+          103ce2a2 80b70255 c2698045 2edc2779
+          0b3f0aa1 d6bc43d6 4dd6ebaf 25142dff
+          e5038596 9e61e30b 09ef16cd 80f3d43b
+          f95e3816 ecc89dad b8902a49 771617cd
+          aa43fdb2 0226dc5f 4a2c3cfc 7f9a662d
+          aa38bde6 bc14ab0f 70a30063 5e3154a5
+          65013878 3809d130 f263fcdb b506a0cd
+          b3c3a92a 4a88cd9d 3f77be31 f90c9578
+          67387d4a 99f5e6db 4b480a0a 35680724
+          33c1bf43 ebfab7b3 1c07d519 5948474a
+          24db5443 b05a4f08 76bbad16 f0048b59
+          e00dc624 c91ee868 a8257f9f 827a5c0f
+          362bf8b6 6e01da40 2ff2f754 611e3e04
+          d272eecf 7c6dea7f a933ee0c d5706ec2
+          c869680e fa0045ad 15b027e3 191b09ba
+          a808b4f7 edf5e277 c527ce80 20e10b3c
+          6550d085 9a6a2bb2 e48c5a8d ddda14a9
+          b2fbe193 abf4cc59 b0169600 53c34b6b
+          e741e1e3 0d011ddb 836003ea 04bb7225
+          6b36cecd 5bf86102 4de3cefd e0fd9e86
+          a4b4d7a4 9e6f7739 09ea7c3b 3098c7c2
+          91808a75 c156540a 65a7cf4a d6bac3c6
+          6ce0238f 9d65359a 3a808706 ae6bd7fe
+          8c541371 497a4a71 35949e44 46ac5251
+          6f3682ba 7504b59f 56d227dc bfcc4b40
+          eefcf99f 982f9ee7 68186ffe a205fee9
+          33de5cc2 69404dd3 3a63eb4e 171900be
+          ed5a0158 ea04ad91 2e949c38 0da6c24a
+          69c6ef44 4fde2298 4ca76b8d 9fda3fa8
+          35271905 5729d5cb 797cdd5e 70e877e0
+          ab0de8cf cecb0b55 60200477 eb44dde5
+          059913cc 7296963d 2879f0c0 17ed6565
+          921eabf9 c239c899 376f2ea7 8016d4c5
+          6d91e716 dab32bb0 1e1eb5e9 28580778
+          8311f20f fc2ed5fe 77a4adb1 3ac03bcb
+          bbdf80a4 4b002fe0 fe07b3b4 91811704
+          892a3e2e b1aa4a2f 8092bf11 d8d7a5a7
+          43a017d2 bb2be823 fc40a010 f4581d80
+          21e5e2cc dc051f74 045eba2d 64325e79
+          e1ff8c17 331f6654 74ad2f6f 01f08a0f
+          85804eed 91ad53a7 b410e940 c95f27a1
+          3aab90e8 8624ad3b 9ed40d1f d5b5bfc3
+          7c09e001 c70982d9 7c50da26 0d40ceaf
+          fbc05e59 e5bcc040 6e2de7ed 05d12307
+          21b39ea1 a3a9c03f 6c7e4e0b facc59b3
+          3fadfeeb 0fe9117e 0802e4bc ff4e7cc9
+          facd620a 8a40d7da b20a0622 870f0046
+          a375261b 63eb0ee9 40ceaffb a53d7c9c
+          86d37ff0 a1ba0739 eb74f958 f0ea3be0
+          9094fbc4 e193ac3a bd100af6 1dae6fe5
+          992de0dd be3584f4 b813ec66 a0525805
+          74baf0f0 c3afdb4a 8a255585 517dec4f
+          55fad4e9 8b198e0f a46d4df1 5e0eeede
+          113c6f4b fc8775a7 82fc7d47 a03a43ba
+          d61d690f 278055df b9cb81ba d919752d
+          3cf01e38 e4a0c030 5592566c b4c0d93b
+          f6832135 c3097af8 b606597a 11230682
+          77423871 036813ec ea559d4a 7a2effb3
+          4ffa4b62 3cb8836f 69095c98 38712acb
+          413fa08c dd43b0e0 34946062 dde18c84
+          dad64068 cfe3bd9f bd639f74 c10e8b0d
+          0dd5d733 55dfe18e 63f5f4bf eebff88d
+          1a7b511d e2f73748 58e1f16d 94b5ca0c
+          196bb780 60b6d6a9 beb003ab d342ec84
+          11a0f4d4 d0776b2b 9086a18a f4196f2c
+          ce5b303f 500a565e fee24f7b 561d3f35
+          9d5153b6 94761ca6 5042ecf8 e1e24585
+          cd56ebca 0a161b64 acdb02d6 4ab3646f
+          66c91ce0 0a8bf0e0 ed1e9dee b25e11f0
+          58bd1ebc 7adfbd9a 973adf03 5280d2d3
+          6990bd6d d725aead 363e1aa2 470d145b
+          29d016cf 431887ac a1b89cb9 f3e699d3
+          53dddaba cbfbec63 bfb4e933 972010d7
+          d0b68e38 af347c50 2fd02726 d4776595
+          4ac8ddb9 074a4fa5 e2124369 bbf368cd
+          03fffbc8 c64b3cbc 7ffec067 d8c8cd3c
+          3006a96f 0adcfb2c 7bfb0128 c715189a
+          3a577726 330474eb 0441dd3a 5019cf63
+          d16160ca ca7b3075 ca23f7bb eb182c99
+          1990f3de fb739141 435d0a0a 8ff6ac5f
+          bb6610da b72739c0 6b5d59b5 1a2a4e27
+          43d6d6bd c04afca6 1a5bb84a 5fcf14cf
+          1ebdf65d 15f0fc86 8f3aab89 08dc2b48
+          3c8e8573 8f043b0f a92b3680 b5a01870
+          375471c7 08e4891a 35183c63 43e88be7
+          89ae2d94 eddcfb61 dec28fe2 dd710817
+          a73cf21f 5346ce44 5643d9d2 21cb4e13
+          e80931e3 868975e3 35ae1afa b3adac1c
+          d2566d40 fbd92edd bcbbfaee ec728f4e
+          5d0c5705 3cecf37b ded9e92b 1a9a63e2
+          a0adb1a0 025257ae 17dba2d6 4948e63c
+          3d2176c2 7050e855 f4c5f370 6f38de1e
+          94fdce3b 1f59f372 dcaae828 efd38f13
+          ca76ecf9 8010f1d0 e4caf262 48227af4
+          10508504 3b2b2a18 b15352fa ea4d509d
+          552279eb ce61b318 82273df1 d36575fe
+          723f0c7b 75e666ce c7f3020d 8a8e6319
+          c5c7ce41 ee8e3d00 aa3aba6d 31833e21
+          1ea286f5 a3329e87 ad234b4e c1b0a421
+          839eb615 16b8c577 ae3ab84f 79f1a9e7
+          16318c2d 8886eec5 75c58e3c 91909e77
+          826fc7f6 c895ad13 8b51aba0 e8c0ef50
+          78f82489 5dd3e0d2 eb9a45ed 087c7472
+          d235039e bee39d55 9ac890af 044adc39
+          6ce9e1d8 46c5a964 12eba871 edf0c609
+          ead51502 ef6c4d5f 3c4f0081 d301541e
+          3bf97afe e285ed5d fdebda8a 0a21f585
+          e7a72283 a61feeee 4cd35261 25f78c0b
+          81c86103 c41bd93a 2928c68c 2c485fbf
+          43bc91a5 209e69e7 410879e2 e945987f
+          fa9a010f 4be07f1f fd1a1936 85346c18
+          bc19706c 2375e546 b016d589 e7e18d83
+          3649e498 21a08ff0 a7329ea7 548357fa
+          acd94b2a 7edbedd2 11b1fccf 17f5283f
+          72f47f9c 96b22542 5e98425f 9382a277
+          f6bac3d5 14260b89 db59ca8d 40437718
+          311d4775 c8a37397 1d57346e aef41741
+          93a6646b e32296f1 94583638 b6519d5d
+          0269ab37 89dd606b f2d0ac36 50fafa42
+          ec3d2390 9bc7d117 cf6309e6 774a9ffa
+          d22c6b91 6b9e7f05 5f2cf2ce 98f1fa12
+          04ce6a9a 6c3b3c54 dcf42272 685fd037
+          8f176f65 6b44a980 9c6dbf42 e9990cc9
+          a7a0d475 ebfd860e 9cefd1a5 db1513eb
+          b859b366 5d7e9fab 35b818ed 5cc9d61d
+          0f724ad0 d499e3c6 5e43b855 9f812dbd
+          eacc2250 79a8c003 6d20a1a6 51223a35
+          d5a12198 c4174a4f 5d24a725 73ebc6d0
+          e473845c 7ec69096 db59e9a1 deefd5ab
+          4f9a5063 fddee8cb 1bb00f19 2e853b37
+          7ecc42be d238101d 5a020dfb b4d69535
+          0104744c 84c8d143 8141ae6c cd7f80fb
+          5b969f38 833c964d 38af12ae 90432ea9
+          39c2dc34 8c527534 fe8b652f 2bc3c2f9
+          ebb6f0f0 a60c7c78 52ba262a 64096fa4
+          239b8930 9a2030cb d8f82b54 269f0346
+          e5bcd212 cc6608e9 db13fcef 48241b8d
+          32d75640 d6933273 f6db8bf3 bf58e4cf
+          b8c862e1 04e3f3f7 8fbbdf9c 57f228ab
+          a52c6e87 2c3b4d90 174463b0 e390e7c1
+          13d665b4 7f15602d 2e81d455 c853b10a
+          924f41a9 b5eecc20 f80e1d30 dba3d35d
+          b6ab382c 571685b7 3744bff3 de0250ab
+          721dec5e d2f7e0d0 8968335a 216de506
+          b05554a2 0dc4397c 07813458 881d371c
+          b4213ed4 c5f33041 33c3f3cd 33a74f7b
+          cf151a0c e04f2ffc facbb8f2 3dfb3fe0
+          682b1de3 45cd8d19 331854c1 8198b341
+          9c109621 96374e41 31e69452 9182526b
+          dda914bb c35f9ebe e16ade03 7b352abb
+          80fb1fcc f31d3660 bee39692 0a3a434e
+          054c557a 2193b166 3361b1c3 3476784a
+          c0666394 017e4cec 3dc390f9 cc32829d
+          1e8a4782 790858ac 45151353 268c1e2f
+          1a59b78e 66b162cf 2eeedcc4 498bd122
+          043bac18 6ad601e9 2213daab 13e37b47
+          3b06cc66 27dda25a cd14ec3b cc14fe79
+          8641162f 1df381fe 6743d65d ccdc77df
+          f0e8dc85 bfdabeb9 26831721 e76256ad
+          3c438b95 27c63071 87e4e350 b0ff30f2
+          1dea9810 b89554db 561031b0 27d8296c
+          188aac29 a662efc1 8f907515 75abbe03
+          b6303366 bc36156d c6feb471 5360c3c3
+          2b211422 87f577a4 a038fe42 ad02636a
+          3a646ed8 29c6ed68 71ed11f8 6be32257
+          053e3871 ef35e9f5 b5fc1242 ce6adfc1
+          fda6132b 8fa1289e 87364ec6 fa1d6038
+          9f463654 ad58ac10 36a037f8 b78d071b
+          6df13c7c 6b6bb387 a5bdf4fc 02dc7ee9
+          5648dec2 8fba95ef 3f3283a3 ad740ca7
+          a0e81c29 283abdd8 f6899c42 1cf04613
+          a4aedc40 3a01311c 251382b0 c86e878a
+          b0679f9d a5f0f1bd d6ed7b6d 12f7f9d7
+          1bb4f151 ab798a12 70f1c6b1 a10d7471
+          f95ab057 19ead4db 225357a9 80d87b46
+          8236d093 3a3e0cdc 6ec95e5a 35f2dcbd
+          a39f6cea cf2efae1 9ba0ecf7 deff42a9
+          06cae0ae 2605a50f e813629d 5d50f0c9
+          8cf665f6 e69d509e 92453c13 6ae6c308
+          e0d3a3cb 07010f4c bc668ad9 6b063c65
+          60101ffa f4333330 a2d2d481 02077eab
+          2ee641e6 ba2d6250 b84e7e1e 0e18c78e
+          1f468051 e0e9523e 5c9d52f6 ebbeb710
+          00b56e32 77aebc0c d29e79f2 53305b5a
+          d2d6d0d3 8e53506e 4f84e05e ddeae7db
+          69d450f6 f709c8dd 7d08389a f83ab02b
+          af549e8d f968c107 0a5fdf6b dfb7d7f3
+          19010f3e 7cc6bbcb 9defd196 96814fcd
+          bc7d47a1 f8e09ff5 e379e894 f5e9d006
+          c2fa75a7 af0a03bb b68ce07d febffffd
+          aaf8c76f bd1bfd34 afaa8294 b123a6d9
+          caabc751 47c46315 bba0448f 1deac81a
+          7004ee94 4ab0e415 42daaacd 20d804a0
+          e6104036 070e25f9 0d1b3855 7f7bc7ca
+          ebdcb6d7 2e0a5f3f 88fdf4b3 0f913573
+          88aaac27 466c2795 be762b18 d3329df1
+          3cbcf190 a5173eb8 0ff8b48a a6aede96
+          246033d0 f1dc830f 2d2ddfba a9d19c29
+          7c497176 d49051a5 bbf6cd61 29033b52
+          dd88f65f ccd821c4 a300ab23 7e823bfb
+          f07648ff 652318f3 cb09e526 35070002
+          3b6d5cd4 8fb10b97 acbb8173 fa7a265f
+          007d878e c698f9ef bd8810d6 44936b8b
+          dd565c93 98ba7c2d e1f324fd c6c8ecf3
+          c8ed5523 d77604a8 fd3ce88b e7e1b026
+          0f63b3e7 bcfe8925 33bd513e 236fdebb
+          dd11d87d a5d00207 94097665 437a7502
+          df3bdad5 7765d1a1 9bbffb00 141f3b0b
+          54e521e2 038055e4 854f9ffe 8a3234ec
+          baab766e c8080e7a e4b1435e 1d3b7cc4
+          5366d160 d7b62225 1bb2366e 07e01475
+          e27956d0 448441cc e841e447 d4c5f334
+          009507ff 9c74bccd 6d3f20f7 d6afc14e
+          f2ca4acc 27db3def b3852b15 6af0a10e
+          eccc22a7 6c04e982 62afd7bd b8fadc45
+          c8dcbc5b 3c706831 3c1caeac efa82133
+          821e999c 7d23258a d70d78f8 43386f1f
+          88fb72d9 6cc10e47 a8eb1387 5caabcdf
+          7e879223 479dada4 b0a0d3d7 af530772
+          1ad3d81a 9e41a0c7 571beebf 30f1a16d
+          99535fb8 db929571 53efb3e6 6443ca98
+          a18f66cd fd701d6f 328550c7 3a465250
+          54240585 f3d039bb a020cf82 afae162b
+          810c167a 5250c0d1 eb2e217a 6dcc870b
+          97deb0fe dee87fa8 6bdbce10 33ef9da7
+          91c95d45 156f006e 0dcf0b90 be660b98
+          b373fe41 f5c843c4 f081e09d 184127e8
+          8953d131 ebfd0f77 9c6c7fdb b719535f
+          ec69c9ca 54f35595 60af74c6 96eb56f2
+          88a68c9d fc3dfe3d 4b76b62e 73facb23
+          4fb46bb9 a57ce7be 2f901beb 471bd861
+          2382a4a0 0ce90dfa 66b14e57 d6918292
+          b5710754 5cc80596 b25b5986 e1f2c3a7
+          4d7b5615 1179c366 16733d66 61cdefd6
+          6c567b65 059ce9d5 fd65c3c9 93ef3750
+          d054a86f c036d6d4 ddfcfbf1 69e3d32a
+          0612273f 448ab76b f903542a c1909105
+          499f2c63 6c065363 9cc06e31 47985f01
+          292daf0a f44c6694 ca63c862 c9087ce0
+          814a6570 08de4782 631f91f7 97ae5fa3
+          329e3def 83dcb318 c166ed68 29a88cc0
+          692f3751 452134d1 fc34ca67 e0c3d2ff
+          f644a1f9 23ff11df 5fb3b7b4 1a28fdfd
+          2f4859b6 426c0ac0 dc7a3d68 9239c2ae
+          ac012068 c2e80712 7e5efdfd 95f0a8d1
+          010f8bf1 d449c5f1 366dd7b3 6a18dc00
+          27b1db00 5e0de845 0cee0e11 6386a053
+          d84aac3c dcb382d1 a8a168df 11e6fcf7
+          6b816df8 188b5bcd 11063ef2 3646b45a
+          842bb819 e4c074fc 5e03948b b92de091
+          1494002f 68f5dc63 82d2cf07 670188ef
+          572ac15c 50086716 2c054b49 6543dcca
+          bacd1ce1 ceebeab8 981f5aee d8fd7fea
+          e818b819 c0bb6988 d2b66a6d 8b9ef7ee
+          14816772 a88be7a1 4d97b3f3 0094fd75
+          aa7ee919 724130d5 6370773a a91eeb9d
+          a80a11cc f03f7137 6285f808 8e87fc3b
+          abadff7b d40ab981 642066cc 505006fa
+          8360ad43 a06db741 faaa8d60 2aaca42a
+          0585b8f7 76b81036 75daf375 c1ee2622
+          5237bba3 19087d71 6a86fff8 b14fd84c
+          40d7fd24 49851220 6dd52692 008a4fe1
+          da93c76e 87a85143 c0332e94 bea46459
+          6ed8950d eddd097c 6e6f5b3f 0545a582
+          bc9d7ba1 f8f879a0 ad7ed86e 026bccfb
+          6f3f15f4 c8e4c206 52d98691 c839efad
+          f36893f8 8160a3cf ca331694 3ba81e6d
+          a4679eb8 5276e03c f4107bef 083aa91e
+          65b93e57 d682bba0 8443f850 b10b4a6d
+          a809815d 55f239c8 dafa1bd9 6b5419bc
+          084b3cda 257e18fc d4735b1b d0466918
+          51c7c641 f3956b66 22c5de0f 9429373e
+          754bd0e9 9bb36d77 ad9557e3 daeae362
+          216ac400 19f064b9 b262f340 0ec598f1
+          c380d33b 5350f065 98bdb20a 2eae5a8f
+          2c1d1b55 29281843 309634fb 79cdebac
+          b6e19899 1af4c25f d3bc8531 eaadd913
+          791e8a69 cccfc3a7 70c5c933 8453c009
+          7a6608ea d90502ef 6a4b5f6b 7859aed9
+          92891cd6 4fec8252 37058563 2173c336
+          a84a2ba4 2e050563 08c29247 34892d8c
+          0daaa70d fa45d122 854dfbdf b9c0ffdc
+          f7046d7d e2709a00 2ee0beb8 620358fe
+          49f5888e f0e83143 411f1d24 c7f364a9
+          6fc89831 114f4b08 eed5b51e 81368309
+          b48f1c25 4d2b686b 618fb103 61c8b308
+          4b52a081 a9041a25 a5337cc6 9b2b742d
+          e2170a94 d595e218 8b21b78c 7081d6a7
+          7ab483c2 db0b6227 8c004eab 90dd5b59
+          88e01414 6db00f44 a1c350fc 414d1714
+          0518b3f3 207dcd36 314d87a2 c47e8c19
+          083b1621 0cf9a151 74b4315e aa894f80
+          c4759ba6 a1c53a42 9b72e3d4 8ba2bfce
+          42eeaebd ce541546 746d3d9a 2790068e
+          bcfda658 0e659182 62e3f390 6344229e
+          007fb15d bbc34be2 ad36485d b10ecc25
+          d5621e27 2d736227 ba720c61 c72b1843
+          1a453faf c44b7bd9 2f741d5a aa0c0840
+          58cd1fae d8b5f75e e0408bd6 f15afe63
+          97e0fbbc 29d716bf 1f1d2395 17d2c033
+          2612d4c1 41c8d5b5 89c7b4dd 0e1e71d1
+          602e2880 ea8cc21b ddcc6e3f 47f2fbc5
+          164761fd ba4070bf 9e2098ea bab26ac8
+          deb213f2 f71f17f0 651843c9 1c216861
+          040b5445 cc9a31da 7fccf88c 6bc19a26
+          691e703d 1231e3cd 5341131f 78de11cf
+          a3870088 c5b1191e 9dd2ebc1 5a5e4eb8
+          426b5c16 9cb6123d 6608e8c2 7da96b25
+          258b2876 9c82d23c 0cc287f6 23fd146b
+          351e7904 15679221 7bfb3e72 49415389
+          3ac60884 152f23cc 38d6a8ba d958347a
+          3512feea 8c6f7509 d14b1cc1 7a7a280d
+          55c05467 1533e9bf 6c24ff2e 0afa3bab
+          8d51fafb 3131e386 332ca67a e4e9a27a
+          a4fdc1d4 9e4abd9a 891d3f82 e1b45a06
+          59fd22c5 a242c1d8 ca2b99d4 559b18de
+          c2e37391 9a3941d8 c0e89bc7 fe88b062
+          49ada5db 48b8d4e8 7d28d4f1 cda0e5e6
+          6d2f332a ee6f9a92 92f14ae2 dbb5c2df
+          4f43fede 43ff283d 3383779b 961031b8
+          b76ce551 15b81353 50a286f6 015d5c74
+          1d221e62 7a40c6ba cd509d59 44153705
+          8e65daed 90123173 e6b3082b 1a3d5cd3
+          248d7734 cd122bc3 5f7ee511 9e87729a
+          f2f36aa9 1e37ee84 eaf3a997 503d86f6
+          eb05feed 9a91aeb6 b250e0ca e22e2877
+          b480a0de 5dc9fa3b ad023514 1ef81d0a
+          0e1e0705 65dd8b79 1b98a367 be3a31e0
+          3fff2d6a 92705353 8d2de28d b7ffd2b5
+          4c788528 374dac67 98eab1da 02177f5e
+          27f684e3 9cade131 214bccf8 e1a00df6
+          962d3d89 0b494109 f126443c e424ac6d
+          27a60463 4616e13f ae75f228 71813016
+          204c9889 b0e14093 e963538e b1f9caf5
+          9f6b1262 be1128eb 20825d94 aad47cc8
+          5cbb9564 cfd7a77a 0c82d871 43498a02
+          65ad1728 42bb9a14 9421a00a 0c705e54
+          e02e2866 0ba4ad58 4ff852a8 4a414118
+          80b0603d c284f94d 6a8034e5 87695bb4
+          84a8396f bd60b3c3 29da941b a718e41d
+          380645fb 8fd4a77a 349bc0bb 7d6b8818
+          d09ddcde c9224d57 36b47767 f0e950a7
+          0b0a3ef3 940ac8de ba0bca92 33e9aaa6
+          e0094547 26c28227 10263469 a66e9337
+          cff6bfe7 be92a857 5f9cc4db 986aeaa8
+          1e194cf5 b80d8ca9 19a40b46 4d1c03af
+          7ed8a0be e0db2a56 8ee7494d b711d879
+          370b8388 21621794 ba443c15 27932067
+          e741ea08 b491eedb 11064c41 5890ddd4
+          1fdff46c 0148eb23 df9e7758 df3a713a
+          6df13cdc dcd25a69 868bcbd7 016f3038
+          eb6ded76 60544a88 9b301234 fe7a10e4
+          789e3474 1bd92e4a 0f1589d3 b27aad93
+          88075976 b69232d2 4791b7f1 54116863
+          9d47baff 0ec280cd b7a266ee 964d75fc
+          f7cb1768 e2a2960b 94523de2 2e182443
+          b966d12d 56508507 43ccb8a1 24454190
+          e3796e6f c9902e28 43fb822e 3ea67e17
+          14f477e9 6b364175 7609553d ee48dc2e
+          2eea57a4 fb6fdd32 fdbb551f ac6bdd16
+          a2e7cd7b 1a797367 696b2545 e279bffd
+          21523dd6 8de7992c e0dbb13d 84f5b94b
+          eeaa2201 57167741 09ea5527 05059f6d
+          68bd0bf7 1d82c223 a7e8ea5e 2c90c84d
+          21d2f949 48f76f59 e0e6961a d37e23c6
+          1446bcf0 cc63bc85 3152a50d 0ea32e6d
+          f5663067 64d789e7 89bb2262 687ff069
+          19453d1f 86db821d 4941f121 2dc1eaa5
+          a0285560 bc980e19 1b763604 eb985bed
+          779b0984 b0298f3e 8174fee2 2df5b06e
+          ada9c341 f4fc8ff7 ea6f6f3d c36e2413
+          434d440f c7f32ca5 46b8b862 1df02693
+          333f0fc7 f3341a88 1d3f02d4 3e3a9c98
+          298b5ba1 1dcebd64 49680213 f1d47641
+          c104da06 23e98282 e3b8d490 15219de6
+          916eabc3 8217844d 7d7555ed 3ea712f0
+          1c12b764 d97c7554 f81a8132 378e452e
+          4d595206 646fda41 14c219cf b380262a
+          9cb40e62 1cee802c ee2138b5 28b4cf5d
+          e0ddaed5 3fba1773 a40b4af9 b91c12c7
+          a5c69345 53c0f97a 1f6eb979 f374754c
+          dcadd739 579814fd ed1d21f6 d3854fa0
+          c3f02252 6e9a9a44 909484dc dd87a0ec
+          e8894ba8 1efd3adf 0ec13d3a caaeadbb
+          18773805 a5793884 0fee4b42 13ce1414
+          15941d3b 05b9bf1e a62b0505 41bddd06
+          65c19326 4ed2b5bb dde01246 86abcc8c
+          cfc0a179 e14f4f79 1429375d c92aac98
+          be90ba6a 239873f2 48a99178 3462aa47
+          1e22470c 04aff830 19f45cdd 92416ba8
+          f05443ec 3d238190 ced4a6a0 28c15a58
+          0ce9ab37 81803b1a d39382c2 d8902b1b
+          3ae5d1e7 a3defde0 940ba99b 8bcc0fda
+          18310b16 edf6ec7c fb6c9b11 e8cacf43
+          18672eae 8234047a 02bed1e3 ea503dea
+          45aa4725 5226b935 bcaba29d 087851c3
+          fb833636 d2d90505 a71df13c a4ffb209
+          0c79e5f4 1068e37b 1aa4c3aa a0806561
+          d3677ced 62f6856b 49ccc70b df518706
+          6da48edf 560d5072 f28248f5 881392eb
+          c4f374b1 311035a2 bf087872 3ccfe504
+          5bdf819d 5a41508f bb486a51 ad205736
+          7fcf7e28 3a9a4455 e918d65d cecff764
+          8b4d9b5e 524746b9 9a43e542 1385dc38
+          8f4e5d84 b8654b1f b75b209d 36c5c149
+          a8b8db6d f989d397 f4cfc3ca 14d4b53d
+          d8e4d233 d7023b9c 8212ea0b 51a3873a
+          43110eb0 ab4eb908 999b768b 37b21479
+          2c360b18 83273dfc b047c74e a5828b91
+          b7b89c85 8727c8bb cf80ac90 c98f4cb6
+          1b81aa22 2b9c9b85 4b8d5257 6e024b61
+          312941aa 55225e40 4a35183c 6282e426
+          03aeb257 1d2928b8 db8dd2cf d79982e2
+          20d04e5d b90e6c06 0b5504da 38bd2cf4
+          b189af44 be35f7a8 e0824c55 2e194265
+          542a885b fce536af ee9ddfb1 d195924c
+          ac3c635e 19a4ad5c 8f5c585e 8c03919d
+          640785a7 27c4dd3b 1238ad92 309fc972
+          8b951b1d 3c617dbb 8077dbdb 9c9cb28e
+          1494ac4d 3ba0f242 3e5504da a47638d0
+          6f45c4eb 6f2e6458 d7bc9d71 c96f2538
+          32d3a3de 9bf7a62a c0770775 548f1a80
+          e2632990 b7eb37e7 ad2d168b 053c9ac5
+          43d4b0be 244e2287 f36e21d8 91149408
+          3105a50e 110fee82 52fae7df 90fbdb1f
+          24cf92b2 032025e1 bbef9e56 858603cf
+          bb663178 a3d134de a83b5b57 d451d182
+          ada2f840 d99e83e3 38257841 c3e8b85b
+          500462f7 b6e27c06 784687e3 2c7510ac
+          b5548f82 475c0c98 0bf3a12a adb0b18a
+          cf659ac6 7f11645d 0b4a9d1a 9a3f721f
+          a8902b4b 6838711e 063a9ccc 854570ee
+          eb9fc16e b2906ed7 94cc1183 5c594bf0
+          c407ee0b 79eab953 d044d69d cbd13436
+          c4802267 bd753164 e2034fa2 0915697a
+          291112cf b3da45aa c7e25292 b6235abf
+          98c05380 e8b1c340 1fe14f82 e6b234e5
+          9e145db7 c811fd40 17130902 4e41c107
+          11ee7063 e7217dd5 06301555 51d5bd18
+          879dbc7b 767923ee cbaf7f65 55aeedc3
+          33d78392 35bfcb34 521fabba dfa5ee67
+          580b0be1 788bd877 6d65d553 1ba02ca7
+          e6431a0b 3c1bf4fd f85636f0 cedb20e1
+          91fbd131 6a071e4f 12ce5fd5 a8a13239
+          8549faec 5b6461d8 1b3a30ee 5673d494
+          ef27fca9 9d5b09f1 0fdf4b12 c3910289
+          9fa1d140 ee965d90 b67a4743 a5a0b8c5
+          1c61f067 f51e5b5a acdf34cc b37b4ffe
+          6abadc18 78713def 778bbc6f 65602024
+          aedb344b e1e7b597 ba781e52 9ea23fcf
+          40feeefd 24d54124 b9c5bdc5 cce0d9b2
+          39440eed 23371868 22c15d50 74213e10
+          357a88a3 c6d99982 52999402 995bf650
+          d5df0e43 a6dd02b9 cd97af9c 5217ec5c
+          59dca6d0 c5b37b2f 73c8934f 4eb259a0
+          80262523 de920253 3dee82aa b3e7093b
+          7dad982d 10d2a707 04dc9128 979e35b6
+          6ef322e5 26ee82a2 f2f72356 b5782271
+          60abc029 281b8037 59c5b64f 9488dd04
+          42d08313 1ef3eadd 37c35dbe b35b2d4f
+          d8b49929 210fdefb 144f59f2 2d5634ac
+          4ca9cbd7 83adbc02 1885333f 0f5fffc7
+          8c1d4efa afc9548f 8d68dde1 14943edd
+          c0a75d2b 102ce6da c308035e e6faad50
+          9d514855 0a0aceb7 f3e9dbfd fdf8653f
+          6cac892f cb80d7d0 5f56a381 e88f17ad
+          54f8e93f a6ae353c da539548 a932d66e
+          a91fb7b0 d94019e0 0fb1e386 a1df6165
+          aac7c600 3b9c8292 58b70b8a e32fd46a
+          283e7214 0a0efc45 57e918ce b7f3f7da
+          1731ebad 190ce75e 59d56e67 802b7c7c
+          a0f9cab5 af727ede 8780b278 1e66a5c7
+          ecf405fb 0f136573 bab666f0 6edb0ac2
+          07f4906f 6d1b41b9 151e6ae4 ca8e0056
+          abaeed82 82ad1adc dd261d1d 4004ffe8
+          2a1d2b0c 7df69949 9e3d7aba dd6e73cb
+          8883d7dd fd8c61cf 3d37c96a 8152ba7c
+          5bd1bdcd 58bf130c 17d3ead7 dbdaac10
+          36f06ef0 6b1b2fc7 f31a18f0 a286f703
+          5d5c742d 37050e23 0808f870 771b7389
+          81aa1414 6ced06df 3feef9d0 175f3deb
+          969e92bb 4e7cc8f3 af9c4613 ff0c6dae
+          2d063c6b 95092eae 580ff6aa ea3aade1
+          7960144a 64890c07 4d802708 f2cdedcd
+          2b374e41 b9ab0d04 f5ec0260 aab3d154
+          4ac8ddb6 1b4a4e5c a0cb9545 53a0f0f3
+          5814b778 d90fac4e e7966370 5bc0c313
+          1ef7f957 df2b02bc 16d37689 81bbe656
+          9ccf21c1 72027835 f13cab15 d4a1c110
+          3b7e9818 e3936bcf 6e5cb971 1794305f
+          881a39d8 91075487 40fb5432 646ddf47
+          5d9d2ce7 eb7dacd9 8f2ba7b1 9e9e6e3b
+          0eb7be44 67f51e90 f0fdf297 157ede7f
+          0914c6f3 f2f61d85 e2437f5e d21adee7
+          f6b610da b78b4cf5 78c3a61d 265962c9
+          4590c2df 971c24e2 a473602b 2d23ae2c
+          6fb65195 8262b340 55d84b2f 4ef4ee3f
+          a8c2ad31 c3dd1702 2d4055f8 d45726a2
+          05a9a04a 296ba81e d76c0153 7a76fdd6
+          f0561b44 0cc3548f d124e622 cb75e21d
+          c2b7b07e 5dc18b74 41a943c4 83100edf
+          92576715 5365dde1 833368fc a817439e
+          7ee16f77 1f8b24ce a8e0279f 3d1e347e
+          e40bb429 370e965b ca1d548f 4653bdd6
+          f08c4ae4 5750fbea e578def5 28374941
+          8984f041 8e2e2875 aa298a0e fe0e0587
+          4f5015b7 c3e12265 80cf0f09 dffef839
+          abd7bbbf ce4842f1 757a48f8 eee7a5aa
+          10bfaf79 cafae761 e52b4fce 84ac8ddb
+          2f690daf 8e0c8398 b1434473 508ee75d
+          55705844 e5ad8598 f1238051 abeb11f1
+          9832b321 7ddd7651 63684941 c1713b1f
+          cfa4f865 df3dcd68 b4d23012 24e3e1a9
+          3510f7e5 37cf73be 9e2768cb cfc30d15
+          72771f81 923f8ed5 8fe799cc e0dba903
+          84f6ee24 bbb65745 3bb17c2c 6ae4a544
+          3cb8234a daca0dc4 9aa62905 c56a0143
+          e4ff5e7b d467c830 c9a47f49 2aecea33
+          785859e4 cc198fa0 85aaa249 5771f01c
+          7b5e69ab 3683393b 975824a2 120bc44a
+          89183e00 b9691132 e8fd9b31 63c62928
+          6d21a06b 2727110f b6e4940a c8defa2b
+          949e4ea7 ca95c5a5 63fe83fb be1e34f9
+          c9839232 0ea4b650 41939ef8 3370d4d0
+          693c85a5 67e6926a 929f477a b4d5c4f3
+          6c76e4f2 6b20e69e 11a0f4d6 c8f1bccb
+          08bea4c0 bd05490a 0af981a3 3e4fa582
+          8a934990 b3f300b0 34e5dba1 f960b5aa
+          b591afcf 9e873321 64c073e5 01e9f5d0
+          6ce59a4f 55e1413f 5217cfd3 00949d4e
+          83ec2dbb c4785e8d 98ada08d 8984e851
+          836add37 596ad00e 480d72ec d8a1a0f0
+          f571b66b 47f3672d 2e230708 2656a225
+          0585a477 2994e989 2b563ca9 efd44572
+          3b4592cb 882b0e62 3f59f42c a3559da6
+          2d3f0fa7 4b64238b a4ecd849 004d1db3
+          04b96901 5def84e0 6eb7cba5 67755d37
+          dc05a55f 37f06cd3 b24edc4e 4c344e5f
+          b3098c79 e554f5b8 b359c0e6 3b6cd063
+          3ec346e6 48523fa4 ba70be23 c714f90d
+          1b34192d a0812605 26f13cbb 0069bf6c
+          02736e7e fd781e72 d522470c 02cfd810
+          399e0762 dccee7b6 68081fd8 47ac93ad
+          494141ae 2c6ed050 f4fb69ea e2767e83
+          fabe9df0 ddf2ed92 3508a4bc 80f15fff
+          74c06f70 bfe9761a a91ef32b 207dd546
+          91fc8775 c6f3384f 0f889d30 02141e2a
+          aaa91e71 97689282 327698d8 54b5366e
+          a704635a 26646cd8 45158136 8edb814a
+          f16be4ec b7df6425 9282421d e0e17adb
+          a839ef2c 0025f70b 6dcd3131 4560f189
+          f390bb73 8fd3cac3 82dc367d 422c440e
+          ef47e235 0285f1bc 1a229ea8 11fd401b
+          1d59db05 051f0c38 813b75e5 7ab0569a
+          a821d0c6 e9388242 9dd7fcc7 1f1ef5e8
+          d849d2c7 a04bd334 3684a8c2 c241775b
+          e29e9275 1b462193 c69f61e8 a020641c
+          ee6de585 0cf08c0a 03757888 b32db99d
+          078f9828 b0141742 656ac1e5 6254929e
+          23ecce07 75690311 c30722cb c601760c
+          4308e033 d76d81c2 2367c881 c1dcbaef
+          df9473c4 d84cc007 8c1efadf c859730e
+          35940e0a 4d7092de c867483e 8d124f8a
+          ffd80905 252b7f9e 94bf7ced 76951670
+          662e157d 8109d5a3 85277c0b b7850583
+          d2c71b04 8ba35c0a 592f98ea b13a3317
+          2a338b49 9c9e0a57 160ddd23 dc17a246
+          0d1675be 86f90ab9 b5657f9d 80dcdd87
+          c9c50f25 d3c1608a 45df0177 7f1cf7e5
+          77ab050a cc7db7a0 696c08b1 959743ca
+          f8d1d3ca 77ec7e87 d392d3cd dd2908af
+          f933ec84 eab12524 4cbc4fd4 78c1f128
+          15602e28 02637e49 bdb40b01 6a9bf832
+          8d34805b f67eecbe 69827cd1 13882606
+          833f8363 57c8da2d 85d31f7d 01e6e28a
+          6bb995bd ee3570c1 7dc4a3b9 60794171
+          a4ed9123 bdf4ed6f 37bb8b2e df0c1e51
+          037858aa 8fffad38 79e79dbf 30ac6d44
+          23e655b9 1ce039e8 f4206654 5f081974
+          3730d63a d9c70ace d944b4e9 c7706bde
+          8f6b646b dc7b6cda a2bd766e e98f50f4
+          6732c965 6c943570 b539426f b51aa024
+          fed38f7b 853cf1cc 2977d3e5 1bc5238a
+          2a0301f4 eddadb12 be59f6e8 b9fb1ffc
+          0b597911 d40c1ceb b4802f31 4e43f0dd
+          dd44d6b3 9a5b495b 1de5a702 edfe212c
+          07793b7f 83e2bf92 e9ea8262 06a3dfe0
+          fecf06fe e7a15334 61004bd3 60317975
+          e581036d 90267852 356edcf5 42a784e8
+          518381c5 5d407899 da4cdcfd 2cb9b428
+          fefb3471 756922e2 417ba220 f4d9e7d6
+          70dede54 2d39552e 6dc99a55 e14963c6
+          ef576a21 a631f792 4bb9b482 98601b3b
+          b61f840c ee0760aa d30f1fe7 9f5d9e66
+          4fda461e 3af8f04d 35119512 2acf9e87
+          e445df01 8f5cfd6b 4c45717b 97566cd9
+          1ef8799b c34726ab 6362dd4e 976f148f
+          a801bcca 83fb99b3 63c7acb7 17170e63
+          14f45c5a e00b8b80 8e89d0ec 91ff88ff
+          659d045b dc1ebe2c 39eddfd2 52240578
+          787bb1c8 9d0f1fd0 0b94b86e d6e68865
+          6ad490b7 f55748fb 6587d8c9 9869d835
+          70c93942 6fb51b00 821e7968 62fc975f
+          7f450be0 5113c34b 7b6af234 4b5ee130
+          850ea829 9e274434 c15e1033 66188955
+          d5723320 cbae0a59 35177f5a 0756234f
+          932747f2 916cd555 9030f17e 27398fc5
+          0a217d7a 40656a3a 141d4db9 d68b0b37
+          df1c2070 5a60f296 7ef3a1c2 c7f3cfe8
+          799f9c94 5d5a0958 78384693 f6cc13bd
+          f3977cb9 85d58046 42d6cbbf 7e068e49
+          31e87f89 93ee03ef 0e6d9c34 83c8c2b1
+          5719e0cc 822fa03a abf04adc 0cd27569
+          05b11d54 dc84c110 d4b7a7e8 dee2df56
+          2ac15254 8ce6e54b 30155d35 35450a69
+          29e4fd02 e622d27b fe79dbce 9d7df577
+          74aa7065 5d6e083c 92fca545 e9da5521
+          398bbffc 9255030d e7b6d392 318b5d40
+          08d8d525 a26119c8 58b719aa 320aa922
+          a2a90b4f 84cc7cd3 2e305c48 238d0288
+          a0835115 1c083163 87a2bf67 29494d47
+          738180dd 5e5ed931 69e8d079 e68be725
+          3f5e4903 5ef51f47 20ed8517 3f46464d
+          3c4d7e1b 063bdfd6 31103eb8 2e110d43
+          38558bf6 1d81fc83 7f539582 71899223
+          c0b3555a 48050a8f ac5d928b 8805535c
+          a20322bc 6f1792b7 4847dc03 37fb445e
+          7d7ed1a4 ec77de7a 58063c37 960b131f
+          78de9c95 7b0f4313 61b2a30b 482c21a2
+          51398968 d44a30a5 e32e203b 449e1f9a
+          027797db f86a91cc 3c6bf30e 31be5913
+          cfb3d921 0c1d143e 2d22e969 a18586ad
+          40a097f7 e5d71f64 bcf44c1b 19f0dcce
+          c4e121ed d927ba19 ce9c9fcd 69809e0e
+          bf8e7162 a6327544 98b30b08 b2607883
+          91740131 9719c5b6 47b20087 ce83bcdf
+          7e87d2a3 c79de447 e8806035 1a7260a8
+          7cb454b5 c4e714e0 93f7c5d2 6586137f
+          7bc980e7 4652fcf3 0f7ed90b 167dc9aa
+          043d4d96 0c4e4109 e97e07f8 75be430c
+          c613ff8d 213b397b f34e284b caa4e306
+          f23a76bf c00b90b6 7a335872 0b9c6db4
+          2c16d044 4742d488 01623480 9203137b
+          427ca5a1 63d2e081 73cde7cf c980e70e
+          623e7716 d2a7bffa a942012d a88adb59
+          003ce343 2062c420 209d3deb 1048971f
+          3b297601 a1386e77 452547d6 aea9b012
+          81de46b1 99404db3 54746090 96f85ddb
+          93838416 0f01c7f3 cc39058f 65bf3be7
+          2119f0dc 40b2df99 f3b8293d fb5eaae2
+          7608df14 3a15c44d 18099ca7 de591b8b
+          532df20a 2075d506 d2f69da1 aa90f03a
+          5c397410 94fc7d0e 7277ed23 09d9e2a4
+          8a1d65a2 460e028f 982072a0 d0027a0a
+          e405e42f fdf6a3cc a9cfb795 01cf8525
+          63eaf377 e67ff5fd 3c056571 3b0c7891
+          c3fb822e 3ec69982 822d1564 b1a4fdb2
+          118c0595 24fd4096 2b997940 08b6b336
+          ef86aae4 73e4369b 086e89ef ed05b1f7
+          8c004ea3 046a08a1 c4f9f0c9 5df8e952
+          e3e99392 aa3b970c e0194f9d f0cdfde4
+          932fd142 e9a88adb 990102ee 6c05c1bd
+          ba392f29 b0204b05 5b2cd872 e16457f6
+          1acc3c3c 977672b1 837b27d6 d25ca203
+          c423311e 2206f722 3c18d4c4 f3d001c9
+          9bac1d93 060f9867 4a499601 cfa5c0ee
+          6c129c19 32f043c1 626f4b93 258373c5
+          74e1fe10 3d7aa8f8 839a3a59 b50a2acf
+          a440d696 3d5411d1 dcb432a8 00aad28b
+          2073fdb6 da246d22 e82009ed db03fc3b
+          34a78ae2 12b7b937 66e63d96 fdceec07
+          65c07321 c9797bf6 245366de 432c6571
+          3b4ec541 ecf8e1a0 0cf07516 c22b3864
+          a15492b8 9ddd64a3 8688a6c1 0c3d640d
+          e71f3c06 4587ff70 5661e083 84e5207a
+          dc30d006 7b839d16 4228dc14 1b815ec1
+          d73f7e9c 39fd6549 e4e7b93d e0654c7f
+          a95dc1b7 3fcd536a e9524cac 74e1037a
+          8057ab44 679d2cb6 4a181632 d66e86ea
+          8c223a4b c76eda97 13ff91be 661b98b2
+          729c9718 561ba803 03217aec 10847d8c
+          d83f8f92 f96095e0 93337fde 32e39953
+          1e32e0dd 4a57f6f4 497dcedc f9cbd082
+          78d1a493 3613805f bb38081b 7837c919
+          ab15e4ca 161e3802 05878ecb 292837a3
+          140a34ad e54648c3 a567f812 085f0061
+          20349bc1 b7431b08 ed23969e d1722f86
+          c32208e0 3b260f1d 3417878f dcda8277
+          479a46dc 1dc1989c 04494307 7ecc5754
+          8e6094d7 b5f7dc9a 82d06e03 41e3e709
+          cd26de0b 4a0f0fe4 daf2e423 19b51a8c
+          19d970fe bb55a491 25cbb9ee 18dce1fd
+          18f40c79 65c0b102 78dd9688 e659bca2
+          65d01ef5 8c8f112a 2f5e2464 e7ac828e
+          39623860 2c45951d f9b2c28b 7e63c69f
+          b89abeba 2a4da3db 597835ad 60b267cf
+          7cc09496 fd384d97 14787df1 f863c60e
+          064d6888 c8a94aba 7f706037 18e0e2f2
+          b5c83231 35a612d2 65e9a1bd 95bdf300
+          949f4a02 46a3260b 80810fb7 c98f9d30
+          92949ef1 767ab61f aeb72dfc 61c5c799
+          335f6de5 ae948e6e d70f0fff 4ed6ace9
+          adb3e7bc fb1b72db fc6ee006 d22d7bbd
+          e197e28c fff07e9d 8568a46c 0272b56a
+          69089102 66acda00 d9db0e36 54e91855
+          3c3eff26 b8779e36 c40f6e7b 761228bd
+          3cc9e510 da8302a3 d540e1fe 23c8a25e
+          cbe003a6 1154c235 e708013c 6f83dfdb
+          9e38d147 d7aa4df5 cde87243 e185a4fb
+          e1194f9d d421b0fb 9c55dd10 d8b9ade0
+          ce1ddecd c32172f8 00b1e553 cd96d568
+          a0b48640 5a8edb35 8a95579d 5d02e9bf
+          6c14a181 11691df1 81137857 4708eed2
+          8eaa5415 9caf88dc db4e29a3 87cf359e
+          3deb7eeb e9566097 920c2963 47bcc528
+          a00b4d7c 6bd86d52 7aa820f6 9e91a493
+          8750cb4b a100737e 21a4adda 487ec6c8
+          f9768d22 0a749014 fd711a0a 7e3be4ec
+          aa52537a 366a0878 4407d253 7a06e225
+          86e15cfa e359af4f ff3f19f0 1a51b266
+          4c9b8026 fa19aada 1b09a21b 113d7a10
+          e86222c5 b81dde74 9862d0ce 43faaaf5
+          602aaa94 e3768daa e18e2ec9 1b7782e1
+          623a308e fc3c1ccf 53203737 0e1d449c
+          46414fe9 1988f5b6 c52b577f 92357be6
+          6dee44fb e91e8087 26347bf6 acf8e25f
+          d62d5068 e9e2d2c5 ee5250d7 7610d8ad
+          93b34e96 58772ac8 dbb5178a e5d2b1a6
+          c13ccc81 5465265d 92ed46a3 d8061e8b
+          c5021e2d 122062c8 dd62e919 458700ae
+          b7cd9a39 7b99f1f4 49b7c9cf 730bf030
+          9c3caece 9cf9e632 34c14134 29197693
+          3ca20290 db3458b4 f46a82b4 b8742ce9
+          2c646ede 7335b219 591a5070 c3d08a73
+          d990bd69 07a9b5ad 0d2198cc 10d2b707
+          04dc9e48 4f2b2907 7a304ae8 9c3261dc
+          fba6f329 32e03584 e0893c77 df843790
+          1bdb9326 db8e948e 213709a7 3f28303b
+          bca3748c 5170602d ab4096c6 46e0cd36
+          b9e55353 831eb2a6 73771f21 1745b55d
+          55048184 18a2c70e 036d8837 b9d9a5c6
+          d0530054 279d7f3c 73da4bff 9101af01
+          24e39517 4654279d 7b85b6f6 46b8b578
+          d4d03ee0 91d80cb9 4dff281d 5bb319aa
+          d20be5d2 b15be4ca 912ec9bf 6c064b7e
+          2180d211 3cb5da40 15140031 08f470e9
+          192dac67 58703caf 64ddc605 39efce71
+          f9789e4b 035efea7 1f4797ac dbbc58a1
+          a5abdf07 768b7067 8ee03e3d c4d2b19a
+          9426cc3a 76f07728 3c7c826c 32596e91
+          d2a0c3d7 54500169 ab37a1c5 e29d5d55
+          30eb59fb d610d6a7 2b55561e b9d45108
+          7ee9afce 5866f8fb 984bc7f3 5c12f070
+          22a13933 5d953377 de128615 42a9b2ec
+          90a2e842 7d2066fc 08b186b3 36054505
+          c6d47448 5bb39de4 42c92d9f 6ebd6b5b
+          7cf42ce4 eddeefec aa4258cf 6c103eb4
+          1f78b788 a287f5cc 017aac02 3a9fffef
+          83ef992e 5e68b464 63c9011e 9e285b49
+          31240f1a 34c39c9e 3590a656 edd80dc2
+          b77f980c 5a89dc23 70a4a000 c7016f34
+          92964fd6 4ae3cdd6 c9cad250 0a8e2cbd
+          cc4d7ba0 eaec857a ac67b80c 8db09e79
+          d3c57a86 75b5eae4 9927d29f 7feafe1a
+          5d9601ef 5a5cd945 0b07579f 499ec6ea
+          809e9614 20963185 f5eb0ade c82daa6d
+          f9448224 9cc83a96 9c25a7a0 b8928263
+          426fa385 7449b657 549183a9 c6b5d544
+          4740f4a8 810ecb8f 16f7448c e7956dde
+          b12077fe 7badc005 eb6d5d0a f0f08990
+          b7f0a3f0 f4ffbdfe 059a3805 556087f0
+          cde7b618 081fd24f 2c1dabd9 2cc85ac0
+          bca939bb 0ec960e7 a2ae6d55 6a0164ae
+          df2ac6f2 6aac1ab3 19fcbbdc 09c1dd6e
+          a7aa0a43 4cd2b6fb a7be346d 69d51f87
+          3d5ccdca 7329c0b3 e4642973 de7b6f09
+          cb42384d 312aecf6 a87c75a4 7b31c9e2
+          b7d7b08e 29c84d20 be11249d 52e41414
+          9705bdbc 037f41f1 ef7f395d 5b5e2c3d
+          8b1c3110 3ca283e8 023d9684 9f3ba73e
+          3ee55d73 46ba0c78 97136b51 01240f19
+          f4b2392b 6f284bd3 0da4c390 8b193d18
+          d491e1ce 869ed85a b0d94913 4acc9b2a
+          2718bbb6 8263495b bd0dcc59 b94e426f
+          9b0d386f 4f881937 1c38ad92 aad233ac
+          c3157f9d 783275f2 c4fb64c0 bb8ce42f
+          f9ac4fe5 f1d3b338 316e478d 338b4bc7
+          427a7404 bfceb7d7 8fdba9d4 905b533a
+          26a7a0b8 be82e32e c9a5d522 0730be6c
+          aa25f4b6 8047cb66 1039a4b7 7881414f
+          3c4fc07c 1865bbf6 7e92b7e0 835632e0
+          d591bccf 3e0ecefc df1b5fa0 0952d104
+          76d8cdf1 8a0b81c8 9183c4f4 939ab89d
+          a3742c6b eb6fb265 e74eae2d 52f0d2d3
+          a990bb7d b7332159 10412fa4 4f4f08b8
+          a3055dad a4181018 c6e67ff1 d9179756
+          eeff4d2f 031e7665 0bf2d8ec 39731631
+          0cc4e109 a2c69345 ee8d42a7 82987b46
+          00abd713 f755d41a 0e6ca5e5 90ba623d
+          d864d631 f7b3f4d0 0195b57d 3f549e3e
+          5b279ec7 138b0f97 9ee9682b 3de34060
+          19e89cf6 e2f3ef5a 7273e806 3c6b6101
+          240d19f4 a225b768 344b13eb 9840bac6
+          42d488fe a06f1647 6ef4c4dd c110c5c8
+          58b705aa 338a49b1 ba2c6ea6 e03857dc
+          6227ae2d 3eb86a09 bd919bab 0cf447a0
+          3714b9bf 2c3dac67 1864906e 57fe7eec
+          a90b0fdd 77cbe379 b714f0f2 172dec5e
+          71f4f81b 1c65f976 a4e5d35d ad21a8c7
+          5df5e376 98756cff 61283874 428edbb9
+          b382a383 aa3aab04 d2d76e76 1e6400b5
+          a56798f5 8caa5b5b 41bcc92e df737041
+          fee285b7 510978f9 4b3ef5cf 7c63ce52
+          a51ab454 e5dba18d ae0bf723 9d728922
+          90ceb940 f84f0d69 9990be6e bbb82a72
+          e9987b83 1e52f082 c32709ef 45bd2ec9
+          561b440c ee0b3eb7 4551d54a 8a214c97
+          b6800b8f 3fbdac62 cfafb7cc 9f6b729a
+          46523a56 5a022963 467d2518 4cbdd8fa
+          148b4d01 7db78cfe 0ebb31d8 9d69f6e0
+          38d045d7 e95eccb1 84fff4fc 37cbc190
+          5b7a3557 56d27324 95f7d718 7595a919
+          e0d33c0e 94febe20 e0382d6e 25a55183
+          3e221c4a 8f9f44a0 77c5165f 929b233c
+          4ec10e11 a6e4335e bea3c76c 61753ab8
+          996a0cd7 a76944bb c05a5408 4943063c
+          652d2a9b c06a28ba a41044eb 2e62602f
+          f06ad312 84ba713b 8502b236 ee80f2b3
+          72e998a4 ac3cdc25 b9c20469 ab36006f
+          34915e86 8400c862 055d5438 448f1e48
+          eaa7698a e7e1504d f9e13f9f 3e3761f4
+          7d4cdd93 a1a9d604 5b5c4df6 a00fccfb
+          e4c3cee5 878fbeef 885131b4 3cbc1918
+          df36b14c e880de0c 58ac0eee 2bf4a855
+          4ce95fc7 99dcdf8e 30ac8a9e f9a0e541
+          0718830e 32267bf3 2e06144a 8608fe3b
+          b39909b8 ab2313dc bd0363b7 0023d033
+          278414a9 62efa105 85cbbe68 c938bcbe
+          a67a9ad4 c22b58f6 b96ff67b 733f57e1
+          b81d4582 d310d401 9e103b61 1421cdae
+          6df9844b c7f270e9 d81664ea 0b72e998
+          542d3d15 40ceee43 50f6f7c9 3af13c20
+          e5679123 0783674c 1055ada4 c4789e3d
+          e0dca38f 2d2bdfb9 ad49b1a0 c954cc5e
+          5a02e92f bf309fb1 dada52d5 aa9d1717
+          3866ec60 500707d5 b66a07c2 3a66279d
+          364c4515 7282b1c4 155cec92 bc092c05
+          45cea464 c27ae641 5a4929b4 74b19ee1
+          d6f0685a eeca9c35 e35d7b59 99b400cf
+          565408c9 23873e66 2faf7e98 a12c4685
+          4fee90de 9dc1ef8e f6ce7c3b 2ce8a4cf
+          dbb90f4a 8e9f97e3 76345879 48c18d79
+          e590b166 93d858a0 6ee95962 3c440eed
+          23b29e51 94b140ea 6d0ffef1 74cab8e1
+          f74a0af0 f23e9adb be6cdfe1 b9b47130
+          e07c3baf c470881c 36c069d9 11b05343
+          d5695c3a 26b38ed1 24f8602b 3a9a0cf9
+          7b0e385d 5b07e805 dfdd9d94 9ed94c74
+          cd8902c7 387f3bf0 49d1775f b59004e0
+          956fdbec 95fdc1c7 5f2bd5e0 45956587
+          5b3e7969 216ec248 60b59a7a 713b5b79
+          b9c86f2a 978e51e6 db8a4d43 3337ff0a
+          d5e72eca ac670e04 621821e0 fcc31397
+          21acd036 c1c735a2 85535901 596fce7a
+          9f3759da 5115b713 c45a595c 3aa68d8e
+          02b03876 30235e54 65aedd02 55594532
+          eb188d98 87bb2457 5948adb4 bdaaca59
+          7a863c00 55801fc4 8e1b867e 87a12a55
+          05c7f3d0 78bb20ac 78076386 db02deb9
+          b1c31ea8 38f8e764 dacaa478 e4968474
+          6f0f81dd 3ad58fdb 69d450b8 ef10141c
+          3a2ed7c9 522cb80a a3ea621e 646dd826
+          c6f26a72 d1d0c1e8 d3be1584 f7eb0676
+          0b5d73c2 9178de9f cf22cc68 d4785ea3
+          015ef18f dfb52edb b9ef23da c00e6f54
+          7d740049 37104d3d 67cb2763 5a26646c
+          d829978e c942402f 6fdf9f50 f2c75ff5
+          5c5b5c7a 1636a80f f8b68ca2 ab959403
+          f410667c 82b0a3a5 5b015ed9 e60d1ee7
+          1f7ae84b 56097e54 b56ac72d 9f344a88
+          bd672428 bcbd9cad da31eb98 c108a9cb
+          d791cc7b 396e270b d10b846f e9abb782
+          392797d4 528bee01 0f2cf204 621cac67
+          bc9dae39 41981180 b06329c2 109d5b00
+          1e5f5505 d9b35f7f 57b00b9d 8132c5c6
+          17159143 ef06cf96 cdeab77c 527090b5
+          690794a7 6491935d 1659c8d6 50a06d52
+          5c0569ab 36828089 9b6a5255 906bab8d
+          8a8098d1 8348e919 4da92a18 33107674
+          4118f236 c6129706 3cc16605 9c535371
+          f8af2759 da5c5984 6ffeb727 42489fee
+          24cda056 d49875ec 04e4ee39 22c7ed64
+          b9540171 97e49317 206fc76f 4e2e0c2c
+          b5ac671d c066a66f 4e10863c 8bb06402
+          c6149705 bcd255cb 9b976edb f3097597
+          14684db4 41de103b 6eb85841 51938282
+          dc14736e 3ec9b017 934d6505 97e53296
+          1eee92bc ed37a84c 4aa98de7 914e2076
+          3b448d1c 049e31c1 74f5cf03 47bb7c84
+          25085312 5d12f04a 37ae539f 7be8c145
+          9c1a02a8 0ac83b5a 3ec58e1f 463ada12
+          d784cc2c 023e9b8d 74cac0ac 638c4256
+          6c59ae00 782cf610 ec68afac 27399a35
+          a92ab8f4 90f3f284 d87b8683 42aba22a
+          55056308 c2924084 295f226c 69b0fcbc
+          06013c5c 0b97fde6 ac57048b d087362b
+          06dfca86 f5eb06de 1ddad476 2f661cd6
+          5deecebd 5072e2bc dcbd5896 ab2ba20a
+          a02aa318 32d66e15 1394eb74 49f6689e
+          4062c382 8db64941 a06f11ba 67bf316b
+          265f5ded 3a809739 735aaf8a 3f8eff8f
+          2a5e0a10 fbdbf9dc 160de183 fa38f964
+          b160d6b1 3367216b b35c3a56 4f84da87
+          713cce9f c9424acf f20ffe0d 85077e27
+          7ba8d651 427b2bb8 7777f0ef 90485daa
+          0ae1c3f8 f3f8d494 f1a386f3 a69bafbb
+          bb69c02b dbb4debb e0ab6f17 2bd44055
+          481e9fb6 2a1f1dc4 dd3b0a18 5c176917
+          fd0ddcfe c9565641 4ac73099 0bb52d9f
+          7008cae8 7c78633d c0bbe4e1 ebfcae60
+          a174ce1c a567e9eb 7680213d cb798981
+          63c21c4b 08bdb5c1 94959ee1 83400b4c
+          c9969d9f 95aefd25 fca6a7f8 7ada24d7
+          fc6e8db9 8dcb40ce dcdd6341 d5d1134f
+          73da8652 93bacbdf 6876c6cd bd5f10db
+          3e253c38 16fcbb74 7412f188 ac63c285
+          ef56a093 fa24a3d0 34eaf777 cd3912c4
+          4a134609 477d870f dbcb7978 1c440a9a
+          a56bd5a2 c27ffc04 fe72fb08 4bc9ea55
+          9aea13a7 fd91457c 5bd5ef87 ef3224a7
+          f5673908 6454b768 8d6fe11a 602bce3b
+          315268f1 f8c3c843 5030b575 d81a0d94
+          fd7d0a52 bef8117d 01fe669b 05bbd51c
+          91366b8c 6265bbe4 9409eae8 58e14afb
+          a851012f f5a94903 733ffd72 8b42d760
+          8cb26e01 78987c25 b44f4788 b9770ca1
+          dfabada6 d0a8a160 cf01e1c2 0f1b714c
+          8669a4ee d52e3b47 82991823 c7a266cf
+          7add77f4 b86dba56 ad6fc801 b3151741
+          c1b2a511 f99f2f7e c2743eed 194e03fa
+          eb1ca95b 031e9903 13081183 bb41d498
+          a14cbd34 27047a59 6b3741e6 a67d37db
+          56ccede6 087b01be 23874c69 f6e3ca25
+          980fa349 01af74f3 06ff7313 ee3d2c58
+          0d090de8 b6b93ce0 e1b89d47 4c08b47c
+          ea11646a 6b9cd514 98750cb9 21673ef9
+          4ab0194c b8ff99db 2adb8dcc 11764575
+          b7c52f88 5ffacdab 1e777533 5ccfc1f9
+          6fc097bf 78518f8c ffcdfc86 5543ec75
+          ec33b707 3c64d1e0 1e2ad0e2 b1fb199f
+          76ad9cb9 9d8ec6b1 c99f7d05 65c91937
+          037a6e39 47562394 26fef47d b7807bff
+          93742380 77435065 375443e6 6bafbe65
+          af6a50b0 73fdb014 2e1dd323 cd9b3012
+          384f8f7a dd8bed46 135c5cbe 162c9504
+          eca81202 762de2df 6db179e7 b35703bb
+          eb11857f 0084bf36 635ff47b 73fa2337
+          ef02551d 4408c397 0069ab36 81a5b8c4
+          d955c5ce 939871cc 3d23480c 99b69b5b
+          851a7c2f 4c7cf463 d3c5f31c 06baeb65
+          2ebb6e9a 46162977 da334ff4 2bd9b8ed
+          4345c3df caba2c05 21a12040 de6bcce8
+          01e07b67 7b1070dc ce71b230 4a2569f9
+          54f44732 4e411118 17fcfe8d f519c8e2
+          65b48909 5fb5dcb6 f3197574 cc356dc0
+          ebd9a478 537b76eb 59cae954 07cbb6ff
+          3a96e140 c75c3d80 e2ee1487 e433f005
+          86a5cc04 d6f252f0 ebd0bace 09c383d2
+          df0f547a 0d941c4f 76f49473 b93134ca
+          fbf13879 a32dde92 9591ef3b 7cc41fac
+          f2fad220 aecb3ec3 6057b66d b3a6f087
+          9fe72253 9aaafb47 1c880fec dc1a827b
+          7713c14e d45cc231 5a72f438 e4ee3e4c
+          ea6469ca b916ac68 ffa9d87d 2d376d7d
+          521d15d3 20bcc557 02c7b057 a6ff1932
+          e5d1c771 6c0b2899 663c48d2 25f9cf24
+          c8df7b48 cc06a899 17b31902 70e959f7
+          dba922f4 26561a32 b40a57af 9b55ba7e
+          75dcf506 caaf8ba6 91371a21 7dea4bd3
+          ec95d5ed 1dae2c1d 148bc88a d185fb31
+          d16386a0 89101846 c00f5676 2563cac9
+          67d27ed9 4c7e0f9d 07f45010 a2f1f376
+          288c786d c623eab8 78638d35 d6184f8d
+          20d05ba9 6f113fcf 516645c5 3c932950
+          0093b961 17537d21 0ded3995 63ff2147
+          c26e6722 470c623c 6383c91e 058aa81e
+          956a08ba 38f9f1b9 c6e424a6 d1681a11
+          a2b6ae3e 9ef4b282 a204631c 3762d51c
+          c48e1f0e 4a5f5f00 6b4dcb27 9630c9a7
+          afde08e6 a24aeae2 7656dce4 74ca23cf
+          85bff6fa b9a6fa4c 75742cb4 dcb27d26
+          a3551d12 28ca45c3 84deb66a 47976483
+          8174df11 5d5b07eb d98411a0 d0a9e862
+          3d43c865 aba81a9d fbd1dcd1 8de2d21a
+          5392e1e2 e34fbec3 a9404793 62636b22
+          62602ff0 6adda27e f7625c3a b6638fc8
+          3a465bb3 04dce4b4 45fcf761 afbcf663
+          537fb63a 26ce14f1 eaf4a790 81534553
+          8506766d 2b2fe48a 5d92b93a a72b2e3d
+          6b164725 eb19c222 a6e8a795 6f95efda
+          eed3e080 97f7e1dc 61d6d2f2 213435af
+          c4b111ff 760910da bf979397 82689d0a
+          aa925220 6beb5efa 4ac77862 58a446be
+          39e74575 4cec2df9 0ae1d367 fda56f77
+          dbeb3c6d b12b15d2 c3bd7f42 c99fc748
+          ce67ada0 bd19dcab 2b04dc4e 57e919c2
+          22c15e51 d522edf9 679ebfd6 b2b36b02
+          bc8adf7e d515feb4 e22d34e1 2c2d6708
+          7699d4fe 7a8841ae 2c835302 6ab2ddd1
+          9f6da5e5 7071c506 e0ad9495 8ee12a0a
+          1b638b9e f5da14ff f1f716dc caafd26c
+          f99a8f39 2fed76aa cad058f1 12277dcd
+          56d276ec 9fa567d1 e386838e 32d6337c
+          81517df2 ec0b25ab 57245ee3 145e658f
+          5bcc90fa f4138fd8 cbabdad2 d2e288e4
+          7ba19989 4360a70a 0912ab29 c8912206
+          d033d66c 86ea8c22 baac3b46 b478f56d
+          5bcc8d78 7dcef65b fd753409 cd6c612f
+          bef804b2 360ba922 af463a88 db8da5fd
+          b2112d48 9d2ec956 1ba802fd 494f4696
+          2ed63301 a9a547ce bcf7a7c3 3564095c
+          15f08a57 2df7c708 4a6a6505 3ab6168f
+          dc82b0bb ef029f3b dad5ef5e 8c59c7f6
+          1f8182df 4f5217b7 c365630a 6fddc166
+          3fad9eed 2adf297c e6ec0b61 cf4c7911
+          5fa0d094 0f84e379 38769cbb 731f804a
+          e54cc233 99c1ab5d 2b08efdf 9daa86a1
+          5817ab8f 9dbe37ed 99c99d6f 0af00464
+          d9e4cc7b ef499681 185a268f 2785db11
+          103ea4bf 58495197 75ec423a 64acdf2e
+          bab15425 dc91b85d 69e833cf 4cd624b6
+          30bad257 0b7d71da 77fae671 3f0934b5
+          4d62c47b 8becadbf 4155d259 27011016
+          b467c306 de0d3ead a2a98ae7 216f4b55
+          f0ed8fd3 4d29c937 0e78252b 7f0eaa3e
+          76e6095a f8297099 8ed24b43 aef9599d
+          d65927ab e080379a 2075e57a b0569ae9
+          621d6348 213b843d 35796ac4 9bef9c72
+          b5afa78e 8a86e879 f39fb3d9 219536b2
+          1b9bc94a 4acfec15 95754acf ec24193e
+          76dc0850 fbd2537a 86dbe4db 2aaa87e6
+          7ef05eaf 1b023cdc 9e3c67fe bb8fa2fd
+          1e4c8b15 838db9e8 5103401b 1de96ce8
+          89dd058e 83ec0ddb a1fc5c36 75ac63d8
+          72d2c647 ff1cfad2 b42f5cf5 3bfa0e1d
+          5110fecc e34f2160 e6198a2c 6fdc25b9
+          32ad0079 1d5b7109 413d426f 4d641844
+          8f18404f 83553446 4e095ce1 cf2b5f34
+          a59cbd7e c02b59fe a35f15b6 ee284932
+          c601f9e0 2eed20a0 6ba74be2 76a57ffc
+          0d399875 8c368a45 b48990e5 9416fbe1
+          87cfe13a 59d7d57c 16c25e9c ba591b17
+          fd499d2a 0c3a0c3d e47de51f 3806c507
+          ffa89faa 82f6b03f 65a567c4 ca2baf1e
+          943bf7ed 2ed70d78 799fccbf 9711209c
+          8689125b 3e0541e4 a8218e13 d17124aa
+          9460c1ac 636bb688 ac6334c5 edd068ed
+          16c61af9 d2b38ffb 0c1991ef ea5f5685
+          5cdbd805 0b6620c7 e42faa5c 5b46dc96
+          e9ebb681 29234bbc c400c71e 1678881c
+          31083ce3 28613dc3 561e03ca f2ddbb9e
+          a84d23bb 16c0335f 38af34a6 664d6228
+          48bbc0e5 389c4629 96e7787b 395b3e21
+          37165fda e0b81d61 1da32b05 85c12d9f
+          3c3bb59f 1f35f7a3 ad782edc 417c060d
+          ad8c78e9 d927790b 63a0c9ca c3e96296
+          322361c8 e3cd26a8 5d2f5b0d ebd948e0
+          b474949e 31c8c835 a6e78d28 fef98798
+          6b06bc9c f7dfee6b 2928694f 05e0217c
+          8b1cd21b 3c9ac73b 4bc7b0aa a81490bb
+          e337283d 71813a57 16275d2b 037c0ec4
+          7ef6c59b eee5df71 103df7a3 c31e9d6f
+          9f6d3302 5516398e e7959d49 879c6d7b
+          d0e2299c 63477b5a df2c96b0 9e51517a
+          862d5e9b dd2bf7a3 b9f75e1b e02153b0
+          6cfb9607 580a6a2a 48e9d81d 2d20a46f
+          cffaa563 c82da83a 9302d9db 1ca56374
+          b9b260b3 4279f063 9326eb3b dc6174c7
+          ef1ffbe9 e2f9aa40 bfdd3435 18c07b14
+          efd59c1d fba1e2c4 995a426f 22160b84
+          f4ee0601 1d5b5091 aa820d35 535ae6ff
+          1993cea8 ae0a7825 ebd78499 b28b06b3
+          12e720c3 e537da50 1f881e3b ccd155f0
+          1fa5633f af05bbc5 0e5475fd c3292808
+          e2c29e7c ec95c8b7 de3fedae c3d077e8
+          680d9efc e864b4c6 25549d54 2c2efde3
+          2175d546 b0e12ec9 4a47aa0a 8e3f230b
+          267acc30 d0a13d2f f5d2330c 78e6c2b2
+          9615bb77 def5ef80 270850f1 ebcefe60
+          b5f94ad9 aa210c48 0a0e62c7 0e055580
+          bf336ec7 8857fb19 6b374375 5629758d
+          01b07baf 0e0bf939 fcd5199f bbf538d0
+          3e8e9cfd de39afee 9d5ea1ce b5457bd6
+          90530ae9 6b36d702 9d68b68b a56731e3
+          8601835b 9b49b9f4 0c0d5bc1 005bf8dd
+          57a3ff15 f0f04629 59fbcb28 56e2568d
+          dd82ac98 3e9dc1bb 5debfa2d 9fd42a28
+          d87b080a 8ed0573a e69897d4 84af963e
+          af0a8f68 94eec54d 2dd11f2c 58aa0e0d
+          5e451bef 038e3917 fe7e1a0a f61fa95f
+          85613283 77db5610 767767a2 03d29e04
+          34dcf3a9 838c6793 955704bc d275ab7d
+          ccf9655d 1995a495 1a3ce342 206c601f
+          a7658745 a502c3c5 0cc8d8b0 83bed231
+          20443cb6 d0c7273d ee7577ff 3c29801d
+          1e83c79d 9d21e1db 6f9e456b 9e46d562
+          3a08bdf1 5e36a466 3853551c 961edefb
+          9e31c192 063de2d6 1695c795 efd8dae6
+          b28027f0 3c54ecdd dd5db058 03a5aaec
+          588f5925 0bd12307 02a7d739 4bc79089
+          8ffb69a5 ada2b074 0cc4b89d 57cf2ef3
+          623ffb7c 1ba39496 1fefd5ab 4f4ec894
+          479f4380 ced3b4a6 780fe3bd 8cd3aa30
+          3503dee3 e2c96627 8c7b9143 fba1839d
+          0141c217 9368c8aa e21fbee9 2158ad97
+          013cf4c3 92953f77 e138e9da 36f8862a
+          a0636bf0 6a9958bf 9a022979 deaffba1
+          3c259bbe 141484f9 aa00df83 51efcd7b
+          439a27bd 12225f9f b34e15e4 f7194d2d
+          d06b5cdb 0ab4a773 76ee75d6 da623123
+          d7b64d4b f0ebd042 da561e42 324b6e76
+          57a64e87 68169bfe 847e51a9 0256abb9
+          139cd577 927a30b1 b1d24329 84f6e981
+          06cc0b8e d259016d 04c19495 23e4ec3e
+          28203358 90eaf8af f080cd02 15c18f4f
+          9eec7957 5753cd5e 90daa30c 0e86841f
+          7e9a8e94 fba423d5 8a9a3566 5520e4a2
+          bd6d48cb 4413a1ac ddf70cc3 08a1bdbb
+          099c5a84 00498e1f dfcd58ec 779ad353
+          55b53857 837cc5ab 57789ab2 8ada8044
+          9b7ce293 ccfff6d6 a08b0c43 d6acad9e
+          dd9bbd63 2f58ca4d 842c85b2 b81d843e
+          fad02b11 33de3c25 48d8b7c1 63f3ec75
+          7765c8a3 0f4db19b c0425384 9690dd54
+          5b217bfb 9e7a1751 d8a3f388 8f01dfdb
+          9a49d6ca c31528a6 dc925084 6dcdeab9
+          b498be0c 997eedec 264ba014 5b96e375
+          e6b42c04 75bd53ec 8a52ebee 28c0989e
+          0525c7cf 10be00ca 5c594619 e0b922f2
+          9db94ba4 16b7bb9c 60c2e6b8 cfbf3ae8
+          dda7fb1c 9b91aeb5 c639b525 27cf42d5
+          f954a859 6b42eecb 3110dcbd 1339e825
+          1ccad370 9e9e6d6a e83e092f 2dbeb0b0
+          e5e7b540 7fe4408a bcb25660 bc9bc530
+          fae84846 b0d91cd9 76e8ef58 8ec93ff4
+          0763abb2 320c659c b2f8e6b2 d9cfab9e
+          53060442 cda127e5 a726a813 39fb9df7
+          5481defb 30e00345 dcb6bc99 67f2f71e
+          66a036db 1418c162 633ce363 198fa810
+          0693aa4b 71ecf8ae a6f8a71f 5af266b3
+          939716b3 98177eb5 b41927e1 fcbb808e
+          1d44329e 1ab39ee3 c05a5c02 a52793a9
+          4b30466e 1d1ffcd0 7d4f78f5 ba3b1728
+          13cfaedd 2d214f3d 3505017e 1955561e
+          dae3a567 ce822927 cf798181 635a7a2d
+          f8b66b25 d9c60218 d94dc949 89351919
+          a24bcb29 b0a9db46 8a03e6d1 3835815e
+          e093184f 8a446b05 2d7af9d9 f384449b
+          2aea49e4 cef9f6ef 392f7ed9 f75b1805
+          6d1c93a2 844ffddf 19bffe3d 5fb353e4
+          da8ab13c 1b14fd75 a2fe8dad cd0e7ead
+          1241a157 4a364585 d5a85b30 1a0d5b0b
+          78b69222 9560b535 97622817 67d97bc5
+          c780c2cf 0769bb98 8a458689 dcf89213
+          67249d87 7499b81d 2803bc0f 87cf9af3
+          06b03415 09ff43f9 d51a4073 b044e1ad
+          5b4b53aa 0a06bdb2 d3678137 189c6e3e
+          b27c34a1 41e01119 22c91a5b 7c71612e
+          2c8f28db b4debf16 f08a7ef8 d6df9453
+          1c28c576 50785d7d 5a24d4ff a182030b
+          7267abd2 3209ed1d 1d684752 502a439f
+          7d768a67 b71e06a0 5cd01cd8 137f59f3
+          8c60812c 5a5292f1 5e3764e7 83212ba7
+          9e5b8b0f 00cff858 69de5c20 84b3579b
+          f4c65327 436a010f 49147a24 d7cc1d17
+          482b3dd4 e081392a ea764065 39a84ecf
+          044ba981 1a771677 bc0df9bf 7ba685be
+          38ed38c8 42c4ab4f ffccc007 26bc400d
+          7135e616 b608507e f682b3f2 8258793c
+          78358f95 725a9686 d56aa36b 018fd590
+          7f919c7d 87dd154d 7000a87c bd9d6564
+          64c00c94 a75ca4c6 9d151b7a 7aae8a5d
+          f4e522b4 f032d2d5 31ff633f fb62259a
+          9b25b4f4 cec31e4f c5853442 dc5debd6
+          22cb4017 1c046a3f 4f495e5e e051da2b
+          2ac2c925 8d60b1e0 869fd152 0ce9600b
+          4f17160c 8c4e27b6 ca01f18e 5e3099a1
+          3a2b17a8 60b8e289 d79291f0 ddf26758
+          0f4f9ad8 1eaecde3 f1f00434 3753d11c
+          25d1e0da 628fc694 5f88bc9b 3267eb28
+          de0e0a2f 0fd004fa 4b12f0b0 2b5fb074
+          49248e5d 12c0ab3a b04f9209 c7784cfa
+          c830a867 ca711c59 6c735109 15eeaccd
+          0cf6ff6f ef6c63a4 baca387e ce9d3bef
+          b30bcbc2 be411ba0 2905faa5 89696c5a
+          ad556b05 d21605a5 d128b5f5 ad44b096
+          6a83a2c6 7ef08b4a 89c536a6 2968c18a
+          4d63b592 5008b68d 89524a8c 5882158a
+          490564d9 65778181 d9d979bb f7fa3c77
+          6781d959 6060e7ce ce7de6ff 4b6ec8ce
+          923b7bff e73cfffb 3ce79e7b ced4654b
+          564cba67 61c34d41 a914d226 491a7d83
+          b4129fe7 719fcf9e 49a9eca9 3317f6be
+          e064201c 56514a0e a4ce4076 0a857635
+          f26a998e 44da44de bda93d63 1dedaa64
+          b543baab 65fa0654 2e3924de f078da45
+          eba28faf bf61f34b db616b97 87347a93
+          b4fa4923 4c55e199 0be9e3a3 ee7f647a
+          6eac487d 78af758b 1bfec51f c5191e7b
+          9c198fa8 308fdfd9 a5b72d36 3cf1b51d
+          9526e694 e6b7a7ff e0c91f69 b3511e45
+          8f231e48 23d2eac7 a4d96ed5 0053555c
+          c3337449 c0443bda 94963b5b a9dd2dfa
+          b4e6992a f20c8fc7 6382cd4d 2a108994
+          96b49a97 c0ee11bf 41513ea7 d2d77d77
+          cdcac46d 770cc2ce 2a83b41a 22cd1e25
+          edc46f5d 9de93f35 fc206f64 209b9282
+          50734299 b190c8e5 dfc9e9a6 19e148c8
+          702c8bae d0753f81 195e4c19 e1d08529
+          29c5cd7a 38c3135f ca2ebc7b 7ddb8a55
+          7f878d5d 1dac1969 f773c9a5 2d6771f9
+          732965a5 062f6479 3cbe150e ab60222e
+          2e1970df 32393b34 39b5f7ad a0d1b779
+          6328db7b 3aae8555 3d9cd4b9 8dc7dbd5
+          8d647864 78f65046 15d24372 5377be69
+          87cd9333 7ef8e4d3 463c0107 bb4a5833
+          d2ee67a4 618fd4d2 76f835b3 b47b9ccf
+          f02846cc 48589914 33e2323c 5efd3939
+          183db36b 47dcb092 c98463d9 a6b8d7ca
+          78e7a258 b4749c82 1ad76d68 363ca153
+          5278126d eb7d0b9f 4bdc76fb 49d8d7b5
+          96b6b7f7 93869bc4 4e48e609 c8990c55
+          021975fe cecf4941 28e4c68c d01dcd4c
+          23126936 5420d0c4 3f886b53 6ad4001b
+          dec5e939 991f37b2 458627f5 6994eda8
+          4ccbfd9f fe2d6c6b 7c90865b 594ba919
+          9e9575e8 c85d580a d52d8982 c5316f91
+          4dca7332 e21cf622 0d8f1bd2 74df2a70
+          4a5cd0ca 66e92888 5cf3965f 1f8b74b5
+          ee69b977 f1bf6159 e334bc7b 171f202d
+          77db4257 03e62c8e 2b9dd240 70dc716f
+          a1eb41f3 9b644da2 0dcf8884 47ddadb4
+          7b57b30b 4a6449cb 9d3871eb adaf065a
+          a6c0b1c6 9b0e9086 a4e536c9 1b561786
+          463d99a1 2c8fab22 a1c33dc6 48861755
+          123d9d4b 5a7739eb d2fc3c3f 2877a110
+          2ac1f22d 4b97ed81 5d5529cb 5bba6c37
+          692a765f 2f2b932d 377a7ec8 27f795cb
+          281b5e44 e4380567 783c2565 d478843b
+          502bb444 3113b1e3 c18ecefd b0aa2ad5
+          401d9def 90a6ff93 9ae5f1f0 cee80ccf
+          08895e14 36cca56c 4c553e4c e9f57066
+          55cf6f84 788aa133 3a8df7d5 35547a7e
+          1e6b8acd 99feeee4 4f2c488f 770732af
+          7730f3cb f949cb6c 64d6f4fd e9038767
+          0522356d e39af423 6b285bfa 25bcd915
+          2709da17 7fffb57c 47d8e087 d122bd9c
+          333cb3fc 65593b9b 937ab9bc c3fc3ec7
+          c18228d5 344ed2f4 80d40acf 2e14ca93
+          846050ec 942d5e22 8167a989 5d204d07
+          caf79fb3 f305999d d7517ada 835f3e08
+          9baa2ea4 e961d6b6 310ccf71 0d4fac1f
+          144bdaa0 bafa2456 d7e6ef1b 5fa2cb86
+          a7cb4abf 7cadaea1 e61a456f 9adbafab
+          787bd61e dfeafd70 7ed2b4f7 0aede9db
+          7ee4e4dc bd194bce 6f04027e 8c854acf
+          1fe48716 22a7e0ba 1b6f8eb1 efa49d17
+          bbe459c6 c9e5f076 45d54dc1 d554e49b
+          b5b6658d 51151992 9fd21a62 0d6ff8ea
+          0c35baa6 156c781c 94495854 d5494a35
+          3ca76095 55467a24 c373845a 8264c3d3
+          63ac5b6f 17c42e76 06c3f3ce f044ce65
+          726cbba2 98916678 7257871c 6b0c47ee
+          534c9e54 95823f55 9dc1a2b6 8d617854
+          d20adeeb c564c3d3 8dd47bc7 6a642158
+          cab60b0a 5417dbe6 924066a7 b19d46cb
+          f074c36d 3fef5802 fb2e8fbd 044dc788
+          276c05aa 5b03c513 16696b4b 1cd32a9f
+          b3e928e9 5bf919e8 d2026ed4 19a5e2f3
+          66db533e b5048657 655853d6 d696368a
+          a7d598c3 3b1a8627 b0a495d8 a63c85dc
+          c0fdabfa ba1aa58b c88acef0 14323ce0
+          9fb21640 5b00c303 0000181e 00008607
+          0000303c 000080e1 0100000c 0f000060
+          78000000 c3030000 181e0000 c0f00000
+          a03246d6 c213b94d a333f609 456ed3e8
+          fea24a6b fd619bc6 2bea2e62 9bc6b1be
+          c4f1cfdf 7f4ddf61 e0354100 4023c05e
+          67485e1b 41a38d01 40cc5c74 6de6355e
+          a7f68bf6 7ae2aea1 e61a557b 2d336cd3
+          58517bfa ba1f691f c57235ce 8f871600
+          80860186 070080e1 0100000c 0f000060
+          78000000 c3030000 181e0000 d41253f4
+          c545234a c5624ae5 f3c31f84 c3d27756
+          07a062dc 5888537c 9881e10f 82c1e198
+          81e1f9ad 2587ff39 beeb2fca 4cc495b2
+          8bfb5307 022a3b70 4a190174 76d0e0a5
+          1dc500c7 c2f197ff a4946515 3f345421
+          3528fa75 0bd1195e f7eb7bcb 5e2f36c2
+          d49e303c d0e8d91d 1bdea9a4 3afa8737
+          ca928580 e0244fb4 e10522e8 d8005cba
+          a4a51889 3658668b 660700c0 f0000000
+          86070000 303c0000 80e10100 000c0f00
+          00607800 0000c303 00806b46 f4368d42
+          af01db34 4eccf91d f453ffc7 32b66904
+          003404e2 b7690400 80111a62 9b46c1e7
+          2ffb0e6c d3e8e9f9 35faa9ff af010f2d
+          00000d03 0c0f0000 c3030000 181e0000
+          c0f00000 00860700 00303c00 0080e101
+          00000c0f 00006078 000000c3 03000018
+          1e000086 07000092 0dcf860c 22b02001
+          b40597c5 66c3cb42 0711e421 81672046
+          84c4081b 5e1a3a88 e01c24f0 8c142410
+          c1201bde 69e82082 3e48e019 03904046
+          3b1a4e2e 77d2a9cd 861bc043 ec5cf604
+          54f04cdb 1ea8e06f d8e3c8eb 7a8df08d
+          730e0522 c19403cb f37363aa e8bc9b0f
+          40096f60 6d111e3e 8e0f6a3c f638f63a
+          a375e903 3da1a993 8ea8821b 37387c78
+          d894844c 5bfef061 de9e1047 f50fd2f6
+          a08d7ee6 df83bc8d 3cee2879 dd49c3ce
+          660b74bc a56ab399 07f068fc ce4aa5f6
+          43066f20 6ddfa17f 4e4209df a2c9e376
+          b3d71946 34aa5a3f f3c09b56 01aaf872
+          7c29ab74 7466d79e 4977df83 80f408d2
+          b69f34de cb5a430d 1fdeb0c8 dbc8e35e
+          67af33b5 69aaa63b ef7aedc4 2f9f3f41
+          c95f6785 4d8a6d1a eb4423ae 676337cf
+          7f29d4de 51fd9363 9b4617d6 96347e31
+          fbdfeefb c66857f4 d37abe06 c7fd6d0f
+          79dc4ef6 3af7d532 72bf334d b7ccdb6c
+          637aa5cf d23b8e46 f370d713 6bb7430c
+          6f218d5f 63adf15e 92ef2a20 45deb685
+          3d8e7f76 0d8f9daf f33b6b9e b51dd58f
+          092a7e19 9550daa2 c66cf9d8 877fda7c
+          e75d4908 e22da4f1 59d27a1d 6bceda43
+          111f405e c69e46de b6813dee bce13153
+          3fb7fc68 e7d71ffa 5e21a3f0 f8c20f77
+          ae214aee a6b7ed9c bde9c517 a0466d20
+          ad7f459a ef62ed41 dd27048a bd8c3c6d
+          0d79dbb1 918f4b56 4be97c62 edf39199
+          d337d869 985e5ddf b82ca5e9 ce7568c6
+          daef7f35 d4d99983 22b581b5 26cd1f22
+          eddfe336 8022f56b 76ec61e4 65cf90a7
+          6dbcf857 25861799 7d83337f e79fbf1d
+          9a39e317 f9b4c2fb 17f5d890 4354cae6
+          d43f666f 587f7ffb 8a55c720 4a6d21cd
+          8fb3f6d4 06fbb82d 607bf557 c6b27791
+          873d4d5e b69a3c4d 5dd2f05c d39b3337
+          377fe7ae 555d2b1e f90aa584 3d6efa0e
+          e39be0fa 959a20ef 36a413ff c02d2fcc
+          fbe3ef17 75acfcd6 7b106662 20ed0f51
+          1b2ca4b6 d8c86de2 604a573d 543dee30
+          0f79d609 f2ae47c8 c31e652f 2bcb199c
+          cbbc53d6 bfe5d7b3 ba9f5af7 b5c17f1e
+          f802fd38 c3080e9b 9f363d2b 799dd27c
+          c6d3efa8 ebf38f04 111b1de9 9d0eb6b7
+          ed68fdfc 83cf743d befa8d60 7b47f169
+          bb471750 ec135e4d 1bf1fdf9 8bdae77b
+          7b54f7ba a73edab7 e9b995b9 81e402ca
+          1e623a58 6c7cb331 fae984c5 b2538c11
+          ae7a2846 cc49b163 e159b3b7 76ad7efc
+          d9a95ffc d2914b16 49ce155e a275ac82
+          eaffcd96 96d4db7b 179c7ae5 e50f5143
+          7e24d73d 709deda8 84aaf28a c9fa126a
+          79f11df5 7c7e3ac7 50a8bdb9 4fa9e0be
+          e84d73ff daf1d863 db277f72 d1bb3c71
+          1286547f e7cf1e3b aa067eb7 755e72c7
+          b685e97f 1dbc83ac f083b9de b32df4bf
+          6292fbe9 04c6b26d 68950a75 b51e21d3
+          fbdb9425 9fddddb1 ea9bdb22 736e3ca3
+          0397bfd3 5cd1f02e a670ee1c 3772e8f4
+          abafb466 df3f3a87 ccaf8d3e e6832391
+          ef6de638 3bd2f93f 467bd45b 47bea38e
+          cecf33bb 38f5e605 3c07a877 0cd06de4
+          fd69cb1f 3e623435 a703718a 196d9405
+          1b0c69e2 cf5ff61d b6adac74 5ad9e7ce
+          c6fa366f ba9e5a76 26453ec7 c7143a42
+          c518317c da4f2732 960bc518 e101b67e
+          32b9def0 acebffd3 b278491f 7d4dce6c
+          6aaaf844 ff07311d d78eb499 ffcd0000
+          00004945 4e44ae42 6082 
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="1024"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000334 00000334 08060000 006d7267
+          77000019 34494441 5478daed dd7b8c5c
+          d561c0e1 73e7b1e3 b159e3c7 c660b00d
+          86865684 261017d1 4a0db48a a23651a0
+          28097688 2d120386 9a504214 444b1154
+          a5a28e50 2b522940 08d86ea9 ed041339
+          d04a4591 daaa2454 150db409 e2d10266
+          b10d7530 7ebf66e7 797bee7a d7d9b5cd
+          c35e7b77 c6fe3ee9 ecce9ddd bd337b66
+          feb83f9d 993b499a a6010000 a0131546
+          eb86de79 64d9f857 16dd7061 5739fc7a
+          dc3c2b8e 2971f4c4 31338e89 03a36be0
+          d78b1e1a 0000683b 8d38fae2 a8c491c4
+          b13d8e75 71bc15c7 a681ef2f d62ae1f9
+          0fdff7ad edd3aefd c3637e87 9263b142
+          f3d692bb c2fabbef 99512c87 4fc6cd4b
+          e3383fad d7a7a7d5 5a2e5e1e 1c616012
+          865e0600 003a4716 13ad81ef e990ed56
+          52eada98 148bcfc4 cb4fd52b e1c959b7
+          dfbae1f4 dbee6cdf a0597fdb 2d61e303
+          4b3f5a28 87ab5a7d 95cb5a95 eaa949d2
+          bf0294ad b6e4fb83 45b20000 c089923a
+          596834e3 68c4e4a8 e7caa5b7 72e3caff
+          d0a88487 a72fbee6 d5594bfe aa3d8266
+          ddd76f0c 6faf583d 3769ecbc 3946ccb9
+          b159c6c5 70298917 00006058 e0a4a116
+          eba312e3 e6e5b430 f15ba72c 98bbfa8c
+          7bbf3d36 41d37bc3 b561ebe3 4f2e482b
+          5bbed1dc 5b3d2bc9 859362c4 e43c5200
+          00c0fbc4 4d2b6d85 ddf9f1a5 d792f2d4
+          fba65cfe e965b3ef 7f78f482 a677f135
+          1fdfb47c e53da155 9d93e4c3 24ab3100
+          00c01184 4d489b61 476e42e9 d9fcc453
+          ff64d2a7 3ef1ec59 0ffffdb1 0b9ab557
+          2f08bb7e fcccedf5 2d1b16b5 f65667c6
+          98b12203 00008c34 6c5a693d 6c08f9ae
+          877ae65f 71f7d9cb 561cfda0 c96266f3
+          cad50fa6 8dfa95b9 aed06d55 06000038
+          aa5dd30c 3b4328ae e8993ff7 e61835f5
+          a316346b 17ce9fbc 79d5634b 43a87f26
+          c98792a9 0600008e 51d45462 d4fca8e7
+          4b575c7d f6f295db 461c3431 66c296ef
+          3dba264d 9b97c598 c99b6200 00e01847
+          4d3349f2 ff38f5ca 79f362d4 d4deeb77
+          73ef1b33 abbebf3c c6cca562 06000018
+          0d597bc4 06f96c6c 917bb326 39a2a059
+          7bd517c3 e655ab6e 4b436b5e dc61c1b4
+          020000a3 183585d8 220bb226 c9dae4b0
+          8366d7d3 4f9d9f36 c3757147 65d30900
+          008c41d4 4ccc9a24 6b93c30a 9ad7177d
+          39d4b7ed f866ae18 66994600 0060ac64
+          4d92b549 6c9443b6 cb214f0a f0dcf4c9
+          f3ea9bb7 2f8b7f3c de140200 0063a955
+          0f7b8b3d 93ae9bb3 71dbca83 82e7c02b
+          7a6fbc3e b4faaa7f 9c2b8471 a60e0000
+          186b599b c446b929 b6ca41ef ed3f6885
+          e6a793c7 cd6deea9 fe9da001 0000da45
+          ab11f6e4 2794165e b8adefb1 61b13374
+          63dd2d5f 0b4948be 91cb872e 53060000
+          b48bd828 e5d82a5f cd9a65a8 612b34cf
+          94923393 343c1f0a a1db9401 00006da5
+          11b6a749 98735135 7d7df0aa 61af412b
+          f44cb9b6 b965eb88 56679224 de4e2584
+          666abe01 00807df2 b1130ae5 10d29174
+          422e940b 53a72c8c 97ee3828 68defc8b
+          3b436bcf 9ecf8764 642f376b d44298f2
+          b15f0913 cf3d2784 6acd2307 000027ba
+          5257d8f9 d22b61eb 0baf857c 7104fb89
+          ad129be5 0bb15dee 9871c75d fbae1a7c
+          c9d97f24 c9ac7c29 bc14ab67 c248ee6b
+          b512c2af 7ee93361 dadc3f08 61e72e0f
+          1e00009c e8267687 4dab9f08 ffbbea9f
+          42a93cc2 7db5c2ee 66359cfb 5b69ba21
+          dbdcbf42 53ec99f0 c9d6ee3d f991ded7
+          248e66b6 3293c5cc aedd1e3c 0000a0bf
+          1192a3b1 a32414b2 768997fe 36dbfce5
+          59ce92e4 77e2d7bc a9060000 da58b628
+          f37b831b fd41f3f6 77ee0b69 adf6dbe1
+          80930400 0000b499 7c48920b 8705cd6b
+          8b6f9c94 f6d54ecd 3e84c6fc 0000006d
+          2c496bf5 e99b963d 347e7fd0 749d5cfa
+          b598325e 6e060000 b479ce84 90f655f3
+          af5e73dd 79fb8326 3acbcc00 00009d12
+          355d2797 3e323468 ce09434f 10000000
+          d0beb276 99955d18 3c09c08c 7d9d33da
+          7723de8f aea28703 00003a55 ad1e42ab
+          35dab79a 0c34ccfe a0e919f5 a0893153
+          dfb92bec 5ef77f21 f1ee1d00 00e83869
+          338493ce 382d1427 768f76d4 64ed72da
+          d8064da9 2bec7a63 7df8ef07 1e0d25cf
+          050000e8 38d5382e 583c2f4c b9f08210
+          2a7da31d 34c35668 4e1ff5a0 49d3902b
+          14fa63a6 abecc900 00001da7 12fa8fe9
+          b363fb51 96b54b77 7661f044 005d1e0d
+          0000a083 1486064d 31041faa 09000074
+          8cfcd0a0 71ca6600 00a05364 8b319386
+          864cb739 0100003a c8b0979c a5e60300
+          00e820e9 d0a00100 00e83882 06000010
+          34000000 82060000 40d00000 00820600
+          0040d000 0000081a 00000041 03000008
+          1a000000 41030000 20680000 801342e1
+          b8fa6f92 109a7b43 483dae00 0070a8c3
+          e5901f1f 8eab03e6 e32a689a 9510cefb
+          c9539774 cd98b9c1 d3150000 86abbdb9
+          61e60b17 5ff2547e 9ca0694b 692ccdd2
+          99b37b05 0d00001c 2c29141a e971f672
+          a6e3ee3d 3469a351 f0540500 8013e358
+          d9490100 00004103 00002068 00000004
+          0d000020 68000000 040d0000 80a00100
+          00103400 0080a001 00001034 00000082
+          06000010 34000000 82060000 40d00000
+          00081a00 0040d000 0000081a 00000041
+          03000008 1a000000 41030000 20680000
+          00040d00 00206800 0000040d 000080a0
+          01000004 0d000080 a0010000 10340000
+          00820600 00103400 00008206 000040d0
+          00000082 06000040 d0000000 081a0000
+          00410300 00081a00 00004103 00002068
+          00000041 03000020 68000000 040d0000
+          80a00100 00040d00 0080a001 00001034
+          000080a0 01000010 34000000 82060000
+          40d00000 00820600 0040d000 0000081a
+          00000041 03000008 1a000000 41030000
+          20680000 00410300 00206800 0000040d
+          000080a0 01000004 0d000080 a0010000
+          10340000 80a00100 00103400 00008206
+          000040d0 00000082 06000040 d0000000
+          081a0000 40d00000 00081a00 00004103
+          00002068 00000041 03000020 68000000
+          040d0000 20680000 00040d00 0080a001
+          00001034 000080a0 01000010 34000000
+          82060000 10340000 00820600 0040d000
+          0000081a 000040d0 00000008 1a000000
+          41030000 081a0000 00410300 00206800
+          0000040d 00002068 00000004 0d000080
+          a0010000 040d0000 80a00100 00103400
+          00008206 00001034 00000082 06000040
+          d0000000 081a0000 40d00000 00081a00
+          00004103 0000081a 00000041 03000020
+          68000000 040d0000 20680000 00040d00
+          0080a001 0000040d 000080a0 01000010
+          34000000 82060000 10340000 00820600
+          0040d000 00008206 000040d0 00000008
+          1a000000 41030000 081a0000 00410300
+          00206800 00004103 00002068 00000004
+          0d000080 a0010000 040d0000 80a00100
+          00103400 0080a001 00001034 00000082
+          06000040 d0000000 82060000 40d00000
+          00081a00 0040d000 0000081a 00000041
+          03000020 68000000 41030000 20680000
+          00040d00 00206800 0000040d 000080a0
+          01000010 34000080 a0010000 10340000
+          00820600 0040d000 00008206 000040d0
+          00000008 1a000040 d0000000 081a0000
+          00410300 00206800 00004103 00002068
+          00000004 0d000020 68000000 040d0000
+          80a00100 00103400 0080a001 00001034
+          00000082 06000010 34000000 82060000
+          40d00000 00081a00 0040d000 0000081a
+          00000041 03000008 1a000000 41030000
+          20680000 00040d00 00206800 0000040d
+          000080a0 01000004 0d000080 a0010000
+          10340000 00820600 00103400 00008206
+          000040d0 00000082 06000040 d0000000
+          081a0000 00410300 00081a00 00004103
+          00002068 00000041 630a0000 00410300
+          00206800 0000040d 00002068 00000004
+          0d000080 a0010000 10340000 80a00100
+          00103400 00008206 00001034 00000082
+          06000040 d0000000 081a0000 40d00000
+          00081a00 00004103 0000081a 00000041
+          03000020 68000000 040d0000 20680000
+          00040d00 0080a001 0000040d 000080a0
+          01000010 34000000 82060000 10340000
+          00820600 0040d000 00008206 000040d0
+          00000008 1a000000 41030000 081a0000
+          00410300 00206800 00004103 00002068
+          00000004 0d000080 a0010000 040d0000
+          80a00100 00103400 0080a001 00001034
+          00000082 06000040 d0000000 82060000
+          40d00000 00081a00 00004103 0000081a
+          00000041 03000020 68000000 41030000
+          20680000 00040d00 0080a001 0000040d
+          000080a0 01000010 34000080 a0010000
+          10340000 00820600 0040d000 00008206
+          000040d0 00000008 1a000040 d0000000
+          081a0000 00410300 00206800 00004103
+          00002068 00000004 0d000020 68000000
+          040d0000 80a00100 00103400 0080a001
+          00001034 00000082 06000010 34000000
+          82060000 40d00000 00081a00 0040d000
+          0000081a 00000041 03000008 1a000000
+          41030000 20680000 00040d00 00206800
+          0000040d 000080a0 01000004 0d000080
+          a0010000 10340000 00820600 00103400
+          00008206 000040d0 00000008 1a000040
+          d0000000 081a0000 00410300 00081a00
+          00004103 00002068 00000004 0d000020
+          68000000 040d0000 80a00100 00040d00
+          0080a001 00001034 00000082 06000010
+          34000000 82060000 40d00000 00820600
+          0040d000 0000081a 00000041 03000008
+          1a000000 41030000 20680000 00410300
+          00206800 0000040d 000080a0 01000004
+          0d000080 a0010000 10340000 80a00100
+          00103400 00008206 000040d0 00000082
+          06000040 d0000000 081a0000 40d00000
+          00081a00 00004103 00002068 00000041
+          03000020 68000000 040d0000 20680000
+          00040d00 0080a001 00001034 000080a0
+          01000010 34000000 82060000 40d00000
+          00820600 0040d000 0000081a 000040d0
+          00000008 1a000000 41030000 20680000
+          00410300 00206800 0000040d 00002068
+          00000004 0d000080 a0010000 10340000
+          80a00100 00103400 00008206 00001034
+          00000082 06000040 d0000000 081a0000
+          40d00000 00081a00 00004103 0000081a
+          00000041 03000020 68000000 040d0000
+          20680000 00040d00 0080a001 0000040d
+          000080a0 01000010 34000000 82060000
+          10340000 00820600 0040d000 00008206
+          000040d0 00000008 1a000000 41030000
+          081a0000 00410300 00206800 00004103
+          00002068 00000004 0d000080 a0010000
+          040d0000 80a00100 00103400 00008206
+          00001034 00000082 06000040 d0000000
+          82060000 40d00000 00081a00 00004103
+          0000081a 00000041 03000020 68000000
+          41030000 20680000 00040d00 0080a001
+          0000040d 000080a0 01000010 34000080
+          a0010000 10340000 00820600 0040d000
+          00008206 000040d0 00000008 1a000040
+          d0000000 081a0000 00410300 00206800
+          00004103 00002068 00000004 0d000020
+          68000000 040d0000 80a00100 00103400
+          0080a001 00001034 00000082 06000010
+          34000000 82060000 40d00000 00081a00
+          0040d000 0000081a 00000041 03000008
+          1a530000 00081a00 00004103 00002068
+          00000041 03000020 68000000 040d0000
+          80a00100 00040d00 0080a001 00001034
+          000080a0 01000010 34000000 82060000
+          40d00000 00820600 0040d000 0000081a
+          000040d0 00000008 1a000000 41030000
+          20680000 00410300 00206800 0000040d
+          00002068 00000004 0d000080 a0010000
+          10340000 80a00100 00103400 00008206
+          00001034 00000082 06000040 d0000000
+          081a0000 40d00000 00081a00 00004103
+          0000081a 00000041 03000020 68000000
+          040d0000 20680000 00040d00 0080a001
+          0000040d 000080a0 01000010 34000000
+          82060000 10340000 00820600 0040d000
+          0000081a 000040d0 00000008 1a000000
+          41030000 081a0000 00410300 00206800
+          0000040d 00002068 00000004 0d000080
+          a0010000 040d0000 80a00100 00103400
+          00008206 00001034 00000082 06000040
+          d0000000 82060000 40d00000 00081a00
+          00004103 0000081a 00000041 03000020
+          68000000 41030000 20680000 00040d00
+          0080a001 0000040d 000080a0 01000010
+          34000080 a0010000 10340000 00820600
+          0040d000 00008206 000040d0 00000008
+          1a000040 d0000000 081a0000 00410300
+          00206800 00004103 00002068 00000004
+          0d000020 68000000 040d0000 80a00100
+          00103400 0080a001 00001034 00000082
+          06000040 d0000000 82060000 40d00000
+          00081a00 0040d000 0000081a 00000041
+          03000020 68000000 41030000 20680000
+          00040d00 00206800 0000040d 000080a0
+          01000010 34000080 a0010000 10340000
+          00820600 00103400 00008206 000040d0
+          00000008 1a000040 d0000000 081a0000
+          00410300 00081a00 00004103 00002068
+          00000004 0d000020 68000000 040d0000
+          80a00100 00040d00 0080a001 00001034
+          00000082 06000010 34000000 82060000
+          40d00000 00820600 0040d000 0000081a
+          00000041 03000008 1a000000 41030000
+          20680000 00410300 00206800 0000040d
+          000080a0 01000004 0d000080 a0010000
+          10340000 00820600 00103400 00008206
+          000040d0 00000082 06000040 d0000000
+          081a0000 00410300 00081a00 00004103
+          00002068 00000041 03000020 68000000
+          040d0000 80a00100 00040d00 0080a001
+          00001034 000080a0 01000010 34000000
+          82060000 40d00000 00820600 0040d000
+          0000081a 000040d0 00000008 1a000000
+          41030000 20680000 00410300 00206800
+          0000040d 00002068 00000004 0d000080
+          a0010000 10340000 80a00100 00103400
+          00008206 00001034 00000082 06000040
+          d0000000 081a0000 40d00000 00081a00
+          00004103 0000081a 00000041 03000020
+          68000000 040d0000 20680000 00040d00
+          0080a001 00001034 000080a0 01000010
+          34000000 82060000 10340000 00820600
+          0040d000 0000081a 000040d0 00000008
+          1a000000 41030000 081a0000 00410300
+          00206800 0000040d 00002068 00000004
+          0d000080 a0010000 040d0000 80a00100
+          00103400 00008206 00001034 00000082
+          06000040 d0000000 82060000 40d00000
+          00081a00 00004103 0000081a 00000041
+          03000020 68000000 41030000 20680000
+          00040d00 0080a001 0000044d 27480a85
+          86871500 004e8c63 e5c271f5 00252154
+          dfe89d9d 361a054f 57000018 aef6e686
+          99d931b3 a06953f9 72082f7c e292a752
+          cf550000 3848d632 f9f1f1cb 7174c07c
+          7cad64a4 fba20600 0078f763 e6e38993
+          02000000 82060000 40d00000 00081a00
+          0040d000 0000081a 00000041 03000020
+          68000000 41030000 20680000 00040d00
+          00206800 00003a21 68eaa602 0000e820
+          f5a14103 0000d071 0683a666 2a000080
+          0e521d1a 343be248 cd090000 d001d203
+          83a6624e 0000800e b233fb52 18d8d83d
+          5039c9a8 dd7c9284 56a3b12f abe41400
+          00749cec 583e3ba6 cf8eed47 59d62e3b
+          8606cdfa 38e68cee 7f5f0bdd 67ce0a1f
+          bf617e48 f29e0c00 00d069d2 6608279d
+          715affb1 fd1804cd e6a141b3 2b8cf67b
+          685aad50 9cd81d26 cf39cf33 0100003a
+          55adde7f 6c3f0641 f3c6d0a0 79278cc5
+          4901b27f bcafea49 0000001c 6ed06ccd
+          2e0c9e14 e0d5e02c 67000040 e704cd86
+          fd4153db 51fd79bc 4ad00000 009d9033
+          add830ff b93f683e bcf4bb2f 26e34a2d
+          49030000 b479cc84 d82ecdd8 302fef0f
+          9a69572f da9b7415 d7052f3b 030000da
+          3c6962bb bc1d1ba6 b63f68f6 5d9d3e1b
+          bf36cd0f 0000d0c6 1ab15d9e 1edcc80d
+          f9c18fe2 a89b1f00 00a08d65 8b30ff36
+          b83178da e650dfbc e79ff3a5 183823fc
+          90cfec35 6bf95257 0813bb4d 350000d0
+          df065923 1c95f7b7 a4a115db e55f0e0a
+          9a9977dd f18b8d7f 7d4f6f5a ad9e13a3
+          2677a4fb 2fc43d6e 7be995d0 5cfdc458
+          7c622800 00d06e62 ccec8c8d 50288c3c
+          669271a5 b5336fbf 75fde055 499afeb2
+          939e3b7d ea9dcd2d 5bff34e6 4ce9486f
+          23494268 5442683a bd000000 30201f3b
+          a150cede ba3f829d b4425f7e ea942573
+          deda72d7 2183e699 52323b49 c37f8542
+          9864ca01 0080b6d2 08bbd324 7cf4a26a
+          da3b78d5 b097969d fa4737f5 e6268ccb
+          3e64b365 b6000080 b6111b25 b6caf359
+          b30cbd7a d80a4de6 a793c77d aeb9a7fa
+          48ae1026 98350000 a01db41a 616f7e42
+          e92b176e eb7b6ce8 f507bdf9 bf67fe97
+          d7e42794 5fb44a03 0000b485 d826b151
+          7e1e5be5 8707fee8 a0159acc 73d3277f
+          b1be79fb d25c318c 377b0000 c0586ad5
+          c39e62cf a4abe66c dcb6e6c0 9f1df2f4
+          cc933f7b d9f79324 fc246d5a a5010000
+          c64e6c92 34b6c9d3 b151d61c eae7875c
+          a1c9f45e bff0fccd 8faefe61 5add7be6
+          483f6c13 0000e0f0 6b26064b 69fcda9e
+          7973bf30 fbc1e53f 3bd4afbc eb076866
+          7fd035fd 43df6dd5 c22e3309 00008cb6
+          d822bb63 93dcf76e 31f39e41 9339e937
+          2e5a922b e696a7cd 50379d00 00c06889
+          0d528b2d b22a36c9 bdeff57b effa92b3
+          416b17ce 0f5bbef7 e80fd2b4 7979920f
+          79530b00 001ce398 692449fe 0753af9c
+          77e5d9cb 57bee7ef 16de6f67 033b58b4
+          79d563b9 b459fffd 18356553 0c00001c
+          a398a984 507c62ea 95577cf5 fd622693
+          fb203b8d 3bdad633 7feee7e2 8e97b6aa
+          617b484d 34000070 344b2684 d81a3b62
+          733c1cdb 235b99d9 fa41feec 7d5f7236
+          d4daab17 845d3f7e e696fa96 0dd7b7f6
+          56677b09 1a000030 e2966986 666e7ce9
+          cde2d499 0f765f7c d192b397 adf8c07f
+          7b584133 a877f135 e76c5abe f26f6242
+          fd668c9a 9343e2c4 ce0000c0 e1964c7f
+          cc6c0fb9 d2bf4f5b 38ffcf66 3fb0f4b9
+          c3ddc511 054d7fd4 dc706db2 f5f127e7
+          a6952d37 35f7563f 92e4c249 316bacd8
+          000000ef 1d316968 c6efbbf3 e34baf27
+          e5a9df9c 72f9a757 cfbeffe1 23dadd11
+          07cda075 5fbf31bc bd62f5a5 4963e7cd
+          ad4af582 2484720c 9b92551b 0000e080
+          90a9e6cb e376e5ba bb7fd6a8 84fb4e59
+          30f7f133 eefdf688 763be2a0 19b4feb6
+          5bc2c607 969e5928 87afb4fa 2a9f8f71
+          734692f4 9f452d1b c57db7e6 71040080
+          13215ea2 d6c0a8c7 e4a8e7ca a54dcdb4
+          bc66c6d7 167f67c6 9fff65ef d1baa9a3
+          163443bd b5e4aeb0 feee7b4e 2b96c3c5
+          71f35371 fc6e5aaf 4f4babb5 ecac6a83
+          23396048 1e0000e8 ac6c3970 64019326
+          a5ae5a52 2cbe162f bf1cc7bf d62be189
+          59b7dfba e5f4dbee 3cea77e2 9804cda1
+          bcf3c8b2 fc2b8b6e 38b7ab1c 3e163767
+          c7312d8e 53e2383d 8e530762 a67be0d7
+          b3cfba29 788e0000 40db68c4 d1174765
+          607b4f1c 9be2f845 1cdbe2d8 18c7abb5
+          4af89f73 1ebaff85 0f5d7575 7534eed4
+          a8050d00 00c0d1f6 ffef0bc3 1bf3b9a0
+          34000000 0049454e 44ae4260 82
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000278 000002e1 08060000 00ccfed9
+          b10000d3 81494441 5478daec 9d079c15
+          d5f9fe9f 99dbeff6 ded95d90 2a224d51
+          90a2803d 768d3196 fc8cc658 628f464d
+          515334d6 24d6688c 266ad4d8 3b7614a5
+          2808d2fb f6decbed 73e7fecf cca2c17f
+          c29d0b52 76ee3cdf 8f236d17 76df39e7
+          7d9ff39e f7bc478a c5622084 10420821
+          c9837d6f fd432d0f dd97baf9 ca6b273b
+          3d3840fc b2523cd9 e2c9174f a978d2b7
+          3d0ef148 db7e2464 5f10daf6 04b68dc5
+          76f1d488 a7491bc6 e2a917cf aa7000ab
+          0ffc64be 2f75caa1 b418217b 81c0fa75
+          b6e5078e 9fe8f060 9cf8e570 f1e48927
+          4b3ce5db e247a678 5cdb3edc 458b91bd
+          4c64bbd8 a1d1b32d 763488a7 75db8f6b
+          23017c55 79e76d5d 45975fbd c7bf2069
+          4f64f0ea 7e7da354 7ff79fcb c4449c2b
+          7e799c78 c6c72291 c2583822 8b90296f
+          0b9cdb3f d8ee4742 0613da04 51b7fd18
+          fbe6d731 4465b7bb 163679a1 f8f52742
+          f0bd39e6 85e73a32 8f399e16 23e43bd2
+          fbc94758 7dccf185 4e0fa688 5f9e2a9e
+          8950d572 3510b46f 8b21f276 3143660c
+          21833476 7c3b6e68 3fc6a04a 4e478be4
+          702c11bf fe5808be b74bafb9 a2baecd6
+          3f0c5e81 5773d565 68fefb53 13ec1e9c
+          ab060327 c442612d 3ba76508 b56c9c8d
+          538f24e1 f4d5264f 543c8af8 49c4e6f5
+          6c911cce 1784d8fb db88471e 6ac9f9fe
+          0f682342 12a4fb8d d7b0ee87 e70e11a2
+          eebc9812 3939eaf3 ef276d1f 433401c7
+          38429227 7e285aec d01ec9e5 6c94dd9e
+          d715113b 0acf3f7b 7df9bdf7 0f0e8157
+          75c98572 fbbf5ffe 3e94beab d46078a4
+          24c12d7e dbc98948 2c2af842 624a056d
+          a9294b14 b8ef1a76 cf1defe7 9d7b3e6d
+          43c80ee8 78f1dfd8 74d125b3 1c52e85a
+          a5af7fda b618e2a2 9c23168a 1df83a76
+          c86ee746 d8d3eecd 3de3e4e7 2a1f7c54
+          dd27026f eb05e74a 9d6fbc77 5e2cd879
+          b51a0857 48325239 2109f966 c246632a
+          faed19a9 2ba372ea 5de5b7fe eab5829f
+          5c42bb10 f2b5b0fb d73fb1f9 eaebe7da
+          63be5f28 dd7d13b7 c5103b2d 43ac9e28
+          d06287ec 716e95dc d90f651f 37f7af43
+          1ffbe7de 13785bce 3ffbe0b6 a79fbf43
+          8a852748 36a453d6 11b20354 a862b2f6
+          dab3d2de 519d1937 965d77d5 d6c29f5d
+          4dbb104b d3fee4e3 459b2fbb f24e29d0
+          7b8c1076 1990f52d 5842c87f 841e6251
+          f4c95ee7 97b6b4fc 1b320e9f ba68d83f
+          9fdb7302 6ff3d967 a0efb3a5 372b9d0d
+          e7aba170 89989832 df022109 09bd881a
+          450d3c69 b756fcf6 d74f165e 712d6d42
+          2c47ebdf 1e42edef effcbe14 ecba45e9
+          ecae946c 70d22a84 c4157a6a 4c410324
+          c7df73ce 38e9e6fd 9efaf7ee 17789bcf
+          3e1d1dcf bff238a2 ca699243 4fa51342
+          7676ae2a e88427fd ce2137ff f2f6a2ab
+          7e4e8310 eb88bb47 1e40d5f5 bfbc3ad6
+          d77da364 478ec4f4 002189c7 8e28fa00
+          fbb339a7 9f74f97e 4f3d1fdc 6d026ff3
+          0f4fcfed 7ce195c7 6350e68a 1517fb0b
+          11f2dd44 5e2fbc19 4f0cf9d5 0dd7155d
+          737d8816 21492fee 1ebacf51 75d36f7e
+          17ebefba 48b62383 16216497 445e4882
+          fdfdecd3 4e3a6fbf a79fef30 fa78dbcd
+          37df6c24 eed0f9c2 cbcf01d1 6385b863
+          036242be 23922c16 49c1d001 3d8bbe48
+          b7d963ef a64d9d4e a390a4a5 e5c1bfa0
+          e69737ff 26d6d775 85ec401a 2d42c82e
+          c70e3b62 eab0c0da 0de302eb 57bd947d
+          ea19ca2e 0b3c4ddc 75bcf0e2 bf00f524
+          21ee5804 4bc8ee9a a862b114 0b86c6f5
+          2d5e1a92 6dd14569 d366d028 24f9c4dd
+          fd7f42cd af6fbd46 ededba4e 7652dc11
+          f29d9121 c762b1ca c0da7545 c1f5ab5f
+          17226fe7 05dea633 4f41e74b 2ffd5afc
+          f40216c2 12b24744 9e530d84 c6f57fbe
+          b43116ec 5f9d3e6b 368d4292 86c63ffe
+          0ef57ff8 e3a96a4f e76f85b8 cba54508
+          d94db143 3be01ac3 88c0dab5 cec09a95
+          1fe79cf6 fd1d68c1 1dd0bf78 e1644475
+          71e7a139 09d9438b 31278a22 ddbd1705
+          376faca0 35483211 aaae2a0b 7774fe44
+          1be3b406 21bb3d41 902634da f9ba56db
+          517cf95f bfb9f9dc 33a174f5 fc5172a0
+          84662464 cf627361 4af7079f 5c5dffdb
+          dfd01824 2968baf3 3674be3e ef62bb0b
+          2c302564 4f893c07 4a8556bb 4368365b
+          c202afe7 dd79e7a8 fee021db 2e752684
+          ecc9492a c31d6e6e 3ddeffd5 9733690d
+          920c04d6 ae3a34d0 d0788a18 dbdc0122
+          648f050f c89a56d3 345b4202 6feb85e7
+          21160c5d 2bd9f5fb 0009217b 019b1365
+          7d8b96fe 5fc36db7 d218c4d4 34dd7307
+          ba3f5c70 9ec38961 b406217b 58e3d9e1
+          129aed32 a1dd1c86 02afe385 e77e2814
+          e14866ef 08d9ab93 d41e6a6c 9ed6bf70
+          c1545a83 9899fe2f 161d1aa8 ad3f5c1b
+          d3b40621 7b3a78e8 59bcd142 bb9d1e57
+          e0555d76 91762dc6 d560bf3b 42f62e31
+          3d8b57ee 5bb1f69c c63b6fa3 3d882969
+          fecb3de8 5bfce50f ec4e5422 467b10b2
+          57b0c12d e6dbc5ba 86db91c0 6b7df491
+          61b16068 04b37784 ec838598 1d8e607d
+          e3c4ae57 5fcea135 8819e97a ebf5ac60
+          75ed81da 58a63508 d95bc103 b2d06e07
+          080d377c 8702cf9e 9375a1f8 404e4c42
+          f605316d 9ea2d291 977b228d 41cc8823
+          2fef5831 8647307b 47c85e17 791ea1e1
+          ceff9f02 aff6866b a1fa7c27 8b0f6253
+          6342f611 b203b9be e5ab8e6e 79e0cf34
+          063115ad 8f3e84be c54b678b 315c406b
+          10b2d705 9e4368b8 53342df7 cd6fc562
+          034badc5 925429bb b04a48be 145a8a90
+          7d364911 f56369da a4fd8f18 bb74751f
+          0d42ccc2 da598764 767dbce4 75871787
+          318347c8 3e40854f 0d61ff43 62b11a3d
+          61f0f5ef db73bc47 88e0c253 4f84ec4b
+          06b66973 6d995907 d118c44c d83232c7
+          8a80924f 7147c83e 4b10d885 969bf3f5
+          2fff5383 2749b3b4 394a0b11 b2cfc913
+          0f9b1e13 5321bb5c 87881f0a 690942f6
+          dd3a4b3c 477e4be0 35ddfd47 c4c2e1c3
+          28f00819 14a4484e c7389a81 98858e67
+          ff05dfea 7513253b d2690d42 f6a1c093
+          a483bf25 f0aaaefd 45762c1c 2980a4ed
+          0e1142f6 25224822 b8a9bab0 e3d9a769
+          0c620a9a eebdc3ee 5bb7a958 e6113d42
+          f669f888 85c3f9cd f7dd9bf2 8dc07364
+          38797305 21836586 8a20e9df 5c95def0
+          db5f17d3 1ac40cd8 b3737244 004963fd
+          1d21fb32 7800b170 c4b6f5f2 ab0ff846
+          e009860e fc112164 9f13d327 a6d79e9b
+          378ac620 26a1423c ecc040c8 be177992
+          23c33966 7b813702 60068f90 4184473c
+          25340331 91c04ba5 1908d9e7 685a6ec8
+          f602af94 36216450 e1124f11 cd404c42
+          e1b64509 2164df53 babdc0d3 eebee416
+          2d218307 b7787823 00310b99 e2e1110b
+          42f63d9a 962bd95e e0e553e0 1132a860
+          068f9889 1c0a3c42 06a7c02b a6c02364
+          d04d5216 ad13b390 4f8147c8 a0891da9
+          db0b3c07 051e2183 0e361e27 66c14113
+          10326804 9efdff17 7884100a 3c427605
+          de634ec8 209b8f32 27272183 165efb44
+          b8182184 ec2c19db 0bbc34da 8390c1b9
+          0a23c404 78690242 060d8eed 059e4a7b
+          1032e8e0 c54f8463 9510b24b f391b757
+          10420821 84241914 78841042 08211478
+          84104208 2184028f 10420821 8450e011
+          42082184 100a3c42 08218410 42814708
+          21841042 81470821 84104228 f0082184
+          10420805 1e218410 4208a1c0 23841042
+          08211478 84104208 21147884 10420821
+          84028f10 42082184 50e01142 08218410
+          0a3c4208 21841042 81470821 84104281
+          47082184 104228f0 08218410 4208051e
+          21841042 08a1c023 84104208 21147884
+          10420821 14788410 42082184 028f1042
+          08218450 e0114208 2184100a 3c420821
+          84104281 47082184 10428147 08218410
+          4228f008 21841042 08051e21 84104208
+          a1c02384 10420821 14788410 42082114
+          78841042 08218402 8f104208 218450e0
+          11420821 84100a3c 42082184 10428147
+          08218410 42814708 21841042 28f00821
+          84104208 051e2184 104208a1 c0238410
+          42082114 78841042 08211478 84104208
+          2184028f 10420821 8450e011 42082184
+          100a3c42 08218410 42814708 21841042
+          81470821 84104228 f0082184 10420805
+          1e218410 4208a1c0 23841042 08211478
+          84104208 2196c24e 13989498 f82f66fc
+          6192a4fd 8fe62284 10c21842 814706f7
+          bc1493d2 91e28164 b70fccd2 1dce4c19
+          6a308868 30cc094a 0821e41b 7167733b
+          21bbdde2 e76a3c79 8798a220 e20b0c08
+          3d428147 f62c9120 b0ff95e7 2075ec28
+          2014def1 0766a4a1 f1a91750 f5ca4770
+          78683742 08210331 a4f4e869 283efb34
+          a0a76fc7 1fe872a2 7ff57aac b8fd1138
+          194328f0 c8de410d 0b611710 b3341c47
+          e0391d50 c5ea8b10 4208f956 0cd16283
+          164382c1 381fa40e c41a624a 78c88210
+          42082184 028f1042 08218450 e0114208
+          2184100a 3c420821 84104281 47082184
+          104228f0 08218410 4228f008 21841042
+          08051e21 84104208 a1c02384 10420821
+          14788410 42082184 028f1042 08218402
+          8f104208 218450e0 11420821 84100a3c
+          428886e4 74d008c4 1c81c4e5 a41108a1
+          c0238418 074ca07f c97269cb 8fcfa631
+          c8a0a6ea 920bd133 7fa1ac8d 59420805
+          1e21c460 6646fb7c 36df8ae5 b40519d4
+          04567d05 a5a75792 184d08a1 c023bb01
+          55056231 c30f9364 1912ad65 4e242d93
+          c7b40819 e4c3548c 5189b1c4 6cae458f
+          0d866831 468b3584 028fec45 121077c4
+          f4d8b63d 8410c258 4328f008 49a28536
+          218c2584 104e4a42 924ce0f1 282d612c
+          21847052 129264f3 93fd27c8 60c7ce58
+          62226262 e5286dab c1e3f6ab 25041eb7
+          824c84f6 b262d1a8 f1e4147f ae1fb290
+          06263531 1d5afd5d 1acd4006 39a9db44
+          1e315520 4920ec8b 18a2c51a 0a0453ca
+          846f045e 84f630d9 224c1777 b1dd3389
+          c960450b 9ae93403 19e46863 94a504c9
+          1969b6c5 1a623222 db0b3c85 f630d9b4
+          fbba4d8a 9180d373 f1b41705 1e217b8c
+          340a3c93 2171f19f e428db0b 3cd64f98
+          4ee0c58c 13784200 ca769b78 586a6152
+          b4fabb1c 9a810c72 b2c15a51 f3c40e11
+          0bb498a0 c586b881 411380b1 6db18698
+          0d797b61 c72d5ad3 09bcc41a 1d338367
+          6a588347 cc803646 59836722 b45b4712
+          6a742ce2 8c1ae106 9f098950 e0995de0
+          25329185 c06326de b4708b96 9801ed90
+          05b7684d a5f090f0 166d2cc6 9b2ccc2e
+          f0b8456b 3698c1b3 02dab657 1ecd4006
+          39c5e2f1 d00c66c9 0e889060 b3e94f22
+          9d187855 9929e116 adb927e9 4e9ca265
+          0acfacb8 b6054f42 063385e2 71d30ce6
+          416f9f65 4b24af13 6301b739 e1295a73
+          eb3b3501 7db7ed90 85be52a3 cd4cb8d2
+          764a0e3b 051e19dc 62c1e9d0 041e0f59
+          98e99dd9 12147831 6ed152e0 91bd8e56
+          f86a5887 a7893a66 f0cceb84 1d90c275
+          8df9edcf 3c496390 4149e7f3 cf22b8a5
+          a644b2eb 07828839 168edb04 9ef1166d
+          8c872ccc 8afcff0b 3ce678cc 12f8b1dd
+          d17523ed 46816766 81875055 4366ed0d
+          d7327892 4149fdcd 3749fe8d 5b3324e6
+          efcce55b 24597f0c 03cdb658 c308623a
+          bed5072f 4a7b986c 1196c855 65da1c95
+          6d09d65a 9041b9d2 96e174e4 e5e7d218
+          643062cf cdcb92b5 fa3ba607 cce35634
+          bf62b743 76d8133a 64a1c71a 62b2c881
+          c0f6028f 872c4c86 1a0a6f9b 7852dcc9
+          293b6cdb 1a5ad266 26453b68 51413390
+          41ca70f1 78690673 a1c5044d e419a407
+          f42dda18 b768cdc8 b7327861 dac36412
+          5d8d1a27 f03481a7 65f0c4c3 8350a645
+          0b9e6369 06328805 5e0acd60 a6e0a109
+          3cbbfe24 94c1639b 1433f2ad 43163e30
+          c7632a54 25ca3e78 d6406b22 3b9e6620
+          8311c96e 2b073378 e6433b60 a195ee18
+          e93b3586 6888f91f f34978f4 6c2ff0b8
+          c96e3681 f7f5295a 03f12639 b6add488
+          59714936 7918cd40 061b7d9f 7e8c484b
+          fb38c9c6 0c9ea9a2 bf96c1b3 69edb3ec
+          88abf0a4 6da76815 6ed19a90 d0f602af
+          1bcce099 67d52c26 5e34144a ac064f88
+          3bbdd682 6fd79cc8 9094aede 217d9f2d
+          e0491932 a8d8fca3 b36cfd6b 36ee27b9
+          d822c55c 0a6fdbc2 df611417 243dc668
+          b1868d18 ccf686bf bd451ba2 4d4c4622
+          279bb4d7 fc75bf23 624e31ef 04821bab
+          53369ef6 bd225a83 0c261cf9 0585b284
+          142e1e4d b86eb47d dd00df68 8f163c45
+          6b4ebe75 c822487b 9829ea0b 7d178e40
+          8d1a6dd1 c6f434bc 369179c8 c2c4ab6d
+          1b521c05 8587d118 6490a11d fee1f6ac
+          d95c8af0 2936b70b 368f3bbe c093b66b
+          74cc0c9e d9f8560d 5e04dcc4 338fbe13
+          934df107 10d36b23 e26fd1da 5d4ec8e2
+          e1db3535 e9e2a5cf a619c8a0 4296b531
+          994d4398 f0d57d7d 8a366e60 186893a2
+          b5e4a2c0 335b5ae0 db7df098 8335197a
+          e16b2c81 abca6cdb 1a1d53e0 9958d1c3
+          a5fa0393 026b57b3 0e8f0c0a 02ebd74a
+          6a5fff41 2282f004 ad19059e d309c9e9
+          4cecaa32 116ba8ef 4c47dff6 02af9bf6
+          3055c047 2c1219b8 ae2cdecc d30e5938
+          1d901d0e ea3b33bf 6ebd0eaf 267fcdac
+          a985b406 190cac3f 66768e7f e58632d9
+          455b980d 7dddef76 41f28897 a71a6cd1
+          0a71a7f8 7ccce099 55c86ffb 3104e678
+          cc3549b5 1629c69d 8e7571a7 3dc4dc1e
+          59b2c3e3 c8cf9f4e 6390c180 3d2f6f92
+          24c3c3a8 614e6c5a d98ecb28 83f7f516
+          6d84a768 cda7e17d db0b3c3f 6d621eb4
+          3ba2c3dd 7dfa418b b8336f5b 06cf4681
+          97042f1d e9b170f8 d4506d0d 6d41f629
+          e1ba5ac4 82a1e3c5 98cca235 cc17fab5
+          8a1d7dd1 afc68c7c 8e2ef0a2 91083378
+          e613782d db0bbc16 308367a6 600f5513
+          777a0d5e 9c992726 b09686b7 89872fd7
+          e4afdc0e 47704bdd c1ab26ed cf4d31b2
+          4f593dfd 20a77fd5 c6d9920b 1e5ac364
+          915f6b72 ec96c4e3 4a600748 12714641
+          b837f81f a540cc42 707b81c7 3e782613
+          7851ed8c 4554359e cd4eed14 2d354152
+          bc761bd2 ed59d927 d112645f 62cfcc3a
+          5c8cc52c ae1acda8 f0f04d5d b6a1c0d3
+          32785a12 416502cf 7c6f19cd db0b3cde
+          6461a640 bf4de029 a1044a27 6303e978
+          59e61b4e 02d284a8 3f53e9ec a025c83e
+          41e9ea44 4c891e27 7e9a416b 9830f28b
+          186077bb f4c7708b 567cb01a 0e236ab0
+          514406a5 c0eba1c0 33b3c8d3 9cadcf0f
+          e84d28e3 d7e1e94d 2ddd60b3 63b3bf73
+          3bece1da 86495f8d aa48a335 c8be60d5
+          8431eee0 bacd274a 4e6ecf9a 32f24785
+          c04b4b13 4faa1078 6afc2c82 1285120c
+          51db9993 eeed055e 0fed61c2 d5b41078
+          7ab3e378 024f4ce2 0181e7d6 53edc4e4
+          c8c892bd 2917a93e 1f6d41f6 2aaadf07
+          d9e3b950 8c4166ef 4cabf0b4 16294efd
+          89db4755 c414ad15 57a4b78f 3633e35b
+          8ec5be75 c882193c 93a1493a 351836be
+          2750cbe0 b99cb00b 81c70c5e 12bc771b
+          52234d2d 972c1f5a cca3d164 aff2d598
+          61b6e0e6 ea2b2407 059ea905 9e5697ed
+          b01b57f7 88d8a232 8367cab7 2cbbddff
+          396421d9 6c3c6461 4214bf7f db3d81f1
+          4fd2dabd 5ed8bc1e c38b2f88 69d47dae
+          e4749ec3 4bc0c95e 8b1862ac 893177a6
+          187b39b4 86a9f51d 1c69a970 a41a6fd1
+          46431184 7bfb29f0 4cf68265 af3734a9
+          a5f73f57 954deef0 778adf8c 318767a2
+          182f665d b847eb85 1736a8c1 5385c0f3
+          c091e265 8e365990 91a6b4b7 5ffb6529
+          632dd93b acd8af14 a19abaf3 253bd269
+          0df30b3c d9b0064f 0b1d5121 f2423c60
+          613e82ff 0915ff21 4cbb9848 e069cd8e
+          7b7b07fa e1c59b80 6a4cef83 a7d5e171
+          8b36a928 17cfe534 03d94b9c 299e8960
+          473453ab 3b59c40a 6dc10fbb 3dfea93b
+          49821a0a 8b18d3a7 c71a622a 0ddff2bf
+          045e0d98 e3310fe2 cd457afb f563ec46
+          872c1ca9 2962d596 c2979b54 0a1fde68
+          4fef8d5f 96e616d3 18644fb2 6278993d
+          d2dcf27b c9864c5a c3c4915f d5b27776
+          3d1ec4cd de6d1378 5af62edc d94d496f
+          3e81d7fd bf049ed6 188f555a 6689efba
+          c0eb4ba0 064fbcd2 b4343833 3298694f
+          baa91ccb 5683c15b 6908b227 1163ec21
+          31d64a69 0993bb8b 28e0cace 86334be8
+          f4682202 2f8c5067 9f76b08b 9868ba62
+          0719bc66 3083672a 81170ac4 a0f4fb8c
+          1bdc69ad 52523cb0 b9c08316 493508e0
+          5003fe33 9757161e 4f63903d c157a32b
+          67281d1d a78a48e1 a435cc2f f01c1969
+          8965f044 48514321 44c2b1b8 f9033228
+          055ecdff 1278095c 8b400655 7c176f2b
+          d8da0ed5 1fc0c055 153b1678 ceb434b8
+          b2d2f549 4e92c96b c75222cd 2d7f5e5e
+          599465e8 b409d929 7137d416 aaae7e5c
+          0cb22c5a 23095c85 789ce9a9 b0a77ae3
+          0b3c5942 4c88bb50 772f777d ccf99a9b
+          ff97c0ab 05b768cd 25f0c413 0924d02a
+          252a049e 58b93933 29f09274 2054845b
+          9a1f8e2b f209d909 568cae44 a8baea69
+          31b6ca69 8de489fc da095a7b 6a9ac109
+          5a196a20 a0270f28 f04cf99a 7bff97c0
+          e316add9 e2ba787b c1a67644 b52bcbe4
+          78757851 38d2b50c 5e264fd2 2627dadb
+          3ff1ab11 153fa029 c8ee205c 5f779688
+          eedf133f 65055632 447da1e7 ec4ec09d
+          9b23fee7 8c5fd623 bc897645 59a8ab9b
+          2768cdc7 0eb7685b c00c9eb9 049ed60b
+          afb7c7b8 175e5485 233b0bae 9c2c2af8
+          e4c515ac aeb9e7ab 91fb5580 9dabc877
+          60e58123 474155ef 113ff5d2 1ac923f0
+          9c59a970 a6a7e9ad b38c3207 d14010c1
+          f60e0a3c 13be6af1 74ed48e0 31fe9b08
+          d90ef4d7 7722a2dd 4b1a6f7b 4e5bad39
+          1d706565 e9ab3866 f19254f0 db5118dc
+          b2e589af 468f42b4 bf9f0621 3b171922
+          11ac1a3f 0681b51b ef134ea2 801649a2
+          77ab9fa0 cdd47772 b41d9df8 81458622
+          045ea0b9 8b2768cd 27ef6291 0e7fed7f
+          093cf19b cde05d16 26537840 341245b8
+          a35bbcc0 88e19565 dae47666 a6b00e2f
+          99459e13 d37deb37 5c67d3ae 22226467
+          c68ec301 ffca757f 901c9849 6b2459dc
+          5701774e 365c99e9 f15ba468 31241246
+          a8a3134a 486506cf 5ce24ebb a124327e
+          f5aa8eff 1278e396 7fd924fe 50a2c433
+          9953164f b0bd1351 5fc0609b 362ac45d
+          1adcb9d9 1478492e fbed2e5c bf6ac2c4
+          f13405d9 19561f3c e930c98d cbc44f1d
+          b4467205 092dac7b f27361d3 7be045e3
+          0a3c2d96 84da3a59 e7614289 27d96c0d
+          9efdc7aa ff25f052 c64f5024 bb9db759
+          986dee8a 5918686e 4144eb87 27dbe20a
+          3c777e0e 3c8505ec a691f412 0f59be15
+          cb1f5e75 d041294a 6707ed41 8cc5dd94
+          299efe2f be7c42fc 348dd648 b2a81f01
+          dcd91eb8 0bf2019b cde08085 0ca5af1f
+          fd8d4dcc de998f6f 1db0f896 c01b1809
+          b17af0a0 85b9049e 6da0175e 34101093
+          377e064f 4e4b87a7 a840d781 acc34b72
+          8de7c194 dea54b6f b467e7d0 1864c76e
+          a1b7176b a64d93fb 3efffc11 316686d1
+          224918f5 f5fabb8c 6d072c0c b66f8400
+          8cf4f723 d0d81c37 5f400625 8a7836ee
+          58e001ad 1478260b e476c0df da8b704f
+          ef403acf a0edb83b 274b3ca9 8829b45d
+          b26377e1 d2d53366 cc014fd5 921dc5f3
+          f474742f 5c78a6cd 8333698d 24157822
+          a2a79697 c1539827 248091c0 93a1f803
+          0874fa79 c0c284af 5a3cf5f1 041e3378
+          6643d20e 5aa8f037 b640d5fa e1c51378
+          8aa2b74a f11617b2 0ecf0a43 434646ef
+          82050fae 39fcf0a2 484b330d 42fe8bb5
+          73e61e60 77e2766d 3d406b24 277afd5d
+          411ee4cc 8cf8f577 b20cb5b7 0fbebac6
+          81f8c065 a1195f75 5b3c81b7 413c0cfd
+          2643eb90 d25f5b8f 7057b7be 02db2162
+          72bbf273 913aa4c4 b0151249 0eec1e0c
+          ef9c3fff 8fb6f474 0670f29f b55e4707
+          d61e7594 a7fb83f7 ef966c28 a3459234
+          e28b68ee c9f2c095 9d655c5d 2f6247b8
+          ab07fd75 0dbc14c7 a4d35a3c 5be2093c
+          66f0cc28 f0ec0302 2fd2db2f 227a9c38
+          aeaa905c 2e788af2 e148b5eb 47e749f2
+          e374e2fb eb4f3ded 0cc462dc 742103b1
+          3c33436e 7ff7dd1b ed5ecca5 35921735
+          a26dcf16 ebdd13b4 1d9cf883 c28e489f
+          96c16bd0 630a31db cb861a6e e9d9bc43
+          8127feb0 5efb205a ca5c68b5 12a12ead
+          3165ab71 3fbc6814 9efc3ca4 89491f0d
+          d3769618 1f7638ba de7efb77 6b8f3f7e
+          a8a19327 9618121b 4e3b7da6 cb89cbd9
+          332199df f2c0a515 69951570 15e4c517
+          785acc10 b1416bb9 1514b184 f5772643
+          bc675b46 aa3af2c5 7fb7ef50 e08d78f6
+          e96a393d 55e1a437 a57a87af be1191ee
+          de81a3f0 3b4289ea 933d7df8 3036c4b1
+          d0e47778 51d9f5d6 5b77ac3b e594b470
+          5d0d6d62 61d69f72 4a4ec72b afdc2b84
+          7f3aad91 c4d35ee8 3957ba1b 29258590
+          5c065718 8998a174 75a3bfaa 967b78e6
+          f4f131c9 edaece3e e5747587 022fe7fb
+          6705658f bb9e375a 980f2da5 deb7b90a
+          a18e2e11 cde36fd3 ca5e0fbc 45f9b07b
+          6ddca6b5 96c83ba9 f5f5d77f 2c16eb6c
+          646b4122 4d8dd870 fae99e8e 975fbe5d
+          8c8503e9 e5931b6d 87267d58 29dcf9b9
+          c6dbb376 3b02aded e8deb099 dbb3269d
+          de42c02f f92f5df0 df8120a6 1db4e03e
+          8e09059e afa91bfe 86a681c9 6cb04deb
+          2d2c40fa 880a6ed3 5a4ce4b9 1cf8f9d6
+          6baf9f1c 0bf8690f 8b21c9b2 d4f4c20b
+          670871f7 638abbe4 9feb1a19 a387c3a9
+          0bbcf8b7 57682bfd 404b9b10 79fd90b9
+          fc33a7c0 03561a0b 3c60d9b6 0f26a6f2
+          de03fd8e fa6beaa0 74f5c43f 4d1b51e0
+          2acc47f6 d8d10327 e1e9ecad b31070a0
+          b8edb9e7 6edff8a3 1f1586aa ab6810ab
+          c4fb4818 55d75d3f d1edc04d 9cefc98f
+          2ad6f829 c5994829 2b85e474 186ccfca
+          503abbd1 5f5bc706 f8e6e5bf 9a1cef48
+          e0ad0533 78a6c426 e671cffa adf037b5
+          ea472777 eced637a 4a3ea5b4 08dea20c
+          dd1910eb acec9d5e cc68fcf7 f397a9fe
+          7e370d92 fc84ebeb b0f9c73f ce69fee7
+          3f7f2704 fe705a24 f9890a9f 9eb9ff48
+          78f272c4 0030c8d7 8858a165 ef7a376e
+          d50ed212 532a7a44 23ad1deb 0c059ef8
+          a02aed83 6931f3a1 df6ad1de afb74c41
+          281c7f9b 3612813b 3f0f5907 8cd19d01
+          9b5a5a4b e4b91db8 b4eece7b 8e8cf6f5
+          f0cd27fb eb0ef85c 0d4f3e75 b9cb8ba3
+          99bdb3c0 fb56c5fa dd6d43fa d072d832
+          0cae27d3 62841080 da013d7f 4b1fb767
+          4dfabe6d 19a9a1f2 3bee6a36 147865bf
+          ffc31639 3d354047 60529127 e66befa6
+          cd0836b5 008e38b3 351a852d 3d1599a3
+          f6832bc3 cbabcb2c 86dd81cc e6279ef8
+          f5964b2f 1d11aada 4a8324eb c2deef93
+          eaeffed3 112e3bae a44fb706 5a5d75d6
+          e84aa494 150f94df c47bef22 46849a5b
+          d1bb6113 0d67de05 bb2aa7a7 6f2abce2
+          9a98a1c0 2bbef686 802d23bd 41a842ba
+          03330a3c a710789b 1bf46ee4 b01b3433
+          52a2f00e 2945dec1 fb43d10e 5b309763
+          1d9f2066 b7c78b49 0d4f3e7d 79b8a921
+          85164942 71d7df8b 2d575c51 def0d7bf
+          fedae664 4b148b04 7b9d9cf1 63e1d4b6
+          670d4fcf cae8af6d 40cfa63a d89c349f
+          49094155 3fff9f7a e07f7b06 7585f83f
+          cf579a10 ad416524 a0a0afaa 06d1ee9e
+          f83df1a2 51d833d2 9135760c 9c696ede
+          4f6b4191 e7b6e3ff 5a9f7cfa a4685707
+          5b9b2693 c7afadc1 d6abafce 68fadb63
+          d70b217f 0897ebd6 40cbde65 8c28466a
+          45d980ef 37e87d17 edee45df d62a847d
+          11363736 2f9a565b 9db8c003 16ebaa90
+          987205a7 1db6e85e b3197d5b aae31fb6
+          d826f2bc c585c89d 3c1a51ed 8d338b67
+          29ec4e78 1a1efeeb f55bafb9 769cd2d1
+          4683248b c7afadb6 d73dfad8 1942dcfd
+          942723ad e3fbb577 9d7fe841 70e6e51a
+          1fae70bb d0b7b91a 9dab36eb 31838b00
+          93be7615 8ad2d9b9 286181a7 74772d15
+          9fc40c9e 49d10e5b 045a7bd1 b3710ba2
+          7e5ffc96 298aa267 f172c68f 83339db5
+          7896730e 035bb507 343dfec4 2faaafbb
+          2e3754b5 85463139 d1ee2eb4 bff8ca14
+          971d5751 dc59e8bd 6b8d8df7 2b42fad0
+          0a480e83 d628b28c a8cf8f9e 0d5be06f
+          efe5e10a b3a21da8 494f6d2f b8e4f28d
+          090bbcfc 1f5fb4c9 9e9e12a4 a237b1c8
+          b3015dab 37c22756 68701964 f1541529
+          434a5138 63221b1f 5b57e49d 51f7f727
+          fecfbfe2 4b0f2d62 5ec2b535 a8b9e9c6
+          a2da3ffd e9468713 a36911ab 4c62ad91
+          b584c2c3 a6c09993 35702779 3cdc2e11
+          1baad0bd 7ee340a9 3663bd39 5fbb0ac5
+          919fbb7a c86d7722 6181577e d79f038e
+          fcbcea58 94b7d299 56e0094d e76fec42
+          cfba4d50 83617dc5 b6e3a55f 14b6b414
+          e44d9e80 94d2cc81 ad5a6239 91e7b2e1
+          b2eef90b 0e55da5a 691093e2 5fb93ca5
+          e6c1872f f27a712c b377d641 09025963
+          872263e4 7e0965ef d440508f 0dbefa2e
+          66efcc4d 30168d2e dde1abde a1c38f46
+          3f163f04 683ff3a2 15cd76ad d988fe0d
+          9b8db378 62c5e710 2bbfe223 6688096f
+          e71db516 c4e1c290 dabfdc77 55cd4d37
+          94866a78 cb85d988 7676c8dd 0b3e3b4a
+          08f5ab29 ee2cb438 d3b6e952 dc289c39
+          158eccf4 04b2774e f4addf84 8eafd6e9
+          3b3dacbb 36312a02 d1bebe8f 765ae009
+          348117a4 05cd8b76 ecbdbfbe 031d2b56
+          23eaf3c5 3f51abc6 20bb5cc8 3e707fe4
+          1f3c062a b76aad17 288428f0 7a717ced
+          a37fbfb0 f7c3f7bc b4887950 3ada517b
+          f36f46d4 dc71d795 42a8a7d1 22168af1
+          21a068c6 848193b3 466a4dc4 00d51740
+          e7ca35f0 3575e93b 3dc4b4e2 0e729ad7
+          977dd229 cb775ae0 a93edf6a f117f046
+          72b38b3c 87568bb7 09dd2bd7 014e835c
+          bc58f9d9 52535078 f861f014 6640e58d
+          c4961479 2e1b7eea 5bbdf668 a5ad856b
+          7b13106e a817e2ee d799b5f7 3d708510
+          e8d399bd b3508c17 3eda539c 89fc6907
+          0bdf9daa 97dbc445 c4809e55 ebd0bd66
+          0baf2533 bbaf56a0 baca4ab7 563ef458
+          78a7055e c69ca39a e4144f35 622cbf34
+          3392d074 c1f63e74 2e5f05a5 abdb58e4
+          a9aa7e85 59d93147 4012ea90 5bb5d6c3
+          e1427ecd 3d7fbeb2 f697378d d6ee3125
+          839bee77 de74d4dc ffd0d95e b644b1dc
+          624c6b99 5076dc1c 3833338d b76685ef
+          573abbd1 b6ec2b04 da7a99bd 337d7047
+          7f4c51de 8cf7213b 1478c3fe f12fb8ca
+          cb3e8d45 5887676e 2f00d85c 2208acdb
+          82b6854b 07d4ba2c c5157892 ddaedf51
+          5b306d1c 54b64db1 64e0d032 41b58f3c
+          7679e7f3 cf72bb6f 10a36dcd 06366f9d
+          ea947111 c59db5d0 b6660b0e d91f99a3
+          47e83e3b eec10a49 12be5f42 fbe2a5e8
+          11b1408b 094cdd98 7d00a057 0d06deda
+          2581a711 f5f5bf23 7ee8a325 4d2ef4c5
+          5b567c0a dabe588e fe759be2 df51abbf
+          f828e414 0f4a8f9e 8db4a145 acc7b3a8
+          c873c838 27d4d078 86d2dec6 cd9c4148
+          a4b90975 b7fca6b4 fab63b7e ee74632c
+          2d62a1f9 2916dede e22c941e 3b572fab
+          31de9a75 c2b76133 da967ea5 c70249a6
+          0dcd3d00 a0c5e8d6 d42953d7 efb2c04b
+          993879b1 ec71f750 e99b1f9b 07f0d575
+          a0f9d325 88f60acd ee3088d9 4a14f6b4
+          540c3de3 043852bc bcc6cc82 08d1e0ad
+          befb4f97 d6fef286 8942e4d1 20838c8e
+          679ef4d6 dcf7e04f bc5e1cc7 ec9d8562
+          bb565cef 7261e899 27c2217c b4f17db3
+          36fd905d d3270bd1 5bdda6c7 0262f231
+          1046d83d 7ce897c3 9f7901bb 2cf0463c
+          ff6ac83d 62d82af1 97b1122b 09d0eaf1
+          7ad66f41 eba26588 697df1a4 385bb57a
+          c488c153 5080b213 8f101f6b 674adf6a
+          4e6460ab 7642c35f 1fbbbcf6 c6eb3323
+          ad2d34ca 2041e9ea 92c36d1d 273a645c
+          4e7167a5 49a93d32 8a8e3818 2965a503
+          fd4d8db6 666d36b4 7df6b9f0 fd5bf5ce
+          0a2429e8 52038157 8c3ec830 51ab0683
+          af6a7f19 ed697eb4 9e4791be 305a172f
+          43dfda0d 091cb888 41121f93 3be940e4
+          1f3486d7 985954e4 b9bcf861 fda38f5f
+          d6fce77b 181e0601 da556475 37ff7254
+          f56d775c e4742383 16b10e6a 14c81c55
+          8ee2c3a7 4376bbf4 9ae9b868 5bb3eb37
+          a375c972 ddf7cb36 da301944 beec71b5
+          bb478c7a f73b0b3c 57e5d0b7 24b7b38d
+          d99be4e0 ebaddac6 77e6eb27 aa6037d8
+          aad5eaf1 84231972 d231f014 6573abd6
+          a20ec52e e19ca83f 304b1317 64dfa174
+          75a2f657 37a6d6ff e5c12b3c 5ecc64f6
+          ce42d350 f85e47ba 0765df3b 0ab2106e
+          867577b6 81add986 f73f417f 0db76693
+          661c8410 f18c1dbd 6ce42b6f 1a363233
+          1478a3de 78b7c37b c0e8a5e2 2f65fe26
+          4982b5cd 0d746fac 41dd9bef 0decbdcb
+          06edcea2 aa700e1e 0cfbe169 b0b95cdc
+          aab52076 0f46d4fd e9be4b6b aebbba50
+          a1c8db67 34dd7d87 bdeefe87 2f727971
+          21c59db5 fcb67635 51e549c7 c2535810
+          bfbc4643 fcb92a9e 8637de43 cffacdba
+          cfa7df4e 1a3ad450 e8c5443e 30a1b334
+          b17044eb b5d249bb 26075ffb 86ae55eb
+          d1f6c922 c4b453b5 09380c6f 71214a8f
+          3f5cf809 1ec1b262 8011a2e2 8486479f
+          b8aafe96 5f72ab76 1f10edef 93842f9e
+          669770ae def38258 67fa8995 78dee431
+          c83c6094 d0790675 771a4e27 3a172f43
+          fb576bf4 56571247 4bd2f861 c9e56871
+          1616cddb 6d02cf9e 93fba6e4 b4b77205
+          903cc876 20dc1d40 cb8225f0 6fdc6a7c
+          aa563814 c96e43c1 a1939133 6e241b20
+          5bd4b9c8 12ce861a 3b49f5f5 53e5ef4d
+          71e7eb47 dd2f7e5e 5073e73d d7d93d18
+          478b5808 e16bdd79 99283ff9 38fdf4ac
+          a1b8130b f6506313 9a172c46 a8ad1fb2
+          83264c9a a1104220 65d281ef 8e7afb83
+          849a9725 e4a447bf f7715fca e4f11f8b
+          bf3c4413 2791c873 69f578ed a87df37d
+          a8fe40fc bb6abf16 79363b2a 4e3b1e1e
+          e17028f8 ad87c38d e2fabf3c 7469f515
+          970e53fd bcc9706f 51ffeb1b 1c750ffc
+          f5329707 c772de59 cc4f8bc5 f7d0b34e
+          4eec5085 2c0b371d 45ed1bef a1bfba69
+          a0a13149 269a628a f27cc263 27e1c5bb
+          a268a769 5b69df24 731e4ea0 6f6b356a
+          5f7e1b31 bb0d896c fcd8bc5e 54fee014
+          c876f6bf b5224e0f 66343df6 cfab6aae
+          be8c0360 2f100b85 c4ac94ce 10cefa22
+          5ac36a2f 5f42f151 b3903aa4 0c926cec
+          9c632e27 9adefe08 3d1bb70c 3433e6d6
+          6c728d06 9bad464e 49f962b7 0b3cc9e5
+          7e4fac0e 3680799b a4427302 dae9acae
+          35ebd1f1 c912c065 bce49324 49389c52
+          941c3593 a540561d 37c06990 6de7c422
+          110e803d e9d12311 545ffdb3 e175f7dc
+          77a1c383 5c5ac44a 2f1f481b 36044533
+          0e85a4ed ae18455e 21ee7a97 afd66f2c
+          527c61ed 4c064922 d400da52 a74efee7
+          980f3e4d f8731216 78fb7fbc 0869d30e
+          7a56fb47 68ea240b d6c21184 ba7c685c
+          b010feea 3ae37a3c 5d18ca28 9c390d99
+          2387d280 16c4ee41 5ed3437f bbaceae2
+          1f4f8c45 d93b678f c477358a ea2b2e96
+          9b1e7cf4 3aa70733 69116b61 f3b830ec
+          ac53f5da 67c3ba3b 210095ee 5e34bcf7
+          0902addd 7a8d3549 b6402dd5 8bb8fbcc
+          ce7ccace 154ac762 dad1dc26 5a3af9d0
+          0a71030d 6da87d6d 9e766a7a a043baa1
+          3094f5c2 5f57167b ad5a54e4 4d6c7eec
+          c96bb75e f8231a63 0f507dc9 85687ae8
+          b16b849d 7f486b58 4ddd0395 da359119
+          69897dbc d381fa37 df87afae 9e99bb24
+          440d2098 71c4b48f c67cb470 a7ce41ec
+          94c0137f 79b7f847 3e14ff18 0f5b24a5
+          ca03faaa 6a50f7ea 3c3ddd9f 08ee9c2c
+          549e7e02 ebf1ac2b f24e6a79 e2e91bb7
+          9c77168d b1fb57ec 8788ff6b 8675d318
+          567aef40 cea40390 b5ff28bd 1cc6108f
+          1b6d1f7d 86ced5eb a04654b6 44494e6a
+          63d1e8b3 bb10d277 62dc6975 0092f40f
+          f1d31ada 3b19e389 76355d14 9dabd6a2
+          63d11722 acb813fa a4f4e143 5138f3d0
+          c49c1149 36dc88c5 ce046287 d114bb8f
+          aa4b2ecc 6c7ef41f bf1202fa 405ac362
+          132a271b 95a71c3f 106f8d10 1f136969
+          47cb679f 23dced63 f62e0989 0680cc23
+          677e39fa 9df9cbf6 a8c0d350 83c15531
+          6029cd9e a4224fab c7ebec43 d3fb0b10
+          6e6a06ec 09780c21 ec8a674f 476a4519
+          8f5c5810 2142f66f fbd70bd7 6cfdf139
+          ecb8b51b d87ae179 687ef489 6b6c8ee8
+          91e03948 4ba1ed84 685bb37a 4b944456
+          e4e2d9fa dc2bf035 b6b2ee2e 79a9852c
+          3f25399d 3b7dc075 a705dea8 791faa99
+          b3a7ff23 1a4435ed 9ea4224f 8c0a7f4b
+          1b6a5f7f 67e0bec3 048ee76b 7723567e
+          ff44d8d3 5269400b c625d9ae 1cdffae4
+          b3b76d3e e74c5ae3 3b125394 b3c4f37f
+          42da3164 5bcaf14a 283ae230 a4565624
+          f6f15e0f 9ade9d8f fe9a7ae3 4318c4bc
+          fe0058ae 06fcefec 9263ded9 4fb0a5a6
+          21160e7d 28c6d352 ae2d93d5 d188478d
+          a167c316 d4bffe5e 42ad5334 dcb93918
+          72d23103 2b4bfa1b ab8d197b 2ca29c1c
+          0b058ea3 31769daa 4b2e18d2 fecc8b17
+          d9dd28a6 35ac25ee 52cb4b51 347b4662
+          35746241 ddbb6a2d 5a172f85 e20f0ef4
+          bc234947 3488a6ec a30e7f7e e4abf394
+          bd22f034 46bcfc96 923577d6 53d100ea
+          f80a9257 e4298130 da97af44 d7b2af12
+          3e74917d e018144e 9f02c946 9167356c
+          6e5474be feeef55b 2f3897e2 6457c4dd
+          c53f46cb 634fde0a 84a68af9 c7e5b385
+          70a4a660 f8796740 4ea42446 96a1fa7d
+          a87fe303 043b7a28 ee923806 c76258ae
+          86c32fd8 323277e9 afd8a5a1 61cfc941
+          2c129ea7 c6f025dd 50128f2f 313a42dd
+          fd687c77 3e948e2e 11c11368 9d223ea9
+          e4e82390 525acc1b aeade790 64c48287
+          b63ff5dc 0d9bcff9 3eedb113 6c3dff6c
+          b43efef4 3592143e 594c216e cd5a85d8
+          80cf1c72 c2517064 6624b6d5 ea71a3f6
+          b577e16f 6c12538e abe86425 1a4063f6
+          31b39f19 fedc4bbb dcb56497 b5fff07f
+          3d1fca3e eaf067c4 17d1c057 91c4315b
+          8ac1dfd4 82ea97df 42424b45 e1a06c6e
+          372acf3c 0936af9b 593ceb2d 0aec6a28
+          7caad2d1 7636ad91 384a57c7 a4682874
+          9eb05f3a ad612171 27cbc89b 3201d913
+          0e10115d 35fe1ce1 5b5b3ff8 049d5fad
+          811a5178 04277917 cb5a95d4 ea5834f2
+          a2232f7f 97ff9a5d 16788ea2 62f11528
+          afa9acc5 4b7e3fa4 a8e859bf 194def7d
+          0ca4788d 3f4155e1 292a44f9 f14742b2
+          cbacffb5 18b21b85 bd1f2fb9 7ceb4f7e
+          348cd630 a6ead20b bc3def2d f8addd8d
+          d1b486b5 82b8a730 0fa5df3b 32b11653
+          361b424d cd68fe78 11227d7e 8abb2426
+          1a405bce b1739f1f f6f7a703 dfc9177f
+          974f1efa e83f03d9 47cffe9b 1a442d5f
+          49723ba2 68308496 4f97a0e7 abd57ad7
+          f444445e b65899e6 4c3c505f a5329367
+          a9f122c5 14ff84b6 7f3c73db d69f9c47
+          7bc461eb 05e7a2ed ef4fdda8 467cb379
+          6ad642a8 42afb95c a838e538 d8b545b3
+          d12a5813 8042e055 bdf03a82 6d9dac7e
+          49f278ab c6f0a9f8 ffb3ce92 d2efb6d8
+          fe2e9fec 2aafd0b6 64de8faa 58c0d544
+          f20fba70 772feadf 781f4a6f 9f717d9d
+          7058b218 1c65271e 8d94e26d 29668a3c
+          eb0c179b 102bb1f0 316dff7c e6e75b7e
+          f4031a64 0704abb6 7c5f2c9e ce13f672
+          d21ad620 26c49de4 b4a178ce 0ca4ee57
+          092809dc e5ec72a2 eee537d1 bfa57a40
+          0c32de26 2d5a0bba dce38e7c acf2fe47
+          fabfebdf f59dcfdf 54fce9c1 60e62193
+          1e0dfbb1 85832ef9 459eafb1 19352fbe
+          9170164f 3b1d36f4 cc9361f7 7a74c746
+          2c345cec 48554391 0b02ebd6 4ca635fe
+          9beacb7f 5aecfb62 d5953617 4a680dab
+          a8bb013f 9a36a414 f9874f4b ec5085f0
+          b5bd6bd6 a3ed8b15 5023518a bb248fb1
+          8a8a4592 c3fe81ab 72e877fe ebbeb3c0
+          730f1f81 614f3db3 30f7b8a3 3e14ca93
+          243b42b4 75add980 d60f3e4d ec2a3345
+          81a7bc14 25471d0e d965472c 4a135a09
+          215e8605 376cfd8d d602846c 2fee2e42
+          cb237fff a31ae89b 24bc3043 b655f49d
+          58e4bab2 3351f983 936173d8 757f1a3f
+          e04b503a bb51f3f2 db50fa7c 1477492e
+          eec27e6c ce9e7ad0 23436ebf 7bb7a8a9
+          ddd241c7 3d6c78c4 96e27e40 28cf351c
+          8016d078 e1081a3f fc14be4d 5bf4ba10
+          43220af2 a61f8cec 716320d9 2566f2ac
+          840c5bd4 ef9bd3f6 f813bfd5 aee02203
+          fdee5a1f 7dec6788 464e921c e0f56e56
+          11778ab6 e0b1a354 2c765d45 05ba5f34
+          c4e140ed 1bef22d0 d44a0326 395a822c
+          f77bc7bc 39ec89a7 3e738f1c b59bdcef
+          6ea2e457 b7aecc3b eea897a3 2184f9aa
+          929f704f 2f6a5e7a 0b6a2081 433e5a3d
+          9ecd8eb2 938e8157 38369eaa b5d8c2d4
+          0eb71a56 ffaf6fc1 c773680d a0efb34f
+          c6abc1e8 6542dcf1 5e3fab88 3b55bbce
+          5146eee4 03917df0 78209440 987439d1
+          3aff3374 ae5c3b90 b963f224 899da4be
+          35bbc691 95fe0ff7 f01191dd b7bede4d
+          78c78e8b 3972321f 56a26c9b 629501d9
+          5fd3806a ad1e2f91 5b2e1405 ceec4c54
+          9c7a2c9c 19295023 34a19590 9d288eb4
+          b6df5a7d c5c55956 b643cd35 3ff3861b
+          5afe28bb c0163216 c35b5a84 92e3e640
+          966d099d 9a0dd637 a1f1fd4f a086e82c
+          939d6808 6adef1c7 bc5674cd 2fbedaad
+          7e7777fe 658557fe bc21f7d8 a3fe21be
+          d84ebe32 2b446da0 7df96ab4 7dba44df
+          4a302418 42eac8e1 289e3515 76af43df
+          b2209619 2b52b4df 37a5e5a1 876fa9be
+          ec224b9a a0fa8a4b d07cfffd 3746fbfa
+          666b5bd7 1c14d640 5bcc3a53 bd283b76
+          2e1c5999 fa62d748 dc419650 f5fc6b08
+          75f73173 97e468af 5b8962a1 b330ef61
+          efb8f1bb b58069b7 0abc9409 93e02a29
+          7c5e7cb1 f3d8a7c7 1ac4a22a eadffe00
+          fe9a3afd 8e444322 11e4cd9a 86acfd47
+          0ed4e371 bbd63a8e cc0e59bc ef1f76be
+          f8dc1956 fcfebb5e 79e164b1 a8b940d8
+          81e2ce2a fe516839 bbd789c2 9987227d
+          cc707d91 6b1c9565 d4bff93e 7a37d7f0
+          9e590ba0 84d09377 dcd14f17 fcf4b2dd
+          de4f78b7 0f9ffc0b 7eda9535 f5e02742
+          7ed452e4 5963f511 eeecd7b7 6ad5449c
+          97108436 97132527 1e859492 0266f12c
+          866c4776 3418f955 ed755756 5ae9fbae
+          bbe1dac2 687fe0d7 b213051c 05161177
+          db5aa2a4 0f2d43fe ece9895d 456693d1
+          bb71b37e 5b05c59d 45e26714 efb88696
+          bf9032e9 a0ddfef7 db6ebef9 e6ddfa17
+          6a9d9753 0f9adc18 aea9cef5 6fda3255
+          b631c19c f4835438 a2507b2f a2a10032
+          0fd04eff 18a4e514 05f69c2c b8d352d1
+          b7652b22 fd612dbb 432c3158 c4e80847
+          f2fb162c 29507d3d 2f661c79 74f28bbb
+          9b7e2e35 dc7ef75f 634a788e 447f681d
+          812716af dee21c54 9c79125c b9392292
+          470ca37d a4ab075b 9e7e09e1 ee3ede56
+          61017117 f2636bf6 6187fca6 e4daebd7
+          384bcb76 ff827a4f 7ce1de71 13822907
+          8c7e38ac 602107a9 4506ab03 6859b414
+          1d5f2cd7 af31881b c6b441e1 0b207ddc
+          18141c36 0536af93 872eac34 56c4f090
+          1d38a5e5 c13fff34 f9c5dd75 68f8c35d
+          378905cc 59e2a137 b4089a3f 73a4ba50
+          7cc47478 87550081 a071b417 0be3ba37
+          df83afb6 95d93b0b a084819c 638e7e6e
+          e8030f2d 489d72e8 1ef937f6 d830ca3a
+          e194eabc a3e63e29 be893ebe 4aab7835
+          11d0def8 10818646 1856066b fb174a14
+          79874f43 f6d8e1ba 7f637f3c 0b618353
+          72ba6eaa ffcd8d63 92f9db6c baf7ce13
+          8498bd86 196aeba0 3573971d 36e44e3a
+          00d95326 ea8b5943 71271eed aeeff665
+          abb413e7 24d917b9 dad6ac82 c56993c6
+          3fee1d37 7e8fa537 f698c04b 9b361de9
+          074f7e45 7c13af73 dd6a9141 2b4653b0
+          bd07352f bd8da856 8f6794be 8d44604f
+          4d41f1d1 b3915a51 c47a3c4b 0d16b11e
+          08044beb 6ebdedce fa5b7f95 9e8cdf62
+          c3ef7e33 4c76b96f 17623693 2fdc2aea
+          6e60a19b 3eb41485 471e0ed9 9ed86d15
+          feba06d4 bf3d9f87 ce2c8212 466fde51
+          73ff9e71 f4b19bf6 e83a7a77 d7e07dfb
+          6fb7f9c2 1bd635f9 ab1a66da 9cc8e66b
+          b540dcb6 01fea66e b1748822 63f8d0f8
+          276b3501 1851e0c8 cf85d3ed 425f550d
+          14ad1e8f 670c2d23 f2641b86 77bdff89
+          5376e0dd f419b392 47dcdd76 6b6aed2f
+          6f79488a 29d3c1ed 36cb8ce7 a858d7ba
+          f3d230e4 84a390a2 f93f7fc0 60a12b21
+          d2db8faa e75e157e b35d3b84 442c304e
+          4221bc54 7cfe797f cafde1b9 7ed30a3c
+          57790552 274e6808 6ddee208 5555cd94
+          6470f85a 0011acd1 bfb50e9e c23c78b4
+          2b790c33 790adca5 c5880503 7af3e458
+          44650d8a 95449e1d 933bdffd b8cbe6b6
+          7d917ed8 4cd37f4b 8d77fc01 3537fcea
+          2ebb0be7 51dc5987 8196282e 141d3e15
+          b9d3a600 c104eaee 622aeae7 7d80b6c5
+          6bb47b9b 49b2bbbb 81fb6637 654d9d72
+          53fef93f 5eaf6924 d30a3c0d 675131a2
+          9dad35ad 6fbd37d4 e9c418be 628b38bb
+          28e0ab6b 46fa880a 38d2528d 459e2cc3
+          535c8050 7b3bfc8d 6ddf4c06 62099167
+          b33930b5 73de475b 44805c9b 7ac85421
+          f0cda98c 9aeefe23 aaaebbf1 c70e377e
+          4f716725 8737e0f3 b2c78d40 e989470b
+          7726c7df 9ad5c55d 0c9d2b56 a3eef5f7
+          f5453149 7ea20a22 99b38ef8 63e59fff
+          f45cda94 a97bfcdf db2b2e28 65fcc4c6
+          ec2907dd c7de7816 8ad976c0 dfdc89ba
+          37df87e2 f319abb5 7018f6ec 2cb1fa3d
+          0ca9e5ec 8f674132 1d1e3cb0 e5fa1b4f
+          936ce6db a38f45a3 68baf76e 6cb9f617
+          a70b7177 37eb8ead 851a1671 ae3c1f85
+          7366c096 96a6d717 c71577da 365d5b07
+          6a5eff40 8c1d16de 5922266a ef3c8279
+          79a79ef8 5ccaf849 7be5dfdc e3193c0d
+          57c550a4 4d9ad0e4 5fb7c119 aca9992e
+          f39a1e4b a0ad4a7d 759db0b9 65a4550c
+          11a2cfbe e33b18f5 dc7504ce 825cd89d
+          4ef4d7d4 b21ecf7a a4086d77 b83d2df3
+          c3d4430e 693295f3 164e6dc5 d469735c
+          1e3c2a82 772e5fa5 b5c49d23 dd83d263
+          8e40e6c1 1380defe f80b5a31 56a2fe00
+          6a5e7b07 3d1beab8 356b0975 a76fcdd6
+          644e39e8 17b93f38 6ba5ab62 eff479df
+          2b024fc3 51541c95 a15437bf fcfa010e
+          27f6e31b b7c8b8b6 0fd4e3a5 14e7c353
+          906f9cc9 1302d053 52a49db0 844fabc7
+          8bb21ecf 62abdc94 b637de19 ebcacfae
+          f67df955 55eae449 a6f8ba5b 1e7af0b0
+          bef7df7a 587cfd15 7c8bd641 6f892256
+          25f9d326 a070f661 90220ae2 1e85d5b6
+          6ea351b4 2e5a8aba 7716c1e1 a10d2db1
+          0850f4ce 2277eff7 e0fdcfa4 1e3275af
+          3504db6b 024f23da ddd51b5c b3a62b58
+          d374b8cd 8934be76 4b046cbd e9a7bfa9
+          4dbfb2c7 91911edf 016a7fe6 b0c39397
+          8350473b fc0ded03 028f5b5e 9641f886
+          b2b657e7 1dd6fec6 1b2dceac cc0d6987
+          1c32683b 24b63efe 047c4b17 cddcf4d3
+          4b1fb23b 319a6fcf 62024f04 eecc3143
+          517afc5c e1db3206 6eab90e2 384341ef
+          962a54bf f096fec9 2c59b242 10d4af20
+          5e38ecf6 dffe2e63 ced1ed7b d597ee4d
+          81e7aa1c 8af42907 d5f6af58 698f34d4
+          cf90b855 6b8df16d 07021d7e a1f00348
+          ab2c87cd ed8e2ff2 1405b6ac 4c385353
+          e06ba847 b8c3c7ab ccac26f2 1cc81682
+          e9e8d6d7 df093b73 7236a64d 99e21f6c
+          5f63cb83 0f62c345 3f39bef3 f5371f70
+          7a318a6f cd5a415b 6b89e229 48c79063
+          e72265d4 7e80cf6f b8351b6a ef14e2ee
+          6de1d73a b4850cb1 c03889f8 d1983669
+          e2cf734f 3d6dc9de da9add27 024fc351
+          58147566 676c6d7c e6f9510e 27467204
+          5824600b 81e6ab6d 83c3eb42 ea901248
+          0e47fc7a 3c21f29c 85f9b0d9 64bd3f5e
+          d4afb01e cf7a3885 c89bdbf2 dadb699e
+          c2dcde60 554dad77 ccfe8322 9bdbf2f0
+          c3251b2e b9e42cb7 17778bb1 5dce5765
+          2db4cc9d cded42c9 ec69c899 769058c1
+          860c1ca0 0c351442 d3870bd0 faf91ae1
+          0769432b a0468194 4907df31 e2ef7f7b
+          3275caa1 d1bd1e77 f7b6c0d3 883437f7
+          0556aca8 0dd5b71c 293b90ce 61608d95
+          8c465f95 568f5700 77419ed0 7109d4e3
+          15e5231a 0ca2bfba 516f3bc0 7a3ceb21
+          168207b5 bff2f611 cdcf3f1f 72e76675
+          861a5bba 3cfb0d13 63612f2b 7e311e3b
+          5e7dcddd ffe9c7d3 d75f74f1 2f3c5edc
+          80184b4d aca7ee06 ca4ef20e 1e8be2a3
+          8f806c17 e3301abf 254a4cf8 aeee956b
+          50fdea7b 5a769a15 27162118 c282518f
+          3cf8dbb4 c366b6ef 8b7f7f9f 083c6dab
+          36e3b0a9 8d3d8b97 849596e6 592268b3
+          0b901534 9e565f1c 8c21d8d1 89b48a12
+          3832330c 03aae472 e9f578e1 ae76f8eb
+          db07b278 f48e9643 04c54ca7 13c7b5bf
+          366f44d3 73cf4aee 9c0c35d2 d9dded2a
+          2b8beaa7 b3f7643c 5722e89e f74e6eef
+          47ef4f5c 73ceb967 75bffec6 5d2e2fa6
+          80dd2d2c 8926eed2 caf35172 ec6cb8cb
+          4a804030 fed6accd 86407d23 b6fefb0d
+          28be2064 ee445802 2580c6d4 71075c92
+          7de2895f ededadd9 6f626e6c 1f5e7ed7
+          fbc1bbf9 cbe71c75 afc783b3 381caca2
+          f2c4c0f7 0385d3c7 a34c2f4c 4ed74f95
+          c5137948 4d41ff9a 0dc241be 3cd07685
+          6d05ac3d 84065a0e ac0c03ff 1875d7ed
+          5fa64c98 d42a7ebb 5b3c7de2 f1a92ad4
+          d4891361 cf4efc76 44a5bb1b fd4b9762
+          5b0b274d 316ae71b f3c493e5 5fb7a660
+          fd65571e 2356a167 39bdc8e0 7da1d616
+          7776af1b 43bf7f3c b2671c32 d01225ee
+          ca4446a4 b70f75af bf87a605 2b06b666
+          397e927f 9c081fe4 1a79e075 a39e7dea
+          2f9ed163 23fbcc57 ee5381f7 c97c6cfe
+          e9c50784 d6ad7fc3 e6c1100e 0b6ba00d
+          39350454 9e7e240a a64f81e4 70c61779
+          5a4477bb d0fee962 54fd7b9e f8dc200f
+          5d9081ab 8cfdfadd eeabc52f 578a67b3
+          786a8537 ed1c76cd 15bd5927 9d9c70bb
+          ecaeb7de b06db9ed ae6c21e2 72c42fb5
+          305c249e 43c53341 fc332942 d8f12278
+          abfb2d55 1b037694 cc998292 636643d6
+          ea88e3f9 2d9b0db1 4804cdf3 3fc3d617
+          3f604b14 0b110ce0 83099f7c 747edaf4
+          59b5fbd4 47c6f6b1 d70aac5b 8db5a79c
+          71ba52b5 ee6f92cc 7a3c2bad 841da96e
+          0c3bfb14 64ec3f4a 1b88f123 a8e62cc3
+          61d4bffd 011ade5b 02c91665 3d1e3112
+          7e09a30d 2507451c 8983766a 3667d248
+          949f721c 5cf9797a 8155bc41 189365f4
+          ac5a874d 8f3f0755 89f29098 55c64900
+          2dce9123 8e1df1b7 47bf4c3b 6cc63efd
+          5af67988 f48c1e8b 918fffed bd50084f
+          80c96bcb 203b8150 77100def 7cacd7a7
+          685b19f1 678d7090 2929c89f 7a10b2c6
+          56889531 6d48fe37 9a48b37b 00e74e3c
+          dac753dc 911d2e48 c380b728 1345d30f
+          85aba870 a0ee2eae 839311a8 6b40e307
+          0ba0f829 ee2ce37b 54849c23 46dcb2ff
+          abafacda d7e26e50 083cdd28 51b5db5b
+          59f16735 84cf3944 ac3213b4 5a16a07b
+          53035a17 7d01a5b7 4fcfd2c5 251080ab
+          b4042573 66c05d90 a13b5d42 08d9c341
+          1bb2c389 a2595391 367a38e0 0f181eaa
+          50fafad0 baf00b74 adaf838d 2d512c43
+          2884d747 3dfbd48b ee91a307 450a6250
+          08bcb469 8761ecbc b7b6da4a 865c2382
+          76078789 85449e1b 68fa6819 3abe5ca9
+          5f4ba65f e5b32334 a7eaf723 75d40894
+          1e73b85e bb175369 4642c89e f3516a44
+          42c16107 2267d2f8 8134afc1 5564b168
+          149d2bd7 a2f9d365 ba7fe3be 943510da
+          a5d63364 c88d6a38 d23a58be a64153c5
+          e41e311a e33e7aff 73b9a8f4 26317f82
+          1c2ed640 d36cdaf6 45e37b0b d0bb7e93
+          5eb76278 7f8f70a0 d963c7a0 68e644a8
+          8a4c074a 08d92344 c340dad0 02e44d99
+          045b663a 103138b7 a33566df b4150df3
+          e6eb7e89 57915944 dc451090 0b4bae1a
+          f7e1fb9b d3a64c1d 345fd7a0 2a53770d
+          1b1e19f7 eebce7c3 413ccb21 631d6407
+          e06ff7a1 71fe4204 1b9b8cb7 6a150572
+          7a1af2a7 1e8ccc31 65dcaa25 84ecfea0
+          1dd5ca48 9c28993b 13de6115 c6757776
+          3bc2ad1d 68f9ec0b f8dbfa75 bf46929f
+          580caa9c 57f48f03 177cfc91 d030832a
+          dd30e8ce 21460381 4e675ede adb12836
+          71e85807 ed0463d7 aaad68fb 6c29a2da
+          9d8e4622 2f1482bb b418a547 1d014796
+          4777c684 10b27ba2 b626f06c 42dc4d43
+          d6fe2385 bf3128a9 d2b66695 08da977d
+          85962fd6 f12a320b 110962c5 b84fe6df
+          ecaa1cd6 35d8beb6 4127f052 264ec6f8
+          258baa90 927d7e4c 859fc3c7 3a0ed5e6
+          061a3e5c 828e652b 12dbaa15 222f6dbf
+          a1283fe1 28e18ded dcaa2584 ec9e4443
+          18c81d3f 6ca0eece e542dc15 a4d612c5
+          26a36bf9 6a34bcf7 311c2ed0 1759256c
+          a9f039b2 b3af55fd 8196c1f8 f50dca4e
+          62420963 fcf2a55f c65c1937 8b89a270
+          185903bd 1e4f3c8d 1f2d44ef 9a7580c3
+          608f432b 76161f9f 3966248a 664e4034
+          c4821742 c8770cda 22e2b8f3 d250346b
+          1a5c8579 40d8207b 67b72350 5b8f9685
+          4ba1f8d8 9fd34249 89a8d028 378eff72
+          e922efb8 0307e597 386887a2 abbcd23f
+          61e5f2c7 2241bccd 91641db4 fe78fec6
+          6eb42c58 8250a358 14390cae ac50a2b0
+          6766a0e0 b029c818 5ea43750 2684905d
+          0cda5055 19a547ce 42da7e95 c6e2ce66
+          43b4b70f 2d9f7e8e ceb535fa 2e04b1c6
+          4811dae4 7da1519e 115a65d0 1e0a1dd4
+          6b0d3518 ec94ddae cbc5a46b e678b20e
+          5ad3d9f6 2f37a165 d1e78885 c2f15ba7
+          6884c37a 3d5ed909 470a076b 63eb1442
+          c82ea184 24141f3e 0959e3c6 0c6c27c4
+          6b89a2fd b92ca163 c56ab42e f91276de
+          916da585 40add026 17098dd2 3698bfcc
+          412df03c 234763e2 fa753551 c57e1676
+          eee62162 726cc259 367fbc08 6d4b9619
+          6fd56a44 2248ad28 47c5c9c7 420d73ab
+          9610b293 09850890 31b400f9 874e863d
+          233dfe3d b31a2e27 7ad66c40 e3079fea
+          37eb706b d63a4345 68928b85 36a9d334
+          0a05de77 c0555e19 9bb865f3 e74a50ba
+          85e3ca3a 68ce526b 7fa26dd5 f6aedd00
+          b80d96c7 62a52dd9 edc8dc7f 240aa61f
+          88283b29 12421244 4fd48975 61e97173
+          e0292bd5 178c7111 be26d2da 2e16a05f
+          c2dfd4cd 96281642 d3224293 7c22b4c9
+          a04f3a99 62cde11a 52ee9b54 537d7f34
+          80b738bc ac83568f d757d38e d6058b11
+          6e6937ae c7132b6e 4756268a 674d434a
+          791e541e cf218424 802a1684 e5271f85
+          f46195c6 993bad64 447c4ccb a78b85c0
+          5bab9794 106b2034 c842a145 1ed43489
+          2962a859 0c2bd96c 9d629175 a9f8693f
+          879975d0 ebf1966e 408b1079 df38d7b8
+          cb2b05ee a27c549c 748cbeaa 663d1e21
+          246ed00e 01d9e387 216bec28 486ea776
+          cac220e3 e0d4af56 6c59b84c f818f643
+          b11011f1 b6cf165a a4dd2c5f b069049e
+          b3a81813 abb6d647 02f83ec7 99b590ec
+          31e14c3f 47dba79f 1b6fd56a 8859985a
+          3904e542 e4a921da 8f10b203 57a1b544
+          c94945e9 31b3e12e c8d54fe5 c717772e
+          f83657e9 d9bb484f 50bf6691 5844dd05
+          70a6d020 759a16a1 c0dbed51 5e82bba2
+          5299b475 eb7c61e8 3b39dcac 24f084df
+          ed8fa055 88bcfeb5 9b008fc1 9e482c06
+          d9e944f6 01639077 e858d6e3 1142fee7
+          42502be3 283df608 a4141702 5183cc9d
+          dd8698cf 87d6cf3e 47cfc646 c83c356b
+          257177a7 d01eef68 1ac44c17 0c9beedc
+          8fbbb2d2 3f796bd5 6d6a18f3 39ecac83
+          b6dddab7 b505cd0b 1641e9ec 32aec753
+          55383233 50327706 3c459988 f12a3342
+          c8762862 e1573c67 0ab2f61f a51f9a88
+          db124517 7876fdbe ecd6c52b 61e3d6ac
+          65105a63 bdd01c7f 14dac367 b6afdd94
+          07bbed79 b95d4a14 178b9ff6 71f85904
+          ade59470 aa1dcbd7 a369fe67 03b57846
+          2ba9980a 4f7e1e86 9c70a47e 2a37469f
+          4c08c140 e62eb52c 1705874e 823d2dcd
+          b8ee2e35 053d5fad 41fbe75f 8ac562d4
+          a49193ec 0261a135 4e159aa3 c38c5fbc
+          2987a9cd ebc5e48d eb374502 389fe3cf
+          4268a355 8da26df1 52747cf6 3990e28d
+          afdab43f 12423073 c47e283b 7e0eebf1
+          082103ae 4168b48a 538f85bb b030fe3d
+          b31a2e27 82b50d68 fe6411fc cd3d6c89
+          622184c6 b854688d 0d9ae6a0 c0db6b5f
+          b50cf7f0 91d1491b d6bf2e5e c0dd1c86
+          d641abc7 0b7707f4 4317be0d 9b016f02
+          f5786e17 72271d88 dc8923f5 13738410
+          eba20484 b83b790e 522bcaf4 9d01a358
+          a39dccd7 5a3575ad ade6a959 6b89bba7
+          85c6f897 a6350cbb 3750e0ed 7edc2346
+          86266d58 f75bc966 5bcce168 1d6421f2
+          7ab734a3 f9a34fa1 f6f50fd4 cf18883c
+          477a1a4a 8e9a0577 760a5ba7 106251b4
+          055ed6fe 95c89978 0064972b fed6acb6
+          3b90e245 fbe26568 5fba424f fb49bc24
+          c71a8904 9b6dadd0 1657088d e13775ac
+          34fb8b70 8f18d513 e98f5e8c 18ba392c
+          ad32fbb4 91aba26b f506347f bc50bff0
+          3b11cfeb 2d2a40f9 a9c752e0 116241b4
+          79efcaf4 a2fcc423 e1ccca34 aebbf37a
+          d0b77a3d 9a3f5b82 706f505f 58122b0c
+          14a84253 fc4c688b 0eb37f2b 49512a3a
+          7ef9972b 94202ee2 6db5d641 b669ab71
+          052d0bbf 40c7e2a5 62a5ed31 3e452184
+          60e6a8e1 28993b4d dfa62184 5807edea
+          c3b2efcd d6177a86 688bc660 086d5a6b
+          a6aa168a 3bcb0c12 fd74f5d5 42537c9c
+          14713219 be09eff8 0938f08b cf5f8e86
+          f000d80e c332684d 4643ed7d 7ad351ff
+          969ac4ea f15c2e14 4e3f04d9 e386e997
+          8b134292 9fa858d0 15cd9a84 acb16306
+          c49bd162 d0ed42c3 7bf3f553 fb921c33
+          aed523e6 47bc6639 c5f382d0 127f139a
+          22299444 d21cf64e 997c50e4 80c50b6f
+          91335297 3293672d 91d757d5 84960f3f
+          452c1018 70de0622 cf919e8e 21c7cf85
+          33cd038e 1542923c 6e8b39ee 2dc946d1
+          e1d36037 3c792ffe 2c3d0d3d cb57eb2d
+          51a2a130 6fabb00a 2a6a94ce c095424b
+          f892e55b 4aaa6e3e a9530e6d 53dafbaf
+          102faa9d a3d52202 4f1ef0e0 9dabd6a2
+          59883cad a541224b 356f7101 2a4e3bd6
+          b0430221 c4ccea0e 7aa6bef2 8ce3e1ca
+          cd19f88d 78b8dd08 d6d4a1e9 a30508b6
+          f6706bd6 2a280846 c3b868ff 4fe63724
+          d3b79574 ed1ac77c f0fec268 043f8f29
+          e0069c55 449e4d6b 7d1041eb e2a5e85e
+          b612484b 35de8291 24648e1d 2d56f507
+          b3750a21 498a08da 1872c22c a40dadf8
+          46f0c55d 2d2a0a9a 3ff8143d 1b6b07a2
+          23b76693 7f0da020 2634c32d fb7ff4c1
+          3b69d367 26d5f796 74022ffd f0d918f3
+          fe3bffb2 e7653fc1 d3921612 79622407
+          db7ad0f4 e12708d6 d6eb3534 8683df61
+          47f11133 90be5f09 af322324 c9d03277
+          99a387a0 60eac162 11281b6f cd66a4a2
+          f5d325e8 f86ab550 86d181dd 0192dce2
+          4ed56ec6 cafe4868 8607d267 1d9174df
+          5f520ee1 8cd94786 a33dddb7 c422607f
+          3ccb28bc 81a76f6b 031adf99 af3b68c3
+          e694c2a7 3b32d351 71cab1b0 7b9d86bb
+          378410b3 446ec0a9 b54439e9 18d85352
+          e2cf6dbd df5d0afa 56ad458b 107891be
+          a0de509d 58609844 502bb4c2 cf846648
+          ca6b4f93 768d32fc 99971a44 c0bf560d
+          a1916976 8b683c6d 91aeaae8 5cb9064d
+          1f2c1868 9d62b8cc 57915251 86f2538e
+          d3efa724 84981f6d 2e971d3b 1bded212
+          18aedcec 0e447b7b d1f4eec7 f037b6eb
+          2d9848f2 27048436 104a1e57 09adb036
+          59bfcda4 157859df 3b11235f 7ae933d9
+          8e5bd400 22147916 99b75a7f bc40445f
+          89f7ae58 6d7c5fad 4654d5af 322b9836
+          81228f10 b38bbb08 f4b99c23 e6b46133
+          630daf1b 8def7c84 eef55503 6290b122
+          f9c55d00 8ad0067f 161ae125 4d2b50e0
+          9951e49d 7032863f f7c23f5c 6545cfc4
+          226c8861 99f92b46 75a8a307 0df3e623
+          dcda0e38 1338592b 49283d66 3652cb8b
+          d83a8510 93a2ade5 bc45d928 39fa08c8
+          0e47021f ec45e7c2 2fd0b6e4 4b210c15
+          b644b1c2 18110b00 a1093e15 dae02e4d
+          23243349 5f469a75 d2a92120 7ab378a9
+          cbb832b3 ce0a4da3 afba0e8d 6fbdff8d
+          803372f6 8eac4cbd 1ecfe675 1926fd08
+          21832d72 0f4cfd8a 538f1fb8 8acc6812
+          bb5c0835 b7a0e9fd 8f11e90b b0258a45
+          6283d002 4d42135c 2bb441d2 b753b3c4
+          39a1b23f dc5965f3 daaf57fd e2c552e4
+          59631e6b f5788a8a f6652bd1 f2d1a740
+          6a8ab1c3 5714a48e 1886b2e3 e60c0c13
+          8a3c42cc a3efc47c 2d9e3b03 e963460e
+          1cb28a1b f96411e8 236878f3 3df81b5a
+          b92d6b11 71273440 bfd00237 094db0cc
+          0adfb225 045eee0f cfc5d0bf 3dfe912d
+          d5758778 c1414e66 8bcc67ad 3f5e4841
+          931078fd eb36001e b7f12785 23c83f6c
+          0a72278d fb262b40 0819e4e2 4e053286
+          57a078ce 0c209240 0b54e10b 5a3f5b82
+          8e156ba0 8a85205b a25842dc 2942033c
+          25b4c0e3 9a26a0c0 4b22727e 70362aff
+          fac85f5d 15652fc6 c2bcb1d6 32f35a8c
+          f070771f ea5e7b07 91ee1ec0 6e7c9599
+          e60d4abf 37172925 85147984 98005756
+          3aca4e3c 5abf6b3a 1171d7bf 76039ae7
+          2f821a89 b2258a15 16006131 462aca3e
+          171ae08f 9a16b00a 965ab7e4 9e756ec0
+          9e93febb a882e5cc e25944e0 49039aad
+          6f6b2d1a de7c5f8c f8045e7c 340a675e
+          2ecab41e 5a5e37eb f10819b4 915b5bc4
+          c9283972 26528655 186fcdda 6444fbfa
+          51ffd687 08b57771 6bd61241 400c0b05
+          b522f6df 243440b5 95be75cb 25a60b7e
+          72e97a47 66ea2f55 3f9a39b9 2d32bfb5
+          51ae423f 29d7f6e9 e7896dd5 0643481f
+          371a45b3 a743b6cb cce21132 08c59dbe
+          709f3c0e 79874c06 0241e3cf 7139d134
+          ef23f46e add13f5d 620c487a 7127627d
+          40c4fc7b 44ec9f6f b56fdf72 022fff27
+          17a3e2de 7bdfb167 a5ff252a 5e3c459e
+          7546ba56 6b53f7f6 87f06da9 019c0ee3
+          cff1f985 c09b81ec 7163064e 5f51e411
+          32a8c49d b7281fa5 c7cd35be b546c3ed
+          42f7b255 68fe6c89 7e008b75 77c92fee
+          448c5744 ac7f4ec4 fc47b4d8 4f816701
+          f27e7401 caefb9eb 7e5765f9 8bb108d8
+          dad62af3 5d4c78a5 b71f75af bc0da5af
+          3fa1abcc b44f2af9 de914829 2e1cf835
+          451e21fb 5edf8979 a8954f94 8ab9e9c8
+          c936de9a 75d8116c 6c46edab 6f430db3
+          df9d25c6 88d6efae b27ca188 f57f1031
+          3f60451b 58760d93 f7a30bfb bca3f7fb
+          8312613d 9e955674 1a3d9bab d138efc3
+          c456fd8a 02b71077 25c7cc86 333d8559
+          3c42f679 e4865e36 913fed60 646ab755
+          0442862b bb584441 dd6bef22 d8de49fb
+          59c4d78b d8de2862 fcef45ac df645533
+          583a499d 79f471eb 5cb959bf 8ffad142
+          91679d89 af65f29a 3e5e828e 652bf495
+          bd213e3f 320f3a10 85d3a7c0 e6b421c6
+          33d884ec 1b6db72d ab9e396a 3ffdb60a
+          6d6e1afa 6ea703ad 9f2c46d7 aa755f1f
+          922749ee e3454cef 17b1fd3e 11e3dfb5
+          b2292c2d f00a7f76 1586dcf6 fb571db9
+          390f8a01 e1e7c4b7 8e03d09e 9a97de46
+          b0ae09b0 25b05fe3 0fa068ee 2c64ee3f
+          0a924dd2 fb6e1142 f6b6c203 dc79d928
+          39fe4848 5a1dad51 4adde180 6f7315ea
+          def97060 ead3c727 ff105110 71950f79
+          41c4f67b b5184f81 6761f22f b85888bc
+          dfdd2706 c42b6260 b01ecf2a 1a4f38fa
+          48bf0f35 afbc8da8 9e0530f0 fcaa56f4
+          6347c9b1 73e02d61 3d1e217b 3d708b45
+          95dde344 f1e187c1 3bb40208 19343496
+          6584bb7b 50f5e21b 50836166 ee2cb278
+          57c25896 3eeda0bb 446c0f59 dd1c3c47
+          a48bbc9f 7665ce9e 7e47248c e5b486b5
+          445ed7ba adfa4d17 0915d785 42f0540e
+          41d1e1d3 e0cc4a65 168f90bd 28ee249b
+          8c9c0907 2077d654 a0bfdf50 b0c55415
+          0d6f7d00 5f6d130d 6811a201 b4baf272
+          ee4c993c 650dad41 81f70d9e b1077ee5
+          cacfbd53 0da283d6 b0d00470 000def2d
+          40d7ea75 c64d9035 45d8d78f 9c291391
+          3f65126c 6e07ebf1 08d9e3ea 6ee087d4
+          f262fdd4 2c4261e3 cfb1d9d0 b97c35da
+          162d1b48 ce337b97 f488d8dd 6fcfcd7d
+          60c8ef6e 79a9e8aa 9fd32014 78ff411b
+          10e5bfbb e5795b6e ee9fc540 f1d122d6
+          410b00d5 2fbe8140 53abf156 ad96e98b
+          46517ce4 2c648e19 a16715b8 554bc81e
+          d4772ae0 ca4e47d1 9c99b067 651ab744
+          110bb550 730b6a5f 9b3710e1 28eeac30
+          46547b49 c96be5bf bfe59efc 9f5c4a83
+          50e0fd37 f9175e82 f23fdc7a bfbdb8e4
+          55316098 9bb18cc2 d3eeabf5 a1f6f577
+          a1f8fdc6 1faf4421 793c289e 3b132925
+          f9dcaa25 640f8a3b 9bdb8ebc 8327226b
+          cac48153 b3069359 0d4550f5 e29b88f4
+          f4d38016 2112c2ea dcd34f7c 40c470be
+          740abc38 22ef828b bb0ace39 e3de7008
+          5fd11a16 d27836a0 73f926b4 2e5a0a55
+          49e0ac4d 2000ef7e 9528987e 089c1929
+          50b91c20 6437ab3b 2da12e23 73e47e28
+          3e7226d0 e74b28c3 deb66419 bad75731
+          ba590435 8c6e575e ce5dced2 210b690d
+          0a3c43ec f9854b5d b939778b 81d3456b
+          58079b07 689c371f bd1bb718 1fbad002
+          8dcf8fdc e9539033 f940d81c ec8f47c8
+          6ed5772a e02dc945 f1d14740 72bb4524
+          570de764 5f4d2daa 5e7c1bb2 9df6b388
+          b80bc899 390f95ff fe96278b afbe9e06
+          a1c033a6 e8aaeb50 71db6fff 2565e6dc
+          a34610a0 45ac4354 89a2fa85 3711686d
+          4f2002c5 f4ed5a6d ab3673d4 b081e9c4
+          7a3c42be bbb8138b 2567ba17 f9d3a6c0
+          3b42ccad 40d0f073 22bd7da8 79e9cd84
+          2ea821c9 b1009073 f2dfacb8 fdb77fca
+          bf907577 14783b81 d61f6fe8 1d7f7854
+          ceca7b4d c4715659 5904ed02 f260472f
+          1ae67d38 508f67b4 25145160 cfccd49b
+          20a70ec9 e7562d21 bb21704b 76193913
+          c7215fcc 2b08e166 340fd570 18f5efce
+          87afa685 872a2c42 2484cd25 575cfc90
+          88d5adb4 0605de4e 9377fe4f 5a4aafbb
+          ea4f9120 56d31a16 9a140ea0 7de93a74
+          2c5d2902 47c4f813 84104c1d 331cf987
+          1e0467ba 875bb584 ecb2bad3 1e09e9fb
+          95a378ee 0c7d6e19 7f4e0cdd 6b37a2e5
+          e3a5901c 34a12586 89823e47 56e6edb2
+          37f5435a 83026fd7 0de4722d 766464dc
+          2582364f e758e9bd 3b81da37 de417f75
+          adf1076b d9857e3f f2664e45 ae76f9b9
+          64e3c95a 42762570 8bc591b7 281b45b3
+          67c09e9b a3974018 116c6b47 f54b6fe9
+          0b336289 31128979 33eeafbc f3f6c78a
+          aebc9606 f97fec9d 07601cd5 b5f7ff3b
+          33dbd57b b78aabdc 7b917b6f f4920049
+          80d00224 21040810 1242420b 01420990
+          9084de42 33ddddd8 72ef45b6 655b9665
+          f55e57d2 f699fdee ccfa4bbe f7bd7847
+          808bb4f7 fc920dc6 d2be279d 3d73cfff
+          dc7bee39 24f0be3d 293ffb25 729f79f2
+          ad8039f2 49e6585e b2082718 d4639f80
+          36caccd3 da06ddc2 1eb51e4f 51903a7b
+          1a62f373 d92264a0 7a3c82f8 86e24eb4
+          19113f6e 34a2470f d36f89c2 122b1f4b
+          ac4e7ef8 25bc9d4e 3a9ae5c1 47d89a1a
+          b0446fca 7be6c9b7 936eb885 0c4202ef
+          bb9378fd 4dc8fbcb 33af29e6 c82fa81e
+          8f238d27 015d954d a85eb51e 7e47570f
+          eaf17c30 26272065 e664d832 e2a1d064
+          6382e861 e4565f02 4b8e0621 6df65426
+          ee5ca19f 37f635b5 7ca279d7 5eb41fa9
+          80209209 79c0eb46 75cee37f 789cc5e4
+          62b20609 bc3327f2 aebda12a efe9279e
+          650e564a d6e007d1 0c346d29 42eba162
+          284cc0e9 8a3ca70b 51238721 79ca7848
+          761a6546 103d4161 8f56645e 9a362106
+          26937e4b 14f6f5ee aa1a547c b616a285
+          ecc7450e 108057b4 d97ec7fe b896ac41
+          02ef2c38 5860b368 b13cccb2 4d3aaae5
+          08031379 e5cbbe82 b3ba56bf 3f9e8acb
+          8de45953 91386134 1378d43a 852042ae
+          ab6a4b94 680b12c6 8e82352f 1bf0ea2c
+          af2cc972 37b7e0c4 3b1f69b7 de091e9c
+          04b21cb0 bc3ae0c5 e79625df f673b207
+          09bc334f f2cdb761 c0cb2fbd ef938d4f
+          808e6af9 1178ffae c75b094f 4bbb36c8
+          5c6f7741 2565fa14 c40ecf81 eca5e220
+          82384de0 8681fd47 1d459634 771ad0d5
+          adfb30fa bb9da82b dc067753 27093c4e
+          bcc4a798 360ff8fb 4b7f49bc eec67632
+          0709bcb3 46e28fae f70d7af5 efaffb7c
+          e272b206 470f8a04 388ed7a2 7ee336f8
+          3bbbf42f 5d787d30 a7a72079 ea24d8d2
+          62b42328 8220fe27 b207881e 9283e419
+          93b51ad6 903be44c dc057c7e b41d3c82
+          faf57bb4 9bee44f8 e375a169 d03ffefa
+          308bbd54 774702ef ec93f083 ebca06bd
+          f9ea53cc f16ac81a fca08e32 abfb7a07
+          3a8a8f21 a0ceabd5 abc773b9 11336638
+          920b266a 2d1ca875 0a41fc07 35e9b167
+          c42165fa 64989213 b5a6e17a b81a1b51
+          f9e51a08 66b21f37 180c4f05 6499fadd
+          91c03b97 ab93b291 fdef6fc8 109cad35
+          4ca8957d f8059c35 754cf1f5 e0f1f178
+          91326da2 76e942f1 90fd0842 454d7644
+          93a01dcd 468f1eae 3f8a4c14 e16d77a0
+          f29315f0 75bae868 9613bc2e bc3ce8f5
+          7fbe9a78 dd8d54c9 4c02efdc 117ff50f
+          0383de7c ed638f0b 7f226b70 24f0d813
+          23bb656d 17c1d3d4 0248925e 2200984d
+          482a9880 98a1d9ec bd644382 50939db8
+          91f94866 c90ff4a6 c5080264 26005bf6
+          ec47db91 2aad5c82 e042dc1d 18fcf69b
+          ff48f8c1 752dbaa7 250409bc 331be845
+          245cfd83 ce21efbd f32a7344 da3ee6e9
+          a131026d 872bd0b4 6d1764b5 285cd4af
+          c7b3f6cb 40caf489 b0264650 3d1ec135
+          6a92133d 2803a9b3 0a20d86d ec2f42f4
+          123a15d7 9dd535a8 f8740344 0b6de470
+          828fa5c6 bf8360d8 671048a6 90c03b1f
+          224f9460 304ac799 23fe91fd 6b3d5984
+          1f247300 d5abb6a3 e3d8f160 61b85e86
+          e9f12266 ec48a4a8 c5e4a07a 3c824fd4
+          96285284 84847123 611b90ab bf7bc7d6
+          58777d03 ca3ef89c 25d5d454 9217dc2e
+          3c32e48d 5757c55f fe3d5a29 49e09d3f
+          e22eb858 c97ff3b5 f5cc211f a251393c
+          a97b6801 47ed8fd7 5d510518 75ce8d98
+          0834f865 244e198f 946963b4 db83e42f
+          046fa8bb d7295327 20513d9a 75eb14a5
+          8a22e4ce 2e346cd9 09674d9b 36598608
+          ff7595c5 d277f3df 78edb584 ab7ee036
+          1869c030 09bcf3e9 8f2613e2 bf77b53f
+          ffadd73f 7239f102 056d8e3e 7b11f0b6
+          b950b3aa 109ec6e6 6007fe50 c83244bb
+          1d4993c7 23667026 6427d990 e0079f0b
+          88199ecb fc7f9c56 e212725a 8520045b
+          a21415a3 ae701f44 131dcd72 e223e543
+          fff5cef3 f1dfbfba 9ac41d09 bcde23f2
+          aebcaa65 d8fbefbd c81c7403 5984a307
+          88ad412d 456568de b9178a56 8fa7d304
+          d9ef8735 274b3baa 35c55aa8 1e8fe002
+          c50b44a4 c62075ea 44985353 823def42
+          c19e2367 4d2d2abf 5cad3e34 b4dbcd45
+          20656e11 c0afc4e8 e83d2ca6 92a22781
+          d7bb449e 3121be84 39e86306 031c6411
+          7e162541 9251bd6a 1bda8f96 9cba7011
+          221a9d3a aa8d1d35 146973a6 429d8712
+          a0a58c08 63d47a53 75b73ba9 603ca247
+          e6eb8b3b b696aa3b e2aab8f3 76b8e9d6
+          2c0fcb28 5b325d4e fc71e8eb afae889a
+          31cb4f16 2181d7eb 889854a0 0c7de3b5
+          0d4e27ee a78c93a3 c5491d37 ebf7a2ea
+          8bd5e83e 5101584c a1e7cf2a 0ad49b61
+          da78a6a9 a382ad53 c85f8830 4d80d47a
+          d384f1c3 9138698c 36962c64 46238a50
+          d802dabc 7b1fda8b abb41d72 22fc7d84
+          7de49f0c 79fdb597 13aebaa6 4bb0dac8
+          2624f07a a1316d36 247cff6a df90375e
+          ff906523 ffa4a0cd d167cf02 91b3be03
+          755f6f82 b7a11930 eb4426bf 0c29260a
+          c9532620 aa7f1ad5 e3116189 ead75103
+          d291326d 32a4a828 ad44e1f4 815e5d30
+          03683f74 14b5abb7 b3674aa1 c48703fc
+          6e3887be fbce9f13 afbabac2 60a2f973
+          24f07a73 32c21c94 396ad3d0 f7def933
+          73dced64 11be445e f3bee368 debd1701
+          8fb707f5 78326c39 99489d3d 0552a411
+          0a1d4c10 618422b3 47c00ca4 4c1e0b5b
+          5e766871 a76eea99 4d7056d5 a16ae53a
+          c81e374d abe02260 025e05b7 59f2f276
+          50dd1d09 bcbee1b3 4623acf9 438e31c7
+          fd1d4b4a bd64114e 3e77415d affca859
+          b35d1b88 0e4947e0 a9f5786c 498b1d3a
+          08a9b326 6b3dc2a8 1e8f0817 64b6f2a5
+          ce2d40dc f8513028 3ace6d36 c2dbd88c
+          da758570 d5b641a0 8d9cf05f 2f837577
+          7fca7ff5 9fcbacf9 43e9ba19 09bcbe83
+          256f8032 e4d57faa f578f7d0 94158e16
+          2d8905b6 6eb575ca d7e82e3d 09582da1
+          039b2c43 305b903c 691c9226 0d578f2b
+          e8588ae8 e30f41b0 254afcf0 1c244d1c
+          0bc162d6 76ab4f1f 85d4423d 05ed878e
+          a0694f09 f5bbe344 dcb1d8b8 6bf02bff
+          fc7be20f afed1423 22c82824 f0fa0ea2
+          3d0249d7 5ee71df2 ca3fde66 8efc1a05
+          6d8e1e2a 13d055d9 8cbac2ad f0aaf36a
+          d500170a 4581313e 0e29d326 22b25f12
+          6417d990 e8bba897 866c49d1 489d3119
+          e6e424a6 f6746a0f 4c26741c 2941d58a
+          4d3004fc 7434cb01 3e379cf9 6fbcfeab
+          a4ebae3b 699048d1 93c0eb8b 598a2022
+          e9c737b4 0e79edb5 27640ff6 9345f8d9
+          c1505b3b b4ec2d41 f38e3dc1 b610a1e6
+          d5aa3b7c 4ce4d9fa 65227d6e 01448ba0
+          1dd71244 5f431bc1 c7fc3f6d d62444e5
+          0f0c3633 3edd0eb6 faf7560b 3c750da8
+          5bb7119e d64eba35 cbc9fae8 51707bf4
+          f4691b59 8ca45164 24f0fab6 37c7ce9f
+          57e29171 1ffb2355 58f1f2a9 ab8dfafd
+          3e346cda 11acc753 bbb2873a ab3fd53a
+          2576d810 a4cd9ea2 d52f1144 5f0bdc7e
+          0fb45283 f8b12361 908ca1a7 55b06742
+          7674a261 cb0eb41f a964890d 9990071f
+          7139f146 fe5f5ffa 504a4c22 714702af
+          ef2346c7 0486bcf4 e25ae6d8 77d1512d
+          479fbb89 65aa2ddd a85d5308 674febf1
+          ac566d94 53fc9801 5a1d13f9 0bd157f0
+          3b81e8bc 34a44e9f a2b5000a d9d0584d
+          765842d3 76e0304b 82f6b33f 0740b5ca
+          e18fecc1 eec12fbf fc68f24f 6eed16ec
+          54774702 2f1c8c6c b723f9d6 dbe4412f
+          bcf04fb7 13ef53d0 e6e8b337 029d6575
+          a8dfb40d be9656ad 15444814 05a6f838
+          a4cd9d86 88f47828 6eb221d1 fb514b0a
+          44e6eba9 3327c1d6 2f434b56 4effcd2c
+          c9b159d1 7db202b5 5f6f82bf cb4547b3
+          3c6080c3 23e3fe84 cb2f3b4e c6208117
+          76a4dc7e 7be7c017 5efcade2 c151b206
+          474f187b 35ef3d86 96ed7b99 800be8d7
+          e33122fa 65217dc1 74ed4661 800e3288
+          5e8e5a52 903ebf00 b123f283 bbce4a88
+          9d6a8b19 de8626ad 29787765 4bf06896
+          8a57c25d dcc1edc4 c3839e7d e66b8399
+          cee249e0 8529c937 df749c65 31bfa45d
+          3c8e1e32 a37ab3d0 8bfacd3b d05e540c
+          98f56fd5 1a441171 2c58a6cd 99a2d535
+          11446f0d dc6a2941 ecd06c24 154cd04a
+          0c42b744 11b424a6 75f77eb4 ec2b09ee
+          dcd15ac8 83b8fb70 c0d34fff 33f58e5f
+          28ea8916 41022f2c 09c83206 3cf5e40a
+          0fcdabe5 e8430f76 f477353a 50b77e13
+          5c15d580 dda65f8f 67b12079 da24c40d
+          cb819fea f1885e88 3a67d696 1c83f4f9
+          d3618a8d 0d3dad42 c56246fb 8162d415
+          ee82e2f3 6b979188 305ffebc 281bf0d4
+          d30fa4fe f297ed64 8d738bf8 d0430f91
+          15ce6532 2349889c 320546ab 6d67f38a
+          b5a34523 06925538 f9ec593a e56eed04
+          7c5e44e6 6469022e e42d43f5 0165df63
+          4d884547 4929fcdd 5e0a8844 af4a5c54
+          fa5d3807 b12387c1 a0de9208 d512c56e
+          87bbb21a 959fad40 57651324 3aa9e360
+          d183c7e3 c18dc356 adda46c6 38f7d00e
+          de7922ed de5f7973 1f7ffc97 010527c8
+          1afc083c 35d0b5ec 3d8ce6ad bb821dfc
+          435d1d54 8322fb7a 447616b2 16cffc1f
+          419520ce 73e0569b d52279ca 28248c1e
+          a125ae21 93159309 fece4ed4 6fd8828e
+          d21a1277 9ce075e2 89bc871f fe3ce0a1
+          3a131278 bc89bcfb ee3beef3 e017ec8f
+          34669e97 07cec802 a3d38bfa 4d3bd0a1
+          d6e3d9ac a1dfa006 4d4140fc b851489d
+          35410baa 74544b9c 6fd4692b 513969cc
+          27a74250 cb0d421d cdaa890c cb4c5ab6
+          ed46f39e e2604e43 3e1cfee2 ce85d539
+          bf7fe8e9 f4dffc06 06bdba63 82045eb8
+          11608b62 f643bffd d2e7c223 640d5e3e
+          f453f578 0d1da85d bb11eeea 3afdfe78
+          eaa50b93 11697367 20666086 360a8a20
+          ce9b0bb3 9c43b21a 91b97426 2cc989ba
+          6506b05a d179ac94 2535dbe1 ed746ba3
+          fc88b0a7 8ac5b63b d31ffc9d 834c71fe
+          a01abcf3 883ab920 6ae62c08 01dfe6b6
+          759b2608 12fa5366 cbcb67cf 32dc3607
+          13f91e44 0ec88560 34ead7e3 994cb0a5
+          c4a3fdc8 71281e1f cdec24ce 0b6a8291
+          b9b00009 13c6c020 ea1ccd5a ccf03434
+          a1eacb35 709ca885 44e28e0b 7c2edc30
+          6c7de146 b2c4f985 42442f20 e3f78fca
+          990ffcea 4e83513a 49d6e047 e0297200
+          cd7b8bd1 bc6507a0 d630e9d6 e3011139
+          d9c85c3c 33e4861f 419c3571 e702e286
+          652365fa 14082ce1 08d9d098 25b0ea29
+          4563e156 b41f3e01 41bd2044 096c78c3
+          d625bf0b 2f65de7f f7c70185 1a7892c0
+          2334321f 79e2a8dc e9bf1b0a e8008e97
+          878fc547 7fa71b75 1bb6a2f3 f011fdd6
+          29a72e5d a8a3cc92 a78ca629 17c43945
+          f103e6f8 28645db8 00a2dacb ccaf7f34
+          dbb6fb00 9a761d64 ef0dd00d f0b07710
+          6d777775 c6afeefc 55d6634f 6a275404
+          093ce214 a9bffcd9 32d98317 40890f37
+          d9ae6006 dc0d0e54 aff81a9e 8646ed48
+          2bf422aa 68db7f99 4be72122 27058a8f
+          cc489c1b 5f350806 642e990e 6b5acaa9
+          9db8102d 51981fbb 2a2ab551 64ee96ae
+          e0b40a22 ac31984d 0d6977fd fc17594f
+          fcb99bac d13ba01a bc5e44cc 8245501c
+          2deb3b37 ef9cccb2 dd3c3ace e027cdf2
+          767421e0 71236af0 c060e6ab 7306ab1e
+          8f599313 d15e7c0c 01af9f8e be88b38a
+          5a779754 3012e973 66c0a057 2fcabe2e
+          7b3ca8fa 6439f3cf 9310a9ee 2efc51e0
+          832cdf3f 74e3f615 648c5e15 5a88de44
+          bf3fffc5 9f72c7ad 770b16cb 09ea79c6
+          49e6abd6 e3f91434 abfdf1d4 7a3c93d4
+          a3f7a997 333216cc 20031267 3776fb00
+          7b5612f3 b5d930a8 7577a1c4 9d21d8db
+          b169e376 b4159f08 261e947c 84bbb80b
+          281ebc92 78c34d2f 913148e0 113a643f
+          fb5291c1 28fe5ef1 83ae98f3 f220b2b8
+          e973b851 bb6e13ba 4b4e9c6a 9d12e20d
+          ea0e1f0b b4c9330b 103f6628 02d44991
+          380ba86e 2659cdc8 be74014c b1d1ba3b
+          cbb058d0 75ec38ea 99c0f332 7fd666cd
+          12e1eb1f b2368a6c 5bf26d37 de99f3e2
+          dfc92024 f0889e10 7be9156f 1902788d
+          3d403259 83879532 d81fcfdd ec40c567
+          abe06deb d0dfc953 83ad2ca3 dfa54b61
+          cf48d2fa 9311c419 754b9638 a4cd998c
+          a881fda1 6dc58512 782623bc 2d2da85e
+          5308778b 2338ad82 4e21c27a cd12acd6
+          eaa45b6e 7830e7c5 7fd0952f 1278444f
+          c97df935 24ddf4e3 7ba1603d 1379b44c
+          72827ac2 d555598b bae56b98 600b9c9a
+          02101a29 c28eac8b 17b17f5a 28a01267
+          2e7eb3d4 327a603f adc1360c 3ae24e6d
+          89c2be5c bf6a031c c7cb4376 fc21c203
+          c5872e31 d2fe54ce 5fffb98e ac41028f
+          f886e4fc f5154fd2 8faf7b40 b0da4a28
+          707322f0 44b6707a 6434ef39 88d6edbb
+          b55d11fd 95564154 fe40a4ce 2860efa7
+          479a3803 e24e61ae 171381dc ab2e0e8a
+          3bbd9e66 46096dbb f6a179ff 6116f8a9
+          250a07e2 3f6010b0 2c7aeefc e7c81a24
+          f0886f2b f25e7e6d a7292de9 4f8a17ad
+          640d4e1e 4a755e6d 970755cb bf86aba2
+          461bd4ae 8bd787b4 05331133 74101990
+          38033e28 2163d16c 98e363f5 bf99f9a7
+          bbba1655 2bd7c1d3 d24da3c8 c25edd21
+          2058ad7b 127f78cd fd79afbd 43f62081
+          477c1722 c64d7c55 300aefb1 acc94bd6
+          e0e4c194 004f4727 4e7ef839 fc5ddd80
+          d8832d11 5941f6e5 17c0969c 4447b5c4
+          7709e048 1c3b1c89 0513f4fd 4814e077
+          bbb55164 eea636ad af23f95e 98bb8717
+          d5a6f494 87725f79 bb96ac41 028ff88e
+          e4bdf92f c45f73f5 fd0683b4 29e0a736
+          c85c70aa 86a9bbaa 16b5cbd7 a9493374
+          0b9b0201 18a32391 79e17c18 236c6443
+          e29b076f e666b6b4 24642c99 0bf874ae
+          66abfe28 4a6858b7 111d2527 b5490654
+          7b17e6fe 21a3db60 14fe611f 39e62bb2
+          06093ce2 4c89bc57 deea8cbf e6fb0f1b
+          4cb6e324 f138d178 42b01eaf 71e71eb4
+          ed3d0848 3dd8c5f3 cb881e35 0c0913c7
+          68c76c04 f14d505b a2e45c76 01a48888
+          1e7cb384 ce232568 dcb20b7e a797eaee
+          c25fdcc9 0683b422 eeca2b1e eeffee47
+          64101278 c4191679 85f63143 ff227bd0
+          42d6e044 e449ea80 771f2a3e 5f09775d
+          63cf8e6a dd1ead7e 2a6a609e 76bb9120
+          7ae66c06 244f9d04 7bff6cfd 7e772cd9
+          f07774e0 e4c75fc2 d3de4dfd eec21d75
+          7756b216 c57defb2 7bfabff9 2fb20709
+          3ce26c60 cac87a51 30193f62 d9144d21
+          e525eeb2 a7d4a7d5 e37d06d9 e3edd151
+          ad419290 7de952d8 12e3a926 8ae81111
+          5919c8b8 60beb60b ac2704d5 0e3e555f
+          ac86a7b9 4df34f22 ccf59d07 75f671c3
+          ffd2ff8d 7f959335 48e01167 8901ef7e
+          8484ab2e fbad41b0 14b2c04d 87b55c28
+          3c6822ad abbc1ab5 abbed6fa 13e8222b
+          30317197 b670168c 91361279 4448cc31
+          d1c8fbd1 152c93e8 41de288a 68deb213
+          2d078ba1 f8641278 61afeee0 32988caf
+          9992535f 236390c0 23ce3279 afbfd714
+          3dbbe051 c58d12b2 063f224f f1ca68d8
+          bc038e23 c77a76f4 eaf5226e dc28c48f
+          1e0141ed a747228f f86f41c0 6c42eadc
+          e93d6b89 c2c49dab b61eb56b 0a217753
+          dd1d07e2 4e0e28c6 f5f1575c f4bb011f
+          7c4af620 81479c0b c4a8a80d 06a3f155
+          16b4dbc9 1a9c683c b509b2cf 8f8a4f57
+          c0d3dcd2 4391e743 e6850b10 919dd9a3
+          a918046f 3e25227e 443e9266 4de9d9d1
+          ac2ca362 d957f076 74d2ce5d b8134020
+          10301d4a f8de25bf eefff687 34ed9a04
+          1e71aee8 ffde3224 7cffd227 15bff17d
+          f620d2bc 5a8e7037 b5a2fcc3 2f98d8eb
+          c171daa9 7abc7e97 2c82392e 46b7769e
+          e00b4b52 bc9600c0 dd83169b cc8f6abe
+          5a8daef2 2a35f2ff bb950f11 9ec86e34
+          c62e9afd 97bcb7de 3f40d620 81479c63
+          d4fe7889 575ff63b 26f23607 1ba5117c
+          64d60138 ca2a50b7 6e73cf1a 8fc932ac
+          9919485f 300ba628 bb36868a e0dd8798
+          5eb3dbb4 8b385a4b 143de52f 08709e28
+          47d3f67d 90bd7eea 7717fefe 21b3c4f0
+          3383d9fc 0a198304 1e71be44 de1bef35
+          245c79e1 132cdb2a 256b7082 3af79d89
+          b6fac26d e86441b7 47d1d6e3 41c294f1
+          881d3618 8249a27a 3ccec59d 5a939938
+          610c2207 f5d71280 d0fe6680 e272a1e4
+          9d8fe077 bae86836 fcfd23a0 f8c43509
+          575e72d7 80f7a9ee 8e041e71 9e03beb0
+          8265d86f b23fb9c8 18fc207b 3dda2833
+          5f6757cf dee0f5a2 dfa54b61 cf4ca3a3
+          5ace1304 755a45e6 258b359f d04512b5
+          ba4f5f7b 27d98e8b 75453894 70f5e57f
+          e8ffce07 5d640d12 78c479a6 ffdb1f20
+          f107573e e2771ba8 03255799 76009e96
+          56ade8bd 47bb784a 00069311 fd2e5c08
+          4b628cda 999ee0ce 6700737c 0c72afb9
+          1cf0f7a0 6e5e14d1 b4713b5a 8b8a1150
+          bf9f8e66 c31abf0b dd89d75c f14eff37
+          ffb58dac 41028fe8 2d22eff5 779172ed
+          550fb307 74335983 a378cd44 5bdba1a3
+          a8dbb0a5 676ff0f9 611f908b 94a99360
+          8cb4523d 1e5ff900 449b0529 d326c392
+          92c404bf ce87cf92 06575d3d aa576f80
+          ac5ec220 71c703ef 31bff813 9981041e
+          d19b608b 71ce3fde 284fb9f1 ba6798c8
+          ab2083f0 24f214d4 ae2e4477 752d7a74
+          f6ea7623 69ee74c4 0c190841 34503d1e
+          174ec216 7b49406c fe40edb3 57dbe7f4
+          644dd14a 00baba49 dc71008b 1bab937f
+          70e58379 afbf4b2b 02093ca2 d7693c49
+          0a40913f 634fa77a f389fa16 7184ecf1
+          a0ec9d8f b57ff6c8 577c7e64 5f71016c
+          996950c8 53c25fdf b145c192 188fac0b
+          16c0d013 712789a8 fc7c25ba abea4005
+          9be18fcf 8593c93f fcfe8bb9 afbc59af
+          b65522c2 03fa24c3 8cec175e 96038af2
+          44fdebef 0c345af1 03b2083f b89a5b51
+          f9e90ae4 7cef12fd 1d174581 60b3226b
+          c93c94be bb0cce7a 478ffa26 137d50dc
+          298039ce 8a7e172d 841413ad 5f7b6730
+          c0515c82 c66dbb83 75774458 e377c393
+          7acb8fff 96fdec0b cb0d2633 a9791278
+          446f45b0 5a91f3e2 cb5e16ad ff58f7ea
+          5bfd4d36 4ca22338 5e227900 4d7b0e21
+          222f1b89 e346e95f bcf0fa10 91938511
+          77dfa61d f312e1bc 300810cd a61e5dac
+          503c5e94 fdeb53ed 9f449863 d0ee5e2d
+          6309df73 82c54a6a 9e041ed1 ebd7729b
+          1dd9cfbd 7844b0db 9fab7de1 6fd9460b
+          52c82a9c a0c8a8fa 74052232 d3604949
+          d6ddc833 a881df66 25bb7192 00f4e09b
+          50face47 f0765287 0c1ec49d d789ad29
+          d75efd87 7e4f3de7 218384a1 16201384
+          276244a4 c282f727 2c3bfb3b 1548f3b5
+          68fb5d5e 94bef521 a08e32eb c9fc5935
+          f0d32bfc 5fbad140 40f3ce7d e8385a4a
+          75771ce0 77a13afd b69b9fcd 79fea512
+          312a8a0c 42028fe8 4b643cf8 b027fd86
+          1f3de571 e23d1279 7c3dd5ce ba66947f
+          ba02b2cb 039a2b45 f4046775 2d2a3e5f
+          ad4d4921 38480403 78dd6014 3f17a3a2
+          a93e8304 1ed1d790 e2e290f5 e4339d99
+          bff8e99f 58b6b691 2cc20f6a 61bdabae
+          51bfd719 41a8fee2 9751fec9 57f03bdd
+          640c0ec4 1d4bfa3f 48bff69a a7d27ff3
+          7b3a9a25 8147f459 91171b07 6bffdc93
+          4a007564 0d5ea235 205a2464 5fb20882
+          d54cc76d 44c860af f8fda8f8 6c05baca
+          ab69ce2c 07f8ddf8 3ae367b7 3edcefe9
+          673b8c09 89641012 78445fa5 f9edd751
+          f9c89f6e 319ab190 acc1070a cbc9b32f
+          5b026b46 1a8bdf74 3c4b844e 06d46b94
+          eee66604 fc940870 b13e2870 d8060f6c
+          94e213c8 1824f088 be4afd4b cfe3e4dd
+          bf5a2a37 d5dfc532 f368b208 078bb70f
+          48993916 f123f261 a0da3ba2 2741c028
+          216be902 f64ff217 1e60c9fe eccac79e
+          baa6e5bd b7c81824 f088be8a bfa96194
+          bba1e9d7 82094964 8df0479b 56101f89
+          b4793320 98cd6410 a267b044 c0969e8a
+          9ccb2f40 805adf85 ffc72d20 4aaeabb9
+          bfecae7b 2e6ef8db 8b649030 86fae085
+          29cd6fbf 6eaf7fe5 addb4d66 4c206bf0
+          a0eed8c2 cd5e79d7 5c0a933a ad2054dd
+          9dfa2549 04d4fe77 a240b368 c33a9ab3
+          97acdeb8 71013e7f c81bd571 6346c051
+          5689c6ed fb215064 086b0433 12dd750d
+          f7faea6b cbd8bf16 914548e0 117d45dc
+          bdf92aca ef7de036 5f43fd95 a2052259
+          84037dc7 6278f6a5 0b11d93f 47ff5285
+          4982bfdd 81eaf73f 81bba903 065a05c2
+          d72f64c0 1c1b89f4 85b3604a 4808f646
+          3c5dd037 1a91b978 0ebaab6b e1ac6ba4
+          ee3a610e 4bfec7d5 bff1ee2f ad8306df
+          157fd50f 5bc82224 f0885e4e c38bcfa3
+          ead1c796 f8ea1bee 14ada0ee 959c04f1
+          98610390 38799cfe 6e9c1ab5 d9abeaab
+          b568dcb6 178a7a24 47851a61 ec1cd076
+          e3024cf4 e7fef86a c0ef3bbd 8fb0ef31
+          c6c6a0df c58b50f2 cabb5042 8841a2ef
+          631020f9 cacb2f3b 79f7bdc5 0145f953
+          c235d792 5148e011 bd1957f1 c1a1eeba
+          86fb4d36 a4d2d11b 1f01dc92 10cd82f2
+          62082653 e8be77ea ce5e6404 9ad66c40
+          eb8143da 5f8934a5 8c8b04a0 ada818f5
+          abd62365 d16ca0b3 fbf447b5 b28ca801
+          b9c860df 57b16c15 edee8639 a20d11be
+          daba5f96 df7bff71 a5b3eb93 a49fdc4e
+          46092328 770f239a df7acdde ba72dd9d
+          46132692 b8e343dc a9355659 172f8225
+          2549bfa9 b1d5824e 16e8eb0a b76ae3cc
+          0c7478cf 05eae7ec 77fbd0b0 6907ba8e
+          9e601981 45f73d49 53262076 f8006aa1
+          c8c11ac2 445eb2bb a6ee3e67 d1bee164
+          10127844 2fa449ad bbfbd5fd b778ca4e
+          5ec9b26e cabb3958 98d5babb d4d9d358
+          201e12b2 b62af8a4 b347dded 41edaaf5
+          7035b452 7d156f22 4f9d5ed0 da8e9a2f
+          5743eeea 62513d84 ba67aa4e 309b9075
+          e14298e3 a8ca8387 b5c468c2 d8b63585
+          f7b4bcf7 661c1984 041ed18b 6878f945
+          54de7bdf 5c5f7dc3 2f583616 49bb77e1
+          bf20abc4 0cce45da dc693dbb 05cb0276
+          d517abd1 5156712a e29319f9 52786a42
+          1040c789 72d4aede c0567e1d 07906558
+          d25398c8 5b04832a 06694d09 6ff79020
+          ba4b4b2f 3d79f7bd 3f697ef7 4d320809
+          3ca2b7d0 b166e550 777dd3ef 98b8cba4
+          85980f4c d191c8ba 6821449b 4dffd6ac
+          dd86d61d 7bd1b87d 0f027e85 c651f11a
+          c4d5cf9d 89bc26e6 0badbb0f 00aaef84
+          c2eb43dc e8e14899 3631e862 b4b68475
+          d2c8e287 dd575bff f38abbef b9b8f195
+          97c92624 f088f34d f33b6fa0 bbe8c81d
+          92119369 01e60025 f8d4662c 990b6bbf
+          0cc0ef0f fdfd4623 dc5535a8 59bd1eb2
+          db43e28e 80bfdb85 9a358570 57d70292
+          4e3587a2 2075de0c 440fcc09 eefad21a
+          13ee222f d95dd7f8 60fbf22f 86914148
+          e011e753 dcbdfb06 caefbceb 66f7f113
+          971b8cd4 ef2eecd7 df40f089 4d299888
+          84896301 8fced801 83018adb 8dca4f57
+          c059d744 b199f8f7 d1bcabae 11d55fac
+          d28e6243 16643281 27454668 bbc5c6e8
+          48ba74c1 81c8938c 18e13c5c 727fcbfb
+          ef503d5e 1f477ce8 a187c80a 7d90a6d7
+          ff89ca5f de35dbd7 dcf68c9a 7551f40e
+          ff85578d c3115919 c8beea52 0892a47f
+          346b35a3 6ef50634 6cdd1d7c 3fa573c4
+          ff2bf21a 5b209824 440e1910 7a275856
+          604c4e84 2818e028 3981801c a04b3ae1
+          ec1e2204 6f7d6b9e 63e3468f b95ffa46
+          dbd01164 943e0a2d f97d55e0 bdf272a6
+          a7b9fdf7 4cdc6590 b8e340df a999b5d5
+          8acc8b16 6a3b2ada ce4b284c 26388a8e
+          a06ed376 2d9e93b8 23fe7f91 a7363fae
+          fd7a33ba 8e1cd78e f243e272 2369ea44
+          248c19ce c49d41bb c14d846f 32c9e28a
+          cdd7d8fc f3f29ffe ecc2e6b7 5f279b90
+          c023ce15 2d1fbe07 6f7dcbaf 4409e349
+          dc71b0de 2aea1829 11a9b3a7 2272e820
+          c0ed0efd 065180bf c3a1dd9a f5773ac9
+          80c47fd7 7886603d 5ec5672b e1777406
+          5be984ca 30d81bd2 96cc85bd 5fdabf85
+          0011d622 2fd1d3d4 f668e3df 5e184a06
+          2181479c 035a3f7a 1f6537df 7ca3abf4
+          e4750613 cc649130 5f67d5b8 ca045bc2
+          d8914855 5ba2743b 43d74ca9 3b337e3f
+          aabf5c8d ee9afae0 691c1da7 11a77717
+          7495d7a0 76f57a7d c5e6f5c3 9c9488cc
+          85b3618a 89a45d3c 1e449e84 21bea6f6
+          075b3ff9 30960c42 028f388b b47cf01e
+          4edc70fd 647f7bd7 c3ea8819 caa0f958
+          646da949 485f322f b8c3a257 776734a2
+          71eb6e34 6cddfb9f 084e1021 149e7a7c
+          dfb06517 9a77ed07 2431b41a 74ba1035
+          722852a7 4d826891 b4316844 18bb8709
+          a2b3e4c4 85276eb8 e1a76d9f 7d4c0621
+          81479c2d aa7e7b5f 8acfe17a 9289bb14
+          12771c68 3b163c4d 5176a42f 9c055352
+          82b68312 12514077 69396a56 6d088e21
+          237147f4 50e429b2 82eaafd6 c255531f
+          faa856c5 e345f2ec 02c4e40f 623e67a0
+          9bb5619e 60b27863 f1b775de 597aed8f
+          16b72efb 906c4202 8f38d3b4 7df99901
+          fec07d54 77c78fb8 53ebee92 a68c47ec
+          a4b14077 7768c1a6 b64471b9 51fec972
+          f8baa8ee 8ef8861a 4f1d65d6 d6892a26
+          f214bd1a 4f4581c1 6442dafc 99b0a725
+          532d1e1f 222fd6d7 e17cb6f2 bebb0691
+          4148e011 6756dce1 f8f72fbf ce5d56f5
+          13830926 b24898af a70ab49b 8a314306
+          2075de74 a087824d bd11d959 56433766
+          896f27f2 44a0f5c0 31d46fda aebffbeb
+          f6c09697 8df439d3 608cb441 a1a3daf0
+          17791272 11101e6d 5fb59cea f148e011
+          678ab29b ae1da674 fb9f146c 3053b61c
+          fe0ba9fa b267a620 7dc95c08 569bb663
+          123a321b d056548c ea951b21 18c984c4
+          77080812 50b3723d 1cc7cb42 1fd5aa5b
+          7edd4ec4 4e1a8784 f1a3201a 45ba7411
+          ee098049 9d575b71 e1f1cb2e fe79c79a
+          95641012 78c477a5 63dd9a24 c1627b96
+          3d5c7124 eec21f75 27c41865 43eacc29
+          b00dc805 5c2e5d71 e7ae6f44 c5b2af20
+          d02c13e2 3b4771d5 0703a8f8 6c15bc2d
+          6d3ac908 5b907c3e a4cf9f89 e841b9c1
+          37d31a15 d6c9a760 8351eef6 dd7deca2
+          c5f33bd6 ac229b90 c023be2d 8e0d5f1b
+          8f2e9eff 6b4f65dd 2c834425 f361bf7e
+          2a806892 90306e14 e2268d61 eabe137a
+          2303fcdd 4e542d5f 0b776b17 5daa20ce
+          8cc613d4 d6290da8 5ef53514 26e042c2
+          be2ec644 6bf578b6 d4783aaa e543e445
+          28aec03f 8e7fff92 5c320809 3ce25bd0
+          b971038e cc9b7359 c08f9f09 66f63951
+          661cf60b 271403a2 07e5207d f11cf6e7
+          5367b521 05a182a6 6dbbd1b4 fb181dcd
+          126714d1 0cd46f3a 8096fd87 4f35633c
+          9d1a0c1e d5460c1d 8ce4a913 618cb050
+          eb140ed6 2a830919 6244d4d3 9d5b36c5
+          904148e0 11df9023 0b660d60 ebea539a
+          b823c27f cd6441d1 961e8f94 59532146
+          46eab744 6171d571 a21cd5ab 0b2159c8
+          7ec49947 f5abf28f be447775 8d8e2f32
+          67ecec42 d2ec6948 183d1c6a ad00d5e3
+          8537eabc 5a4f75c3 85c533a7 ffbc6bfb
+          56320809 3ca2a774 efde192f c5c6fdc3
+          20219dac 11fe284c cb491166 a44c9b8c
+          a8d1c37a d412c5d3 d286f20f bf80ecf1
+          d3d12c71 96a23820 bbfda8f8 722dbc1d
+          8ed0e502 ea2e9fac 2065ce54 440fc864
+          4e0daac7 0b77f160 86c03ee2 fb0f1714
+          cceedab5 830c4202 8fd01577 fbf6980e
+          8e9ff880 afb97506 b5bb087f b439b382
+          88f851c3 90387502 d0d9ad33 8acc00bf
+          d389da75 9be1ac6b d36e3d12 c4590b10
+          46a0fd50 b9361d45 717b42fb a6c70373
+          563a5266 14c09a14 4347b57c f88785a9
+          88770e4f 99449b11 24f08850 380f1e40
+          d1987117 0926fccc 40819b03 75c75eb2
+          01d183fb 6945eada 35589d96 28ea9cd9
+          d6fd8750 b7618f56 27451067 1bc90ad4
+          aed988f6 a3c799bf caa71779 eadf3bba
+          10337604 12278f63 ef33d2a5 0b0e3088
+          4896e2a3 5f741e2a 8a226b90 c0234ec3
+          8111a3f2 58d07e1a 2248de71 807a346b
+          4d8945ca cca930a5 a5683b20 ba494075
+          1d2a3f5b 4de28e38 b7b90813 6aea940b
+          57538bfe 377bbc48 99310571 a386c1a0
+          86183aaa 0d771561 90db3a2e 2a1a3ef2
+          0ed7d162 2a182181 47fcffb8 4b4ba28c
+          b196d7d8 8a9849d6 e023600a 66231227
+          8e41f484 515a917a c8e32fc1 006f7b07
+          ca3f5d01 d9e5a369 15c43945 3d5170d6
+          b6a16675 217c0e1d 5f956518 ec362433
+          91179597 a6253244 98236a35 79bfdb3f
+          64e83416 cbc81e24 f0887f27 bce527a5
+          bd0306dd a7b8dcd3 a8609e07 75a7be04
+          248e1f8e d43953b5 632dbdba 3bd9e541
+          d38ebde8 28a98181 5aa210e7 23865b80
+          c6ed87d0 5a7438d8 1f2f94cf 3a5db0f5
+          cf4672c1 4458e223 102091c7 839a1099
+          8f7cbc6f c0a06432 06093c22 1080a7a2
+          dcb02727 779664c5 fd24eef8 4061b131
+          b27f3a92 a64e04cc 66fdba3b 45d64647
+          557c5ea8 d54311c4 f9147915 9fac4077
+          650f5aa7 743b1137 712c1226 8d654989
+          18bc594b 84370624 4891d2df bdd55536
+          3206093c bef59ddf 8fddd939 d92c68bf
+          41d6e0e5 33679a2e de8e1426 eeec03f3
+          00974737 503aabeb 51f6fea7 104d643f
+          e23cc76f 7594992f 80ca2fd6 c0dddcc2
+          149f1032 81555fc9 0513103b 7400143f
+          8d32e324 ae5db83b 33eb4e6f 6d0d0d4f
+          2481c72f fea626bb 64c2cbec 8fa9640d
+          0e507730 58404c9e 3609715a 4b149df1
+          6282005f 4707ea37 6c85b7cd adde5623
+          88f31f34 d4d62925 d568dabe 077257b7
+          561f7a5a 7c3e1813 1390327d 0aec5989
+          548fc709 92158fec 4ecf98e4 abad3568
+          429f2081 c713ccf1 cdbbd3d3 af67417b
+          1e59830f 64af01f1 a3062371 d238eda6
+          61480c06 286e379a 7717a17e fb21ed68
+          8c207a0b 46e68fb5 abb7a0fd 58a9d6e0
+          58af1e2f 72f86024 4d19cffc 58a4fe78
+          fc88bccf 77a5a727 294e2719 83041e27
+          b06cc657 572b32c7 9f2a5af1 1732081f
+          284ccf45 0e4845ca cc0218e3 6298c2d7
+          dfcae8aa ac46f5ea f590a825 0ad1db30
+          04a347d5 67abd15d 55135ae0 a9b8dc48
+          9e361129 051310f0 53b13127 c489225e
+          6602cf4e a62081c7 05fe9616 ec4a4b4f
+          62d9cd3f c81a9c68 7a193046 99903265
+          3c22d4ba 3bb74edd 9d24c155 df888a65
+          cb21bb65 6a8942f4 4e8d2732 ddd6d489
+          fac2edf0 b6b50362 881a02f5 2211fb7a
+          52c178c4 8ecc637e 4df6e3c2 474cb868
+          5752d2cf fd4d8d94 a692c0e3 00458962
+          f9eb6bec 4f39640c 1ed41db4 6efe6a67
+          ff04b5df 9dded12c 0b82beb6 0ed46dd8
+          82aeca66 1a4546f4 6ab4d629 3b82ad53
+          025e6fe8 7a3caf0f e68c3424 4f9d045b
+          6a8c769b 9ce0c047 ac786c57 52f2187f
+          6323e90d 1278e18b afb1c1bc 2b35f51a
+          e6f00bc8 1a7ce077 0309a307 21a56022
+          201943b7 44311810 f0f9d076 f8281a36
+          eea7ba3b a26f0411 1350f9c5 6a384a4f
+          6a178342 e2f62066 643e52a6 4dd47600
+          03d43a85 1791f705 8b7d49be fa3a3206
+          09bc3014 77f575c2 ee8ccc02 d1ac50dd
+          1d27a83b 14b69428 244e1a0b 536ab27e
+          dd9d246a fdc5aabf 5aab054d 82e80ba8
+          25048a3b 809ad51b e0ae6b00 8c213a71
+          ab372afd b2f64c24 178c87e2 21fb7142
+          3c4b6e5f 30188d11 640a1278 6185e764
+          19f6e4e6 258ba24f 6d8942cd 2e3840dd
+          9950c73b a5ce9986 e891c3b4 22f39018
+          2578ea9b 50bdf26b 783b5cd4 1285e85b
+          81842524 edc76ad0 b87517fc 1d8ed0f5
+          78b20c21 22028913 47237a60 3a641279
+          5c205a71 f1eeccac 5bfdcd4d 74364102
+          2f8c8c6c b5462a2e d7933020 8facc107
+          6ad04a99 3a1e89e3 47c120eb f48560c1
+          5071bad0 b47b1fda 0e97d3ee 1dd12791
+          cc01d4ac db8df623 25c15bb6 a16ed67a
+          bdb0e7f6 d36ed61a 234dd41f 8f138d27
+          045c0fef c9c91de5 6f6c206b 90c0ebfb
+          f81aea4c 7b87e45f 2d597025 40c3c8b8
+          10772e20 6e442e92 a64c8060 b36a3b16
+          a7450d82 82c082e2 7154afd8 0ad1444d
+          41893e8a e6ca322a 3f5f85ae 9232c0a4
+          3334d92f 236ef430 a4ce9cc2 9e111a72
+          c1898f98 e1ebfa64 cfa0c1e9 9e13c7c9
+          1e24f0fa 2edeba1a ec1d3a7c 309ced4f
+          31c7a611 f11ca0ee 44481112 92268e85
+          352b8305 319dad09 1604bb4a cb50f5c5
+          2a35e251 0a40f4ed f82d019e 56276a0b
+          b76a2507 21ebf114 857dbf11 8913c720
+          7e423efc 2eb21f17 3e2220c5 d7defeb4
+          60b74792 3548e0f5 499c870f 62ffe8b1
+          69e86c79 dd20820a 4b3940dd 81502f56
+          642c9c89 d831c3b4 314d21b7 258c127c
+          2d6d68dc b60bceba 0e6a8942 8405eacc
+          e496fda5 68deb50f 01b5e7a3 4e3d9e29
+          214ebb65 1ed92f81 eaf138c1 68c125fb
+          468dbdc5 d7506f25 6b90c0eb 7b8b9cdd
+          1ee36d68 789c89bb 51640d1e d2d2e0d1
+          6cdaccb1 da8e8441 bb5a18a2 0784da2f
+          cceb43f3 ee7d68d8 5e0c91da 801261f4
+          2c180c32 ea0bb7a2 edd011fd 29177e19
+          110372d9 b353a025 39d43a85 031731c0
+          1468adff edfe3163 47ab275d 0409bc3e
+          83b7ba4a 3c3863d6 6223d5dd 71832aee
+          22b21391 307134a4 e8e8d075 772a6633
+          da8f1e47 f5ca4d6c b1a3a359 22cc020b
+          136abe4e 2f6abfde 04a73aca cc1ae2e2
+          64200003 7bc58d1d 8eb4f953 e1f3d0aa
+          c985c893 102537d6 feab68d2 e42c67d1
+          01320809 bc3e20ee 2a4ee2c0 b4e9c3fd
+          3595cfb0 2c85ae83 73803aa9 42b28948
+          9f3f53bb 19a85b77 c7829dab bc4aeb1b
+          e6eff640 a0962844 38061723 d055d680
+          86cddbe1 6b69d5ad c7132c16 248e1d85
+          8461b9f0 d17c7a5e 7c24d355 59f547c1
+          668b216b 90c0ebd5 74efde89 8373e665
+          c995e52f 0b262491 45f8c0ef 05526717
+          2076e860 1802c11d 89d3c282 9cbfb50d
+          f59bb6a1 b3b44eab 572288b0 443daa35
+          06d0b4ed 105af71e 8456901a eab8d62f
+          c39296cc 9ea529b0 c6db6994 192798cc
+          b8f4d0e2 a5377aab 2a694384 045e2f5e
+          cf4cc6d8 ee13271e 10cc9840 d6e02380
+          f95c40d2 f821489a 380682c5 a4df1285
+          05b9d6bd 455ad033 48013a8a 22c2fb11
+          514b5165 1f6ad76d 84e3f031 c0663b7d
+          02a4febd 1240d4a0 3ca4cf9f aeed8c07
+          a8770a0f 3e62f695 96dc7770 eefc09ea
+          09184102 afd7e139 598aa357 ff7081d9
+          8c6bc81a 7c20bb81 88f478a4 cd980c53
+          427c6871 a7462a9b 159dc74e a076fd16
+          283e2f4d ab20f808 3246c0dd dc8dba0d
+          9be1a9ac 0e5d8fa7 b54e9110 3f660452
+          678ed166 39531214 fe8816c4 7b4b8ebd
+          7668f192 bcae5d3b c82024f0 7a91b83b
+          7e0c872f b8789ce7 f0e12758 3662278b
+          843fda28 32f6f4a4 cf9b16ac bb536fcc
+          9e6eb741 fd7b26ee 5c2cb8d5 ac2d84bb
+          d1a1053d 82e049e4 b51dae44 c3b65d08
+          a863fb42 b54ef1cb 902223b4 d6293103
+          d221533d 1e1f22cf 8adccee2 23bf3718
+          1047d620 81d72be8 dcba19c5 975f91eb
+          3e7cf879 e6a05964 110e508f 663d40da
+          dc49881b 99cf849e a0537727 42e972a2
+          79eb4e6d 1499da0c 96762508 ae1e192d
+          d2c868da b64f2b51 d0a65c84 aac76309
+          93353d15 e90b66c0 1869d48e 6b89f0c7
+          6cc665c7 6fb9ed06 cf89e354 8f4702ef
+          fc13f0b8 ed8ea283 374b564c 266b7022
+          ee9c40e2 c881482e 98a4ce19 d66f8962
+          b16841ad 7ef30116 d39453c1 8e20380b
+          3646b575 8a0bb5eb 37a3eb68 49f0a836
+          543d1e13 80d103f2 903a772a 142fd98f
+          0b1f1160 71eedd7b 57f1e557 16b88f1f
+          258390c0 3b7fb88f 16e3e47d 0f5c6836
+          e146b206 2782dec7 f45a8c0d 6973a6c0
+          9c1817ba 99b15677 6743e7a1 63a86341
+          cddfeda2 a359827b 91d755d9 8486cd3b
+          82ad53cc 21ae91b3 c4c9c0be 9e386134
+          12c70fd6 2e34d1ce 77f823d9 90ecdabf
+          fff9a357 5d33a873 cb263208 09bc734f
+          e7e6421c fbe18f26 77eddcf9 b020219e
+          2cc2077e 26f03297 ce42446e 76b0b62e
+          d4d1acd9 acb54469 d8bc0d5d 55cdc169
+          15742b90 e01926d0 d4be8f2d fb8fa379
+          dbee5351 28441862 09942936 862554d3
+          10911a07 85469971 90456b22 2fbf63cf
+          dedfca8e 8e043208 09bc731f e85b9a32
+          da77eff9 bdd1863c 0ada7c04 2675187a
+          ead49188 1f3d0206 49d41945 16accb6b
+          dcbc1dad 074a8373 6669f781 20b4dbe3
+          b2db8bfa 8d3bd0b6 af08b085 9e72a1be
+          ec99e9c8 583c537b 86a8750a 1f22cf6c
+          c265958f 3d7183fb e861aac7 238177ee
+          701d2eb2 57fff9f9 db9803ce 2571c707
+          6a4b9498 41fd903e 6f3a445b 0feaeeac
+          66b4ecdc 8bba0d3b d97b3dd4 128520fe
+          1fd4dd6c 774b9736 afd655a1 b64eb19e
+          5eb929ec ef450171 23f2913e bf805aa7
+          f0224e24 583a376f bee3d875 37cc6231
+          970c4202 efece33c 7800c76f bcf952c7
+          c64d3731 07a46586 87645261 f1c52420
+          63d10c98 9312426f 21a85fb3 dbe02aab
+          64e26e0b bc6dddc1 a3598220 fe67f031
+          028ed21a 34b0e744 eeee068c 52880c4b
+          d1eaf192 a74d465c 7e36fcd4 3a858385
+          97b9840d a95d3b76 3c7efca6 9bf31d9b
+          0ac92624 f0ce1e8e 8d1b507a cb2d531c
+          db77fc86 395e02ed def1815a f7a3d6dd
+          45f6cf86 ee1991c9 087fbb43 bb54d15d
+          d940e28e 204e837a 9b3cc084 5bd3aec3
+          68deba4b bb6d1e72 6b4e09c0 141d898c
+          a5b3618e b5b1f792 0d391179 235bb7ed
+          78c05356 9a480621 8177d670 1f3d9cae
+          3a9ad986 8124eef8 40adbb4b 183f18c9
+          0513b50e fb21ebee d4be5ea2 84a6cd3b
+          d0b2ef68 5008d213 4610a70f 4026f68c
+          75b951bf 711b3a8b 8a8108ab 6eeb94c8
+          7e59c85a 3a87041e 4722cf6c c4a50d6f
+          bf7793f3 d001aac7 238177e6 7116ed8b
+          68fce8d3 9f32475b 4045be7c a00e3bb7
+          a5c52363 d16c0866 b3764c74 fa458839
+          45841d0e 16a4d416 10fe6e0f 0cd41285
+          207483b7 bacbedaa 6fd7a6bc 781b9b01
+          4b886d6f 35c11204 96748d46 d2949190
+          5d64421e 908cb0b4 af5d777b e94f6e9b
+          cf623119 8404de19 1477fbf7 e2c46db7
+          5fd1be66 ed8dccd1 a85c9e87 b8a3051e
+          23fa5d34 17d69464 fd37582d 705756a3
+          7acd7ab8 9b1cd412 85207a8a 2178b3d6
+          71bc12f5 6b3769a3 caf45aa7 a8b7d833
+          16cc4264 6eb29688 11e1bf1e 9b6d4873
+          6cd9fabb b2db6e1f dab97923 198504de
+          774775a4 b29fde3e b663cbb6 7b998325
+          d0ee1d1f a89df353 674c40ec f0fcffac
+          30a77d8a 0404bc3e d4afd980 ced22a1a
+          454610df 54e369ad 53fc68de bd1fadec
+          a58d32d3 c114178b ac0be743 b299b58b
+          50041722 6f4ccb96 6dbfeddc b231892c
+          4202ef3b d3f1f5ea 8c962ddb d5babb41
+          24eef840 66e22e6e 782e5266 16fcbb0f
+          57486c56 346fd989 96fd47b5 4043a3c8
+          08e29ba3 ee7a7b3b 82a3cc9c e595da14
+          9890f578 8ca841fd 913a670a 198f2391
+          67927051 fb86cd37 3b8bf651 3d1e09bc
+          6f8ff3c0 3ebb63fb eedb9943 5d4ce28e
+          93054466 59626c04 b22e5c08 2932427f
+          b5b15ad0 5d728205 a52df03a dc5ad138
+          4110df0e 7527afbb ba01b56b 37c2dfd9
+          09188da1 9f3f2580 14b575ca 884134e5
+          82132413 2cad2b57 dd7ae2f6 db17b318
+          4d062181 f76dc49d 5a7777db 45ad2b56
+          5dcf1c8a 0edc7809 309280cc 25b3614d
+          4dd1dfb9 3319e16d 6b47f597 abe16a6c
+          a3ba3b82 f8aecf9f 10ec3bd9 76a8048d
+          2c69823a 31c6107a f9152d66 642c9a0b
+          4b720c1d d5f29084 9faac7eb d8bcedd7
+          65b7df96 dfb57d2b 1985045e cfe9dab1
+          15276ebd 7578c7d6 edf73047 4aa6dd3b
+          3e508bb5 13278d66 af7141a5 16b2eece
+          a0059efa 35855a71 b8168328 0d2088ef
+          1e942440 76f9d0b0 65271c45 47424fb9
+          3815f1ad 6929c85a 3a97bd97 421a4722
+          6f6ccb96 ed7f6879 ff1daac7 2381d773
+          9ade7825 b575dbce 3f30071a 45e28e93
+          05430622 b35391b9 684eb015 83de076f
+          36a175d7 7e34edd8 0fd9eba7 51640471
+          26039311 f0b475a1 6ac53a78 9a9a0193
+          4eed832c 236eec48 24154c40 c04ff6e3
+          45e49944 2c719694 dd4afdf1 48e0f508
+          e6282677 45f56daa e390b8e3 65a5008c
+          5136f4bb 6c09a488 08fdef37 9be1aeaa
+          43d5576b e173b8a8 ee8e20ce 02ea716d
+          77751d6a 97af6139 9712ba75 8a0afb9e
+          ac0be623 3237839a 20738264 86a565f9
+          ca5bca6e b979298b dd641012 78a1c45d
+          114edc74 c3052dcb 575fc71c 87dad472
+          1349808c f93311d1 3f27f4a4 0a155184
+          dfed4635 0b3a9ed6 f6603363 4a0408e2
+          ac08bc80 5741ebbe c368ddb9 27f4ac5a
+          2d510bc0 c09ecfec cb97c21c 1b49cf25
+          0fb979f0 a836b57d ebce5fb3 d83db87b
+          ef1e320a 09bcff22 eef6efc3 891bae1f
+          d2b17dcf 43261b32 68f78e97 1502881d
+          361849b3 a7025e9d 8ea96aa1 9d6040e3
+          bacd682f 3e8e801c d0abff26 08e2bb88
+          3ca33a2e d08b9a55 85c1d629 7afdf1d8
+          c26dcbce 44fac259 548fc791 c863317b
+          74fbf63d 4fd43ef5 38cdab25 81f7bfa9
+          7ee4c1e4 f69d7b1f 638e328c 323f7ec4
+          9d352509 39dfbb08 f078f5bf df644247
+          f131d46f dc0ad945 75770471 4e441e8b
+          529e9636 547eb60a fe6e2720 ea842d96
+          a8254e9f 8cd891c3 c8781cad e59288f9
+          fe36c7cf 5c478ba9 1e8f04de 7f701f3b
+          2ac95dae 3b58c2b7 84c41d3f 88362b32
+          97ce8314 19a9ffcd 4609dea6 66547db1
+          1abe2e57 705a0541 10e740e1 057769ba
+          4e56a1e1 ebcd41c5 a7b775ee f620fb8a
+          0b604b4b a1a35a5e d673b51e 6fe59a9b
+          4aaffdc1 052ca693 4148e0b1 75e0f831
+          1cbfe67b 0b5b57ad bb46b450 dd1d378e
+          2f49489e 321e3163 47007e9d 6b772c98
+          28b2a2ed 20b86a1b 83ed50e8 689620ce
+          9dc613a0 dd56afdf bc031d87 8f06fbe3
+          e9057c2b 4be02e5c 0063949d 441e0f04
+          8f6a531c 3bf73dc0 627a7fd7 d1628a73
+          5c8bbb12 26eebe77 d900c79e a2278c36
+          64d122c0 0f91fd73 9071c922 c0e9d2ff
+          66a384d6 6dbbd15e 7c0c8a42 75770471
+          5e441ed3 74fe6e37 2a3e5d0e 5f5b3b13
+          793adbe8 2c718b1e 918f4496 c80966ca
+          dd791179 2c968f64 31fdf98a 3b6e8d23
+          81c73165 b75c1fe3 d877f871 e610f924
+          eef85900 2c0971e8 a78a3bbd 4b152a92
+          084f5d03 2abf5a03 d9eda339 b304713e
+          451e4bae dccd6da8 fc7c1514 75e75d2f
+          db72b991 316f2662 06f70765 66fcacf1
+          a2809901 c570a7a7 fc24d7f5 78dc862b
+          4f658591 a5843f63 8eb088c4 1d3f0fbe
+          146143c6 e2b9b0a4 a5eab744 1104ada8
+          fbc43bcb e077ba48 dc11c479 5778d06e
+          afb71e38 8ce62d3b f52f5ca8 c57ba288
+          ac8b1669 891d7547 e044d858 606d5b5b
+          78d3b14b 2eb880c5 7a12783c e1ada9c6
+          b10b16ce 695fbff9 27cc116c f438f021
+          ee04a388 f8d1c311 37713453 f8fa93c9
+          032ce3af fa6c15ba aa6a8235 3cb40140
+          10e75fe3 a9fdf17c 8ad668dc 5953a72f
+          f26419a6 a404642c 9a035324 d5e37193
+          ccdb90dc b5fff0ef 58accff2 565592c0
+          e342dcd5 d6e0e8c2 39f1dd45 479f620e
+          90460f3b 2f51c100 7bbf0c64 5d381f70
+          f7a0258a 20c071f8 285af615 21a00448
+          dc11442f 8b5cb2d7 8b136f7f 0cd9e5d1
+          3f7ef578 59623746 4bf00493 44228f1f
+          913794c5 fa574aae b83092d3 c7842f8e
+          2d9d67ef 3e54f292 487577fc 3ce70a60
+          8e8b41f6 254b6030 99f5e7cc 0a0678db
+          da50fade 2750d439 b324ee08 a2570670
+          774313aa 57ac638f 740f1673 b7075997
+          2cd2123d 7aa8f9f1 11f6514f 31982cf7
+          fa9a1ab9 1b2ac995 c0f3b7b6 8882c576
+          17fbc0d5 7e77f484 f392c5d9 2d5a4b14
+          6b4e3ffd 96280cb5 254af9b2 e5909d6e
+          dab92388 de8a568f a7a071cb 0eb41f3e
+          d683b540 add31091 75c10298 e363b4c4
+          8fe040e4 5861736c da71d391 d9539732
+          0d40022f 1c913bda 7178dac4 298e6d7b
+          6e661fb8 9ddc9e93 ec4d1410 933f1029
+          8be7002e 574fde82 fa759bd0 71a444ff
+          12064110 e75fe4b1 87b6f2d3 e57035f7
+          2078b304 cfde3f17 c9d327c3 18612591
+          c7491c10 6d48ea3e 74fc51a6 0152e4f6
+          76127861 25ee3a3b 7168d218 abb3f8c4
+          df442bd2 e9689697 e7da007b 462ab22f
+          5daa1dcf f4245874 9594a196 093caabb
+          2388bef2 a007e06d 77a0fab3 55ecb9ed
+          81626389 5ecabc99 881e980b 4134503d
+          1e2f22cf 8ac14c03 bc7b78ea 783309bc
+          30e2d084 9146f7b1 93afb30f 7810793a
+          27cfb30c 58e2a291 b97401c4 087b8f76
+          e3e46e17 4e7eba3c d85f8b20 883ea4f1
+          02683b5a 82fa8ddb d0a35e28 6e37b2af
+          b808f64c 96ef0728 93e30583 800982c5
+          f21bc5e5 e2629278 d80bbc80 d76b10cc
+          e67ba1f6 bb63229e 5c9c8b84 1ea2c588
+          d811f988 1c3e18f0 f97af4a6 f28fbf84
+          a7b119d4 2c8b20fa e2830fd4 ac5c8fee
+          ea5afdef 65099f18 6947fac2 d9b0c447
+          6b0921c1 81e031c3 debdefd0 cd87c60d
+          bf806903 12787dfa 79670f71 d1c8c183
+          9d878fde 62302192 dc9b8f45 5e257a40
+          0eb22e5f dab35164 4cd0b5ec 3f84d603
+          c5745a43 107d1875 f7fdc4bb cba0f4a0
+          cfa53ac9 267ad450 244e180d c96ea27a
+          3c4e3098 91e42a39 f104d306 f18130af
+          b30e6b81 77203f4f 749f38f9 9ec1880c
+          726b4ef4 1dcbc423 b2d2917d e585805f
+          d6df8d63 5f773536 e1e4fb9f 317147f2
+          8e20fa3a eea61654 7cb15abb 61ab8bcb
+          8db4f933 5942d83f 38a98696 003e449e
+          11039836 f8906904 12787d91 fd83b2e1
+          29af78c7 20219fdc 991f7167 8c8d40f2
+          d4093026 c46b1dec 75dfe3f7 e1e4079f
+          53dd1d41 84118ddb f7a2adf8 a8fea58b
+          407070a9 da00dd9a 92c4d601 b21d2f1a
+          8fbdc6b3 cfff2112 787d32d8 fbae661f
+          de3cf647 23f93207 e24e51eb 2b44248c
+          198ef8a9 137b766b 96094035 d3efaaa8
+          21031244 5845ef00 2a962d87 a7ad5d7f
+          17df2fc3 949a8c94 590530c7 45b0a48f
+          ecc7858f 4888f054 54de7660 58ff2524
+          f0fa1007 86e60df0 d6d4fdc9 202296dc
+          98077517 14789139 59c85c34 0706affe
+          a50af5d6 9da3bc0a 8d5bf782 ce650822
+          fcf039ba 50fec1e7 903d3d28 a6f77890
+          30653ce2 860f81c1 64a07a3c 5e449e10
+          48709f28 7b866986 6812787d 43dc099e
+          13651f18 0c813450 27332e50 d8fa6d4f
+          4b40d6d2 f930582d 3d3a9af5 393a51fa
+          e6fba095 9c20c235 7a038ed2 0a346edb
+          8980ba26 841a4fa6 b6bdf4cb c8bc603e
+          a2f27282 47b594f7 71e1254c 2be431cd
+          b0ec407e 2e09bcde 4cd1d03c 78caca5e
+          60bfd510 12779c88 3bb6109b e26c4899
+          3609b6bc 6ca6dcfc 7a8fb356 7cadb644
+          f175bac8 4b0822ac 09a066d5 467455d5
+          e8f7c264 2250b0d9 90b97016 ece9f15a
+          e2487081 aa8326f9 db5a1f24 81d78bf1
+          35d47f8f fde36af6 3293cf72 b07407b4
+          c695881d 3618f1d3 26027a47 31a7c45d
+          c3e6ed68 3f5c1abc 35471044 f8625077
+          f87d38f9 de27f075 77b388a7 93d1f9fd
+          b00dcc43 72c10498 62ac74e9 821f8967
+          931d9d3f 3f386af0 421278bd 9083a386
+          f4975dee 3fb3073a 9abc9593 85db0d44
+          e565216b c95ce6c8 86d0c5d4 eaf10ccb
+          e0bbcaca 51f95521 fb0b3a9a 25084e82
+          375c8d6d a85eb14e 137ba18f 6a0330f8
+          7c489c39 85258e83 fe9d4812 3cec1828
+          f1ae23c7 5e605a22 8a045eaf 1277f946
+          e791a31f b20f288d bc940f64 1760cf4a
+          40c6a239 106362b4 cc3bb420 34c0dfed
+          44f9a72b 10f079e8 68962078 ca0759a4
+          6bda7e10 adfb0fe9 d7e32901 08ec0dea
+          948ba8fe 995a2249 eb052788 c8655a62
+          19d31424 f07a0bae a347fe68 103182bc
+          930f14b5 df5d8411 89e34622 62c840fd
+          51648200 c5e345d5 97abe1ac 69848106
+          d6110467 0a4fdd89 f3a3fce3 d57037b7
+          8416782a 6c4d3125 25207556 01aca931
+          41914770 e1292c3e 4c679ae2 011278bd
+          80439346 5d0ed170 133898ab 4b9c1278
+          5e2076e8 4024cd9b 0e83dfa7 7f342b2b
+          683f7c04 8d3b0f53 dd1d41f0 1ab9d9b3
+          ef773ab5 a935feae 2e401475 455ef4a8
+          11489a38 1aa255a4 fe78fc60 649ae26e
+          a62d1690 c03bafe2 6efca0ae 9d07fec2
+          023ccd99 e5240b57 8f66a307 66207de1
+          2c884693 26de428a 3b467755 8d96b96b
+          2b341db5 1004b708 46c071bc 1af59bb6
+          05e7d50a 2142a07a 541b5090 3c633262
+          470cd626 e5503d1e 27040231 4c5bbcc4
+          34469fee a5db2705 9ec2b2b0 4353261b
+          ba76ec7e 4eb02085 bc910f64 37604b8b
+          43c6bce9 b0a4a769 cd494322 0a2c53ef
+          46f9672b e0ede8a4 a3598220 d40906a8
+          5dbb0b1d c74e045b a7843aae f5fb2146
+          d8913167 3aa2fa67 686b1025 899c2403
+          16e4328d f12fa635 34cd4102 ef5cfdd0
+          361b1cdb b6ff51b0 622eb921 2709953a
+          8a8c2dcc f1638622 6ad430fd 4b15a208
+          d9e942ed ba8de82c ad866022 1b120411
+          3caa953d 6e547fb5 1ace86a6 53022f84
+          6af3f961 c9ce4452 c13898e3 6d503c64
+          436e449e 15f399d6 f8b5aa39 48e09d23
+          8a67cdbe 4834e336 358c930b f281cc16
+          d5f8d1f9 489e330d 06f59c44 d1399a65
+          dfd375b2 02751bf7 d2ce1d41 10ff33ff
+          63095f77 4d0b6ad7 6cd01241 18432c12
+          ea7ae397 113b6e24 92278cd2 76006900
+          0e47be62 c6dd4c73 f4c9fe78 7d4ee015
+          cf9dd7bf 63c3fa67 59161641 aec743ba
+          cdd65697 3a673619 694cdc19 a3a3f46f
+          cd8a22dc 2c332fff 6825025e 2f5dac20
+          08e27f2f 2d4cd3b5 ec398ae6 ddfbb45b
+          f66a49c7 e9334c99 89423392 67162066
+          d800f869 178f1f3f 1110cb34 c70b4c7b
+          2491c03b 4bf85b5b 7064d122 73fbbab5
+          cf895664 93dbf181 7a1c628e b52175e6
+          24d8b2b3 00b7ceca 2a49f0b7 77a04a3d
+          7ea96da1 a3598220 4e17b811 08c8a85e
+          b119dd15 55d02dae 63c9a231 210e6933
+          a722322b 414b3ca9 1e8f0f98 e6c863da
+          e375a641 44558b90 c03bd306 8e8941cb
+          ca950f49 362c2677 e345dd05 17e1e482
+          b1881d3f 3a782c1b ea1a9b28 6a3b764d
+          bbf7a265 ef318816 32214110 2102a0c4
+          745b4717 2abf580d 6f6b1b60 32a9e36b
+          438abc88 81794899 3611a648 33cdabe5
+          08a63d16 310d728f d6589f04 de99e5d8
+          e5572e31 9af033d0 35756e50 074ec40e
+          eb8fe4a9 13209acd da31c9e9 d3f160dd
+          9de36829 6ad6ec08 7a3665d7 0441e86d
+          1eb044d0 515283fa c22d90bb ba99c893
+          42249d01 6d59899b 38168913 860767d5
+          524ce203 f639330d 720fd322 8bfb4a74
+          e91302ef e86597e5 b57cb2ec 4983043b
+          791907fc dfbabb7e 49489d3b 0dc6f8f8
+          d047b3ea ae9ed108 4f730baa 57ad87b7
+          bd4beb77 451004d1 a340c8d6 8bfa4dfb
+          d17ee848 30911442 dfaa95ec 56a4ce9a
+          8ab81139 f053eb14 7e429384 38a6459e
+          629a2483 04de77c4 575f8763 dfbb32a2
+          65d9b2a7 8d360ca1 4c890fd4 630fc966
+          44cab4f1 881cd85f bf258ad9 047f8703
+          b56b0be1 28ab8364 a5ac9a20 886f10b8
+          45b6ccb0 24b266f5 46ad31ba 5acb1b2a
+          0155134e 536a3252 674e8135 295a6bc0
+          4e228f03 d4bd04a6 45982679 8169138b
+          aa5148e0 7d5b44c1 58f7c187 7733835e
+          44019b9f 07486d41 905c301a 71e34605
+          77e742b5 44110404 98006c3d 70080d5b
+          8bb49a1a 8220886f 8a6406ba aa9a51b3
+          a610bee6 56fd7a3c 9f1f9183 072065e6
+          04082681 4699f125 f22e64da e44ea651
+          7a7513ae 5e2bf002 7e9fa1fc 9e7b675a
+          8cb893c4 1d2f6934 b4e38e98 21fd903a
+          630aa4c8 08c01ba2 258a5a77 270ae82c
+          2943edea ad08c80a f5bc2308 e2dbef29
+          3091d7ba f7189a77 ee85e276 87ee8f27
+          cb4cd899 9038612c 12c60f83 4c172eb8
+          12794c9b dcc334ca 3c55ab90 c0fb26b6
+          f3ba517a c38da9f5 6fbcf1a8 60441479
+          131fa8c7 1cb6a458 a4cf9906 537222e0
+          f69e7e8c 90bab367 36c1dbd8 829ad51b
+          e06c68a5 5bb30441 7cb71cf3 5444ac2b
+          dc0dc7b1 d2e0fa13 6a9499c7 0b29260a
+          69330b10 3d208d5a a77004d3 26b14ca3
+          3cc6b44a aeaa5948 e0f5006f 75154a7f
+          f29388fa 37df7cd0 6cc378da bde32421
+          92d5eef2 2252674f d48e3d20 2b08793e
+          6234c2df d9a50d0d 6f2fae80 6401d5dd
+          1104f1dd 83a299e5 96ad9da8 5d5d0877
+          4d5df0a8 3664d0f2 c19a958e b439d361
+          898da051 66dc042d 806994d1 4cab3cc6
+          344b94aa 5d48e0e9 2077b409 d5afbd71
+          0533dc2d 14b0f9c1 ef059226 8f40fcc4
+          31301825 fd9628ec bf1d078b d1b0655f
+          b0ee8eb2 668220ce 50e0562f 6ab5abad
+          53366ed3 12499842 5ccb574f 13d87fa3
+          f3072265 fab8e05f d128339e 44de954c
+          b35ccfb4 4bafebdd d0ab049e d2dd85da
+          bfbe3cde 22e15724 ee38e154 4b94e8bc
+          34aded80 a48e22f3 e814b398 4de83e59
+          89da75db e0ebf230 41486624 08e2cc22
+          b275a571 e741b4ee de87805f d62e749d
+          3e43f543 b05a9038 791ce2c7 0cd26667
+          13fc883c a659ee61 da659aaa 6148e0fd
+          173c5595 3879d75d c9352fbe f45bc984
+          c1e4357c a0b64431 47db91b9 6816cc29
+          c9c17e77 a16a5e4c 26789b9a 51bba610
+          8ef27a6a 894210c4 d9c93d99 c0f33b7d
+          a8fd7a3b 3a8f97f5 e0a8d60b 637c1cd2
+          e6ce4044 467cb03f 1ec1054c b3a433ed
+          f2d0c95f de952b77 76f49e24 e5a1871e
+          ea153f88 ebf001eb 91db7e76 87dd869b
+          0314b0f9 487cd463 0cf6ca58 380df1e3
+          46c1a00e fb0ed512 4514a1b0 4cb961fd
+          66d46ddc abb535a0 a3598220 ce166a03
+          64779b0b b2d381a8 7e991063 22019f7c
+          fa758705 2f635424 248b091d c74e2220
+          cbffbeb8 41843746 13fab56e df6355da
+          5a37da47 8cf048d1 e77fa459 af703db9
+          a3ddd0fc f1a773cd 12ee2271 c70fea31
+          46e2f821 482a98a0 1d6fe8d6 dd0906b4
+          1f388c86 ed07829b 7c24ee08 8238ab59
+          280bdc6c 696a3d50 86864d5b 21bb745a
+          a7b004d5 2089881d 391429d3 46d3ac5a
+          9e5c85f9 8ad5869b 2bfff6f7 1fb94b4b
+          ccbd2241 39df3f80 b7ba1215 bf79a07f
+          e59f9fb9 9729e048 72133e50 8f2fd463
+          8cf48573 b53603f0 e814ad98 4d7057d7
+          a26ec316 b89a1cda 4d3782f8 df89008b
+          b1ae605d e7b779d1 4402e2bf f994dace
+          b6714711 daf61405 7ba9841a 65e6f543
+          8cb06b53 2ea2f333 a9750a67 22cf2ce2
+          aed6e52b c7c9edad e7fde739 ef7dff9d
+          45fbed15 2fbc7473 840d05b4 7bc707ea
+          806ea3dd 868cc5b3 614e8a0f 36330e59
+          77670c8e 222bdc0a 4769ad96 5153dd1d
+          df015776 6a7f6a67 af06f652 e705a973
+          045c324b 15620bc6 f92d7903 be958778
+          2acbc5d6 0ddb6c2c 9eab0557 6a856722
+          7b25a82f d1467ec7 2b5aeb94 36a776ab
+          d69a9c04 7b5ebfd3 37615797 324fb01e
+          afdf050b e0aeff00 5e8783e6 637382d1
+          8ceccaa7 ff7c77c0 e32acdb8 f7be0653
+          46169f02 4f6e6b15 3b366d59 cc14efad
+          24ee7849 7182b577 69b3c721 66d86018
+          44750864 88a35941 8022cb68 dcb2034d
+          2c833650 4b14de45 5d13739f 8371f367
+          1c37c627 1c61ff7e 88bdf6ab e24ecd17
+          7d1e20fb d13fc03a 38ff5bfd bff1949f
+          84e1ae7b d4455a15 7871eca5 5ef81ae4
+          6f6b1bda b2f2eba1 02308009 bd4c127a
+          fcad5b6a 62d959d6 80ba0d9b 911d1703
+          89bd42de f89715d8 32d391b1 641a4ebe
+          bf8aad7b 7eaac7e3 c15598af d86cb8b8
+          e285bfee 88993deb b9b88c2c 177702cf
+          5b538d9a a7fe34a8 f2d9bfdc c98c6127
+          81c707ea 389fd861 b94856eb ee2c666d
+          9e63a8a0 ae363476 ec2d42c3 e63d503c
+          0a4dabe0 14bf1327 e216ccda 2f4444ac
+          f67bf0c6 c0f7def1 881167be a2c39c9d
+          83211f7f a42d51ec 557feab5 411d5b75
+          f48a2b6d 82af6b69 ebaaf597 32d71c25
+          5a31883e 19be920c f5666deb 81a3b0a5
+          a52265c1 4c967f86 b818a6d6 e3b1f52b
+          7ecc2874 57d6a27e e33e5abf 38127966
+          117774ee dc73286a c6ec2fa5 b8f8f3e3
+          b281f3a4 ac5ade7d 2ba2e89a 1f3d1261
+          c31d24ee f840f131 a78f8fc6 c01bbe0f
+          7b5646e8 4b155ab4 35c15bdf 88b2f73e
+          45ebe172 487444c6 9fcfb8d0 c6c2e7ee
+          a869d39f 1e56b861 55c8a3fc 73b47217
+          2f5a0c21 e09ad6ba baf07e16 de270a56
+          6da78fe0 25497503 96a46864 5fba08b1
+          63476aed 51102a88 4912bccd cd2879e5
+          5d745534 4230910d b9c807d8 52e574e2
+          ebac9fff f4d6f47b ef2b31a5 a59ff39f
+          e1bc6c18 fb5b5b8d ddc5472e 350b7434
+          cb4d46a3 a8b5c912 b296ce81 2d3d35f4
+          82a8228a 6c21f5a0 6efd16b4 1f2b0f66
+          bee42b7c 0552174a a2e74c7d 2c7ad6ec
+          25c33616 9e7f7177 6ad5ce5f b9028357
+          6dd8c47e aec5d173 a73dca7e ce43f469
+          f1836805 5c0d1da8 2fdc024f 5dbd7e7f
+          3c96c89a e2629175 f1124876 9b369691
+          e020e605 8f6a6797 3fffc2ed edab96db
+          cfc7cf70 ce059eaf be0e558f fc7ef8c9
+          471fff85 d102ca65 787176b6 a8254d19
+          8ed8a1a7 eaee42f5 bb5303b9 24a279eb
+          2e34ed2e faf75f11 5c89bb3d 111327fd
+          68c8da4d 4f0dfd7a 9daf37fe 8cece7c2
+          90351bff 1c3579f2 358a4bab 0324b858
+          cca0259c ed47ab50 bb6e1364 af474b48
+          43467a18 10d12f1d 697327b1 7f172859
+          e548e499 04dce02a 2dbbd8df da72ce4b
+          e2ceb9c0 6bfdf4a3 c88a679e bf9e29db
+          d1b47bc7 07ead16c 44762a32 97cc83c0
+          32d89e1c cd76959c 40e396dd f07578e8
+          4883377f 716157c4 b871170d dfbe6d47
+          5ff87987 6edd5a14 3171c202 f673af82
+          d6ba9b08 77b471d8 4cd3b5ee 2b46cbe6
+          1d5a421a 320b6509 ad6036b3 24773c62
+          87e5699d 04083e30 59602f7f ec8f7754
+          3dfcd008 5f437df8 0a3cb9bd 4df2d4d6
+          5f6514f0 5312779c 64304ab0 254acee5
+          eaf1845d 5fdc4912 fc1d9da8 59b5019d
+          158da0d6 149c893b 37f6dbc7 8c5e387c
+          d7ae9abe f4730fdb bea3d13e 61f4258a
+          0785f429 f281daf6 c4dbe146 c3d6dde8
+          d64699e9 f441616b 9fba0666 5db400d6
+          c4680448 e4f11103 8347b5e3 2b9e7de1
+          fa967fbd 7d4e8f6a cf99c0f3 3737a3ea
+          0f0f0d3b f9f063d7 9be82611 47115b40
+          d685b361 4b4ffb8f b79fd61b 056dfba3
+          76c55ab4 1f3d01d1 4ce28eab 85d08b43
+          b651c32e 1ebe676f 6b5ffcf9 87efd8eb
+          8a183be2 96800f47 e8d3e4c1 6183f578
+          dd954da8 5953087f 5b8796a0 ea457b4b
+          4202b22e 5e048364 026d74f0 23f28c02
+          aef536b5 5c22b7b7 896127f0 9a5e7dd9
+          5af1ccf3 d731253b 899c9a13 a796832d
+          51e2c78c d4da0584 5ccdb4ba 3b09ed7b
+          0ea0f5e0 512dbba5 9e511cf9 8a1f8dd6
+          e143ae1f b1f76045 5ffe3d86 ed3c709c
+          89d4cb98 ef97d3a7 ca076a6f cef623a5
+          a85b5bc8 12d440e8 a35a750d 140d881e
+          3c00a933 c721a050 71312f98 2c882c7f
+          f48f3fa9 7ae8c121 fed696f0 1178b2c3
+          017f67f7 12c9801f 90b8e304 05302744
+          21f7aa4b 82fdeef4 8e669900 544791d5
+          aedd0867 3d8d22e3 2b421a3a acf983ef
+          18b1af78 7738fc3a c3771d3c 621b3ef4
+          5e96a1b4 d287cb81 fbaa77c6 7c01b4ec
+          2f46c7de 22fd5bb5 4a0082c9 88b439d3
+          1199934e f578bc24 b1c159b5 0595cfbd
+          706dfd4b 7f392711 eeac0b3c d9d181aa
+          dfff36a5 fc91c7af 365a114f 1f330f9e
+          0c6dc72e ef075704 ebee149d ba735164
+          0ba40f55 cbd7a2ab a2019215 7434cb8f
+          afb80493 e9a511fb 8ffc2b9c 7eade1bb
+          0f7d20da ac2faabf 1f7dc8e1 8f5a8fe7
+          6a74a076 fd5678ea ebf58f6a d99a285a
+          2dc8bbfa 52b6de99 69bde348 e4a91b5d
+          8adbbd54 eeec3ceb faebacff 3fa87dfa
+          0963d59f 9fbfd16c c325e4c4 dc2c7748
+          99350976 b5ee4eaf bf09fb7a c028a16e
+          d57a741c 2dd5b261 821b64b6 26ac31a6
+          a6fd3a1c 7f39f67b 3dc8feb1 4efb3d89
+          b047ad19 ee3a5983 aaafd630 fd266b35
+          c57a6b9f 39360659 17cd678f 012d7cbc
+          20599152 f1e813b7 54fdf6d7 fd98c8eb
+          bb024f71 7623e057 e608065c 4fe28e9f
+          0c256a60 3f64cc9b c5b25a49 bfa1b1c9
+          888ebd07 d1b4731f fc2e3f09 3c8e5c85
+          fda7d29c 9773cbc8 e2b2b0fc 05471495
+          c03270e0 35507002 b44713fe 1882af8e
+          a365685c bf0501f5 56ad21f4 62699044
+          248e1f8d c4b1f95a c701828b 950f661b
+          e6553df7 c28f6b1f fb83b1cf 0abc9a47
+          7e1f53f1 d8133f36 5a904b9f 2a27d989
+          dd8aeccb 2f8081a9 7a5d7127 49f0b5b4
+          697577ee c60eed98 83e06591 33b49973
+          727f3ef2 70597d38 ff9a230e 1c735806
+          0dbc2310 109ae943 e740e3b1 04d5e708
+          b64ee92a 3ea6d516 eb65c406 51d06ed5
+          5a1269e2 1d4f228f 85c8ab02 81c05cc5
+          7df6aa38 ce9ac00b 78bdeaff edabd9ff
+          5c4c9f26 2f08c8bd f22298e3 627b347a
+          22c03cbc 7ac53a74 55d66837 d1084ed6
+          36196e63 72e24723 8b4f7cc9 c3efcb44
+          de4a7346 da5becf7 a67a3c1e 564113e0
+          aa6b656b dbd70838 d9472eea 8759d166
+          d3eaf104 89b25c5e 305a9057 f9c4d357
+          553ff86b 5b9f1278 01bf1f95 bfbe6768
+          c5e34faa 172bc863 f9c85d91 327d0262
+          f207326d d783abff 160b1ad6 6e446bd1
+          1116f003 d412852b 57311c34 98ccb770
+          f52b1b4d f7b0dffb ffb4771f 70725575
+          ffc77f77 fa6c2fd9 924d4f36 bd6f1282
+          40e84511 1444f0b1 3c585eea a3d81015
+          fdcbf3c7 02162ca8 204d50a4 08080a22
+          01911688 94505248 4202a467 379bed7d
+          a7cfdcff 397736fc 43c9cc10 52f6cefd
+          bc5faf31 6d37c173 cf3de77b ceb9e7dc
+          9785a55a 875c6f91 81ed8db2 fdde07c5
+          f466ef02 758b5938 66948c3a ed38c9bc
+          ae8bfcea 35e51cd5 f9fdb7ca 4c07e5a2
+          1f946e75 e7772f32 9a7e7dd5 f9bea01c
+          cd257486 82513532 fafd27aa ba9a4395
+          520ddee0 e6add2fe dc4b12ef 0ff3dc9d
+          83984969 0f8c1bf3 9d79afef 74d4ffef
+          b91bb6a4 82f513bf 6326a485 5ae08431
+          8c482a61 4acfc64d d2f9cc8b 6a409bfd
+          540cfd58 4bed7147 4be9f449 14a04378
+          8352d0f8 8b2b3fbe e3e26f4e cd7adac4
+          700978e2 721daffe f7035c3e 6770e923
+          513e798e b87c39bc 34d66af9 92b2f3fe
+          7f4b6877 a7b8589a 754eb84b 48283071
+          fc6d735f ddeec8d7 79cd59b7 e9e9e0b4
+          c9379a71 19a03638 20e4a981 6bb47b40
+          763fbedc 3ae333eb d1294321 6ffcd91f
+          146f4931 05e89c90 775cf36f 7effd9ed
+          dffceaf0 0f783b2e fa9aecba f2eaff52
+          ffd133b8 744e18a9 1a32e1bc 0f49b0ba
+          2ab76ff0 fb64c7df 96ca40e3 2ee16400
+          a7253c79 c54c242e cee5f9cc 3cbd59f4
+          e32b3f52 e5f01295 c121835f b73e1faf
+          431a973e 2aa9582c fbd129ba 89ac2897
+          711f393d b7475d90 2f3eacea c6a9c33e
+          e0299f96 54ea1ce1 41024784 bbf23933
+          a462deac 9cc35dcf eaf5d2fd ca6b928a
+          2684f6cb 41d92e26 dbfc13c6 7c6acefa
+          4d8e3e13 6ef6ea0d a9c0d489 5f31a3d2
+          48ad7042 2329d6f1 277d9bb6 49cbe3ff
+          c969a956 2b9f394d aa8f3e82 90e7109e
+          a04cd9fd fb1bcedf fe8d2f1f d03d0b07
+          34e0edbc f8c28ae6 dfdf708e fa8f65bf
+          b7132a65 51914cfc af0fe7d6 08b9dd12
+          6fef92a6 871e9568 571fcfdd 3929dc45
+          a42f503f eeaa39ab 376e72f9 038e2e0b
+          fdff7ff6 8b6b3716 34ccbe2a a5ca85da
+          e1808ca7 7ad94428 2a6d2fac 92de975f
+          b1cefecc 65f03cf6 f4932558 5b238c84
+          9d514dcc 44e20c31 cdcf0ecb 80a792a7
+          34ffeeba cfbaddf1 5384d9bb fcefa8bc
+          1ea93fff a3e2f2e5 3022d50d 94fa6cb9
+          f35e196c 6e570d1e 1b099d22 15954441
+          c3ac7fce 7a61edb5 aec2422e bcbe770a
+          0a4d331a beda34e5 096157ad 337a6ffd
+          3c9e1ad8 363df4b8 24070673 5aaa3554
+          10acffcc b9e2f6f2 a0b21378 0252daf2
+          873f7f50 65a903f6 00e6010b 78662c36
+          35158b9d a5a25d80 4b95c7f4 7b66dd2e
+          19f3c153 a468e2f8 ec87196b 054169fe
+          f71332a8 cfbba33f 7350af66 2d4fbd9a
+          8a842e76 9794c428 90ff6fc6 53cf470b
+          66d67f29 19965719 0e3be576 3025b46b
+          b7ecb8f7 c19c976a 03555532 f6c3ef57
+          83623743 0107b497 a968f454 95a53e3f
+          ac02de8e 0bbf226d b7def571 95401773
+          95f2bf12 168f1b23 55c72c16 239770a7
+          46a17d6b 3748db8a 95920847 99db7550
+          3d4986a4 a560da84 efcc58f6 dc6e0ae4
+          2da3f5f2 0a99f9e4 b3ad05b3 a65ca6ca
+          a99dfbc2 211d7832 253d1b36 59af32d3
+          67816695 4ac988c5 0ba462d6 3411b741
+          c8cbf776 212081d6 5beffaf0 ceef7e73
+          c2b00978 c981fe19 8950e834 558139d4
+          389fa9c6 c55b5a22 f5e79f27 ae5c9e0b
+          71b92439 1892a6a5 8fa49fbb e33063c7
+          48852552 fcbe0577 ce78f2d9 c7bd55d5
+          14c83b35 e623aa64 c6634fdd 5bb264f1
+          52555ebc 89d40919 4f359bf1 0135f259
+          f6b4449a 76e57674 8afaa671 1ffb9004
+          ac370451 86f93e08 3092a1a3 765f7dc3
+          67b77ffd 4b873fe0 e9b35b3a eefec757
+          54f25cc0 d5c9e36c 97d2a7b3 bb65fcd9
+          a78b4785 bc9c9666 837ed9f1 f7a532b8
+          ab85a559 873552aa 7aac34a3 e11f7b6b
+          6ae314c8 bea9f289 9ab1c8c5 295356d3
+          793be4f6 50bd6ea4 b35b76dc f72f9164
+          22fb260a 7533790a 0b65c2b9 1f12b75e
+          da652890 eff5c39b 0c874f49 f4744f3e
+          ec012fd1 d9be20d1 dfbfc460 f62e8fd3
+          9daa281e 97d41cb5 48cae64c 578d520e
+          275de857 913dbc4c bad7bf2a 6632c5c8
+          d341e12e 19924d05 d3275d30 e5de077b
+          2890ec26 df7d7f47 f1dce93f 4e84a499
+          fbc42154 a2efdfba 439a1e78 54249703
+          e2130929 9a3c5146 1e779435 d036192f
+          e7354f40 1abaeeff d717b75f 78c1e10b
+          783bbef5 75e97ee0 df5f76fb 651a9724
+          7fc39df5 dcdd8471 5277c6a9 eaa7b92d
+          cd467636 caeea79e 9324cfdd 392adca9
+          90d25f38 6bca75d3 1e7a64ad 7fdc78ca
+          2407feb1 e364cafd 0f3d5c76 e292bfa4
+          2212a544 9c71afa4 6271695f b946fad6
+          6db05edf 98f55b54 aaab39f9 5829993c
+          217d3415 212f9feb 872fd1d7 7f72bca5
+          79ee610b 78b1a61d 0df19ede 23f59422
+          57244ff3 5d4a8d26 8a0b65dc 59a789c7
+          ef939cde 97e776cb b6bbff29 b1ee5ec2
+          9d932625 222265c7 1f73dfd4 fb1ffca3
+          7ffc440a e4dd84bc 71e36386 a4ae48a6
+          e429ee19 e784bc78 dfa0342e 7d4c12fd
+          fd392dd5 ba5510d4 bb6a0355 e5e4bb3c
+          e7f6cb8c 9e479efc 8c7e3bd8 210f783b
+          2ffea6f4 3cfe9f2f a8ff88c9 5c8a3c0d
+          774955c9 025e1977 e669121c 334ab546
+          891c7a2a bf34def7 a00cecdc 45013aac
+          b352e164 8de1322f f54face7 00dffd30
+          feda9b3a 4be6cfba 241e922d 843ce718
+          dcdd22db fff68048 2ee7dd25 1212acab
+          95d1a79d 286e9f57 bfdf19f9 daa4bac4
+          17efee3d 2eba75f3 7eaf90ee 77c00bbf
+          b67166bc b3fb68fd 1fc1a5c8 c370a71f
+          9bf31852 3167ba54 2c9a6ff5 de59a9d1
+          65d7ca35 d2f6fc2a f5fd3c77 e7a89988
+          903415cf 9d71e9f8 abaedf41 81ec9fe0
+          d46932f9 ae7b5eaa 38e5841b 9211e9a7
+          449c71ef e899b99e 575e97b6 e5cf5903
+          e4ec212f 29650d73 a46ae15c 71f9dc56
+          5b8dfca4 67f17a9f 7efef37a 42ed9005
+          bcc6ef7f 47fa9f7b e9abea1f 9fc225c8
+          c77497fe a1684c9d 8cfef007 d22b07d9
+          9eea555f 14efee91 a6a58f4a 321ca10c
+          1d448591 78f949c7 dd39f9ce bb1f0ece
+          9c4581bc 078129d3 c415f4dd 9448c9a3
+          0c909c23 158fcbae c7964b68 9b1a1fb9
+          b374cbaa 2dd6c754 d59d718a 148caa7d
+          539b8d3c cbff2ef1 c73bbb4f 08ad5bb3
+          5fe7e2ed 57c01b5c f5e29458 7be731fa
+          1fe712e4 61639310 f19514ca 98334f15
+          5f799935 62ccc5d6 3bef9370 5b1705e8
+          b0190815 4696bb8b 825706a7 cfe44894
+          0360cc8f 7fd65dba 70eea5b1 90ac23e4
+          3947acab 4fb6dff3 40fa9482 6ccfe3a9
+          aff19614 cb8473cf 145f6991 d566233f
+          b97d3263 70e5bacf eb89b583 1ef09a2e
+          fd3f32b8 fa954fa8 7f741245 9f7ff473
+          779ea057 461e7fb4 144fad17 89e6b0a9
+          cfe79396 27fe233d af6ee630 6387853b
+          15423696 ce9ffdfd d1fff7b2 160ae4c0
+          28983b5f ea6fbef5 95cad34e fa4d322a
+          ed948843 6e27d576 f6efd895 3e1fcfed
+          cefe0dd1 98144c1c 2fa34e5a 229e021f
+          cfe3e56b bd704b20 d6de796a df938fd7
+          1ef480d7 fbe8c335 b1b68ed3 0c8f0429
+          fa3c0b77 43ef992d 9f3555aa 8e3b2ab7
+          e7ee5443 d4ffda26 697ef259 31986d70
+          5ab8eb2f 5d30f7fa fa9b6f79 a170c142
+          cae44086 bc5973c4 5755f1b7 4452ee63
+          16cf5921 4f3fc3dc bd7a9d6a 5b73d874
+          118bc988 258ba562 ee0c7179 5d9c8f97
+          bfcded64 4f79c5c7 0e7ac0f3 54549ead
+          feb169ac f9e79f54 5c752c35 9532faac
+          0f885b1f 8992ed40 63fddc5d 6fafec54
+          23ce787f 88027490 6454a4f2 d413ef9e
+          f4c73fdd ae679c70 e0d57ced a2feb223
+          1a7eae82 f4330c9e 9cd40e27 64e7038f
+          48b4ad5d f5d0592e 7c2aa582 9d57469d
+          798a0447 55338b97 af01cf27 a503abd6
+          7ea8e9b2 4b0f5ec0 dbf5b3cb 6470f5fa
+          b3f43f46 91e71753 85bb4045 918cfee0
+          c9e2afa9 cabe346b b53ba675 86d3c0ce
+          1696669d d4d818d6 63992ff8 c78dfe59
+          c1dc061e ba3c488a 8e385226 fde1c66d
+          a58b17fe 321a9226 429e73ee af706bb7
+          343ef888 2423b1ec df108d89 bfaa4ac6
+          9e7e8af8 2b8aad81 3af2ac4e 7844622d
+          ed53bb97 de7fe241 0b785df7 dd734274
+          77eb6cf5 8fd1d4e4 d388d13a efce23d5
+          47364859 c36c9150 38f343be fa8f0cb7
+          b43efd82 74bcf4b2 55f9e09c ce47858d
+          9d658b1a 7e5af599 cf6fa144 0e2e15a0
+          65d2f537 3c5c71da 29372762 42d7ed10
+          2eafea6f d76e94d6 ffac483f 8f67644b
+          84612999 33536a8e 5a28eea0 974d17f9
+          c6b4ea44 75acb1e5 d3cd575c 7ee003de
+          ee5f5f21 b15d6dff a3ff1196 67f3a8de
+          98e9ca53 3265a254 9f746cfa b9bb1c1e
+          e408ed6a 965d0f3f 25668aca e0242a64
+          24ca4f39 f19689d7 5dff60f1 514b2890
+          4311f2e6 35440b67 4dbb3e96 907f338b
+          e7a491b7 ea77973d 2b7d9bb6 4ad64345
+          759b9d48 48f50947 4bd98c7a 2bf3f13c
+          5e9e0dae bde28ded 6e5bdcfe e73fe5bc
+          c135e780 d77ae3f5 6362cdad f3d53fc2
+          7c4d3e05 bcb83eef ae5a469f 798a78cb
+          4a446259 260954cb 91523dcd 8ebf3f28
+          b1be4196 669dd4c0 e88d152a 6414cd9d
+          f587c205 8b982338 842acefa 6873c5fb
+          165d190d c936429e 43a8b635 de1f969d
+          ff7858fd 98c3abcc e271f194 94c8a853
+          8e93c2d1 d5d68908 c8a7ceda 8af9a33d
+          d5559f38 e001cf5b 5dfd31f5 97d7327b
+          974703c4 98baae25 011979c2 3152503f
+          5e642094 6569363d 2c6c7e7c b9746fdc
+          692d23c0 29e9ce5a 9add5071 c482cbcb
+          cff87013 05726815 1f73ac4c bae6dae5
+          15a79c7c 5d2226ec 68724ac6 536decc0
+          d6ddd2f4 f01343af 17cad23e 87425230
+          619cd49d 7cacf84a 83d66628 e45133ec
+          95c2e896 1d1f68b9 fab7ee9c ea4f2e5f
+          d47afd35 12ddbef3 0cf59797 50c47932
+          1850a33b 97df23d5 8be74bc5 c279aa15
+          09677fce 4385bbce 952fab80 f7b4b839
+          24c751e1 2e1692be 922316fe 6ee235d7
+          ac2839ee 44cae430 2898bf30 597aecd1
+          b7c612f2 0f9e8276 50c80b88 b43db3ca
+          6a7bdf68 8b3389c5 a562c13c a95a34df
+          7ab69a99 bcbc0a78 12dbd53a 6ef795bf
+          9877c002 5ef3cf2f 9f136b6c a93798b1
+          c9937497 1e0c964d 9b203527 1dab2a8d
+          d7da6e9f 31dce919 9c8e4ed9 f9c01362
+          c653f42f 0ea277e5 959f78c2 1d93aebd
+          e6dec285 8b2990c3 a8f89863 5b2b8e3c
+          e20afd96 0b966a9d 65e7d265 126ade9d
+          3de4e9b6 dce5929a 1397586d bc15f058
+          79cb9bbe 5b5dfa72 6f4d4d4e cbb43905
+          3c6f6ded 27f45f4a 25c90fc9 9848415d
+          85d49db4 447c5595 22912cef 8e55173f
+          190aab06 e63109b7 f7b26bd6 492346c3
+          9a105851 71fa69bf 2a5c7044 07257278
+          951c7fa2 4cbce69a b565271c fff3449c
+          b75c38e6 3e74ab66 baad579a fef5b8c4
+          0707b3cf e245a356 db5e77b2 7e1e6f84
+          d5e6234f 7824186b 6e39a9fd e69b7cef
+          39e075dc 7e8bc477 b79ea0fe d2024ad6
+          fef46c8c af2428b5 c71d2545 d3a7880c
+          66391245 8d04f588 b06dc54a 697b71a3
+          78589a75 50af623d 77d75876 c4c29f15
+          cc5fc091 28c34461 c3421971 ce594ba3
+          71b98ba9 74e7d08f c574aeda 246d4f3f
+          2fa6de0c e7caf23c 9e6adb8b 664c919a
+          638f146f 7180f3f1 f2a559d6 cbb48d2d
+          23775e72 f1b1ef39 e0edf8ce 85b3a23b
+          778f6679 d6fef4b2 ac6e142a 1b664ad5
+          918b44e2 7a23a499 b991304d e97d7d8b
+          343ff2b4 78fc94a1 9324e392 285d72cc
+          f593aebb f6a1d213 4fa64086 91e08c99
+          7d158b16 5c1b0fc9 0a429e73 b87c22cd
+          8f3eadda e4cd43ef 96cc74f1 4d6b26af
+          7271838c 506dbea1 da7e3345 19dabf23
+          b766744b 7db523cf 79cf01cf 37b2ee53
+          fa2f6379 d6fef4ae d99289a3 a4e6b8f7
+          895110b0 b6d5671e 2a18126e 6993c607
+          1e93587f 9823511c 354cb496 661faafd
+          f4276f2b 6ce04894 e1a6f484 93a5fe86
+          eb5f2d5a b8f0272a e4b510f2 1c725baa
+          36381949 4ad3c34f 49a4ad33 87a35312
+          e22e2c94 9a63df27 c513eaac 3e00f990
+          f4c51fef e85ad879 cf5f5dfb 1df0bafe
+          71af243a bb4e525f 554889da 3fdcf9ca
+          0aa466c9 62094e18 97fd6d15 6e972443
+          21d9bdec 19e9dbba 5bdc01ca d049e14e
+          8586d7cb 1ae65fe6 1f3fb191 02199e0a
+          e62f94c9 37ddf078 f1d147dd 904a087b
+          259dd2b7 abb6b86f 4bb3ec7e e23f561b
+          6dbde922 c3205db7 f5c1f163 a5faa823
+          c45f1664 a9361f9a e8f432ed e86d5ffa
+          dca4fd0e 785b3ff7 dfe5d19d 2db52ccf
+          da9b757c 92cb2d23 8e982315 f3675b67
+          25656e41 5c622612 d2b96aad b43db726
+          1dee98c1 7552b8eb 2f9c3fef f2c937fd
+          e1a59293 4ea54c86 73c89bdb 101efdcd
+          affd311a 937f338b e794065d ac36b965
+          f91ae95c bd4eb5d5 c9f4b3d2 9944a352
+          b1608e54 2e9cad32 9f8bb75c e4411d30
+          3c12f08d ac3b79bf 039eb7ae ee74f597
+          04e9dced 4d1f7659 3c659454 1f7d8418
+          01d53224 b21c89a2 0c6c6b94 c6879edc
+          fbb7e000 fa1d9685 8b16dd36 e5cf7f7a
+          48cf1061 f8f35456 3596ce9f f7934448
+          1a09790e 198719e9 599ca687 97cbc0d6
+          ede90178 a6865a0d d85d4585 52fdbe45
+          523ca156 5211cad0 f65c5298 e8eb3fb9
+          e7df0fbd fb80d7fb f8a392ea 1f385d7d
+          05871bdb 39e8ab0e db5b1490 ea450dd6
+          34bd8423 990f3456 23c15877 8fec7ee2
+          69897487 ac877ae1 1cd1983c 3feed2ef
+          ffb260ce fc4e4ac3 1e4a8e3f 49a6defa
+          e7e70b16 2cf8850a e8614ac4 21fdbb0a
+          78918e7e ebcd42b1 aeeeccb3 7843bb6a
+          8313c64a c5bcd9e2 f67bd970 61f790ef
+          116faca9 75dea673 3f5cf0ae 03deeb67
+          9fee8d36 b634e8bf 84a2b46b ba4bcfc8
+          54364c93 b2393354 b8cbd2f6 eba5d978
+          4cbad6ac 93f6d59b c41b1496 661d2411
+          96dd25b3 66fec85d 54bc9dd2 b097e0ac
+          b9c9893f bbec6e15 d0efa534 9cd3beeb
+          63abba5e de2a1dab 5e16331a cdfc3c9e
+          fe867852 cae7cd94 f2d91378 162f0fae
+          bfca6725 deda91f3 df75c053 df34597d
+          73051dbc 7de91b38 505d2895 f3678967
+          44c5d0b1 28927194 37b06397 343ff6ac
+          7888f5ce aa2b2989 0767cffe f5b4bbee
+          784ccf08 c186237a 7fb0ad64 e68c9f27
+          c3b295d2 70d07557 6d75f313 cf4adfb6
+          1d629a66 e6159a58 54fca3eb 6444c35c
+          f195f8ad 0900d89a 3ebcec7d ef3ae029
+          470e7d33 6c9aee4d d390cab9 b3a4b87e
+          82c86028 cbae59b7 24fafaa4 7dc54a09
+          770d0a1b 6b9c45b5 fb0fd75f fd9b3b83
+          33e730ae b7a9e263 8f97e9f7 fc75bd7f
+          e6cc8bcc 94f45122 cea0976a a33d1169
+          79ea3989 eba5da6c bb6ac361 299e3249
+          ca674d13 93bbddee 82ea9a2e 79f701cf
+          3016a9ff e5700c9b d2b37785 234bd44d
+          3c5d5cc5 c559df35 6baa3fef dfb25dda
+          5f7859bc 7e6169d6 41921169 2a9c32f9
+          1231dccd 9486bd05 a6cf9229 375dbf3c
+          1a953f53 1ace19cc 7b037aa9 76b37500
+          b2194f66 1eccc713 e2a9aa90 b21953c4
+          5bea6316 cfce0cf1 a422d1d9 83ab57ba
+          730e78a1 f56bf57a fe02f5cd 3c626fd3
+          1b5ecfd4 97cd9c26 05e3468b 44a299bf
+          dee39668 7b87ecfe cf8be983 3039d0d8
+          39e12e2a 83de0993 2e9a79ff 7debf40c
+          10f2e0f6 4f99dd85 93265da5 aeed6a4a
+          c3311dbd f56959fe 82849b5b ac363dd3
+          805eefa6 2a9a345e ca674e65 16cfce97
+          5d5de6d8 aed6a257 8e5e3426 e780b77e
+          f1fc8268 53eb58f5 cd6cbab7 213d7b57
+          505b6acd deb94b4a 542f9ecc d830e8d9
+          bb816d3b a56bc376 71f3c661 07050149
+          79c74fbc 6dd6bf96 3e1e9836 9302c913
+          c5472d91 990f2ddd e2193bfe 223560eb
+          a1449cc1 ed17e9db d62a3daf 6d123312
+          cbbcab36 96105f6d 95944f9f acda7c0f
+          3b6aed1d f2bcde9a 9a869c03 9eb7bafa
+          38f54dcc ded935e0 a93c5736 4bcfde8d
+          b20eb8cc c8e39168 6bbbb4af 5c2b6e1d
+          e7599a75 0c553556 cfb8ef6f 9707a64c
+          eba234f2 8bbaa632 fbf1475e 708f1ef7
+          2bd31416 e11c3162 5321cf23 d2f1c21a
+          09edda9d e5593cdd 00c4a570 fc18299f
+          314992bc c2cccef4 435547e4 1cf09439
+          3ae7516e 36bcc755 b8f3957a a4b47ea2
+          b84b4b33 cfde49fa e5d383db 9ba47bdd
+          36eb1538 70c82020 2ebd8151 75df4b45
+          a3bb288d 3c0d7913 278766fd ebc19b62
+          11594e69 38833eb7 b4bfb153 fa366f15
+          3316cd3c 8b178f5b 3b6acba6 4d16437f
+          1d837bbb d293710b df4dc05b 24eca0b5
+          67c7ad46 62252adc 0547568b c4b20ccb
+          bc1e89b5 b64afbaa b57b1ee1 80130601
+          a6c48d11 b53f98f3 d493cb8b 161d4981
+          e4737b10 0eb7fa6b 6bfed74c 4807a5e1
+          849b5b75 ea6e918e 55eb25b4 ab25cbb9
+          78626dbe 0bd6d54a c9f8119c 8b675f6e
+          33999c18 dbd5e4ca 1af0e2ad 2d622653
+          fa811c66 f06c7873 8bdb2515 7366887f
+          cca8ece7 deb90c09 b5b449cf ab5b98bd
+          73907844 1e9bfbd4 b2bb0293 26b33093
+          e70a1b16 cabce79e 5d659456 5da6823d
+          d7db09bd bd2ffd2c 5eff961d d63bc533
+          eea88dc5 a570f448 29993ac5 7ab407f6
+          a3375a24 5ada4bd7 4cd33b2a b304bc35
+          53c6ba13 2d6d9586 9b82b3dd 685dcfde
+          4dac49cf de25b284 3b8f5b12 ddbdd2ab
+          c25d2aca fb661d33 064849bf b7bcfcc2
+          5428d44a 6938836f fcc4e8dc 15cfdcae
+          82fd7d94 8633e815 d7ee751b 25b25bdd
+          e6de0c73 35a99418 25c55234 61ac788b
+          ddd6233e b0e30517 b7a7ac7c 6ad680e7
+          2e2999a1 7ed74389 d94f3225 523a7992
+          14d4e880 97e54e75 bb25d2da 26bdaf6d
+          b10eca84 23c25ddc f4957c7a deca1737
+          17cc6ba0 409c34f8 0b87bb3c 252597a8
+          3ad04d69 38a0bff7 89f4bcb6 53c22d6d
+          d64a4d46 f1b804ab 2ba5b49e d797d998
+          ce6cd903 9ea47763 d0e5dbaf f3165f91
+          db1a8919 c545598e 4631ac19 bec1c6dd
+          32d0dc2d 2ee2bc33 aa88bbe0 d6f9eb5e
+          5ee69f30 8943111c 26386b8e cc5ffbf2
+          7655073e a77ec9ae da3c6718 e93ea16f
+          cb764974 f5647e16 2f919440 f508299a
+          384e6818 6c1df0e6 e412f0a6 0c7d31ec
+          3442b736 574c9040 55654eb3 77b18e2e
+          ebdd85ac cc3a4322 2ceb1b36 bc72897f
+          dc78ce45 732875ed 93aa0e2c 5375e116
+          61cf64de d32b335d eb37caa0 3e32c59b
+          a14bd76f 39f2f9a5 68549d04 cb032cd3
+          da37e04d 21e0e56b c03375c0 1b9f0e78
+          c92c77a8 c72d91b6 0e1950a3 3b96679d
+          513d0cb7 fb42331e 6fa7289c 4dd5815e
+          55172e57 3f6da434 f23ce0a9 5e3cd236
+          2851d5d6 5baf36ca f4a0b5ea 337c232a
+          ac152096 696d494f d18e3593 89ac016f
+          0e01cf66 8db61a80 790b0c09 d6568b04
+          0259df3b ab67f8f4 eed95067 98e55907
+          4884e5db 0d9b373d 17a89fcc 0a8cc3a9
+          3a20aa2e ec5075e2 e3948633 f46fdb29
+          311df232 2dd3aa80 17a82c97 e2f163ac
+          c902d82d cdab76be a3b37cd5 a811a3f7
+          19f0568d 1ee1565f 54c6bb48 ed458fb8
+          8ad5c8cb 5f519ed3 ec5db4a3 4bfa7734
+          b13ceb8c 70b76cc1 f66db7f9 c74f0853
+          1ad0545d 30559d58 1b0fcb2f 288d3cef
+          f7bd22bd 9bb6a637 5b645ba6 0d04ac49
+          026fd0e0 d565b68d 7952bdcf 80672612
+          e3243dd5 071bd137 63415dad f84a8b33
+          cfde697a f76c7b87 0cee6c64 79d601f9
+          4ed5862f 181e3787 dce2cd21 6fecb881
+          85dbb7ff 321996e7 288d3cee f1f5326d
+          6748c2ad 6de967b3 332dd3a6 92aa0f29
+          91825135 2a0b5076 360d7835 fb0c78ca
+          5811e6ef eca8684c 9db84b8a b3cfe0b9
+          5c12ebec 9650cb00 cbb3792e 1e968f2f
+          dcbc69a7 af6e3485 8137531d bd2be0ef
+          5403806f a95f4529 907c9e01 1009ed6a
+          95444f6f 9665da94 f8ca4ba5 68ec680e
+          3db6277d 71a7660a 78fa0fe9 f66d442f
+          cf168e2c 137f6585 647dd998 c72389f6
+          4ee9dfde 987ee816 f91cee6e 5fb079d3
+          bf039326 c539c51a efc45b55 6daa3ab2
+          5ad5954b 288dfca5 07f2838d bb24dcda
+          6e3da2b3 efce2425 1e3d8357 57cb7129
+          f60d7853 32053c66 f0ec3638 53232d7f
+          5585780a 0b24eb83 136e97c4 d4284e2f
+          d11a5ce5 fc0dfd51 7975e196 2ddf0a4c
+          aaefe70d c3d877cf ef125547 22aaaefc
+          51d59947 2990fc64 a880176a ee946877
+          af35c8df 776762a6 eb4475a5 14540499
+          c5b367c0 1b9f29e0 4d21e0d9 ac3357f7
+          64a11a71 794b8ad2 afb2c878 f9dd12e9
+          ec96c1a6 669667f3 5822255f f55455f1
+          dc1d72a2 ea4a8faa 33ffa37e 3a4869e4
+          61c0d307 27c4cdf4 0cdee060 fa3d6619
+          3a144f41 81044654 f01c9e0d 876cea62
+          d7ee3be0 19c674e1 2d16b653 386aa4b8
+          f5db2bb2 1d8fa2c4 7a7a2436 9812de35
+          9c9fe261 f9de828d 1b9f7617 16b2ca82
+          dc86fd85 85a2eacc 0e55773e 4769e469
+          cfaf9a7f fd6aca68 674f9680 97126f51
+          a1f53e73 76d2da2d c98b91ec eb1bb776
+          fef4b707 bc758b66 8bfac33a 5674ec23
+          7dfe9d4b 3cc58592 f5392b75 5327ba7b
+          d428ae43 0c1ebfcb 4f2979a6 61c386eb
+          82d3a645 3336e2c0 5bda0655 6752aaee
+          3ca0ead0 1d14481e f6fdaa39 88b4774a
+          ac3bcb46 0b1df00a f50c5e25 af3ab165
+          1f90f245 b76d1df9 b6801779 fdb502f5
+          872cdcd9 e95a2644 8ac68d12 afde3d9b
+          6db8a51a f1786f9f bac17b78 e63e3fc5
+          e351f9b4 eaa8fb28 0aec0f55 77c2aa0e
+          7d57fdb4 99d2c8b3 80e7d6c7 a57449ac
+          af3ff346 0bfd1c9e 751e5e95 78bcecc5
+          b3e3a536 3c9eb707 3cc3eb65 8385cde8
+          0d16beb2 327107fc 22c92c77 a2db23b1
+          ae1eeb3c 2417cbb3 795611ac a5d9cfcc
+          5fb37a0b 8581f742 d5a1266b a9968e3d
+          ef025e7c 30a506f9 bdd95f5b 2686b569
+          cf5b56c8 32adfde8 0c57fbb6 80a7940b
+          5beeecd6 af4b6044 997882c1 ec432db7
+          21513582 0bb70f5a bbaa9047 e12e2277
+          cc5bf5d2 df0ae6ce 13a667f1 1ec6fea2
+          eb90aa4b 8fa93af5 3b425efe b515918e
+          6e49f6f6 a71fcacb 3073e0f6 fb255056
+          664d22c0 7682ef14 f0c612f0 eca7a0b6
+          5a5c0539 bc7f361a b5a6e753 7aa68fab
+          9c370db6 2b18dc30 f78515df 2e9cbf20
+          4681e040 507529a9 ead465aa 6ead26e4
+          e5517e57 9f68778f 24f42c9e 2bf332ad
+          9ec10b8e ac22e0d9 f332d7bd 53c02b17
+          9668edd3 b7ab86d7 e337c453 5494f9a1
+          59eb2abb 24391092 58ff00d9 2e9fa424
+          1eeb0e7f a368d1e2 dd14060e 2455a73a
+          55ddfaba aa63fd94 469ef4fc aa9bd09b
+          2c627a06 cf9df9a8 14773020 7e7d540a
+          c56637ae 7d05bcd1 c2dc8e7d 029e3ee0
+          b8bc28fd fc5db6e5 59bdc162 6040e2ea
+          c6e602e7 0975fd93 31f9d1ac a7963d41
+          61e06050 75eb6955 c77e2acc e2e44dc0
+          8b7677ab 80d797f5 a81497df 6fbd9796
+          47f06c19 f06ade29 e08d1266 f0ec13f0
+          527a8345 a9b87cbe f469c759 025e62cf
+          0c1e1b2c f2e0e2ab 017859c9 ea994f3c
+          7a75c9b1 c7d306e3 a050754b 541dbb56
+          d5b52755 9da39ee5 41c08b0f 24243118
+          cefc460b cde3165f 49b178fd 063b696d
+          7699651f 9b2c4a29 1b1bd167 e09516ab
+          8097c35e 76b75ba2 5ddd1269 ef22e0e5
+          43be4bc8 40a2abef 7325279c cc912838
+          b8214fd5 3155d7be a7ea5c88 d2c8836e
+          43f51bb1 3ed56c44 22593664 99e20af8
+          c5571460 27adbde8 4c37f29d 02de2449
+          bfcb0c76 e8e455a6 d353e86e 3d83972d
+          e0a91b39 de372089 fe3801cf eed73d2e
+          09d5d95e 39f51fff 5c4369e0 505075ed
+          7955e7ae d1758fd2 b0371de9 f46a4e4a
+          7d322fd3 ea7d181e f1140485 07f16c76
+          890da3f0 ed01cf30 2a8567f0 6cc5130c
+          a89bd09d 35dc495c b5cce170 d6955c0c
+          f3709714 d3535db9 7eea3fef fb5dd907
+          cfa44070 48e8baa6 eadcaf55 dddbccae
+          4afb073c fda84e7c 20cb3b69 4d535c7e
+          9ff51810 d7dc5e17 d88c462b b77efed3
+          ff3fe06d bbe0f3fa 378b8877 36eaecd5
+          47bfc1c2 f06759a2 5537b1ba b692d053
+          f2b0f78d 1b97706a b0fffaf2 33cfeaa2
+          407028a9 3ad7aeea deaf551d 0cd14fd8
+          b81971e9 19bcc1a1 8067640c 786e15f0
+          fce5652c d1da2d1b c413c1d6 3fdeea7d
+          23e0b55c f747affa 4d1f4563 2fbee262
+          fd0692cc 01cf30ac 1b5a7fd8 4163e39b
+          36a1ae77 5d55e3c4 3fdc7c17 a581c341
+          d5bd3b54 1ddc6cb2 506b5faa 13488523
+          92549fac 33781e8f 755c0a6c 3719607a
+          4a7d956f 043cf58b 62fd9b94 8c9daea1
+          a4375864 7ba9bcde 411b0aa7 774ec1b6
+          17db8cc9 a099885c 5779de27 7a29101c
+          0eaaee85 541dbc59 d74566f1 6cda94e8
+          2776c2e1 74c03332 cfe0192a e0798a0a
+          0806b68c f1e94db3 7bd20187 1cdb889e
+          3277070c eb06cc25 1c242311 49a81bda
+          e00adbf3 7aebd9bb d1d53bc6 fee2b737
+          531a389c 541dbc55 d74566f1 ecdbf5eb
+          be201109 679d1c70 f93ce2d5 07e9c38e
+          0af70e78 6594879d 7a7cbdc1 c22b2e8f
+          3ba7438e d3337821 22bc5d25 24e2ad19
+          f148d5f9 9fe35814 1c56aa0e 76a9bab8
+          4cd5c928 a5613f7a d22e1535 25158b67
+          9dc1132f 337876bd cc929eb4 7ba3cb2f
+          1476d0da 27dfe983 6efd0131 f40eda6c
+          77df9e80 170a3183 67d35bd5 4c4977bc
+          a5e5160a 03c381aa 8b7f5175 b2971ec3
+          9eed492a 29e98097 f56b5de2 f67a2933
+          7b2ad83b e015501e f6a2b7b0 677dfe6e
+          48321295 64384e84 b723bd3c 3baaba71
+          d4f77fc0 b9771816 545d7c41 d5c94daa
+          6e32b963 c70902dd ace85315 b2cde2e9
+          8ce7768b c7677016 9efdbc69 06af9af2
+          b0d71dea 0d0687ce c0cbbe44 6b261292
+          8a0b01cf 8e973a2e 03beb175 0fd55cf0
+          750a03c3 82aa8b49 5d2755dd eca734ec
+          daaec4ad f351b32d d3babc1e f1167a39
+          2ac55e5c 6f0d786c b2b0d3cd a9375904
+          735ca255 52f184b5 ac4bbeb3 19c36a63
+          bbe26d6d f7501818 4e549dbc 5fd5cd1e
+          1a155b36 2be93e21 9ec3aa8e cb252ebf
+          9f42b31f efde018f 255adb65 74d7d0e8
+          2bf31978 7a94a677 d19a7bee 6cd827c8
+          ebe5d991 55bb6abe f8958d94 06861355
+          2737a8ba d9cc6e5a 7bb21edb 519f8c0f
+          669be925 5ab70a78 264bb476 f3a619bc
+          62cac366 f9cee755 192fdb19 782ad145
+          62928cc6 28303b06 bc98c4fd 5326aca9
+          bbf8fb34 af185674 9d5475f3 25554769
+          5cecd8b6 2493d627 cb57a9fc 67a4cf5b
+          a505b213 3d95f3a6 73f0fcc2 fc8e8dee
+          4e11b775 c871f687 5f53f1b8 351dcfc5
+          b5e56dda 6dc6e22b 28080ccf 01487cb9
+          aaa3bc36 cf865289 a4fa2432 f7fa6fcc
+          e0f90878 36b527e0 f13e129b 053c97d7
+          ab465759 66f00c63 28e0c529 331b8ec1
+          4c53ba93 fdfd4f50 18188e54 dd5cce73
+          78366c5a 8ca1817f d65db4a6 d5c7e823
+          b958a2b5 9de2bd03 5e21e561 ab7c671d
+          9362b85d 590e3a26 e0d9f61a ebe7efaa
+          2a3a2a3e 747613a5 81e148d5 cd565547
+          5b790ecf 8683477d b2823583 97e59814
+          f5e78641 82b7df15 7ef33978 25c212ad
+          ade81741 4b0ecfe0 a5a23135 528b09f7
+          a8cd025e 4c9285f3 66be3ee6 27bfa030
+          302ce9ba a9eae846 55578978 36632dd1
+          c61399bb fd3d4bb4 413f4bb4 f6e3d93b
+          e0b10fda 66f47311 86cb9d25 c7eb19bc
+          58fa4626 e0d94dbf 0ae7af50 0c18d641
+          211a5bab 7ee8a124 ecc35aa2 8da507fe
+          5947fe7a 89d6c733 7876bbc4 ea53b477
+          c00b0811 c05e5750 8dacd2f7 66e63bcf
+          4c99eac3 2995760c 78eab381 62c030f7
+          ca505d85 1d935ee6 de23bd44 abfa1af2
+          9dedb8f7 0e78cce0 d9886905 bca173f0
+          cccc3770 72e89814 96686d77 8dfb5291
+          08337818 d6541ddd a9ea6a98 92b053b0
+          dbb38b36 997d17ad 4b053c8f 9b32b39f
+          376db2e0 a0e3bc1b 7da5ef64 53bff682
+          2d5036eb 35453ca5 c5bd850b 16eda430
+          309ca93a ba5dd5d5 416191c0 5601cf3a
+          072f874d 16d69b2c bc5ecacc 7ede3483
+          572c2cd1 da8ade64 a1475759 e97047c0
+          b357be8b 8a941cbd a87fc2b5 37521818
+          d6741d55 75b54fd7 59d8488e fd8261a8
+          80e7f350 5eb68bf0 6f9ec183 edf2b93b
+          f36b66f6 8cd4627b ce3ba2c8 ecd4fca6
+          a2b1368a 01f61890 c4b68a30 87679bde
+          3fe773f0 f6ce0bb0 233659d8 f5c25901
+          cfc87a27 2753a9f4 260baeae 9de8679a
+          1a2906d8 44a7fa44 28061b8d 2093ba5f
+          c8ed5565 8687193c bb65f8a1 4cf746c0
+          630dcf8e 973087a1 9a194f1f 68c9260b
+          5b09a94f 33c5009b d0b3cd2c d2da29e0
+          59037f33 eb260bdd 71b8dc2e 02820d2f
+          f1de018f eedf66ac 193c9791 fd390a9e
+          c1b323fd 02f70e8a 0136a197 68072806
+          bb4500fa 853c66ec 1df07817 6d9e5ee2
+          379eb580 9de8d990 4e8a0136 d1353428
+          815dfa85 442afdaa b22c733b 7a89965d
+          b4b6c412 ad6d075e 32f4aab2 6c9b2cf4
+          31297a2a de3499a3 b517fdc0 3acf34c1
+          2e74b84b 520cf918 065dd95f 8989e11a
+          1358a2cd fbab6c3d 6b4139d8 8c5eeeda
+          4631c026 7ad58765 02bb6436 eb1cbcf4
+          59783c9c 9dbf9779 ef80c79b 2cec26c7
+          5db4d681 96dcc776 bc39b962 b08b5dc2
+          db2cf236 0dbad845 6b47febd 031e80e1
+          43efa26d a2186013 7ac63941 31d8477a
+          068fa59d 7cc70c1e 30fcd0f2 c2567981
+          3a9b8f57 d5b44e6a 70f12e5a 3b6206cf
+          1923b594 355a83ad 70c50000 ef09010f
+          00c0a0c4 29868e4f cd699385 7e17adc7
+          c3311b04 3c0cbf1b 79689385 293cb20f
+          0000010f c0e1a2f2 782a15e5 183cd883
+          aeab26cf e001043c 00fb9654 b9aeeca4
+          6393d3fe b58cc280 2de8baaa eb6c9231
+          0940c003 b00ffaa5 235e8fb8 8b8a280b
+          d882aeab bacef2a0 96ad9a19 eb10fcac
+          f4337a6e 76d112f0 303c6f64 fd260b8a
+          01000002 1e000000 08780000 0020e001
+          00008080 07000000 021e0000 00010f00
+          0000043c 00000010 f0000000 40c00300
+          0000010f 00008080 07000000 021e0000
+          00087800 000020e0 01000080 80070000
+          40c00300 0000010f 00000004 3c000000
+          10f00000 0040c003 000020e0 01000080
+          80070000 00021e00 00000878 00000020
+          e0010000 10f00000 0040c003 00000001
+          0f000000 043c0000 0010f000 00000878
+          00000020 e0010000 80800700 0000021e
+          00000008 78000000 20e00100 0010f000
+          000040c0 03000000 010f0000 00043c00
+          000010f0 00000008 78000000 20e00100
+          00808007 00000002 1e000000 08780000
+          00043c00 00000c7f 1e8ac0a6 c9dce713
+          0906d44f 326474f5 e72e0f97 1800f096
+          3e44f70d ba0f89c5 f7fd457e 5fbaaf01
+          010f8786 57dd93af df789b18 567833f7
+          fd85864b 529188f5 f50000ec e943763f
+          f98cb4ae 58a9ba90 5486af34 c44c24e8
+          43087838 540c4324 311816d3 cced6bc5
+          a0cc0000 6fe43649 46629208 c772ea43
+          0cfa1002 1e0eed0d ca4d0700 a00fc13b
+          61930500 0000010f 00000004 3c000000
+          10f00000 0040c003 00000001 0f000080
+          80070000 00021e00 00000878 00000020
+          e0010000 80800700 0040c003 00000001
+          0f000000 043c0000 0010f000 000040c0
+          03000020 e0010000 80800700 0000021e
+          00000008 78000000 20e00100 0010f000
+          000040c0 03000000 010f0000 00043c00
+          000010f0 00000008 78000000 20e00100
+          00808007 00000002 1e000000 08780000
+          00043c00 000010f0 00000040 c0030000
+          00010f00 0000043c 00000002 1e000000
+          08780000 0020e001 00008080 07000000
+          021e0000 00010f00 0000043c 00000010
+          f0000000 40c00300 0000010f 00008080
+          07000000 021e0000 00087800 000020e0
+          01000080 80070000 e0c88067 5014c0b0
+          c1fd08ea 2c80f774 3fee0978 51ca0318
+          36621401 a8b300f6 5374ef80 0760f830
+          29025067 01bc177b 025e9ca2 00868d04
+          45009b49 5204c0b0 11db3be0 f5320203
+          86d7cd09 d8483f45 000c1b6f 5aa20d51
+          1ec0b031 4011c066 22140130 2ce8c9ba
+          bebd03de a0308307 0c070902 1e6c282c
+          2cd302c3 25e0f5ee 1df01a09 78c0b0a0
+          9767bb28 06d84cb3 b012040c 9780d7b1
+          77c0e319 3c607808 0d759680 9de85967
+          360701c3 23e0edd8 3be07550 26c0b0a0
+          67f0ba29 06d88cae b39ca70a 0c0f6f9a
+          c1dba43e 29ca0438 ecf4b34c ad14036c
+          66bbf0ec 28301ce8 2cb7eb8d 8017ef8d
+          ad159325 5ae0b032 ac3bb33b d1d9f132
+          85013b51 7576634a efdce385 65c0e1a5
+          b29cca74 2fbe11f0 265e75e5 7ac3e74d
+          11f180c3 38ec8a89 14cd98da 53f7dd4b
+          d864015b 5175b64d d7dd1427 38028733
+          dc89ca72 4995e95e 7923e0d5 7eed9b83
+          86cfd724 6cb4000e dfbd9910 299839a5
+          ade29cf3 280cd88a aeb3aaee 36aa3a4c
+          1f021cc6 6e4465b9 3695e9de f22e5ad3
+          d4537a9c 63041c3e 7da9686c 35c5003b
+          52757783 f0460be0 704aaa2c f7f49e5f
+          b8f6fa83 47846dee c0e1d4a2 3e2b2806
+          d894ee58 38e20738 9c014fe4 c9b705bc
+          4467e851 31794416 382cd21b 2cda92bd
+          3deb290c d8b267e9 ed59a9ea 7027bd08
+          70989892 5459ee89 b705bcba ef7dabd9
+          f0fb760a cfe10187 febe8c4b aaa07ec2
+          ab233ef9 e91e4a03 76a4ea6e 54d5e12d
+          ba2e531a c0a1ef46 5486dbae b2dcb6b7
+          05bcb13f fb95b80a 0bef54f1 2e4e3901
+          87562a2e 2dc5472e 7cacfa0b 5fa63060
+          4bbaeeaa 3afca0aa cb9ce308 1cf27827
+          3195e1fe aeb3dcdb 029e96e8 ecbe4d7d
+          51989202 0e21c39a 36df126f 6f7f94c2
+          809da93a fc98aacb afb14c0b 1cf28097
+          5019eed6 bd7fcbf5 e611d817 371b01ff
+          7af5854c b10387ea be4c482c 307eecca
+          f2d3cfe4 fc3bd89a aec3aa2e bfa4eb34
+          a5011cb2 709752d9 ed159de1 f619f026
+          fcfe063d 9b709524 25428901 87802192
+          88c996e2 231beea8 fdfa4594 076c4dd7
+          615597ff aaeaf466 66f18043 24295175
+          bffdd6ca 70fb0a78 5ae5473f 76b7ab20
+          f02ab378 c0211878 2524161c 3b7a59d1
+          a2f7bd48 69201fa8 bafc92aa d34faaba
+          cdb15bc0 41ef4424 a532db7a 95ddfef6
+          d63f7a5b c09b78e3 2d6204fc bf553727
+          b378c041 168fc9e6 b21397dc 3cf2a28b
+          290ce405 5d97559d be5dd5ed 2d940670
+          d0270922 2ab3fd5a 65b778d6 80a7959e
+          fafedbd4 0f2bcc24 47a60007 4b2a2a61
+          ff888a7f 0667cc7e 89d2403e 5175fa39
+          55b7efd5 759cd200 0e52b84b 67b4152a
+          b3fdf59d fedcfdc3 1ffef06d bf5971f6
+          4725baf5 f50d91cd 5bde2fc9 4429cf52
+          0007f8c6 4c8978aa 473e36f6 07fffb83
+          da6f7cbb 8f12413e 293e7a89 788a83af
+          0fac7a79 6e2a3450 6fd08700 07b81311
+          71f9833b 2acf3bf7 8bf5b7dc f98e6f90
+          71edeb7b 27fdf98e 97bc3555 7f346332
+          40490207 56222a8d 23ce3deb f7355ffe
+          7a23a581 7ca4ebb6 aae3d7ab babe8bd2
+          000e70be 53d94c65 b4eb7556 dbd7d7b8
+          32fd0585 f31b7e2c 1ee33633 c9c3b2c0
+          81928a49 8fb7b2e2 57bebad1 0f511ac8
+          67aa8eff 43d5f5cb 749da734 800314ee
+          921257d9 ec6f2aa3 5d91e9eb de718976
+          8fca73ff 4b22afae 7f30b2e1 b545629a
+          93551c64 a21d782f e12e2e03 ae92f25f
+          8ebbec87 bf1c79e1 b72910e4 356ba9b6
+          28b8b2ef 99e77da9 70e408c3 2d3e4a05
+          780f9292 30c4fd40 e5473ff2 a9c977fe
+          dddcef80 a7559c73 9e845f5d f76878c3
+          eb334d33 35d67089 871206f6 23dc25a4
+          c7282ebf 7ac24f7e f4d39a0b be9ea444
+          e004458b 168bb7a4 e0b99ea7 57149b91
+          c84cd587 042815e0 dd339312 31c4f340
+          c547cffe 72fd5fee 19ccf6f5 5903de50
+          c80b855f 5b77970a 79632491 9aac6e50
+          3f737940 8e37654a 7de2d261 1495fd70
+          c21597ff bcfa4b5f 25dcc151 0a172d4e
+          f9ca8b1e eb79ead9 90198e2c 52fd4701
+          1b2f805c 3b11ab0f e91797e7 b6ca73cf
+          fa4c2ee1 2ee78067 85bc8f9c 2791d7d6
+          2f4df686 0d33169a 68269225 ea067551
+          f240c611 57cc5351 b6c55539 f2c2f197
+          5d7a4bf5 172ea050 e0cc90b7 e008f18d
+          287da1ff e50d5bdc 5e99910a 454a5911
+          02b24e10 245d7edf 2e6fedd8 df97bdff
+          a48beb6f bf27e7ef 354cf3dd 1f75b7e5
+          739f9ade fe977bae 52496f91 e1961235
+          1a632c06 ec2d2529 7563769b c19207ea
+          7fffdb4b 46fcf767 9b291420 ade3b69b
+          cb377ff5 c29f1be1 be8faa90 5722043d
+          e02dc9ce 9a20e835 0ddf8aaa 4f9efb83
+          497fbafd f977fb57 ec57c0d3 b67efe7c
+          77d7d247 3f6646ba be910ac7 a6a99bb4
+          4898d183 c36f48f5 49a85baa df5356bc
+          266114fe 6ffd9557 3c5bf989 f3291be0
+          2d3aefb8 55365ff4 dd133ce6 e0b7123d
+          fd470ff5 21043d38 bd0f49aa 3e24e40a
+          fab61981 8a2b2ace 38e58e89 37ddba5f
+          7fdd7e07 bc3db65d f005e9b8 fbbeb325
+          d17f612a 129b6b18 d603b43e 66f5e0a0
+          9b52df46 514f4951 afe1f33f 178bc84f
+          27df70ed 8b95e79c 47d900d9 82dedfef
+          964dff73 c162af11 fd4ea27f e0c4a13e
+          c4cf8401 9c14ec74 1fe20af8 06dd45c5
+          6b9311b9 76c47967 df33e1da 1bdfd35f
+          fb9e03de 1e3bbef9 5569f9d3 ed933c41
+          f94c2a12 fe88198d 8d55bfed 561faffa
+          78887bc8 9b11965e 804d7fe2 ea970977
+          61c19678 dcfbd719 f7dc756d e9a9efe7
+          ad14c07e e859fa4f d9f8c9f3 2b7c41f9
+          9499887f 3c39189a 698835a3 b7e7c3b4
+          01f26652 40f48127 629d311c 37fcbef6
+          9419bc6f e4973e7b fdd82b7e b3f940fd
+          33072ce0 edadf1d2 ef4bd3af 7f37c61b
+          9425ea97 a7a8cf09 663c5e65 c6e2aea1
+          5199f10e 1fd9eb47 e070c7b8 b77f4c49
+          b9828190 b85c1bd4 aff54df8 cf58581e
+          99f5afa5 8325c79e 40a90107 48dff265
+          b2fe0367 94a8b077 a2fae531 ea73baa4
+          526352e1 88873e04 76ee430c 9f376e78
+          bd5bd4af 37aacf93 f1b0dc37 fa5bdf68
+          1ff3e39f 1ef0ff88 8312f0de 49eb7557
+          7b365ff8 ed99ea86 9da37e39 417daad5
+          a7467d46 0ffd5cdf 98c5433f ea297a2f
+          75048748 547d2243 1fab7fd1 55567d5a
+          d4a7578f 59d4e775 7523be3a ffe5351b
+          82d3a69b 14197068 855fdd28 abe7ceab
+          f70665ae fae5e4bd fa905af5 193bf465
+          a5433fa6 97798143 23be571f a2fb8790
+          fab4a9cf 6ef5e919 fa71534c f521f5bf
+          fdd5869a 2f7f2d74 28fea30e 59c00300
+          00c0a1f1 ff00ae6e 48046a1f 55290000
+          00004945 4e44ae42 6082 
+        </hexdata>
+      </glyph>
+    </strike>
+  </sbix>
+
+</ttFont>
diff --git a/Tests/subset/data/layout_scripts.ttx b/Tests/subset/data/layout_scripts.ttx
new file mode 100644
index 0000000..ddf0cd6
--- /dev/null
+++ b/Tests/subset/data/layout_scripts.ttx
@@ -0,0 +1,997 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.17">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="i"/>
+    <GlyphID id="2" name="uni06F0"/>
+    <GlyphID id="3" name="uni06F1"/>
+    <GlyphID id="4" name="uni06F2"/>
+    <GlyphID id="5" name="uni06F3"/>
+    <GlyphID id="6" name="uni06F4"/>
+    <GlyphID id="7" name="uni06F5"/>
+    <GlyphID id="8" name="uni06F6"/>
+    <GlyphID id="9" name="uni06F7"/>
+    <GlyphID id="10" name="uni06F8"/>
+    <GlyphID id="11" name="uni06F9"/>
+    <GlyphID id="12" name="uni06F4.urd"/>
+    <GlyphID id="13" name="uni06F6.urd"/>
+    <GlyphID id="14" name="uni06F7.urd"/>
+    <GlyphID id="15" name="uni06F0.numr"/>
+    <GlyphID id="16" name="uni06F1.numr"/>
+    <GlyphID id="17" name="uni06F2.numr"/>
+    <GlyphID id="18" name="uni06F3.numr"/>
+    <GlyphID id="19" name="uni06F4.numr"/>
+    <GlyphID id="20" name="uni06F5.numr"/>
+    <GlyphID id="21" name="uni06F6.numr"/>
+    <GlyphID id="22" name="uni06F7.numr"/>
+    <GlyphID id="23" name="uni06F8.numr"/>
+    <GlyphID id="24" name="uni06F9.numr"/>
+    <GlyphID id="25" name="uni06F4.urd.numr"/>
+    <GlyphID id="26" name="uni06F6.urd.numr"/>
+    <GlyphID id="27" name="uni06F7.urd.numr"/>
+    <GlyphID id="28" name="i.TRK"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="0.114"/>
+    <checkSumAdjustment value="0xe87b6e18"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri May  7 21:08:01 2010"/>
+    <modified value="Thu Jan  1 00:00:00 1970"/>
+    <xMin value="-581"/>
+    <yMin value="-898"/>
+    <xMax value="11462"/>
+    <yMax value="1814"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1124"/>
+    <descent value="-634"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="11435"/>
+    <minLeftSideBearing value="-581"/>
+    <minRightSideBearing value="-970"/>
+    <xMaxExtent value="11462"/>
+    <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="29"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="29"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="456"/>
+    <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="260"/>
+    <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 00100000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ALIF"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="105"/>
+    <usLastCharIndex value="1785"/>
+    <sTypoAscender value="1124"/>
+    <sTypoDescender value="-634"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1814"/>
+    <usWinDescent value="897"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 11010011"/>
+    <ulCodePageRange2 value="00000000 00001000 00000000 00000000"/>
+    <sxHeight value="433"/>
+    <sCapHeight value="646"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="8"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2010-2020 The Amiri Project Authors (https://github.com/alif-type/amiri).
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Amiri
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      0.114;ALIF;Amiri-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Amiri Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 0.114
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      Amiri-Regular
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x69" name="i"/><!-- LATIN SMALL LETTER I -->
+      <map code="0x6f0" name="uni06F0"/><!-- EXTENDED ARABIC-INDIC DIGIT ZERO -->
+      <map code="0x6f1" name="uni06F1"/><!-- EXTENDED ARABIC-INDIC DIGIT ONE -->
+      <map code="0x6f2" name="uni06F2"/><!-- EXTENDED ARABIC-INDIC DIGIT TWO -->
+      <map code="0x6f3" name="uni06F3"/><!-- EXTENDED ARABIC-INDIC DIGIT THREE -->
+      <map code="0x6f4" name="uni06F4"/><!-- EXTENDED ARABIC-INDIC DIGIT FOUR -->
+      <map code="0x6f5" name="uni06F5"/><!-- EXTENDED ARABIC-INDIC DIGIT FIVE -->
+      <map code="0x6f6" name="uni06F6"/><!-- EXTENDED ARABIC-INDIC DIGIT SIX -->
+      <map code="0x6f7" name="uni06F7"/><!-- EXTENDED ARABIC-INDIC DIGIT SEVEN -->
+      <map code="0x6f8" name="uni06F8"/><!-- EXTENDED ARABIC-INDIC DIGIT EIGHT -->
+      <map code="0x6f9" name="uni06F9"/><!-- EXTENDED ARABIC-INDIC DIGIT NINE -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x69" name="i"/><!-- LATIN SMALL LETTER I -->
+      <map code="0x6f0" name="uni06F0"/><!-- EXTENDED ARABIC-INDIC DIGIT ZERO -->
+      <map code="0x6f1" name="uni06F1"/><!-- EXTENDED ARABIC-INDIC DIGIT ONE -->
+      <map code="0x6f2" name="uni06F2"/><!-- EXTENDED ARABIC-INDIC DIGIT TWO -->
+      <map code="0x6f3" name="uni06F3"/><!-- EXTENDED ARABIC-INDIC DIGIT THREE -->
+      <map code="0x6f4" name="uni06F4"/><!-- EXTENDED ARABIC-INDIC DIGIT FOUR -->
+      <map code="0x6f5" name="uni06F5"/><!-- EXTENDED ARABIC-INDIC DIGIT FIVE -->
+      <map code="0x6f6" name="uni06F6"/><!-- EXTENDED ARABIC-INDIC DIGIT SIX -->
+      <map code="0x6f7" name="uni06F7"/><!-- EXTENDED ARABIC-INDIC DIGIT SEVEN -->
+      <map code="0x6f8" name="uni06F8"/><!-- EXTENDED ARABIC-INDIC DIGIT EIGHT -->
+      <map code="0x6f9" name="uni06F9"/><!-- EXTENDED ARABIC-INDIC DIGIT NINE -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x69" name="i"/><!-- LATIN SMALL LETTER I -->
+      <map code="0x6f0" name="uni06F0"/><!-- EXTENDED ARABIC-INDIC DIGIT ZERO -->
+      <map code="0x6f1" name="uni06F1"/><!-- EXTENDED ARABIC-INDIC DIGIT ONE -->
+      <map code="0x6f2" name="uni06F2"/><!-- EXTENDED ARABIC-INDIC DIGIT TWO -->
+      <map code="0x6f3" name="uni06F3"/><!-- EXTENDED ARABIC-INDIC DIGIT THREE -->
+      <map code="0x6f4" name="uni06F4"/><!-- EXTENDED ARABIC-INDIC DIGIT FOUR -->
+      <map code="0x6f5" name="uni06F5"/><!-- EXTENDED ARABIC-INDIC DIGIT FIVE -->
+      <map code="0x6f6" name="uni06F6"/><!-- EXTENDED ARABIC-INDIC DIGIT SIX -->
+      <map code="0x6f7" name="uni06F7"/><!-- EXTENDED ARABIC-INDIC DIGIT SEVEN -->
+      <map code="0x6f8" name="uni06F8"/><!-- EXTENDED ARABIC-INDIC DIGIT EIGHT -->
+      <map code="0x6f9" name="uni06F9"/><!-- EXTENDED ARABIC-INDIC DIGIT NINE -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x69" name="i"/><!-- LATIN SMALL LETTER I -->
+      <map code="0x6f0" name="uni06F0"/><!-- EXTENDED ARABIC-INDIC DIGIT ZERO -->
+      <map code="0x6f1" name="uni06F1"/><!-- EXTENDED ARABIC-INDIC DIGIT ONE -->
+      <map code="0x6f2" name="uni06F2"/><!-- EXTENDED ARABIC-INDIC DIGIT TWO -->
+      <map code="0x6f3" name="uni06F3"/><!-- EXTENDED ARABIC-INDIC DIGIT THREE -->
+      <map code="0x6f4" name="uni06F4"/><!-- EXTENDED ARABIC-INDIC DIGIT FOUR -->
+      <map code="0x6f5" name="uni06F5"/><!-- EXTENDED ARABIC-INDIC DIGIT FIVE -->
+      <map code="0x6f6" name="uni06F6"/><!-- EXTENDED ARABIC-INDIC DIGIT SIX -->
+      <map code="0x6f7" name="uni06F7"/><!-- EXTENDED ARABIC-INDIC DIGIT SEVEN -->
+      <map code="0x6f8" name="uni06F8"/><!-- EXTENDED ARABIC-INDIC DIGIT EIGHT -->
+      <map code="0x6f9" name="uni06F9"/><!-- EXTENDED ARABIC-INDIC DIGIT NINE -->
+    </cmap_format_12>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-512"/>
+    <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="Amiri-Regular">
+      <version value="0.114"/>
+      <Copyright value="Copyright 2010-2020 The Amiri Project Authors https:github.comalif-typeamiri."/>
+      <FullName value="Amiri"/>
+      <FamilyName value="Amiri"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-512"/>
+      <UnderlineThickness value="50"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-581 -898 11462 1814"/>
+      <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="253"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            3 2 3 1 vhcurveto
+            endchar
+          </CharString>
+          <CharString index="1">
+            177 113 -106 callgsubr
+            return
+          </CharString>
+          <CharString index="2">
+            487 531 rmoveto
+            -5 -82 -21 -36 -38 10 -36 10 -16 18 5 25 5 25 1 18 -2 8 -2 8 -5 5 -8 1 -8 1 -4 -7 -3 -13 -14 -69 -26 -36 -40 -4 -29 -3 -23 4 -19 11 rrcurveto
+            -8 5 -24 31 -39 57 -17 26 -12 0 -6 -23 -25 -101 rcurveline
+            -3 -13 5 -16 14 -20 79 -117 45 -148 11 -178 1 -8 2 -6 4 -3 4 -3 4 0 6 2 6 2 3 4 2 7 20 79 -11 123 -42 169 39 -3 35 16 32 35 rrcurveto
+            14 -11 17 -9 20 -6 63 -20 40 23 18 65 13 49 7 46 2 41 rrcurveto
+            20 1 -6 9 -10 hhcurveto
+            -10 -5 -6 -12 -1 hvcurveto
+            endchar
+          </CharString>
+          <CharString index="3">
+            rmoveto
+            -45 13 -33 20 -23 28 -3 4 0 3 3 3 27 30 33 15 38 -1 rrcurveto
+            28 20 -8 -13 13 hvcurveto
+            5 -5 3 0 4 2 3 1 0 3 -1 6 -6 13 -11 16 -15 16 rrcurveto
+            14 -13 -14 7 -14 hhcurveto
+            -30 -33 -21 -43 -37 hvcurveto
+            -15 -19 -12 -20 -7 -23 -3 -7 1 -9 3 -8 11 -25 31 -23 53 -19 -51 -45 -38 -53 -24 -60 -5 -12 -1 -6 5 -1 6 -3 7 4 7 10 26 40 32 36 34 30 rrcurveto
+            37 33 37 24 37 16 10 4 7 8 4 13 16 50 rcurveline
+            4 10 -5 3 -12 -4 -18 -7 -23 -14 -28 -22 -7 -5 -9 -2 -9 3 rrcurveto
+            endchar
+          </CharString>
+          <CharString index="4">
+            311 -77 rmoveto
+            21 89 26 81 29 73 29 73 39 74 45 76 rrcurveto
+            6 11 2 9 8 vvcurveto
+            -6 97 rlineto
+            9 -1 -2 4 -5 hhcurveto
+            -5 -5 -6 -10 -5 hvcurveto
+            -79 -149 -53 -135 -27 -121 -4 -19 -7 2 -7 21 -55 159 -61 140 -70 124 -11 20 -8 -8 -5 -35 -13 -81 rcurveline
+            -2 -10 1 -8 3 -6 57 -95 46 -93 38 -90 38 -90 20 -68 2 -45 rrcurveto
+            -7 3 -4 4 -3 vhcurveto
+            4 -3 4 0 5 2 5 2 2 5 2 7 rrcurveto
+            endchar
+          </CharString>
+          <CharString index="5">
+            rmoveto
+            19 69 -12 87 -41 106 -10 24 -4 14 2 3 rrcurveto
+            10 10 11 5 12 hhcurveto
+            31 25 -26 -51 20 hvcurveto
+            2 -3 1 -2 3 -1 rrcurveto
+            2 2 1 3 2 hvcurveto
+            12 34 21 34 28 35 2 2 2 4 1 4 14 51 rcurveline
+            3 0 2 -3 1 vhcurveto
+            -3 1 -2 -1 -4 -3 -24 -22 -21 -32 -16 -43 -22 49 -28 24 -36 -2 -32 -1 -21 -19 -12 -38 -12 -39 2 -39 13 -38 29 -80 17 -68 4 -55 rrcurveto
+            -6 3 -2 4 5 -107 callsubr
+          </CharString>
+          <CharString index="6">
+            158 296 -95 callgsubr
+          </CharString>
+          <CharString index="7">
+            136 657 rmoveto
+            -13 -53 -16 -49 -17 -44 -18 -43 -22 -44 -27 -46 rrcurveto
+            -4 -6 -1 -6 -5 vvcurveto
+            3 -58 rlineto
+            -6 2 -2 3 3 3 3 6 3 vhcurveto
+            48 90 31 81 17 72 2 12 4 -1 4 -13 33 -95 37 -84 42 -75 7 -12 4 5 3 21 8 49 rcurveline
+            1 6 0 4 -2 4 -34 57 -28 56 -23 54 -22 54 -12 41 -2 27 rrcurveto
+            4 -1 3 -3 2 vhcurveto
+            -2 1 -3 0 -3 -1 -3 -1 -1 -4 -1 -4 rrcurveto
+            endchar
+          </CharString>
+          <CharString index="8">
+            220 500 rmoveto
+            -67 -86 -32 -66 2 -47 4 -86 75 -35 146 14 5 -82 22 -87 39 -91 15 -35 9 -2 6 33 18 105 rcurveline
+            3 15 -3 16 -10 18 -32 59 -18 73 -3 86 -3 87 -16 66 -32 45 -39 56 -43 0 -46 -56 rrcurveto
+            102 -152 rmoveto
+            6 -19 -1 -13 -6 -6 rrcurveto
+            -2 -3 -3 -2 -5 hhcurveto
+            -50 -3 -36 6 -22 14 -16 10 -5 16 7 21 12 39 24 15 36 -10 30 -9 21 -22 11 -35 rrcurveto
+            endchar
+          </CharString>
+          <CharString index="9">
+            53 654 -94 callgsubr
+          </CharString>
+          <CharString index="10">
+            38 655 -88 callgsubr
+          </CharString>
+          <CharString index="11">
+            hhcurveto
+            54 3 49 -4 45 -12 5 -1 4 0 2 1 54 31 rcurveline
+            10 6 0 4 -10 3 -61 14 -61 3 -60 -9 57 83 42 104 25 126 1 7 0 5 -2 2 -2 2 -3 -1 -4 -4 -40 -41 rcurveline
+            -4 -3 -3 -6 -1 -7 -25 -113 -35 -98 -45 -84 rrcurveto
+            endchar
+          </CharString>
+          <CharString index="12">
+            119 655 -85 callgsubr
+          </CharString>
+          <CharString index="13">
+            4 -4 5 0 6 2 6 2 3 5 1 7 20 return
+          </CharString>
+          <CharString index="14">
+            -3 -13 4 -15 12 -19 return
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          111 endchar
+        </CharString>
+        <CharString name="i">
+          10 -106 callsubr
+          -94 466 -80 callgsubr
+        </CharString>
+        <CharString name="i.TRK">
+          10 -106 callsubr
+          -94 466 -80 callgsubr
+        </CharString>
+        <CharString name="uni06F0">
+          332 -84 callgsubr
+        </CharString>
+        <CharString name="uni06F0.numr">
+          39 -82 callgsubr
+        </CharString>
+        <CharString name="uni06F1">
+          332 -87 callgsubr
+        </CharString>
+        <CharString name="uni06F1.numr">
+          39 -95 callsubr
+        </CharString>
+        <CharString name="uni06F2">
+          332 -97 callgsubr
+        </CharString>
+        <CharString name="uni06F2.numr">
+          39 -98 callsubr
+        </CharString>
+        <CharString name="uni06F3">
+          332 -105 callsubr
+        </CharString>
+        <CharString name="uni06F3.numr">
+          39 263 662 -107 callgsubr
+        </CharString>
+        <CharString name="uni06F4">
+          332 175 515 rmoveto
+          -12 19 -10 8 -7 -1 -4 -2 -4 -5 -2 -10 -25 -105 rcurveline
+          -93 callsubr
+          83 -128 46 -149 7 -168 rrcurveto
+          -7 3 -5 4 -4 vhcurveto
+          -94 callsubr
+          98 -4 110 -26 122 26 -13 33 -5 38 3 63 5 49 28 34 54 9 14 1 10 -7 7 -6 5 -9 -3 -13 -11 rrcurveto
+          -21 -18 -39 -7 -56 3 -38 2 -39 18 -40 34 20 66 50 34 83 1 rrcurveto
+          26 35 -15 -31 42 hvcurveto
+          10 -7 6 0 5 7 3 5 -2 9 -7 14 -35 66 -40 33 -44 -3 -76 -5 -57 -50 -40 -93 -4 -11 -5 0 -5 11 -11 28 -19 35 -27 42 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni06F4.numr">
+          39 76 651 rmoveto
+          -12 19 -8 1 -3 -15 -15 -63 rcurveline
+          -2 -7 2 -9 7 -12 50 -77 28 -89 4 -101 rrcurveto
+          -4 2 -3 2 -2 vhcurveto
+          3 -3 3 0 3 1 rrcurveto
+          4 2 2 3 4 vvcurveto
+          12 59 -2 66 -16 73 16 -8 20 -3 22 2 38 3 30 17 20 32 5 8 1 6 -4 5 -4 3 -5 -2 -8 -7 -13 -11 -23 -4 -34 2 -22 1 -24 11 -24 20 rrcurveto
+          12 40 30 20 50 1 rrcurveto
+          16 21 -9 -19 25 hvcurveto
+          6 -4 3 0 3 4 2 3 -1 6 -4 8 -21 40 -24 20 -27 -2 -45 -3 -34 -30 -24 -56 -3 -7 -3 0 -3 7 -6 17 -12 21 -16 25 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni06F4.urd">
+          332 229 -83 rmoveto
+          31 115 -20 145 -69 177 -16 40 -6 23 3 4 rrcurveto
+          18 16 19 8 19 hhcurveto
+          52 -1 42 -43 34 -84 2 -6 3 -3 5 -1 3 -1 3 2 3 5 21 57 34 57 47 57 3 4 3 6 2 7 23 85 rcurveline
+          1 6 -1 3 -5 2 -4 1 -4 -2 -6 -5 -41 -36 -34 -53 -27 -73 -36 82 -48 40 -59 -3 -53 -2 -36 -32 -19 -62 -20 -65 2 -65 22 -64 49 -133 28 -114 6 -92 rrcurveto
+          -9 1 4 -4 8 hhcurveto
+          8 5 3 6 2 hvcurveto
+          endchar
+        </CharString>
+        <CharString name="uni06F4.urd.numr">
+          39 108 303 -102 callsubr
+        </CharString>
+        <CharString name="uni06F5">
+          332 235 526 rmoveto
+          -12 12 -8 -2 -2 -16 -17 -121 rcurveline
+          -2 -15 3 -8 7 -4 8 -4 8 -5 7 -5 -75 -103 -42 -88 -8 -76 -11 -105 29 -55 68 -6 39 -3 34 17 31 36 16 -26 28 -10 40 7 54 9 33 60 13 110 rrcurveto
+          5 39 -17 59 -39 78 -29 58 -63 75 -98 92 rrcurveto
+          16 -184 rmoveto
+          76 -52 58 -68 42 -83 6 -12 2 -10 -4 -10 -10 -27 -17 -15 -23 -3 -17 -2 -14 3 -12 8 -1 7 1 6 3 5 -1 6 -3 4 -4 2 -5 2 -5 -3 -6 -6 rrcurveto
+          -64 -62 -52 -8 -40 45 -20 23 2 44 25 64 15 38 27 48 41 56 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni06F5.numr">
+          39 112 656 -99 callgsubr
+        </CharString>
+        <CharString name="uni06F6">
+          332 331 285 rmoveto
+          -74 21 -56 34 -37 47 -5 6 0 5 5 5 45 50 54 25 63 -1 47 -1 34 -12 22 -23 7 -7 6 -1 6 3 5 3 1 5 -3 9 -9 22 -18 26 -26 28 rrcurveto
+          23 -22 -22 11 -24 hhcurveto
+          -50 -55 -35 -72 -61 hvcurveto
+          -26 -31 -20 -33 -12 -38 -4 -13 1 -14 5 -13 18 -43 52 -37 88 -32 -85 -75 -62 -88 -41 -100 -8 -20 -1 -10 8 -3 10 -4 12 7 11 16 44 66 52 60 58 51 rrcurveto
+          61 54 61 41 62 26 17 7 12 14 7 21 26 83 rcurveline
+          6 17 -8 5 -20 -7 -30 -11 -38 -24 -47 -36 -12 -9 -14 -3 -16 5 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni06F6.numr">
+          39 170 512 -104 callsubr
+        </CharString>
+        <CharString name="uni06F6.urd">
+          332 -90 callgsubr
+        </CharString>
+        <CharString name="uni06F6.urd.numr">
+          39 -97 callsubr
+        </CharString>
+        <CharString name="uni06F7">
+          332 -103 callsubr
+        </CharString>
+        <CharString name="uni06F7.numr">
+          39 -101 callsubr
+        </CharString>
+        <CharString name="uni06F7.urd">
+          332 104 -50 rmoveto
+          -3 -6 -1 -5 2 -3 2 -3 4 -2 8 1 90 5 83 -7 75 -20 8 -2 6 0 4 2 89 51 rcurveline
+          18 10 0 8 -18 4 -101 23 -101 5 -100 -15 95 139 69 174 42 210 2 11 0 8 -3 3 -4 4 -5 -2 -6 -6 -68 -68 rcurveline
+          -6 -6 -5 -10 -2 -12 -42 -188 -58 -163 -74 -140 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni06F7.urd.numr">
+          39 33 312 rmoveto
+          -8 -4 3 -3 9 -96 callsubr
+        </CharString>
+        <CharString name="uni06F8">
+          332 -98 callgsubr
+        </CharString>
+        <CharString name="uni06F8.numr">
+          39 -100 callsubr
+        </CharString>
+        <CharString name="uni06F9">
+          332 -99 callsubr
+        </CharString>
+        <CharString name="uni06F9.numr">
+          39 -93 callgsubr
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+      <CharString index="0">
+        rmoveto
+        -3 -50 -12 -21 -23 6 -22 6 -9 11 3 15 3 15 0 10 -1 5 -1 5 -3 3 -5 1 rrcurveto
+        -5 -2 -4 -8 -2 hvcurveto
+        -8 -41 -16 -22 -24 -2 -17 -2 -14 2 -12 7 -4 3 -15 19 -23 34 -10 15 -8 0 -3 -13 -15 -61 rcurveline
+        -2 -8 3 -9 8 -12 48 -71 27 -88 6 -107 1 -5 1 -4 3 -1 2 -2 2 0 4 1 4 1 1 3 2 4 12 47 -7 74 -25 101 23 -1 21 9 19 21 rrcurveto
+        9 -6 10 -6 12 -3 38 -12 24 13 11 39 7 30 5 27 1 25 rrcurveto
+        12 -3 5 -6 -6 -3 -3 -7 -1 vhcurveto
+        endchar
+      </CharString>
+      <CharString index="1">
+        rmoveto
+        237 vlineto
+        -92 callgsubr
+        return
+      </CharString>
+      <CharString index="2">
+        vlineto
+        -29 0 -27 -5 -16 -102 callgsubr
+        -46 -77 callgsubr
+        vvcurveto
+        return
+      </CharString>
+      <CharString index="3">
+        rmoveto
+        -100 callgsubr
+        return
+      </CharString>
+      <CharString index="4">
+        -81 callgsubr
+        49 0 -17 7 hvcurveto
+        5 -12 0 return
+      </CharString>
+      <CharString index="5">
+        vhcurveto
+        -11 -3 -47 -74 callgsubr
+        26 -79 callgsubr
+        26 -78 callgsubr
+        return
+      </CharString>
+      <CharString index="6">
+        5 5 -1 18 -4 5 rrcurveto
+        -12 return
+      </CharString>
+      <CharString index="7">
+        -27 22 -22 27 27 22 22 27 27 -22 22 -27 -27 -22 -22 -27 vhcurveto
+        return
+      </CharString>
+      <CharString index="8">
+        rmoveto
+        -7 7 -5 -1 -1 -10 -10 -73 rcurveline
+        -2 -9 2 -4 4 -3 5 -2 5 -3 4 -3 -45 -62 -25 -53 -5 -45 -6 -63 17 -33 41 -4 23 -2 21 10 18 22 10 -16 17 -6 24 5 32 5 20 36 8 66 rrcurveto
+        3 23 -11 36 -23 47 -17 34 -38 45 -59 56 rrcurveto
+        10 -111 rmoveto
+        45 -31 35 -41 25 -50 4 -7 1 -6 -2 -6 -6 -16 -11 -9 -13 -2 -11 -1 -8 2 -7 5 -1 4 1 3 2 3 -1 4 -2 2 -2 2 -3 1 -3 -2 -4 -4 rrcurveto
+        -38 -37 -31 -5 -24 27 -12 14 1 27 15 38 9 23 16 29 25 33 rrcurveto
+        endchar
+      </CharString>
+      <CharString index="9">
+        275 527 rmoveto
+        -21 -89 -27 -81 -29 -73 -29 -73 -38 -73 -45 -76 rrcurveto
+        -6 -11 -2 -10 -8 vvcurveto
+        5 -97 rlineto
+        -9 1 3 -3 5 hhcurveto
+        5 5 5 10 5 hvcurveto
+        79 149 53 135 27 121 4 19 7 -2 7 -21 55 -159 61 -140 70 -124 11 -20 8 8 5 35 13 81 rcurveline
+        2 10 -1 8 -3 6 -57 95 -46 93 -38 90 -38 90 -20 68 -2 45 rrcurveto
+        7 -3 5 -4 3 vhcurveto
+        -4 3 -4 0 -5 -2 -5 -2 -2 -6 -2 -7 rrcurveto
+        endchar
+      </CharString>
+      <CharString index="10">
+        137 -96 callgsubr
+      </CharString>
+      <CharString index="11">
+        521 rmoveto
+        -25 -102 -3 -13 4 -15 12 -19 rlinecurve
+        79 -124 46 -149 11 -172 1 -7 2 -5 4 -4 4 -4 5 0 6 2 6 2 3 5 1 7 20 99 -12 128 -42 158 79 -13 57 18 33 49 21 30 13 33 6 37 rrcurveto
+        6 37 3 24 -3 7 rrcurveto
+        7 -3 -6 4 -9 hhcurveto
+        -9 -6 -6 -12 -2 hvcurveto
+        -69 -9 -43 -36 -79 hhcurveto
+        -55 -44 32 62 -33 hvcurveto
+        -11 21 -9 10 -7 -2 -5 -1 -4 -6 -3 -13 rrcurveto
+        endchar
+      </CharString>
+      <CharString index="12">
+        rmoveto
+        12 53 16 49 17 44 18 43 23 45 27 45 rrcurveto
+        4 7 1 5 5 vvcurveto
+        -4 58 rlineto
+        6 -2 2 -3 -3 -3 -3 -6 -3 vhcurveto
+        -47 -90 -32 -81 -16 -72 -2 -12 -5 1 -4 13 -33 95 -36 84 -42 75 -7 12 -5 -5 -3 -21 -8 -49 rcurveline
+        -1 -6 1 -4 2 -4 34 -57 27 -56 23 -54 23 -54 12 -41 1 -27 rrcurveto
+        -4 2 -2 2 -2 vhcurveto
+        3 -2 2 0 3 1 3 2 1 3 2 4 rrcurveto
+        endchar
+      </CharString>
+      <CharString index="13">
+        rmoveto
+        -15 -62 -2 -7 3 -9 7 -12 rlinecurve
+        47 -74 28 -90 7 -103 rrcurveto
+        -4 1 -3 3 -2 vhcurveto
+        2 -3 3 0 4 1 3 2 2 3 1 4 12 59 -7 77 -26 95 48 -8 34 11 20 29 12 18 8 20 4 22 3 22 2 15 -2 4 rrcurveto
+        4 -1 -4 3 -5 hhcurveto
+        -6 -3 -4 -7 -2 hvcurveto
+        -42 -5 -26 -21 -47 hhcurveto
+        -33 -27 19 37 -19 hvcurveto
+        -7 13 -5 6 -5 -1 -3 -1 -2 -4 -2 -7 rrcurveto
+        endchar
+      </CharString>
+      <CharString index="14">
+        103 641 -91 callgsubr
+      </CharString>
+      <CharString index="15">
+        10 6 47 1 7 vhcurveto
+        5 -6 11 -5 vhcurveto
+        -39 -21 -50 -103 callgsubr
+        -26 -13 vvcurveto
+        -182 -105 callgsubr
+        return
+      </CharString>
+      <CharString index="16">
+        rmoveto
+        -40 -52 -19 -39 1 -28 2 -52 45 -21 88 8 3 -49 13 -52 23 -55 9 -21 6 -1 3 20 11 63 rcurveline
+        2 9 -2 10 -6 10 -19 36 -11 44 -2 51 -1 52 -10 40 -19 27 -24 34 -25 0 -28 -34 rrcurveto
+        61 -91 rmoveto
+        -16 5 -2 -9 -10 hhcurveto
+        -30 -2 -22 3 -13 9 -9 6 -3 9 4 13 7 23 14 9 22 -6 18 -5 13 -13 6 -21 rrcurveto
+        endchar
+      </CharString>
+      <CharString index="17">
+        111 -89 callgsubr
+      </CharString>
+      <CharString index="18">
+        522 rmoveto
+        -15 -79 -3 -13 4 -12 9 -10 rlinecurve
+        50 -56 78 -14 105 27 rrcurveto
+        1 1 0 0 hvcurveto
+        3 -1 1 -4 -7 vvcurveto
+        -5 -150 31 -142 70 -132 15 -29 11 3 6 32 17 95 rcurveline
+        3 17 -4 18 -9 17 -71 128 -29 128 13 130 2 16 -8 6 -18 -5 -109 -33 -75 12 -38 57 -19 28 -12 0 -5 -27 rrcurveto
+        endchar
+      </CharString>
+      <CharString index="19">
+        rmoveto
+        -9 -47 -2 -8 2 -7 6 -6 rlinecurve
+        30 -34 46 -8 63 16 rrcurveto
+        3 1 1 -3 -5 vvcurveto
+        -3 -90 19 -85 42 -80 9 -17 6 2 4 19 10 57 rcurveline
+        2 10 -3 11 -5 10 -43 77 -17 77 8 78 1 9 -5 4 -11 -3 -65 -20 -45 7 -23 35 -11 16 -7 0 -3 -16 rrcurveto
+        endchar
+      </CharString>
+      <CharString index="20">
+        246 -86 callgsubr
+      </CharString>
+      <CharString index="21">
+        524 rmoveto
+        -34 -100 -5 -14 3 -17 11 -20 rlinecurve
+        71 -126 35 -148 -2 -171 rrcurveto
+        -12 4 -6 9 -2 vhcurveto
+        9 -2 5 5 4 10 23 69 4 99 -18 129 -13 92 -27 96 -39 100 -17 40 -14 6 -9 -28 rrcurveto
+        endchar
+      </CharString>
+      <CharString index="22">
+        rmoveto
+        -21 -60 -3 -8 2 -10 7 -12 rlinecurve
+        42 -76 21 -89 -1 -102 rrcurveto
+        -7 2 -4 6 -1 vhcurveto
+        5 -1 3 3 3 6 13 41 3 59 -11 78 -8 55 -16 58 -23 60 -11 24 -8 3 -5 -17 rrcurveto
+        endchar
+      </CharString>
+      <CharString index="23">
+        251 -83 callgsubr
+      </CharString>
+      <CharString index="24">
+        312 rmoveto
+        -38 -106 -5 -15 1 -10 8 -5 rlinecurve
+        91 -53 17 -10 11 2 6 17 rlinecurve
+        32 99 5 15 -3 11 -12 8 rlinecurve
+        -83 53 -9 6 -7 2 -5 -3 rlinecurve
+        -4 -1 -3 -4 -2 -6 rrcurveto
+        endchar
+      </CharString>
+      <CharString index="25">
+        122 527 rmoveto
+        -23 -63 -3 -9 0 -6 5 -3 rlinecurve
+        55 -32 10 -6 7 1 3 10 rlinecurve
+        19 60 3 9 -1 6 -8 5 rlinecurve
+        -49 32 -9 6 -6 -1 -3 -9 rlinecurve
+        endchar
+      </CharString>
+      <CharString index="26">
+        -13 -50 -7 -4 -5 2 -19 2 -2 rrcurveto
+        7 return
+      </CharString>
+      <CharString index="27">
+        -104 callgsubr
+        endchar
+      </CharString>
+      <CharString index="28">
+        3 38 hhcurveto
+        38 return
+      </CharString>
+      <CharString index="29">
+        -3 -2 40 hvcurveto
+        -101 callgsubr
+        return
+      </CharString>
+      <CharString index="30">
+        5 11 -3 hvcurveto
+        -5 16 0 27 29 return
+      </CharString>
+      <CharString index="31">
+        5 -5 rrcurveto
+        2 40 return
+      </CharString>
+      <CharString index="32">
+        -5 -12 hhcurveto
+        -4 -5 -1 return
+      </CharString>
+      <CharString index="33">
+        -75 callgsubr
+        -18 -76 callgsubr
+        return
+      </CharString>
+    </GlobalSubrs>
+  </CFF>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="i" class="1"/>
+      <ClassDef glyph="i.TRK" class="1"/>
+      <ClassDef glyph="uni06F0" class="1"/>
+      <ClassDef glyph="uni06F0.numr" class="1"/>
+      <ClassDef glyph="uni06F1" class="1"/>
+      <ClassDef glyph="uni06F1.numr" class="1"/>
+      <ClassDef glyph="uni06F2" class="1"/>
+      <ClassDef glyph="uni06F2.numr" class="1"/>
+      <ClassDef glyph="uni06F3" class="1"/>
+      <ClassDef glyph="uni06F3.numr" class="1"/>
+      <ClassDef glyph="uni06F4" class="1"/>
+      <ClassDef glyph="uni06F4.numr" class="1"/>
+      <ClassDef glyph="uni06F4.urd" class="1"/>
+      <ClassDef glyph="uni06F4.urd.numr" class="1"/>
+      <ClassDef glyph="uni06F5" class="1"/>
+      <ClassDef glyph="uni06F5.numr" class="1"/>
+      <ClassDef glyph="uni06F6" class="1"/>
+      <ClassDef glyph="uni06F6.numr" class="1"/>
+      <ClassDef glyph="uni06F6.urd" class="1"/>
+      <ClassDef glyph="uni06F6.urd.numr" class="1"/>
+      <ClassDef glyph="uni06F7" class="1"/>
+      <ClassDef glyph="uni06F7.numr" class="1"/>
+      <ClassDef glyph="uni06F7.urd" class="1"/>
+      <ClassDef glyph="uni06F7.urd.numr" class="1"/>
+      <ClassDef glyph="uni06F8" class="1"/>
+      <ClassDef glyph="uni06F8.numr" class="1"/>
+      <ClassDef glyph="uni06F9" class="1"/>
+      <ClassDef glyph="uni06F9.numr" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=3 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="4"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="arab"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="4"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=3 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="KSH "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=2 -->
+              <FeatureIndex index="0" value="0"/>
+              <FeatureIndex index="1" value="4"/>
+            </LangSys>
+          </LangSysRecord>
+          <LangSysRecord index="1">
+            <LangSysTag value="SND "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=2 -->
+              <FeatureIndex index="0" value="1"/>
+              <FeatureIndex index="1" value="4"/>
+            </LangSys>
+          </LangSysRecord>
+          <LangSysRecord index="2">
+            <LangSysTag value="URD "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=2 -->
+              <FeatureIndex index="0" value="3"/>
+              <FeatureIndex index="1" value="4"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="4"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=1 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="TRK "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=2 -->
+              <FeatureIndex index="0" value="2"/>
+              <FeatureIndex index="1" value="4"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=5 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="locl"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="locl"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="2"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="locl"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="4"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <FeatureTag value="locl"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="4">
+        <FeatureTag value="numr"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="3"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni06F4" out="uni06F4.urd"/>
+          <Substitution in="uni06F6" out="uni06F6.urd"/>
+          <Substitution in="uni06F7" out="uni06F7.urd"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni06F4" out="uni06F4.urd"/>
+          <Substitution in="uni06F6" out="uni06F6.urd"/>
+          <Substitution in="uni06F7" out="uni06F7.urd"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni06F6" out="uni06F6.urd"/>
+          <Substitution in="uni06F7" out="uni06F7.urd"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni06F0" out="uni06F0.numr"/>
+          <Substitution in="uni06F1" out="uni06F1.numr"/>
+          <Substitution in="uni06F2" out="uni06F2.numr"/>
+          <Substitution in="uni06F3" out="uni06F3.numr"/>
+          <Substitution in="uni06F4" out="uni06F4.numr"/>
+          <Substitution in="uni06F4.urd" out="uni06F4.urd.numr"/>
+          <Substitution in="uni06F5" out="uni06F5.numr"/>
+          <Substitution in="uni06F6" out="uni06F6.numr"/>
+          <Substitution in="uni06F6.urd" out="uni06F6.urd.numr"/>
+          <Substitution in="uni06F7" out="uni06F7.numr"/>
+          <Substitution in="uni06F7.urd" out="uni06F7.urd.numr"/>
+          <Substitution in="uni06F8" out="uni06F8.numr"/>
+          <Substitution in="uni06F9" out="uni06F9.numr"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="i" out="i.TRK"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="364" lsb="33"/>
+    <mtx name="i" width="263" lsb="32"/>
+    <mtx name="i.TRK" width="263" lsb="32"/>
+    <mtx name="uni06F0" width="585" lsb="210"/>
+    <mtx name="uni06F0.numr" width="292" lsb="97"/>
+    <mtx name="uni06F1" width="585" lsb="210"/>
+    <mtx name="uni06F1.numr" width="292" lsb="97"/>
+    <mtx name="uni06F2" width="585" lsb="111"/>
+    <mtx name="uni06F2.numr" width="292" lsb="37"/>
+    <mtx name="uni06F3" width="585" lsb="67"/>
+    <mtx name="uni06F3.numr" width="292" lsb="11"/>
+    <mtx name="uni06F4" width="585" lsb="110"/>
+    <mtx name="uni06F4.numr" width="292" lsb="37"/>
+    <mtx name="uni06F4.urd" width="585" lsb="100"/>
+    <mtx name="uni06F4.urd.numr" width="292" lsb="31"/>
+    <mtx name="uni06F5" width="585" lsb="100"/>
+    <mtx name="uni06F5.numr" width="292" lsb="31"/>
+    <mtx name="uni06F6" width="585" lsb="71"/>
+    <mtx name="uni06F6.numr" width="292" lsb="14"/>
+    <mtx name="uni06F6.urd" width="585" lsb="95"/>
+    <mtx name="uni06F6.urd.numr" width="292" lsb="28"/>
+    <mtx name="uni06F7" width="585" lsb="78"/>
+    <mtx name="uni06F7.numr" width="292" lsb="18"/>
+    <mtx name="uni06F7.urd" width="585" lsb="101"/>
+    <mtx name="uni06F7.urd.numr" width="292" lsb="31"/>
+    <mtx name="uni06F8" width="585" lsb="78"/>
+    <mtx name="uni06F8.numr" width="292" lsb="18"/>
+    <mtx name="uni06F9" width="585" lsb="123"/>
+    <mtx name="uni06F9.numr" width="292" lsb="45"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/subset/data/sbix.ttx b/Tests/subset/data/sbix.ttx
new file mode 100644
index 0000000..e5214d8
--- /dev/null
+++ b/Tests/subset/data/sbix.ttx
@@ -0,0 +1,12532 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.43">
+
+  <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="0x50cd2b65"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000001"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Jun  7 11:21:57 2016"/>
+    <modified value="Tue Jun  7 12:30:54 2016"/>
+    <xMin value="-49"/>
+    <yMin value="-362"/>
+    <xMax value="1393"/>
+    <yMax value="1138"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="860"/>
+    <descent value="-140"/>
+    <lineGap value="200"/>
+    <advanceWidthMax value="1417"/>
+    <minLeftSideBearing value="-49"/>
+    <minRightSideBearing value="-43"/>
+    <xMaxExtent value="1393"/>
+    <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="3"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="3"/>
+    <maxPoints value="88"/>
+    <maxContours value="7"/>
+    <maxCompositePoints value="90"/>
+    <maxCompositeContours value="5"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="512"/>
+    <maxSizeOfInstructions value="1023"/>
+    <maxComponentElements value="2"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="667"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000001"/>
+    <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="0"/>
+    <yStrikeoutPosition value="300"/>
+    <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="djr "/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="88"/>
+    <usLastCharIndex value="89"/>
+    <sTypoAscender value="860"/>
+    <sTypoDescender value="-140"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1138"/>
+    <usWinDescent value="362"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00010010"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="720"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="X" width="737" lsb="60"/>
+    <mtx name="Y" width="705" lsb="45"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x58" name="X"/><!-- LATIN CAPITAL LETTER X -->
+      <map code="0x59" name="Y"/><!-- LATIN CAPITAL LETTER Y -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x58" name="X"/><!-- LATIN CAPITAL LETTER X -->
+      <map code="0x59" name="Y"/><!-- LATIN CAPITAL LETTER Y -->
+    </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="X" xMin="60" yMin="0" xMax="677" yMax="720">
+      <contour>
+        <pt x="60" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="677" y="720" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Y" xMin="45" yMin="0" xMax="660" yMax="720">
+      <contour>
+        <pt x="45" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="660" y="720" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2008 The Bungee Project Authors (david@djr.com)
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Bungee Color Regular
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;djr ;BungeeColor-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Bungee Color Regular Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000;PS 1.0;hotconv 1.0.72;makeotf.lib2.5.5900
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      BungeeColor-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="0"/>
+    <underlineThickness value="0"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <sbix>
+    <version value="1"/>
+    <flags value="00000000 00000001"/>
+    <strike>
+      <ppem value="20"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000011 00000011 08060000 003b6d47
+          fa000000 85494441 5478da63 bc1fe17f
+          ecd7f397 324c2c8c ff194804 fffefc67
+          6493147f c2f2fbd9 136589fa 8e387679
+          85fba41a f2f3e103 c5978d95 8b581859
+          d97e70a8 aadd6493 957b40aa 218c6c6c
+          bf185959 7f308138 ff7ffd62 632003c0
+          f4313150 018c1a32 6a087d0d 01e50172
+          34c3f4b1 fcfffd8b e3c7ed5b eae4e41f
+          502e06e9 67a44679 0200f1c6 3ba42d08
+          36000000 00004945 4e44ae42 6082
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000000d 0000000f 08060000 003f2345
+          77000002 65494441 5478da4d 924b4c53
+          511086e7 9c7bdbdb 162a9447 2d854269
+          6e79a81b 5de80a4d 24d1b060 c5c2950b
+          89efb0d6 95c68531 d1b53181 1817269a
+          1837ac48 30113151 b006049b 622d6221
+          8437a9d8 2269ef73 3c73ab49 4f7273ce
+          3fff3727 33e70e4b f59c9c2e 2dadc498
+          8c606b0c eafacf8f c59e3ebf 04ffd6f2
+          d0b527b9 d7a3035c 41409381 478d6665
+          ed47a6db d8cad770 0f805502 2865b31d
+          50b1b4e5 6c5cdfd9 09cac2b7 85cf5053
+          38935d3a 5700aa22 61074243 972a9384
+          9619947d e21c9e57 57efdb06 0909649f
+          047f66e6 8fff1e1f 3b470985 c9b767f6
+          a712a728 4e3e71dc ef2ff0c6 c12bc396
+          2dcadafd 05be9690 e8cb70af 0c5d1fc9
+          bd7a7961 f9c6e567 d681e6f5 b51c767c
+          e2828357 8745b621 a5fb7ac7 3f884a92
+          6d0df8b5 b50ea7c4 f9ff37df 5c8bc968
+          23929fee 3bfb8678 4044d037 d643f3dd
+          6a665ac8 543c2cc0 00ce04bc 3817aa71
+          f4b40c38 d7155bd4 d6d79a88 e754bbab
+          29bce589 45b3b625 5e50d3c0 1da80197
+          ff1028f5 b560e93a d8268057 5597dce1
+          e6cdf2eb 88ccf587 f76f7f14 c7d97a1f
+          a63ac4cd 1c9034ed 29b5c989 93de78f4
+          e016f190 7fffaee7 93221909 8f84df8e
+          b462c22b 61a24a32 3203fda3 9ffdb24e
+          7aa13b82 e413979f 9c382d5d 5c597c51
+          fcb91af5 b787a094 db03bda0 43e4ee9d
+          7bed8f47 6e8a42ac bdf1895e 0013bcc1
+          7a28ee16 b89efdde 095f228d db0937e0
+          4257c479 ade489a3 49db3425 2a43ec9c
+          34c5173a 5b90b8d9 48c33607 c618885f
+          8ea6e9f4 a8b44557 99245974 16bb4dda
+          e9dd1221 c131c619 475d73db 1ac0c1da
+          268816c1 2e16bd95 63449ae2 e41347bc
+          acc43bd3 c8ca53ce 359ae278 a632c9a3
+          aa197730 75ac72ca ff02b8ca 3d01d4f8
+          daf30000 00004945 4e44ae42 6082
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="Y" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000000d 0000000f 08060000 003f2345
+          77000002 1e494441 5478da65 52cf6b13
+          41147e33 3b3b9376 931aa9c6 36b1b5d9
+          76a3053d 085a2d55 441114ff 073d8837
+          41a41eda 438f5e04 6f82880a 7ae9bf20
+          ea4554a2 21153d98 62b431a6 d1fa63d5
+          923449b3 bbd9dd71 764d65b7 3e981dbe
+          f77ddfcc be790f2d 1e9d78d9 2e555444
+          38701341 fcccc947 a3f7e6cf 4137aa33
+          d3d7f5fb f3673113 bc8d2032 365226c6
+          d2fbf1ce f7fa161c 01700c00 b3b4b407
+          0261964b 194bd713 44f0aee0 11371946
+          94999802 28c3495f 8428b582 2624ffc5
+          ca5012bc 837d3d6c 0e84dd10 c69bb097
+          02ce5157 ed7fdd56 43090adc 66187b7a
+          2c4e725c 47b82509 482f86e6 9bc2beda
+          c307a73c bef5fad5 fefa9367 c7084380
+          8804bce3 dfece0e8 a1c9ac67 327eaf42
+          6f6a07b8 a62d2f5f b974a399 cf1dac5c
+          be78b3b3 b6ae28c3 0360aed6 c011bae8
+          e4d47364 944b2385 a9c379eb dbafed51
+          35214c16 b4bfd6c0 7b1cd704 e849c541
+          62141a65 1de860ff cfbdd9fc 0466e9d1
+          8a7afbee 79f1a3b0 fe450739 1603daaf
+          0017e5b3 44d4c7ad cf3a78bc 7aebce05
+          96562ba2 2eeeafea dcccd5ac 2833af10
+          fe766c90 170f8cf3 82961458 e25e7e79
+          76fada86 f69fc9b5 6dfceef4 f1c72f44
+          2ac78017 76eff477 0f2f9e38 f2d4b52c
+          f29fc95b c6a7f2ae 856d7df5 9c2c4c99
+          14cf51e0 0b5b9546 fb43510b ea42cd95
+          07923f48 bc6f0d36 da297629 1e6bd2d4
+          d04ab8b9 c1be752c 991b460f 174fdbaa
+          ae880115 39c38870 cba2411d 094d9098
+          3ba6658a ae8d3524 77a75a4b 7f448c99
+          41dd1f60 83089d7d 4ca1ba00 00000049
+          454e44ae 426082  
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="32"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000001a 0000001a 08060000 00a94a4c
+          ce000000 9d494441 5478daed d6310a02
+          311404d0 f9c1282c 7b8f2862 61612108
+          1ec44eb1 f45e165e 62c1de4a 30368b77
+          58029a25 dfe40efb b7fa03d3 ce6b87da
+          e3e116bd dff32f54 200c1b06 685a05eb
+          5c43efed aa83b5dd 6cb9bec3 0c0c25e0
+          fb7cec10 fb1a7eb3 089fcbf9 cacc9068
+          d92e8601 1123a509 a452b6b3 61305214
+          52482185 14524821 19889960 4c2f27e4
+          ed6c8c74 b7624ded 291fc897 f0819cbb
+          e60f5bd3 81f73472 f64f0000 00004945
+          4e44ae42 6082  
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000014 00000017 08060000 000b1d6f
+          a3000004 7c494441 5478da6d 555b4c1c
+          65143ef3 cfcc327b 19765b60 29502217
+          a1146c4d 6b9188f6 8190d434 356aa26f
+          9a16052f 0f7dc418 1f4c888f 4ddf8cd6
+          62d24a7c 3031a689 315e1a23 4d0cb045
+          9416435a 2e61b72d 7459a034 0b7b6197
+          b9fc9ef3 0fbb6c17 ff647667 ce7fce77
+          ce7fbe6f ce48b36f bc722d31 f6f70bac
+          0476970d 20b9dcc9 a6ef7f78 d37becf8
+          54c10e64 e6669e9e 7dfdd59f ac642220
+          c9052159 00bdf344 48498c8e 7419b178
+          40520038 0149b88b 97698ae0 b63d8091
+          85e6d4dd f9167907 8c737467 f88ffe84
+          a5304dcb 50264905 905d2ad8 8629bc98
+          4936d584 a22529aa c5c0f107 2681ac2a
+          606f1b18 838f88c5 728edc02 286dac17
+          a0b4e99c c3b68b01 c1264f67 912fc5e4
+          2d54089e d1ca954e 199d333b 2bf957a8
+          a3182f39 1eeac8e5 0386e132 73629d67
+          8ba915c1 582e83b1 990477b0 028f8dd9
+          91a4d817 5f9d4f84 46f2a0a9 db9347a3
+          172f7ecc 5c207cdc c1728c49 e44fa856
+          1e88b2f2 b7ce7e43 e762484a f27e145c
+          fbfc50b2 df27b2da 5b592df2 7eef156b
+          73d36ba7 d35ab8f7 dc90b591 d2893457
+          c08b971f 121843b1 8451f176 cf55c9ce
+          66d599d3 ddd7e3c3 235d241d c5ad41a0
+          ad09d66f dd412f0b 1b0ee03d 7a781a64
+          d94a4d4e 3fcb342a 8741d9b1 5688cf2e
+          8099da12 920974bf 74a3e5d7 e15312c7
+          52b2f722 b5d39dcf 4f18ab8f 2ab1abe0
+          a92a07ad 623f3c9e 9a03861c d9999d16
+          69ce5103 6d0da23d a9a55590 b034a5a2
+          6cf599d0 447b495d fd03c132 de2c360c
+          7efdaec8 8d9674f4 11061aa0 d7558195
+          21a93897 859578aa cb50770c 528babc4
+          89684dc3 e5c13e02 13cc0f0c 0c88ecee
+          e6967949 b2ed8d3f feeca2a6 67d6e2a0
+          373e056a a907148f 06aadf07 5af93e41
+          447c7a1e 41b968c7 c14f3ff9 acf2c3f3
+          9773c429 4f88d6e5 ca163e73 94a18432
+          226938a2 964549c2 bea360b9 b474e309
+          0cbe23a2 8ddf7feb 9e3973e6 bac46c19
+          b980c0e1 7a30d35b 9008c7c4 7145027c
+          6f7c7541 507d5e88 df8d0023 7c908d96
+          9f7f79d9 df7dea86 e833fd6c 3f5caa5a
+          e87b6788 5b086613 29654886 82328a81
+          4cac924e 118ceea9 7712bec8 d44bf2e5
+          86a5867b 7b86b6a3 0f0f0840 6e592cfc
+          5ecf95ec 83588d84 bd533c25 e0adad86
+          f84cd8d1 1711d1da 34eb3972 e80edd93
+          6d633602 de9a2ae1 4bef74e6 fe726db8
+          efdc556e 9a32ac0c 7ed93b8a 89c635e0
+          374b243e dfd1ca27 833abfe9 021e42fb
+          d49143d3 663caee3 b872ff7b bced16d9
+          686f32e8 13be1433 ee064e18 b14b9f7f
+          20f7acc7 2e198bcb 0739aa5f afab16d3
+          26bdb426 9acedc5a baf9da8f af690d8d
+          f79030d3 77a27d7c fdbb6fcf 72cb54cd
+          d436283e 0d5cba07 b28f9342 8fe67aac
+          926d2f47 6b7283d2 e5f7c3d6 caaa2366
+          9444f547 fd177ced 1dffe418 f43ed77e
+          bbaabfff 02ed91cf 566c0d65 55eab08b
+          18c64aac 9a616705 8762c888 17d89958
+          f4a8779e 1c2d9e36 64937675 2502f303
+          cab66556 38ae78f1 f8c3f9b9 671e16db
+          0a63102b 3f60a9e4 cd850858 347d6177
+          60ee5905 36f2a598 c26f8b62 67329a98
+          87886366 8d7cc1b6 9871d69e 0ac9467b
+          92c8cbc1 cc18ce37 c5a22192 d114fdc5
+          93c389b1 89cec2af 1e6d6aba 1ed7ea1b
+          e78a01c9 e66d6d9a b112fff7 d56b1ffb
+          0f34f801 89b13b7e 89000000 0049454e
+          44ae4260 82  
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="Y" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000014 00000017 08060000 000b1d6f
+          a3000003 92494441 5478da95 544d4c53
+          59143ef7 bef7da57 a4fca6ad 0202821d
+          91288944 62820a89 23260c19 5db870e9
+          44673299 851b171a 12634c8c bad0bdd1
+          681cdde8 c6052b34 88986861 8609c264
+          046ae21f d08e05c1 a1b4d8d7 be9febb9
+          171e03b5 153cc9cd bbefdc73 be73cf39
+          dfb9e4e5 e11fefc7 7aff6aa4 4ef85f2c
+          5c8a3aef bf7bef48 6ec3ae81 6527900a
+          4d94bc3c d8daa94f cf7889b4 cc2509e0
+          6edcd92b c7024ff7 e991d902 22033004
+          22044f71 19068016 1cdd9e0e 980c4d54
+          c40787eb e8221863 684ef18b f6b1c0b3
+          7d325555 8d47e280 925301a6 1b68c480
+          1a5c271b 90264492 2caaf02f fe500292
+          228395d2 11117f11 8bda86cc 04c8abaa
+          0449758a c3d584df 8c2258fe e64dc2d7
+          16ba323c 816f9634 1fba8a31 5b0d205d
+          be0e6859 2c43ae26 6701c90a c8167c98
+          1d1d978d 121fe8df 99eef069 70608765
+          c2222259 68f1b2c2 52a22849 8180cb4c
+          24c05954 2828401d 0093d76f fc3a3ff4
+          bcceb6d7 27239ef0 c5f36729 32c2421b
+          67613e98 494dd08d fb138743 a3ae2d35
+          a396c53b 863c7a17 06d5530c f23aa708
+          6ec6b59c 77277ebb 6a038e9f 3e79591b
+          8b94718a c92e07e4 94ae87d8 9b108800
+          88e1da5a 3b4ca2dd 5dcd2307 5a7a5049
+          78f68e02 37b8abca 61e6f9b0 e09a9500
+          f0fe7cf4 a6e2f14e fd7be572 bb20313a
+          17d7d742 7c2c0cc9 8f515129 2c03ab7d
+          f4b859ba 74fbce18 4b25e4d9 9e409384
+          691af329 8ceec49b 16823615 053e92b1
+          febfebe7 9ef5eee5 65e0f5cb df5281e5
+          d1603ef4 41dcced0 00cada4f 5df41efb
+          e577c2a7 8299060d b6b53c98 7df8a445
+          722dd4a7 78c756f8 147a0f89 a959510e
+          d1741c08 97af1072 4a7c3033 14146026
+          6650b0bf e9714d67 f7019c2c 5300da43
+          ffa2b1a1 3f159e2c e53721b2 02457535
+          58a37148 fe171536 aaa70872 2bcae0e3
+          d008bf84 08a0f83c 916d7dfd 0dcef2ca
+          90e8bb0d c825daf5 e0fb60db 0f0f512b
+          f1a6f031 74575580 92978b75 22a0cfc5
+          61eed55b ec6c4ab0 06336135 1d1d6d05
+          6d073b97 5187ad58 a10be7da 7b51fda7
+          0bd81f2a b000d2f2 857f031b feae44ec
+          b98e9f05 d066fccc e9f3e9fe 5f0032ec
+          7ff0506b 870dda87 0fd03fd5 eb0528df
+          731d3f1b 6969eeb2 749da6fb d34cb35a
+          75edd671 7553e904 d333bc32 9cd0a5de
+          48f5cd3b 3f6113ac 35cdb2e2 f34dbbfc
+          fed7ccc8 00884154 7ff55bc7 c6f27026
+          5f39eb7b 679ad28a c78f6539 5b2be052
+          0ae89af8 308d5d25 626fe95f b7cf0a68
+          2535952d d6ccd2cd a5a790eb 2c4d53bf
+          19d0bd7b 6f8ff67a a20c478f ac0c042c
+          6f4f5377 36bfcf39 75b68398 55857400
+          00000049 454e44ae 426082 
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="40"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000021 00000021 08060000 0057e4c2
+          6f000000 ec494441 5478da63 7c909f39
+          e9fbf193 414c6cff 5918fe33 d00f3032
+          30fcfbc5 f887d3d2 7c1de375 13adb79c
+          dcec42df de7c6260 62a19f1b fefd6160
+          e012e163 f8fef5e7 3b16260e 8edfdfde
+          bc6510eb 9ce2cca9 acfc905e 8ef87ef7
+          aefcabf2 9cbdcc82 c2bf8151 f01f1802
+          2c0c5c9a 9ab73995 941fd32d 36d8d87e
+          81ec05db 0f13fcff eb171b1d 53048a7d
+          4c0c8300 8c3a62d4 11a38e18 75c4a823
+          461d31ea 8851478c 3a62d411 a38e1819
+          8e00f588 e86931b2 7d2c0c8c 8cc0cee9
+          1f866fd7 afabd2b3 1706ea8b 82ec6506
+          da3f387a e59cd60e 4bbf1f3f 11c42cc8
+          47d7f109 66466068 7c67fcc3 69e3b00e
+          0043ca56 20f696f0 16000000 0049454e
+          44ae4260 82  
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000019 0000001d 08060000 005f78c7
+          75000005 e1494441 5478da95 567b6c53
+          5518ffce ede3b65d bb76dde8 c6185933
+          d846c718 303ae710 4d0c7fa9 a022687c
+          f0580463 4c345188 2224240a 269049f0
+          0fe33f46 88046690 0445343e 628c068c
+          636c13b6 754fe6d8 9864838e aeebdade
+          3e6eeff5 3be7f6b1 3e66f424 cb7acf39
+          dfebf77d bfef3b64 f4b5dd9f cc5cfae1
+          29a29539 c8587214 647d4d95 abf2cb8b
+          cfaa2d16 5fe6b924 08fcc88e e75afded
+          5d4d4403 244b3e42 a482cd8f 7d433a17
+          5bee4727 bd56a282 ec25e145 19a0ee7a
+          57bd614d fdf5cce3 f0d8a8fd c68a65b7
+          e430402e 793906a0 595ce051 733a7d98
+          a8bc40b4 a983a400 1a61ee11 02b91721
+          9c9e0f4b 72984fc8 a4c94700 389d2e9c
+          0591ce56 80a22453 97bc808d 8c4fc2e4
+          33579a11 0a8db97a 397a82ae 50b5a843
+          a69089b1 9c36e498 28cba248 58b83230
+          392a2fcb d9466269 3988c552 1ee23f29
+          0a200cf6 adc86524 343cb45c 0ac6b4c9
+          8038824e 494ccfbc f0444e6d b6786529
+          85a7148d 8226dfc4 7eb33b18 d49d23ef
+          bf277a3c e6b49a08 04741387 0e1e4b3a
+          83f7b5f9 f95891d1 a42cd58b fa67b83c
+          67435b22 3ca206f0 8d8c83a5 7a99120d
+          ee735810 c1c1d1ea f17d6f7c 34dfc8ed
+          03fb8ecd 75f6ace3 f8547af2 ab2ac077
+          738ce961 8ea0bcb1 b1e90f12 e8e976b8
+          9a1afe94 23111d3d 94b022cc 0e3ba80d
+          7a98ee1c 0015afe0 1d0b012c 3ff5e9ae
+          a25dbbcf 78ce7fb1 ede68b3b ce530728
+          e01296b0 756d1543 c1db778b 39268b68
+          98e783ab dabbd672 86557503 4b0f1f3e
+          108bc661 c50bb303 63a0d6f1 60b297b0
+          9c5038a8 c7b75e7f f594cb59 db33fa4a
+          732b924f 3180ca0c 6585a031 19c1dbaf
+          18a08bea 43bd07f5 352b8789 1cc76a78
+          ebe60bf7 bffaee19 955ea932 4ea586e2
+          0d4e705f eb063128 00e194c2 a091524e
+          d16f764f ad01dbfa 7a705fbd 01b17098
+          edc70480 c22d4f7c 5d85fa18 94092351
+          f7bd4257 93f35a78 74a282d3 2b55a55f
+          64018ba3 12a6ae74 e46434bd 636b5a0d
+          81894908 fc7d8f45 21a101de 5e3656db
+          d6d9a029 2e9e4ee3 896691ed feb293a7
+          77125e13 a6787298 1fe1ae17 04f73414
+          d4558328 285124fe e8777ee5 52cc5518
+          8da0018d 4206a251 452b3e3b d59c3090
+          1649624d 1e3ffad6 d8db074f 50d818b6
+          98d49247 eab12c25 88ce0520 413c4ea7
+          054d9e01 ee5eee48 561385a9 fce8e1fd
+          a5ef1e6a 99af539d d5f4266e 9727c985
+          ca54bc8a b50bc1ed c1086842 6485dd02
+          1ad1eba8 e70a4988 52c6e2f4 745156f7
+          991f89fb f4c9ed23 cd7bcea8 747182a1
+          ce450fae 0261ca0d 73a3530c c278b761
+          55652cb7 81614931 b8db7b15 b8e2a55e
+          79f6f317 8a5eda75 2ecb48d0 d5e3e87b
+          78fd55c9 1fc8a7e5 490d50cc b56613f2
+          a53f599a 6989c73b 85f52b20 ea0f806f
+          788295b9 4c3bafc9 385bfb7b 5ba3bea6
+          76289978 29e0d7ff d5bcfd8c e845035a
+          a5bdf056 23e4952d 064ff7a0 e2a532c4
+          18ee5411 13c67d4f cf10184a 4b802f34
+          29044479 71c66f46 7d67b1f5 e893466e
+          efdfdb32 d7d5bb8e 259bce10 8e03eb9a
+          1a34d0af 343c4ef1 5a5b5672 c7b6f3f9
+          56debe64 9c7e3369 44c2d33d 00d63a07
+          93a3f254 8fafa3db 39fece9b 1f32b866
+          7ffd6543 ffc68d57 b8048351 b8a8a106
+          22b3730a 048916a1 e58595bf 5d7e28cf
+          f9c0f560 6f774ddf 86f5ed92 1034265a
+          1183d682 d076c4a1 8d13d7f1 f34f8faa
+          5e0ecdb5 045d83b5 0965794b 6dc01758
+          c0736358 b92c2b97 edc75bf6 5ab76cbb
+          c438555c e2569b4d 1ecfb73f 6ea2c540
+          0d85dc3e 30da4b99 f6e86c90 91979255
+          0efa4caa 3d443c22 ba3d45f4 22cd8515
+          89e71b1a 417e4458 8ba0cd0f 5bc4c5f2
+          131fef9b 9f746343 6397d0db b93ae8ba
+          e948f044 0c04c054 6147724e 312304f5
+          c9445471 31bfdf44 b8d4eca0 b88a42bc
+          57c9cc4b b9f4c0a1 23b98616 92ee037a
+          ceeed19e 150a31f9 640be2d8 dc3172f4
+          2d90620d 28899653 43873368 22b4e5e4
+          3242f7e9 7962e825 4944d2df 1a590f09
+          36dfd336 b8acb740 daf826dc bfcb670a
+          d3b6308b f950e63c fcbf4594 f70195cf
+          7cec6479 18ba3703 994df3bf 2e2a47e5
+          33975a0a 857836f8 23e92fbf e4c38284
+          e30338b7 5e4908f3 ac03a816 900f8574
+          6aebd35b cfcd5cfa 7ec1b7b0 b1d1d986
+          bc98cc99 78dc2fd8 b4f982bf bd73e1b7
+          f0938f5f fc07df31 a8fddaee 55570000
+          00004945 4e44ae42 6082 
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="Y" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000019 0000001d 08060000 005f78c7
+          75000004 6d494441 5478dabd 567b6c53
+          5518ffce b97d5dda ad5b570a 93ee0163
+          8ccd0d2b dba4665b 16a3fff0 870fd098
+          2c9a101f 31f20f98 2046f15f 3353597c
+          44f9c318 8d1a430c 12438c89 d18410c3
+          78d4960d 3b47acc0 36d858e9 581f2b6d
+          b9b7bdbd d773eeed 8bbec686 f14b6e72
+          73cef738 dfeff77d df39686a ef2b9f85
+          7efae519 a4913014 882480c4 b66ef2b6
+          1e3bb15b 55670e17 eda75278 fab5973e
+          0fff7a72 6749fb04 126b9fdc 7902b9eb
+          6b02495f d8841828 16094014 01ba9ce7
+          ec8647ec cec26d21 18acbdb8 a5f1ba10
+          88194ad9 4b290075 7d6d1063 1dcb5305
+          a4513ec8 fba71f56 933584a0 8c20ccb2
+          1c5295b1 27ff58a7 e371e1c9 75965a62
+          89615542 0e43ed0b e52e6f12 81c6b8a5
+          859c5e25 075c710c 8cc0d8b6 b9729034
+          99703f52 ca1ec3ff 20f81eb1 2e079e04
+          f7002c5e 3e7df2f1 7c69bd94 80a404af
+          06b45c10 49ca9e44 fea3e59a a92ea414
+          c3edb3a7 fb4a19c7 2e8ed992 c1a851ee
+          91b42dad 4c6a933b a52461a4 5627b209
+          934d3191 006d8d11 44217d0a d227be91
+          c36ff333 d3d6fc00 22c76966 0fbdf501
+          8812ca1c 466d3010 3b41ce3e 0326f1cf
+          63f5fafa f94c64da 544b9767 a0aaa519
+          30831525 b2965808 9baf1dd8 f7497e10
+          dfe1e183 11f74437 d6e61cd6 b4b742e4
+          ca0cc926 d7129a07 36cc61d3 53bb8e8b
+          d93a270e 835188df f081c9b6 9564a5ac
+          333a80c0 8f3fef5e fcee9b21 19a6f10b
+          db6e381c ef329a34 353c80e9 a136e003
+          41e0fc61 c88c18ea d7b4ebb9 1f901089
+          e827fb7b cfc73dde 4ecca637 89734b9f
+          0de2733e 885ef7cb 90510818 3d1bab7e
+          fc89df62 6e576f62 f6a61569 155dbdd5
+          0286662b f847c794 31447ddc 0158636b
+          f7748efe 614794f7 a8cbb9fd d26383a3
+          a452580a 0f2d00cc a8615d7f 0f2c9c1f
+          8714c7c9 59d240d4 294ecf2a 39b04e07
+          16fbc3e0 3fe30631 9954f404 baafbdd3
+          71eaf77e 43ef8e31 193dfad3 3432f286
+          984c9345 884c2592 10bef40f 98bb3bb3
+          d5426160 584aa6a2 47d7e97e 68d24b20
+          5302c893 9bf86972 380e50bf b25d5e05
+          c3d53d43 5ffbbffd 7e8f8acd 615d676b
+          950d03e3 97219f64 9a91b997 f29684d0
+          c4d5ec5e 8ac0b476 e8d9a39b 8f1e7f21
+          dbcbf941 524b61c3 e480fd5c 7c22c70f
+          4ddddcd3 2597666c d64732e4 81d1b250
+          ddd20022 99538bae bf723c70 006cdb26
+          ef83675c 3b5426d3 52c92072 e58cb9b7
+          4d0e0e9c 95784e4f f9c9c052 b5d10ada
+          3a13e81b 37406c6e 9e54d12d b83d334f
+          b84b372d e541a5e6 3b4e9e1a 303cdae7
+          aa3856f4 db7b3ccd 1f7db82f c30f7540
+          b9884ccd c12da707 e2be9bb0 e8fc5309
+          a052f633 3c34be3f 7cb03040 d9d96579
+          75ef57eb 5e7ef14b 81cb5324 0e31bded
+          4855600d 56026460 267ae6e7 9f3eb67e
+          ff9b9fae 6840367d 7c647f55 77d7854c
+          43961da0 248335ed 2dde8d47 be787dc5
+          5398a9aa 8e350c3b de91dbb6 c2a0a733
+          aee1bde1 432af3da d0aa46bd ca581345
+          cb5c0694 121220bc eafb845c a5450f1d
+          c414bf7d 244160ee ff66cc93 25ef1528
+          2cfbffe6 facd136e 2194bedd 8a502b0f
+          7b458fe4 f9289221 88a5bbdd e45f4aa2
+          a227ad3a 88ae6deb dfc641fb 696eea5a
+          0be97e54 e2ad0c86 06eb34db d1e9a9e4
+          e75f5877 d73180f1 bc360000 00004945
+          4e44ae42 6082  
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="72"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000003a 0000003a 08060000 00e1bb4a
+          28000001 90494441 5478daed dbcf4a02
+          511406f0 739db18c fc13b6b0 8c264633
+          28c88816 1906f500 41b8b0e7 30a8a728
+          b05ea320 e8092a48 7230a274 61658cd6
+          a26891e4 9f087366 6e2aa88b 5e202edf
+          b73bdcd5 6f71eeea 7cac9e49 878b89ed
+          fd46499f 26896c24 524c6e0d 28ea937a
+          78b0c372 91c5334b d7d7bd91 0522c314
+          ca49b244 e5f41dd9 02817396 f18f3e8f
+          2fcd2992 cb49955c 8178f35b 0823b30f
+          91673e44 66ad4e6f 37f91799 c932e7a6
+          459fd93c d5deabaf eee88a26 02b49aba
+          5ae63cef 772a13c4 2489cbbd 97e60f79
+          d6565333 c7a75b22 401fe39b 4756ee3a
+          de9dfb9f 0f63c40d 5316653d 3b9696e9
+          2f54f000 0a28a080 020a28a0 80020a28
+          a080020a 28a08002 0a28a080 020a28a0
+          80020a28 a080020a 28a08002 0a28a080
+          020a28a0 8002fa3f a19c1393 25431458
+          c7d23275 d33f44b6 0f50e5e2 32fa10db
+          381101da 3e3577f9 dc7d2837 0dc6241b
+          8d846789 e50a7e7e 7f1b1301 ea1ef3f6
+          ca036da3 ec50a6f4 0f2dabb4 eb20c393
+          3ea1f6d2 a87f5159 cb922318 d4593da3
+          858b89c4 5ea3a487 c42bf890 35a8060b
+          6a32b9fb 0bf4417e f7b34c45 e8000000
+          0049454e 44ae4260 82 
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000002c 00000034 08060000 00f101da
+          f700000a 8d494441 5478dad5 5a0b5054
+          d719feef 63597659 96e5b1e0 f25a1745
+          d010a515 a291f808 44698c8f 68e338b6
+          46a2f551 5fd13658 6b9a69c7 ce04db26
+          b593264d 0405ab6d 8d93b1d1 26526c06
+          7c2022d8 448d2f2c f2461ecb 6b97dd65
+          59f6716f cfb948a7 8673965d 9d7126d7
+          b9c3ba7b cef9bffb dfefffff ef3ff732
+          d6aacad4 e65db9ef ba3ada26 000b0cfa
+          37f62100 b04ab531 66f7cff6 85af5a7d
+          0afc382c e7ca66b5 bcf9e63e 778f31de
+          277ba264 4f948d8b aed7bff3 6e2e736b
+          d6f40bfd 955fcde1 b8073ffa 72202382
+          074016a1 ea49ad69 48e423b4 665fa689
+          4e277733 2de54bdb cd7ba9fe daf3207b
+          211969e5 bcd3d869 e058f45d 00f87570
+          c8cb82cd 16e1eaea d2f90ad8 63b3aa5d
+          3d3d711c 8fecc9fc b43704e0 ecec30b0
+          0cc789f0 a807cb88 c0308ccf e3d1d8c7
+          b187e7b2 f02d3bbc 0216116f 44f79303
+          836d619b 8f04184f 54c64482 4aaff30e
+          da0f4648 6329e3b1 0d6c0bdb f4069a0a
+          58400b04 1be2413d 2941fa4c 4b6fa2cb
+          e53b60b7 1b017303 2995611b d816b629
+          b8bd0316 e9a09101 8f404d35 a24b6404
+          bbcde7fc 22381cbc e81894d1 722fb625
+          babd7202 059d48bf e1224a7e 0cc77acd
+          c5432dcd e37d05ec 6c6fd379 ac0e3543
+          599295f1 684d2fee 45585936 50611745
+          32dddc76 07f04a85 574a761f 295aeb2b
+          e0ee2385 afa1db4d ad6e7c50 10b23948
+          a439c6c8 2a94032c af8de826 91824195
+          c8d1d50d f2500d30 3cc52372 005371e9
+          92ae4307 568f05d6 5c727a9e b1a0701b
+          27a77917 401ea601 87b147b2 4d2ad132
+          adb68b55 2426dd15 810c78d0 689678ac
+          491a0f9e 21322d58 74312dbb 77bde7b8
+          f71f3d0d acabcba8 69dcb2e9 1008224f
+          0a73bc76 489201fd 1d926c92 00638c8a
+          e429b7d9 9017b2cf 78abe1ed 6515a0cb
+          7c0e642a 3931dde0 12ebeab3 85356e5e
+          7f00739e 7434edd8 f29ea3a9 7d024bf0
+          ae88629a 5706806e de2c682f adf05a19
+          42b217fe 93d5bcf8 d217cac9 863b02c1
+          83d87b03 2d3dd07b ed16c42f 990f0225
+          837188e6 a6b28aef 75ecffed f651bc2d
+          ca5fd573 fcd3355c 20257338 01e21765
+          42dfad1a 64ab5bb2 393abb00 044d4bba
+          16323ffb 2ccb06a9 8662f7e6 fd1c5f29
+          89cb188c b1e2baf4 39eab954 f00c5240
+          a3e4767f efaff759 2f5e481b f96ee0fa
+          d5e4a6dc dc0f2410 8440f220 201169c9
+          c005cac1 587e55b2 45e2020e b8b8bd79
+          7bd8c040 37233e48 11753ff8 fe9fbb3f
+          fe348734 09538193 cb2171ed 0a683ef5
+          2fb0b777 4b41326a 1cba039c 46d51bb6
+          74d94986 97794cc5 9f2d7676 f444b301
+          e4ca16a0 09868455 4ba1fe6f 27c1651d
+          2072173b 28f2d595 45138e1e ff9144c1
+          11c0ee9e ee905bb3 d2ab1d75 cd492ce1
+          f661ca04 4f8c8598 f9b3a1b6 e813c43d
+          94a35932 9011eab0 3419290e 57b6c49c
+          e5d073f5 06f45daf 03126530 150227ea
+          6b532aff fd0c92b0 fd0f9566 fc45c2c1
+          a21c8667 9da4e0c2 01d35f7b 1f2c758d
+          10f7d2f3 40e2bce4 017e9846 f8a4695e
+          4c05ddbc 74709afb a1ef1a19 2cc6c0c8
+          58574241 61ce08d8 515a423d 37b33af6
+          57bfdce3 7152123b 5ab8bdb4 0a0242d4
+          887b4992 617f0f1c 64c18628 08993411
+          5a4b2e00 4bc9cb18 43ec5bbf 784b3d2f
+          abea2187 88df2c73 82007717 2df887a9
+          a46c0991 cfe856ca d42a98b8 7a39d41f
+          3f05437d 16626413 2b2b0a6c 060d4e5c
+          fb0ab49d 390fb6e6 4e20f11b f356337f
+          6e497249 d94286e3 c6506b2c 0b86fcc2
+          8d01d1da 56c149be e5437d36 e8387f09
+          f44bb387 ebac8f3d 04a651ec 8b73c052
+          db00d606 325811d9 0c880aeb 4c2838bc
+          fe9b60a9 f2521ea7 3726e417 ac434004
+          208835cc b9beafeb 611095ee 980519e0
+          f6811ab8 9a854d9b 80324308 bad82b44
+          de625b58 50193ef8 70837cbc a1dd2f3d
+          1cbae8e5 d2e8dc9f bceda104 173678bf
+          a41c82e2 6220744a 3cb80687 838974ba
+          d16f016a 0544cd9e 01ad9f97 0e8b1b86
+          7c51ba6d 1b7f1ff6 cacad3d4 e22b8a22
+          78939735 9919a596 8aea2c52 70e0f425
+          0f57a3f4 b4024c35 f56831b2 76163c0c
+          a8514a34 965741df 8d7a720a c36973e6
+          f48b53ce 5eca64e4 72aac6f4 1a2e1eab
+          25c86db1 6aa8821b e1534445 c050af09
+          29bb3e60 290db1e0 66c0ad0b a36beb91
+          86c0e3e1 d0c97a6b babc7ab8 ee872b0e
+          771f3bf1 1a2d5be0 4a6558b9 04ea8f9d
+          44816807 9a30c7dc e4830350 665906cd
+          27cf80a3 b79f9859 7076885a bfe6c384
+          8347b6fa ddd319f3 ff942381 0df452df
+          17bf00c6 8b55e034 d981c7c5 424e39d1
+          6fee0127 0ab64a54 74b286b7 3244725c
+          180f1ddd 8284fe4a bf3c8c45 cbed3919
+          d5a203b5 33044fe0 208a5d30 1338d48d
+          b49c3a47 162d40f6 60ecc20c e96aef97
+          5412e761 3dc2aa54 a6948acb cf289e4a
+          a91bd3c3 c2c080bc 617dce51 c14a068b
+          73734862 0c044fd0 a3e45f0e 6ca01fdb
+          4d52a5ac 0465ac0e 4292e388 4d012ee7
+          6eb32db4 7eddab47 8441bb6c 4cc0cdbb
+          76fcc6fa d5ad7456 410e324e 110031d9
+          73a1b5b8 0c0593c7 af6d0929 b004e4dd
+          e2b3109d 456f0ab0 e72d57ae cf6ad9fd
+          d33caf80 7b3ffecb cb9d1f15 eee4bd89
+          edc55960 ba512309 7bd6cf0d bd91decd
+          de6e929a 82b84559 d4a68047 dcef783f
+          3fb7f793 634b8880 1d75b5f1 4d3b5e2f
+          a08a6dc4 3fedcc14 a915efac b846e52d
+          9684782c 3e85417a 87d275f9 a6f439f2
+          d9a7c94d 013b2c4f 9bb66e3d e8b8571b
+          ff306054 201a376f c877769b b5244988
+          bda0880e 056d7a2a b47c564a 153b18a0
+          2a7d5af5 f8fdbfdb 69f8e3fe d7d57366
+          9ca381c6 9e6e3d7d 16c2a74f 05654c18
+          d1d3188b b3c71cd9 b069ed21 d1ed66fe
+          9725ba0a 3e5a53bf 69cb116a 8b223052
+          b761acb8 02e69a26 20b5eab8 52c9f5b1
+          f529555f a6c922a3 a4fd628f c512783b
+          23bdca7e bb761a29 3871d069 92e2216a
+          ee4cb857 7402e571 81787771 564a78ff
+          0f1bc76d db799071 9b4d4137 5227df74
+          b6761a18 8ad48b5b 3c47fadc f279b994
+          6f49c188 0a8930b9 b8380b75 b6e7ffff
+          376b4579 da9dace7 2fa14b0a a0b54071
+          8b664bbb 32ad687d 5aaa9345 45dc9ffa
+          75cd53dc f694294b bb8afeba 91d816a1
+          20d34cd6 43d8d3c9 d074a204 3819bd99
+          8c7963c7 bea8cddb 8b4629bf 787dbbe8
+          1c14cde7 2e659282 14d3cb52 d70ae3e6
+          cc40e06d a8c4f78f eaedf0ff 5d7d76b5
+          3279d26d eec71ad5 8e816b37 a693239e
+          01fdb26c 68fbe202 aa6636e2 0e10f690
+          3a7d6a35 6a127318 998ca87e d419b32b
+          2da5c599 8ea6cef8 517698e1 7668a8b7
+          1bb54d19 481cd550 7b455ea3 ea671d75
+          f7263194 fd5a658c 56ca9bb6 6623596c
+          7bb04650 580d0587 d7b10a05 75df15a9
+          2f4fc281 c20d68ac 85d82fa2 8bb03577
+          49795da9 0b276fd8 e04c565f 379175f5
+          f6684957 843a2508 d4862351 63068a6a
+          942813bd 7bd7db41 a9dfbd33 56fe554e
+          fbce5d34 364f70d2 959fd36c 06790405
+          30c2e8ee eb0d6745 a753417b 3e86f722
+          dc763b55 81710ad6 15b67ce5 df7d2d1a
+          782c9e03 024d6b0c a135e540 1390a2cb
+          15c8a2e8 a46a6206 2953da06 335e9453
+          29ad7c68 a8c957c0 782c9e43 0584fa23
+          96e5bc3d 72e0c77e 8a445b1d a74c79a0
+          80f8e9f3 632c3c16 cfa17958 929ccc63
+          3c45fad6 3df6c26d 36c3734f 0c0cb645
+          6aed7dea e97042b7 36b6a03e 8df379a3
+          e4b13c87 0b486d03 6a583d5e edd1030e
+          5da8bdad 4be215c3 3f09efe2 5cdc2171
+          98e11e01 f008e827 79f8e298 6f5fd089
+          1e0ff3a8 931fccf5 e7e9bcf8 b8f6f800
+          9daec15e df16c73b c1ef172e 14dac856
+          4e156cf1 b9094563 6568ce50 87295cda
+          73f1c39e 1be56e95 4ed7c858 ab2fa736
+          e7bef1ce 83576858 5f5f69e1 b5e39ae3
+          f3f2f6a8 e7655ef6 c74b96f3 679f6dd9
+          b307bf42 a3f7e315 1a41161d 8b5fa1d9
+          f55fbebb 98e434cf e5b40000 00004945
+          4e44ae42 6082  
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="Y" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000002c 00000034 08060000 00f101da
+          f7000007 88494441 5478daed 9a096c14
+          5518c7bf 7933bb3b ed6eb7dd 9edb76db
+          ed0172b4 ca5590b3 4d95a308 84080962
+          0272a951 c37d888a 1c8980da 1201130c
+          20a7f1c2 4440440c 01ca2147 398a0481
+          62038585 b6bb2d94 b674bbdb 3d66c6f7
+          0650c179 bbdd5e8a 619a4d93 9d79effb
+          cdfbfeef 7dfff75a a6aee078 57cbbc79
+          2b3cd6d2 5440c0e0 1fff9708 8078dded
+          d8593356 444d7aed 5b08e072 165d4cbd
+          3e63fa2a d7b5ab69 381ef21b 4f92e389
+          2a635c89 396fc55c e642df1e 876a8f17
+          66b1ecfd 9b8db970 105100e0 745c7d97
+          a29254b5 29a1a2b1 c0c5a386 6dbbb563
+          cf182ec0 78028e17 da2fe308 e7aeb0a5
+          b0087fa7 86802e16 07131bbc 5a8fb53c
+          21106057 69e95384 35e0782e 00b7cd9a
+          8c189695 a0a91722 1fc404d2 a439f148
+          5b048fd9 e51358c2 ba11bd6d 0743e249
+          de260293 c67cb401 f4ed12fc 76d222b0
+          3886d614 03da44a3 cf783e81 83e38c10
+          9edea94d 4699c408 493183be 7db2cf78
+          9cef14e1 05d0eb6d 434908cd d3f06337
+          e99e003f 016e52f9 615af7f9 16052685
+          c5e50ae0 79012457 0300f32f 0193b55a
+          b0db831b 5f1cbc48 a8af0fa6 466dc4e8
+          237feb22 23fb4065 cb475c4c c3a5dfd2
+          1a0becb6 961b3d56 9b89a175 896da324
+          497e8025 49a07a50 9c6e2e28 889a4212
+          f8d6964d af8a4ea7 aa31c095 1bd74ef1
+          de756969 c02ccf63 89b9e992 c1ac88e1
+          540d3418 d79d6a50 e975c06a 1845b38d
+          b0a7b59f 2bea66cd 5b3ecb1f ace3fcb9
+          0ed695ab e6231f3e 58136180 86aa3bc0
+          50f2cea8 d44ec485 87574ba2 c24ddcc8
+          75db0e42 830bd7f7 2410dc94 146180b2
+          bcbc458e 0be7dbd3 8d820896 59533f15
+          eadd8aa3 4bbc4350 6c28a8b4 5ab05bca
+          01291806 c28859ab 90c66c2e 917c6c4d
+          acf9c7c0 98d907a7 8b03c517 63c9c473
+          69091001 53ba2ad6 7f36a126 ffd86036
+          88b267c3 9f84a103 a1f2c419 2c094151
+          12e4314d 72ca5514 d22ff330 55e05899
+          774b6c50 5b7c1512 8665e3ce 68da03a8
+          d9ff4b4e e5c675e3 ff31d16e de88295d
+          bc301751 6c96170b 326e601f 70d7d642
+          d5d962f0 25197d66 763e328c 1cbd5315
+          a1aba279 508e27a3 7c12d4a1 7a88e8de
+          1e4b4439 1304e8e6 c205b9ee f2b2c8bf
+          dfb2bc3d 2bcf5d59 13cd284c 4b010f40
+          580713e8 5393e0e6 ee838034 94b9e601
+          501b0d36 c3c8177f 44eaf8f8 dbb13367
+          7f2c78e8 b2209f1b bbf6c9d2 d084eb14
+          0d3601f2 54541baf 8c1ffbb5 fdf4c9ae
+          cedf2fb7 2b5df2fe 7b55df6d 1f4f32a0
+          b486ab74 3cc4e764 cb7d8b5e 813ad904
+          1c2f76f6 dc0f5551 d1350c59 f7448743
+          7d7140af a3f5672f f6444194 46786423
+          333a82e1 e94e7065 eb0e592e 4a5a139d
+          f86b0d91 13eb16ec 821af1ca cf91d16d
+          376e04d4 9558a0e2 e8796069 71717f21
+          bdba1474 3e5c3000 f1bc577e 27141cec
+          4ef97ccb 44a4e3eb 68d220a3 74fbf465
+          f03a9c60 ccca90b5 a7a8fbfb cbb6e8c1
+          b0411458 0c61ccec 2e6f102a 8ed16109
+          0babe3ed c96b374e 21b00f55 3a6df78c
+          4be6bcbc e9a2877e c0413476 73f70130
+          a4778490 a46810dd f4fa494b 2f69a3c3
+          6dc39fe9 0c961ff6 d1271939 f7c02ce6
+          dcdc19da 6e3d2e29 96e69837 a66e891a
+          3766b340 193d0221 383d50fa f341bc6a
+          0cc4c158 c5a58e5a eac91197 868384e1
+          83a0744f 3eeecb4d d72d91e0 d8515fc6
+          bc396dd3 430c8fd6 6e6f75b5 ee62ff9e
+          05cea2ab 6988a72c 4538a5a6 9cbec005
+          f360d99e 4f4da992 14925f1a 02aeaa6a
+          28db7f0a 977d4a16 302c9f9a 589c7ea2
+          b0171719 59ebd3fc 7006833d 75e3d6f1
+          78249c14 97212f75 e5fb8f03 1f1901e1
+          5ddb012d 238fbe64 54ef34fc 9241603d
+          784aee83 e6001915 72a76cd8 3ce15158
+          aa5bd3f5 eef76bc2 d20fe6c8 1a95e84b
+          9d65e75e 3c797a43 706cb83c 7a4acf92
+          0412d8b0 8e8910f5 6c77b0ec d82b5747
+          9ac12131 4d8b162e d0673d57 a0284b5f
+          76ae78f4 f06faab6 ff349696 7252f974
+          c971607a 211b2a8f 1542cda5 62ec39c4
+          bfc03114 17acc6a0 5d21bc4b 67b8b66d
+          17386d77 a8138dbc b46148f6 ee8e7bf6
+          8f00a42c 6e9fc09e ca8ab00b 7d324eb9
+          ae97b6a7 552102cd 4787e1f2 da1f389d
+          16bc76e7 3dc72a91 eac70187 dd9eacd9
+          bd87c053 eba0c292 9155c744 96a59f38
+          93a14e34 dba8f6c6 9f61be7b f040efa2
+          9cc1d86f 886a9a8f 7d7026a6 89d002ab
+          0d85e85e e9184c0d b62385f8 056ac055
+          ed940b0d ad3d3920 c7c05287 1ddf0f35
+          8c1cb5b7 595b247d f6f305a6 c58bdea5
+          d9cb078e 8d64c07d b71eeaae 9783a3dc
+          86535f09 7537ace0 a977ca45 870a4b26
+          a48b94de e91ff983 6df49e2e fe9d859f
+          440c1bb4 539e58be 36c40864 e9312c2b
+          6f77c8c9 3183fc2f 75a17d7b 1c4d58fa
+          f1a296db 84628ae4 f59b5fd7 241a2d62
+          009b64bf 85045732 2e4c7727 65c3d609
+          0f4a6f8b ed9ad571 f1b752d6 6d988867
+          be00624b d0dedb69 24ad5efd 5650a7b4
+          9256d9e6 87e50c3b 143f7fde 626f0b8c
+          32293631 93c7ad8d 7a65f2b6 563d9730
+          2d59b6dc 30286b4f 73a4211b a06e6985
+          e6956be6 b4fa410a a35249c6 e933d736
+          47166419 8c993663 0dabd73b dae8e487
+          69f6991c deb237e9 af494d0b 2c494cb3
+          452c8a4c db01ffdf ce87e5c2 c1b1ad02
+          ccb5f808 e01eebae ddc0bf59 40dc6300
+          4c3c83a3 ac522e0c cce300fc 00fabfa6
+          61a625de abcd8025 41909a4b 2b894deb
+          a34992d0 249aaffc 79ca13e0 3f6adcdf
+          8c009f94 72b9cd80 b5dd7a5c 48cc5d36
+          fbd6e62f 2649de06 7da3f344 2622d2d4
+          45bc3ce6 ab90ccac 82a6c4fe 0320ebef
+          78f0bf77 21000000 0049454e 44ae4260
+          82   
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="96"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000004d 0000004d 08060000 00e309e9
+          b0000002 16494441 5478daed dc3d6b53
+          511cc7f1 ffb939e7 e64ab114 75698574
+          68e31411 4ac17751 2d4e1d3b baaa8b52
+          548af501 b5a38e8e ba15f46d a8a80d0e
+          a2432ed5 a9884442 6feec339 9e9b2660
+          93a6e8dc ef774cee f4e19c33 fe54b1b7
+          a77fdc5f bffef3f5 9b659b74 ce88121a
+          ce89a8f0 c4eea9a5 a5adb36b 779eeaef
+          1b77afc5 f71e3d0c 239120d4 008dc966
+          f95cbcf1 f9a23f54 4e7d68d4 dfa5dfbe
+          2e346e5c 15a95444 8a02a1e1 fa2ecd27
+          cf259caf bfd72e4d a7025311 e5fff8f2
+          e2a564ed 8e489e02 35488762 2627e4dc
+          ea8a944e aedb9dd2 1204ae77 6dbd64d6
+          fe2d7937 cfa2d95a 4b148f9b 3827492b
+          9e95b635 6e7003bd d7c1472c cfc583c5
+          e7df362f 2863b263 6f966566 7bb1f129
+          df89e70e 1cbe912f 95724114 255ed41e
+          7734ff64 15a5c7f0 efc1a1c2 4551e16e
+          8e7708a0 f9ff4003 0d34d040 038d4003
+          0d34d040 23d04003 0d34d008 34d04003
+          0d34020d 34d04003 8d40030d 34d04023
+          d040030d 34d00834 d040030d 34020d34
+          d040038d 40030d34 d04023d0 40030d34
+          d00834d0 40030d34 fa17b4de 6e188d75
+          181d9d73 4ed92489 58eadb5f ea2b3d8e
+          46d3badc 40ac6d2f 363eb209 29834dc8
+          9aae0e31 89b5aa7f 14c54c9e 1469774c
+          be13cf23 d607aaee af8f963e bdbc9756
+          d5ea2f9b 15bdf5d1 faea0a3b b787e5c1
+          4a9fd249 7b2f7d7a f9caabd6 fa8385e6
+          e3672c2a 1f914d73 49139169 efa5676e
+          ddde1427 eaafed6e 1eb391b7 4d9c8926
+          76a72f5f da9ab9b9 b6f9070e f8b1b035
+          80f7da00 00000049 454e44ae 426082
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000003c 00000046 08060000 009045f7
+          d500000e 2a494441 5478daed 5c095454
+          47167dff ffa69b86 6e686804 44765044
+          dc125722 eeca8989 6bd4d144 31711d8d
+          a368a263 e2926db2 9b28064d 84889ab8
+          448f1a27 1aa363dc e296c115 513683ec
+          ab2c4d43 d31bddff 4fd56f4d cc8ca68a
+          eef61cce 9c94a7cf c1c3afea f7eabdfb
+          de7dafea c3588d06 aee2c3f7 966a0e1d
+          9ec81b75 3ec0a07f f60c0180 91c8349e
+          23461c09 7cf39d0f 390f4f23 3830049e
+          87aaa44f fe5abb67 ef345ea7 6d0facdd
+          72098cd4 add67bdc 980301af af592fa9
+          f8e0ddc4 e2b7df5b cbe15fb2 e0d8404a
+          376666f7 e69b75ee 619bd396 3bb2d4dd
+          944dd30b 5e5db1d9 2972f110 d99471b3
+          3f3289c0 dce81175 459f99d7 8b7505a7
+          0cde0c20 0bf42dee 71eb4e14 ab5098ec
+          5d277b78 dc51eda9 0b4f7372 27c985fc
+          cdad67f4 5556309b bdec7496 870e06ad
+          25b49895 bcd1a870 c8594c46 b513c502
+          aca36034 7ab1c0b2 02387b30 ac4d7347
+          06cb3a5d 2caceb63 58b56d8f 3f157e24
+          a6acb64f 5b1bad95 8b4a6181 479157ed
+          09522fa5 f8739b51 16c98265 c2b2d1ca
+          45a5308f 924bc088 41e01bdb 5bfc992a
+          543b1ab4 28e66359 b04c5836 dee4740c
+          a3602e50 04742427 6f3000af 6f66ec77
+          532bf03a 1d50a54b 5126a155 2e4df734
+          a27a0c4d aac0f9ce 6496f126 83cc6eeb
+          5aac0caf d7cb6914 1665e2a9 1516d312
+          15e4798b 15188ea3 4ac1bcc1 e2d6527d
+          d7df6e7d 1bea3d2d 75b5fe0c 47613117
+          0992cd42 a9ae6045 949ca122 f956a309
+          38b98cce a5d1de68 8f1f1d66 afc24d17
+          cff76ea9 d5f93012 f2b31237 375136aa
+          b0c0490c ac44e5a5 059eecf8 264d0348
+          559e54a8 675d00aa 5352124d 25c5bead
+          e6bc06bd a4fcfd77 dea47267 e401526f
+          1598ea1b c872211d 392f2f0d ebe2e75f
+          29502cdc 5c5a0eae 6a6f90b8 4b882900
+          5bc65c55 d7a1e8e5 b99ba802 dd03a374
+          f58a35ba 2b37e358 19392549 14529021
+          85b16c24 f7c75248 fddb97b3 ae9da272
+          8816438b e9cb6ac0 62308277 f72e60a5
+          f0205ce5 d41df971 5255f2ba 59b4ca36
+          1cf96e50 65d2c655 1c0572b0 0cea1e31
+          60d1e941 5f5e23ca 481af2ce d139aca2
+          5fec45aa c88b76b4 fcf86908 1c350c64
+          2a390814 718273c1 165bbd4e 9f793d92
+          f46c4b65 85aae0e5 f95bf134 927b6266
+          25f57485 80f8c150 76ec94cd e32820a0
+          1830f03c eb3978d8 4559a04f b1602658
+          590a507f 3d1f1ab2 6f43c4b4 09383011
+          131a766d 4b93d1b3 60deec2d a85cfc43
+          350a17ce 4b369554 45b014d6 e55b00c2
+          9f1f0bda dc7ca8cf c8176523 d5e8f2c8
+          c0dbcaa7 06a6b39c 4ad5ec3b 73ce462b
+          4572c2c2 141d3886 7657091d e263c162
+          a473eda6 f4eb83cb df59b3e2 51cf546f
+          4a9a5177 f0c8748e a2096131 00b41fd2
+          0be4ed7c 902c4781 6a83906e be73e727
+          710a8589 115050b1 68eadd6f f6ea76dd
+          545cd191 b400de5d b99f37c4 2c9e0d79
+          5b7643e3 9d722062 8ec76ec8 98a38f1f
+          1fe83174 c4a5077f a5bf7eb5 d3adc171
+          e9a83857 91d210b6 945b7b35 44ff6d26
+          e4a6ec80 e6926ab2 7511d6e5 1d43b3ba
+          5eceeccd 29953637 93787937 87ac5db7
+          58c48240 4e39cde5 f5507ce8 5f10317d
+          228ada52 72b5c2e2 9c2f48f3 13a6edd5
+          1e3f168b 58948c37 1a5c7497 2fc5dc7e
+          7ef27e5e 475616cb c6700c44 244c84ca
+          53e7a1a9 90acaca8 0b0f42f0 27498bb1
+          b222cc84 07d246c1 ec84a4aa ad3b1325
+          147d242b 72adc897 c621eb4a 212f759f
+          cdca0c89 728acaf3 b2b0e022 86617853
+          5959206f b0ba8afd 3481ecca e1934782
+          ab9f2fe4 7ebecba6 2c43e1fe f367ad0f
+          fb22ed95 5fc57850 616b63a3 2c6b50ff
+          73fa1b39 7d5839c5 8eb312e8 b66c1e54
+          5fbc0415 27af00cd 4689ee6d b9a7a08b
+          8d8a9259 1e8057b7 70089f32 0e6e7e9a
+          8202a11e 88ee8f94 75eb117d 39e6dcbf
+          07714a0f e343ab25 cec3c314 b17d6702
+          ab70d592 d28ec899 cd16c8ff 7a1f04c6
+          0f056598 9f88319a 728541d6 616474ca
+          da52901c c2268f85 c2fd87c1 ac212b8b
+          6567dd65 8d115bbf 7ef14165 1f5a1eba
+          f77c322f e4e38f16 e2e044c4 3312bca9
+          b8064a8f 9e84c884 4928624a 9cdb1511
+          eea7a071 d090950b 75282d12 23f9bd39
+          216b3f5e e4fe64ef 1caa7ad8 6fc1e25d
+          eda6ff25 8526ed60 37aefae9 1ae82baa
+          207cea18 3a2bd356 4de8fbdb 0fed8d38
+          bc07141d 3c0e540c 0ccdf199 3a611bd2
+          e1eb5635 00c29237 bfe21ed3 31036381
+          a65828d8 731814c1 81e037b0 a7182c1c
+          1d78e314 c13e1030 7230dcd9 f52d7253
+          9e0801dc 6c974785 65856d4c 4d6c75c7
+          835379e9 23b66c4f 60e42e3a 229e118f
+          b5eacd90 bf733f04 8d1e098a 20b54396
+          1603a284 458cee39 a8f8f10c e84a6a89
+          29084389 9172c6f0 2fb7ce90 a87d9aec
+          6af128fa 3f752be4 83f71753 e119b95b
+          637e258a d6672172 c6646475 ceee861f
+          2e0c42c6 8f44014a 0b95a7af 52457f2b
+          dae0a0b7 df5ae631 70c83587 7a5afe89
+          cbb6f94c 99b0dd4a 83671450 2a7e4c17
+          6be7d0c9 a3a8aaaa 876150dd 33125431
+          5150f0cd 77225c68 38817a74 fcbe80e5
+          2b3739a5 8917b629 7591bc53 68164f52
+          9ab1e1f9 cece83e0 d929027c fbc78878
+          c696a6f9 604f92aa dc2074e2 b350b80f
+          a5a04623 b1cec5d4 5116e457 1096b275
+          3ecdf10c 95c20813 ba882fb7 25704a77
+          0da90386 056cd199 c44013f4 cc085086
+          faa1e82a 03891bf9 c3c95d11 b91883d2
+          cf2dd0dc 2c24a720 1e4349aa 0f4f4d7b
+          491ad0a1 9eaacd23 b4a22351 3067c6fa
+          eab41d4b 688e305b 9065bbcc 9f02cace
+          9160aed5 50b5a919 dc334364 26e3dd64
+          915c90e6 6057f69b 35edb3f0 b49d89b4
+          3a48681f 6c46554d fde12353 8884fd1e
+          0edb3dd1 115c7dbc 21fbd354 e4aa66b2
+          2f09b602 3ae285b1 e0d3ab0b d4a46703
+          6963317c b4274f3f 6bfce5f6 5ad78e9d
+          ca9c6661 ab4e27cd 8aeb7b16 71ec7e24
+          8ecda314 26f55040 ccd23950 b0fb2034
+          641703cd 26dd2f3d 1508029d 664e859c
+          cfb783b1 564b0c5a d8ca1e03 fa9c8c3e
+          792e9e95 c978a760 b8f8d545 1f355328
+          7baf1c43 65e304a8 bd9c011a a42cb612
+          c635cd07 6316977d 55677f16 bb2a8c78
+          ba4e6e30 682f5c1e 5efafab2 b7694f1e
+          fe70d47c 95f65c75 eaf62554 dd08e4ca
+          81a30602 cb71507a e48c98a6 5a3bc4d4
+          76ea1258 f406081a 3394aaab 2241d0af
+          5cbf7155 ddbe3da3 1c52587f 2b33ac28
+          31319595 906b4f9c 73559d83 c037b60f
+          625cdfda 9eb7e774 09a736f4 7d05df1c
+          02ef1e5d c1bb5b18 1039002b 7a0853b8
+          70fe5684 e740bb14 c60df182 392f7e65
+          d136ab19 1732ad73 7197a1aa 663c14ff
+          f328186b 1a45a1ed 1dd8bdcd 5a0314ed
+          ff1e4227 8d11cb43 22bd4571 a2a546eb
+          7f67e6f4 edbce9d1 0dc347fe a2e4ef4b
+          df6d4ccf 18489382 c4120e45 d7c6db05
+          50733907 3827dc08 c26bd4df 2c004d66
+          365a7b1c 5597f437 3c2f7fab 550ad7ee
+          de31ba72 63ea0a09 453976bf 8be8eaa3
+          163b9a9c 0c9c36f0 5a25874f 828bc21d
+          0286f76b 0d9e57d7 eddff334 95c286bc
+          dca0a245 0bb7882e 492ac750 7a5586b4
+          830ef143 217fc701 64692b55 1783dab5
+          71f38f17 1055fd16 d5c503c0 23dc9f5c
+          85ddc7f3 cb0bb63d 0ccfecef eb492387
+          70bbada5 bec98f88 5b4ceb50 45149130
+          493c91d0 95d4d0e5 5bc1d682 115a6c29
+          8ca6d6d6 5768a0ec 871362aa 12bb2a3c
+          0d9e1bfc efcc4af8 1f3cffee 3f656fbc
+          b64a7bfe d2701adc e2a88c2b 22634d2d
+          549eb94a 4e4138a5 9a6d014e e2a3aa75
+          f153d720 ce63150b 12868ccd eaf399d0
+          5c510561 939fa1ba de20e219 e952ba72
+          f91b0f55 b8f1a753 7d2bd77f b686aa8d
+          8270ebdb af0b7876 0c476cea 908d0d31
+          e46e842c 3428aff3 b113b1dd 6fe475ea
+          9e9917d5 e5f4b95e 6e3d62d2 793d85a5
+          91d50af7 7e0fcab0 606817db 5594810a
+          cf491b57 6b8f1f8d fb1db5e4 0d0649d6
+          805e179a afe7f425 5247e48a ae6a4f88
+          5932177e f96a2f68 6f971203 95d8d863
+          25869853 a76315b1 71371efc 9d312f27
+          f866ff3e d7f86694 fe2464af f288ec20
+          52cfac0d 69887a36 10a9a7d8 f6898ec8
+          ec7629a3 2febae30 8916bebb 65f30b4d
+          14ca8ab4 1b593272 c644b117 dd905b4a
+          159571a0 097af3cd 15ffad2c 1eae51d1
+          25211faf 5d44d355 c1dfa5bd 5d8edc3b
+          1d221326 daee7592 3a31086a cdd977ba
+          576dda20 1edbb2bc d120a9fe 2279194b
+          d9100f1e 334cbcef 51f6c359 2aea885d
+          4f153fe4 70fbe52b 931ff58c efdc05df
+          a89f7b76 076d57a5 ecd87924 430b048f
+          1d0e3473 f0d594ea 94cf97a2 2248c6e2
+          fb1486dc c26e3447 8e5e5d82 c1a7cf13
+          284d1cb0 a19f805b 1ca4a47e 5e95e129
+          69f318ee 8f773434 3965892c d0b78898
+          76185be4 c1a94add ab872813 690e8eda
+          c6828a8e 4de7cef4 679b2e9c 8bbbefaa
+          a4fc1634 3a1e2a4e 9c05438d 8e4c1d05
+          db3165e8 868df364 a1e15524 2be08e45
+          58f2a6b9 08ef0229 5de1efc6 32605902
+          470da7e6 ec4d3f5f 8c630dd9 595d8918
+          4479d33d b09dd810 afbb9649 855bcc8a
+          fce6bdb4 413de585 ef698986 d7f84927
+          fc17cc5e 67a1493b 4886daab 37c0c543
+          096e016a aa1b09c6 dcec2eac b9a23c88
+          b4417831 45481098 ea34d0d2 682237c4
+          7113bd5b 5446c8da a4d75bcb ae823ffc
+          74359a7b 9de8a648 8696a616 5126f7e0
+          0e36ae4d 4082b9b2 2280b56a 342a9af6
+          8bcccb76 3d88eaec 083d13f8 d63f5671
+          1e9ead3e 83c02f87 a0b9aba9 aecb21d7
+          37373480 abb717d0 5cbdb234 683c59c1
+          6a71a3ab 5e64a828 273304ec 0d2efe9e
+          351e8386 9cb79743 e3b9780d 1a37c537
+          8b70b793 ee484390 e39b7854 952bbe76
+          c89b5bc8 6b62eae8 adaee03c 558d7657
+          49682e5e 83c69b04 ca2b91f7 b31a4572
+          79b060a0 60fbc8fd 39a5d2dc 0a211eba
+          b9780d9a 6baf02ed a5d7dfa0 efe4815f
+          d892c91c 7b4903cd 15d770fe eb278fe9
+          9d074168 1b6b38a4 30036d77 308fc5c2
+          4eb8d6ff 58946d5d 7b942a42 e3b35f4c
+          e3708060 656d4757 2ccbdd9f af88418b
+          562eba94 84ef4bd7 697f6da1 b619e322
+          b9cc9aa6 56c945dd 3d6e4b8a 3a22d79f
+          6fa6fdff 2bcc3f86 57cd9cb1 e663928b
+          655c5d1b 9cc968f0 52acdcad ce614ba0
+          359c4a3d 300344ba b2dee327 edb60ab6
+          cb98b8df ebd0c760 abd2bc27 4ed9e5a8
+          7c780dbc 96b3e4c2 3a625d25 1d5e5bb5
+          01d138e6 de1f3d68 67f71f3d e081e794
+          aa2af5f3 5377facd 5bb0d351 85f11abc
+          bed9ad76 cfdee9bc 4eeb8fc0 c7da2997
+          c0ba296a bcc78d3b 10b062e5 86ff0068
+          224493d5 98cd8100 00000049 454e44ae
+          426082   
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="Y" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000003c 00000046 08060000 009045f7
+          d500000a 0a494441 5478daed 5b0b5054
+          d719feef 63975dd8 65792d08 8b3c1791
+          84446b63 7cd6fa8a 1044aab1 1263416d
+          a2c6d849 b4b13a51 71d0364e 5b1335be
+          5a1f544c 3515aa60 8c36556a acc652a2
+          611a276a 8a28ca43 9e8b3c17 d8ddbbbb
+          b7e79cd5 ceb455f7 dcdd4549 e538ccec
+          c8dd73ff effefff7 fd8f7361 6c261357
+          f7eb0dcb 5a8f7df2 43bbc918 040cfae7
+          ca120118 4ed6aefe deb8bf0c 5cffeebb
+          7c90d608 6eae96c2 c349f5db b62fb536
+          d5c5000b bc8b7689 8c97b721 202dad20
+          6cd5da0f f8ba5ffd 6269d5ba 0def73f8
+          97ac9b16 22d09d97 cb8659ef 1882e2f2
+          0a17b8b3 556749f1 b3e5b3d2 4f8876e0
+          1977edb2 83bef3ab cba3902b 45bee5e8
+          91391cf2 29ab008f 2cc60ad0 f159d174
+          c1d0b45c a60d6e77 759fd68f 0b67d811
+          585ee919 bbc08422 e658e11c 56b458fc
+          5d0ce207 20468eb6 5a7decdd dd7e6e39
+          a5bb2bc4 936611bb 4c267f16 5856044f
+          2f8671fc b8b358d6 e36661ac bdb06bdf
+          5efd801f 28c076f4 63eb7b00 a4dac5d2
+          6e2ad7a8 4011e447 3ef725b0 725f1f50
+          68e9eda2 026cb700 040d1f0a bae489e4
+          735f59d8 96c061cf 4278ca64 6abbe839
+          2c8a7d93 9412ed62 7b73f3be 08ba5fa5
+          fb01f703 ee07dc0f f8c903cc 80fbdd12
+          c37e7b00 8b820df5 9e3d6eed 61ebec78
+          cc80c5bb 9ea36885 45b3c0db bbbae46e
+          958d5d46 ba198cc4 4892d02d d95194b1
+          54e16c17 80179a0d 21ee0016 9a1ac3a9
+          a0b08cc3 19120053 5d6e1704 60e572e7
+          5e661c95 5ee7f973 635d066b 68f2edb9
+          7a652823 737e2d27 93a1f690 ba3f2413
+          0f1b8dd7 acdd3dc0 fb28a9b4 84e5010c
+          bfdfbfd0 da6c50bb 02b869f7 8ed72c4d
+          1d5a8677 4e33ce5b 09369389 d6cb366c
+          be994630 cd775a40 ae5603af e49df69e
+          d833a6ca fa989a35 2bd74905 db73f572
+          74edc68d d92c8d02 204728b4 81606a6e
+          a1d217c4 7713cb69 341de00c 007ad2dd
+          f506c217 9f081d88 568a50f3 0268ccd9
+          bfb4fd74 d148291d 7de5b29f 6cb5759a
+          350ce7bc f997a965 a00cd142 e7cd4a70
+          1a0de87a 5ee3d7ce cab4218d 2285105a
+          8d5668fd fa1b081d 3f1aec36 6a75e0aa
+          7efae64e a4d832aa 50debb6b 4edbe9f3
+          d3388a59 b40dc565 c898e7c1 d2d206c6
+          ea264223 67494616 1252cf2a f4fa722a
+          75431ebb 7dea73f4 448341fb 7c02d828
+          d22c1eee 1baf940f abdd90bd c2d9b5e6
+          aacae0ea ac359b58 8a03153c dd504769
+          61c0f747 4165e1a7 d401a488 8b2f637d
+          9e1b7181 b6f0113a 4d7033ff 1844bd34
+          1571470d 769ad046 5cacdbb2 35ababf4
+          62c2c3ae ab5eb174 a360681b e04c9971
+          28b35e3c e8e7ce82 daa2b3d0 5989bc2b
+          a303ac1a 39a684d5 4c987c4e ae551b68
+          79d97ab5 120c17ff 01facc99 8417ced4
+          11735134 5995889b 3b4441b8 afb4b41c
+          c94b6e3e fcc93c8e a2d4b0a3 508e7939
+          157a1a0c 50ffd752 a0398a11 05002f5d
+          e06ddf71 e38b5914 d76d81e9 afe4d804
+          baa7848d aa3e7e06 890407e1 29e3c06a
+          a2086d64 544771e9 c486ed9b 17fdf7ef
+          ac2d777c aa562cdf 4e44ca89 d2621a85
+          8c1d02aa a808a8f8 c3316acf da903383
+          32e6ede2 03028c24 ab86bd93 b559aed5
+          3488028d b23bbc7a e34001ba f908f08b
+          0f2702e2 14340aed 9aec75ef dd399c9f
+          225a2c04 9aa5a65a 5b313f23 d75459a7
+          77968630 6f7d0606 c2c06953 d0bd8f80
+          addb02ce 949c7817 7d4fa10b aa097d7b
+          e50e62bf 787700d6 b86b7bc6 cd37de3a
+          40c28a22 a7e1a71d 3ce61908 9f3201be
+          7e6f1732 c8b90184 36e876ca c1facbac
+          4a6532df ba152134 b687b04a e7bc65d0
+          e689cb17 42f39797 e076d117 54a18cef
+          654311a8 cfdd9bae 9dbfe0f0 7f00c6f5
+          e0b51f24 1f6a395e 349ba33c a2c4a0e3
+          5e9d8104 8d81f29c 42e0288d 203364bb
+          a3406128 54d98aee a3cf4c45 79570565
+          bfcd235a 42eb94c0 97523f1a 54703ce3
+          7f9b0714 abd1bb73 9728a2c2 aedbcd74
+          807118de ca3f8142 4d47b845 93aab0a1
+          38c561af d2802591 34ea69d0 0c8a858a
+          83471df9 96a11337 84e546f4 ce3d6f3e
+          b05b9287 86b5c6e6 e466321c 6ba639af
+          c1216ced 11902185 845bde61 feb853f2
+          d8c27b29 d19e91d3 53a0e2a3 42108c66
+          aa87846d 6738c682 b0cc950d 086d7d68
+          7be83b69 ca85f0f5 d92b6d94 471738bc
+          daafd742 e3e72510 8b521583 22c513f3
+          7ac25bd4 8ec6cd9b 050de7fe 0e6d6535
+          8e50a6a1 1ab25db7 66f56a84 a584aa1f
+          d6bdb376 5b605ad2 1f6d9443 0b1e09dd
+          ed937f43 dc142022 6d22110a 771756fe
+          a89949a8 a4ed427b 17937bd0 ea4a40f2
+          c463baac f59be807 00c84b31 bb73172b
+          a275747c 661c3b55 1c2c80a0 e7864240
+          62b45ba0 b1d1dae1 83c13f71 304a4185
+          c07294bc 459e9587 69ab62f6 e42e6438
+          4edac403 c77eecef f6233e33 547cc662
+          626a3642 65c19f20 66f67490 fb2aa8ba
+          aafbf156 11e28bbc 9b8a8a8b a36069ef
+          a1e2eddd aacf16bb 2767be7c 6084c1a5
+          118fef84 c917c2d7 66d1f319 855d7369
+          19b4fdf3 1ac466cc 20b5b694 f3647c3d
+          c3b330e8 d5d9d058 7c9194b1 1c65285b
+          512486ad 783bdb6f 6adad987 06a3e844
+          61f02ceb dab4a4fc d64f4fa7 d3e45907
+          40161296 64a21eba 116ee59d 241e7256
+          06620af0 4a19c4bf 3e073529 4628df57
+          401a0fda 7cab193f fa5442d1 d9171999
+          4c740b30 2901eb6a fdaf8e1e 7ec15c53
+          1fc75228 250e65d6 4b0ef10b 5f019b20
+          4075e129 04fece7d 279ff8f6 980e9ac1
+          3110959e 0add3575 70e3c302 6c19d538
+          09978e5c 805f5d62 c997c315 b1fa3aa7
+          722352e6 90f6d3a7 4694a5bc 780e7dc3
+          8baa86b5 3a268a03 a74e8280 6189606a
+          6c86ae9a 7a540377 918120be 2d871e8a
+          dc4f03aa e8089069 d4d070a6 18eacf7c
+          4122826a 0e6f2742 250e3a9c 971230f3
+          e593546d ae282169 d66e58f7 5655d6fa
+          adb46fc7 e1aded26 3c775281 dfd3f1e0
+          1d1e0a9c 4c81ba9d 3014e23c 74945721
+          ee7583b1 ea36b45f 2d074b87 99ba6cbc
+          5772ea96 2dd910b9 6567162d 06498049
+          bd9d969c df72a228 9d93f04a 20567952
+          81898e11 69e4d4b1 48c5d570 fdd09fe1
+          de3b9e98 e3524e57 306fd5a3 be7bf6a9
+          cfce4f66 954aea39 adb4a316 9c9ff7a0
+          fc2ca1de be578262 b5c50f89 23298c07
+          9c27f167 f27f5ed2 c0e23656 16a036e8
+          730fcc97 02563a60 9c9f71bd 4df2335d
+          bdedf125 3ad257d4 d66d8b14 f1095552
+          bfeed261 9aefc417 48bdfd38 5e61c213
+          96016f2c d81c9431 ff6357be eff2e921
+          aeb703d2 92a9eb6d 8f744f98 b7c3124b
+          22366e59 e5ea1eae 1f97927a 7bdf62af
+          c850497c 76399211 6f398d77 6becfe83
+          733995ca f2e801df abb7f7ee cb6458a6
+          77f97c97 b791ef6f 5ae2fdcc 901bee6c
+          e5f681b8 e685e40b e1d96b7b 95cfb8ec
+          0cfef18f 7e13bc60 719ebb7b 79e40d00
+          ddea6cc4 e7a4fcde 086dfc20 55df79ea
+          62d4073b 7fe689fd 3cf3ca03 cb42f06b
+          8b0ef5c6 9bb6982a c10b5eff 90f3d5f4
+          f41dc0c4 30bbbcd7 629af5dc df0378ee
+          a596de7c f1541499 be07f85b b2fa013f
+          b2c53c49 80c989dc e341cc3f f2278c5a
+          c1a69252 326467bd 9e00c0b8 efb5b476
+          febb4ffe bf07fcb8 80f6ab74 3f60c955
+          7e2ffec9 9a07f7f6 5c8deaa3 32f4522b
+          0cacdab7 a1cf8996 6ae4e852 65acee7a
+          77456d9c a74a7dec 58b95653 af9930e9
+          acc70453 f460d1df 7de9ab84 9a9fafcf
+          32957d33 0418bbc2 e5da8298 c49abd62
+          e2aee856 adfaa57a f4d84b9e b2f15fed
+          6ad933be 6227c000 00000049 454e44ae
+          426082   
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="128"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000067 00000067 08060000 001d415c
+          f9000002 c3494441 5478daed d6bd4f13
+          7118c0f1 e7771cb4 d4221243 a089c441
+          6b0c0353 c55772c6 18fe1057 4a60d281
+          8585c5d8 8481aefc 21c481f2 92882d0b
+          03c41406 44539118 a9a52d87 f4cedf1d
+          1a0956c2 d2a3c3f7 9b5cd26b ef96e793
+          e7aeca75 5d39dcca c70aa954 b2bc9a1d
+          72ecea75 a5440905 9a667095 d9f62d3a
+          f8602136 3e9e0edf b95b50d5 0f1bb1f5
+          6756a6fa f96b9c11 3547edb1 ee7cfffc
+          826516a6 df8c7a30 919ea844 6ff68952
+          2ccde56d 8f2b07db 3b5229ec c50bd3a9
+          51f360e5 9de5fdd0 a1616e8f bc10e77b
+          d1df311e 6c41aae8 432f85d1 d5299be9
+          59a9ec6e 4839f7de 32a556eb f22fd03f
+          3afb4559 7b3d23b5 caa188c1 cc02cb11
+          69898465 e055d277 f0bd8eec 2e534e3f
+          c6f4c61c 572a725c 3e12f9e9 df430dce
+          df81566f 799c9327 d69f9421 e6d98b95
+          61f86bd6 f13891eb 18b2328c afb19516
+          33566925 97f0e77e 26b3ee96 1d8b5c7d
+          3e3cd737 3935c1f8 1adbcee4 c4547139
+          97f8ef56 d57d47d9 7688d105 f05fe09c
+          39f3da6f f6f71181 43e08043 e08043e0
+          1038e010 38e01038 040e3804 0e38040e
+          81030e81 030e2300 87c00187 c02170c0
+          2170c021 70081c70 081c7008 1c02071c
+          02071c02 87c00187 c00187c0 2170c021
+          70c02170 081c7008 1c70081c 02071c02
+          071c0287 c00187c0 0187c021 70c02170
+          c0217008 1c70081c 70081c02 071c0207
+          1c0287c0 0187c001 87c0a10b e1a850c8
+          663c8def bc399b75 c5f4b73f dece0d7f
+          b4ed10e3 6b6ca5c5 8c659817 c4711d47
+          73ea9b96 7389a23e 185f008f aed6df73
+          ff07c775 dcbf3ba6 c48c44b4 8dc1db28
+          c8b44b4b 24eccfff d496b8a6 6a0bed9f
+          9cb8625c eb948197 49ffb328 661658ee
+          c96278f3 f767ef9d 6a17f3ca bdc1f962
+          76ed5169 7b47f2e9 597d0d2a 9766a461
+          0eb48397 76c9a8ea 663eb6fe f449a6fa
+          6937ce78 9aa3f61b 3df9fef9 254b795a
+          875bf9de 422a3552 5ecd0e39 76b55b2f
+          0feb13f8 d6886b84 daf7a2f7 1f2ef58e
+          8dcd846f c5bffc02 2061d186 71d4b857
+          00000000 49454e44 ae426082 
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000050 0000005d 08060000 00328fe1
+          73000013 58494441 5478daed 5d077454
+          d5d6def7 4e6fe9dd f4820192 50420945
+          29820541 419a0a08 218841a5 a8bffadb
+          9ef04091 278a204d 11835204 41418380
+          413a5244 14880442 92499bf4 5ea6cfdc
+          fbce3983 ed7f8f72 0e93acdf 65ce6216
+          b29c7bee d9dfddfb dbe5ec7b 86134511
+          acfa82c0 f277973e 6dfcf1cc 9d82d5e4
+          c701a03f ee1ba200 66794868 aedfa449
+          9bfc264f dd036d38 5a4e1e4f ac5ab53a
+          dd7cf962 b2e8b47b b8531011 fde1a48a
+          5a6ddf94 23c173e7 ad56c6dd 5ec999af
+          e406e50c 1d74c462 a8ecc4e3 6f716d23
+          98289205 40f8eb2f cf0e9dff c6cab6b8
+          47e3de6f 5272c78c fed66975 78b4992c
+          480801fd a5bc2d20 b7cba163 8325694e
+          cbff361e 383a46aa 42f793a1 8fb46d3e
+          bccc258f f1ec0ffd fc273fb6 4ee2e969
+          76ab604e 2714a44e 5a6f292a ebd2a6b2
+          c85cb2d8 ea8d7e20 58ad7ceb e9938379
+          0eda65e0 9b3b9aad dea6f33f 7773f7dc
+          b6ca0a2f f3a59c64 2c5c7b0c 8c99f1cc
+          e9c13c7a 72ded0ce 433099fc dd6e5976
+          bb270882 06b8f693 43b4597d 78e038ae
+          bd0104be 0d741ecb d1dea270 3cf0d031
+          6e4d173a 20e800b0 03c0bf1f 80289874
+          5a4986f1 971f5806 2c0b89f2 db054011
+          079252f0 498a0599 4efd9706 11af1dcb
+          8065c132 b180c8b3 dc54aa51 c3edb3a6
+          82363c04 440753c8 d1062105 7d068fd7
+          8e65c0b2 60995894 81d18445 1444dad1
+          0dd9f45e b058dcaf 4d562b02 4460d042
+          972c2459 6f2f0efc fd561cd3 7ded9515
+          6e4fb81c f57512c1 6ce3399e 4a0f7eab
+          38305220 015060d1 40fce124 8c0aecb0
+          7bb95d03 9d0e1d42 81fac110 19aecac3
+          624c1801 07fd6205 a4fa0270 522993b0
+          56bd3eda dd005a0b 0ac20427 52274a1e
+          e4657297 193b99bc a1834709 b895ee91
+          21d81d0e 72438942 4eadfb98 eb5bcf9c
+          ea87eeeb 56005b4e 7e3f8085 8ba46a25
+          02d04964 a22e4408 8285e775 ba565a00
+          04bb1dc5 4e5690eb 74d400f2 0a00e385
+          4bbd9abe fbb6b7bb c0b3d754 abeb77ed
+          9cc04be9 01947b79 80d36225 32d10407
+          d8e2255e decdbcd4 d7af96ca fc318056
+          116c0d8d a00a0e60 0837c8c2 a5252ffe
+          cf72674b 8b5b9c49 c90bcf2c b255d445
+          7132fab5 a88303c1 5adf4864 a2d54089
+          8f4f2d2f 0f0e29a3 e7406432 8525a08b
+          0a075e09 4c5ad872 2ea75fe9 2b2fbc76
+          abe0d56e cc1851bd 61cb3312 25433cab
+          e6411b11 06ad85c5 4426daa1 b82dac84
+          57c676ba 42ad44c8 54ea2fe4 80dcdf17
+          7411a1e0 b4d1df5c 8a40ac58 b9f6e586
+          cc5d0359 c1b3e45d 092a7a66 de3a62ba
+          94da23a0 d04f171d 01526f2f a8cfbe44
+          64a21dca b84eb9bc 3a29e902 2d77e2b2
+          798bbe1c 5a0b8a20 74e43060 0aa47852
+          8f94143e 3d6b3d8a 0b3d1842 21d0cf4c
+          5d6baf6b 0ea1365d d1c5e5b7 dd37145a
+          f3f54416 2afe14c9 da41d3a3 e7795ed3
+          23f9822c c8ab5cb4 d32fa268 c76ef088
+          8f8390a1 bdc1c190 5cf0c889 5b4b2a3b
+          15ce497f 87f6dab2 45f36737 1e3ef1a0
+          44c5107b a0b5060d 4a065d5c 3491813c
+          7c1a0782 b0528405 e8d5dd7b 5ee4a57e
+          01ad9e77 0dff5a70 d00bdf5c 500925db
+          332162e2 68f08809 72553528 07e6aeba
+          ed5fcfa8 5eb766ec cd5ed37c f840b7b2
+          37172f91 c819225f 1bce7ffd 2072e218
+          28fdf21b 684132f0 94f3a0a8 07bcee1b
+          b543a2f3 b0925422 6046fa6a a4920e5a
+          339422e1 cbbe3b05 8de72f42 5cdaa388
+          9465f464 ccb928a1 f885e757 992f5fba
+          ed86da53 57abd2cf 4cdb203a 441527a1
+          771cbc4c 0271d31f 81e6dc7c 28db7f02
+          689d0fce db783967 0e9cf9e4 87bfe5c2
+          1e838766 fb8cbe7f b3d3c220 3c12227f
+          e3976852 19443ff2 002167ea e01af18f
+          a3d11888 386d8de8 b8be2914 cd7bea2d
+          735e7177 5ec9a07d c84222c6 dc0d72e4
+          38f23fdd f17b5845 31b095f9 3d3cf123
+          758f9e05 7f2a2684 2d5cf29a 44ab68a0
+          2d4f61e1 edcd16c8 fb680bf8 f6ed85b8
+          a527131f 622e6b3a 767a54d9 9b0b665f
+          eb3bd8cc 6b367d3e 47c2001e 560e9fee
+          b110386c 10147cb2 0dacb5ad 40bb878c
+          b191f968 ab4317bc b9e8b775 cf9f3f9f
+          fc87cc3f a099e3c5 c686ac43 236927c6
+          1ecc54d9 4cd83962 fc03d098 9d8d026d
+          13d09a18 fe7ef3a1 a377493d 3d0b35c9
+          bdb3ff58 aca8dff1 f9507dfa cc2d1c08
+          4ada7931 bf2b3cd5 d065eee3 5073fc14
+          94679d02 16e74334 78c9e227 bdee1971
+          e2b7358b 7f484344 94ce5c1a 76e7574d
+          474f3d40 7d03d115 5b759efd 18287cbd
+          217bf12a 925fb2f0 948816aa e999705c
+          d5a58b1e 172c70f3 53cba9d3 77e10c86
+          9332aeeb e929c474 b397ac26 b92f4759
+          48729a91 e3b87bd0 179df71e 1c07fcef
+          17ff0940 129ce65f 09fca57f ca19677d
+          431827a7 7fd232ad 1ababd36 0f69610e
+          e46dd805 2ce64684 b6fe5e61 e2ae662f
+          2cd54b07 123cf4ee 14081f3b 122ebcb9
+          1c4c861a 6aaf2b22 cf2df1f5 2e4d3cf5
+          632f4554 74f5750b aa2833a9 8a5eb336
+          15698293 b652884d d9d66882 bcf59bc1
+          6f605f08 1c90449e 1c4bbe8c 9d04b602
+          fce1556c e0e187e0 111d08e1 e34641d1
+          b69dd05a 4c0f1ec6 00612144 af5e3bfd
+          ff8207d7 5a96cfd8 0907829f 9dbdc0c1
+          18d7355c 2c86b2af f641f4e4 f1a009f3
+          25b1577b 0f12b228 a51097fa 08349cff
+          052a0e9f 256117b5 06230c82 e6cc5a88
+          30f9ee1a 09d57f1f c82b2ff2 48e9b18f
+          45833088 a57b8e40 739e9ec4 87bc5cd2
+          eebb7758 fb22c7de 0b12b512 f49b77ba
+          b898a3e7 3d84c181 f0c54bff 799d8cf4
+          1aff43a5 12633236 a7c97c3d ca68d3bc
+          5feb6af9 1bb623e2 f684c871 2340b0b6
+          1f785870 bfdef110 30a83fe4 676c055b
+          9305686b 855866a9 b7ae1a61 90caabd4
+          02358078 a8e23b97 47ad5c3d 5dc4390a
+          2d1fa250 c85adf0a f91f6f81 802103c0
+          3fa52b1b 1fd26a1e 125ce9af 83982913
+          a07ccf01 a8ffa588 de91892e 8718b96c
+          d90c8441 e90d6a22 d71fbe0f 4fca0a9a
+          3b6b2173 9e7b5e0f e5bbf743 cc631340
+          1de2ddb6 7c78d56b c74e1d07 e6ca2a28
+          dd7d1024 0ab6a03b e889d4b7 fda7a665
+          de4451e9 c623fcad 7716687b 77fb4e60
+          e1432440 69e64130 1697223e 7c04652e
+          7c9bf121 ce8042ef 1908dae8 08c8cbd8
+          46368b68 e33d2ca3 a65bfc89 f0a52b5e
+          b9c9aadc 4d7c49a9 12623efe 3455e2a5
+          ada4e643 b26b2812 8194017e 10f9d03d
+          6dc287d8 423ce342 20f4c1fb 90d3f802
+          4c158d4c a99a44a7 6c88c9d8 3455a2d5
+          dadd0620 1eea8424 43d4fbef a7098c7c
+          68a96e21 841e387c 10f8f58a 772b1fe2
+          0a905425 235596ba 1fce42f5 f7d92065
+          cca4c297 2c99a5e9 919c4f51 17bef9e1
+          3779da9e a0593316 33c58748 a0da9fae
+          40e5be83 10336d22 a8023d5d 951b3739
+          8ea88923 499ca2df 9a491f2c 5f357fff
+          49e3d704 ce9ab38d 4a39686f 14b1f4bd
+          7f78f4e9 7ed8c9c8 87c5bbf6 83b9bc12
+          69cbc3c8 bcb95be6 43bc0eff 9404f04b
+          e98568e2 337098ec d4f9b780 c0d3c447
+          ff14b9f2 83e768ef 4f0d20af d63863d6
+          7f3a4dea a1aea62e 7df1ae66 9ebc8fb7
+          823a3418 c21f1ce6 ea3374b0 7db0b754
+          057942cc a4b160f8 6a2f345d 29a3f6ba
+          d8fc7985 ac35faa3 8cc7a45e ded46af1
+          1fc5849b 1d3519eb 1ed03f31 eb734ee2
+          54b044f8 38d08d9d fe28147c fa39b414
+          18a8099f bc772570 283c7a08 81e9849c
+          151b807a 77ce55b4 1023dffd 575ad0bc
+          e7335870 6006108f 9ca1fd77 361f3e39
+          9a670854 b1f624bd 3413994e 2c88cd46
+          fab60abc 33269390 169373f3 df4641bb
+          917e6f03 adc1e38e 3ebbbb1c 393d8a15
+          0329eb85 8d7b32fb b59e3937 9895b043
+          8624832a 3810f256 6680bdc5 046cfb1b
+          3264be63 9057ef0e a57bbfa7 e623bc1d
+          6acac9eb d9fac3c9 786d9f7e 97db4d03
+          adc5453e 17fbf739 6dabaa89 a57eea28
+          13d18405 40e24bb3 a168eb2e 283b7416
+          2412b687 e844fce5 df230e3a a53f0639
+          cb3e401c 68a0e640 ac85aadb a37fea7a
+          fc878152 5f5f6a0e a46ff145 ab2e4c9f
+          bed65a4e 0f9e8bb0 a5d00965 24f53fbb
+          4a4c32a5 6b8b94e5 23c3a1d1 cf795079
+          e87b52b6 926915d4 bb82987e 4c97f53d
+          0b9f7a7c 19cb43a4 06b0ec8d f9b3ebf7
+          1d1acfb4 a780b42f 6ac20804 a202f45b
+          76b91cc7 2db64b93 d0686716 388c2688
+          797434d3 ae20ced9 6bb7ed7c a272f93b
+          53da14c0 c6acbd7d 0c0bdf78 9b65431b
+          7bde807e 09e03fa0 2fe4addf 02f6562b
+          35ef5d33 34720824 34f24eea 02c18393
+          e97705af ee4d97bc f4d2aa96 13c7bbb6
+          09803643 a9977e46 ea461044 0575a08a
+          344f15ec 05d193c7 a1786d1f 34e61a98
+          aa24d714 023dd056 431d146e dd099113
+          1e046d84 3f75d507 6f560916 bbae2075
+          ca26475d 8ddabd00 2247a39f 396db5b5
+          b4aa134f 1ba80a78 bb922399 478bbe08
+          0cfb8e31 95d66f18 4e204aa9 3c760eea
+          cf654327 9413932a 38031f9a af14752f
+          9cf5f872 b70258f6 d6c2271b f61e7c84
+          85f770a6 11317a38 a9c4e47f c2d60d40
+          53b4d06f f91a719a 0222c7dd c7547b24
+          7cb8fdab 1915cbfe 35cd2d00 361fdc9f
+          6c787dc1 5296780f 07cbbedd 6321e8ee
+          c150b061 2b58ebe8 bb01a8cc 10518bdd
+          68457cf8 1904dc91 027e7d3a d3577d38
+          172594be fccafb2d c78f24de 1280f68a
+          725d415a eaa728da 57317503 f86a2136
+          f561a8dc 7f04851b f96c7bc4 0c5e1973
+          ac21f35b 8899321e 54811ed4 551f2cab
+          6075680b a64fdde8 a8add130 01885f63
+          d0cf4c5d 850f71a0 e53d1246 88b8b43e
+          1eacb575 a402e34e a771433e 440fcab0
+          f718188b 0de8014e 245a455b f5217c98
+          57dc4d9f 9eb68209 c0f2258b 1eafdb9d
+          3585b581 31f4de3b 40171b49 c20b1c66
+          70edf962 edafbb82 9f6c0775 4810848f
+          1ccadebb f845e6f4 8aa56f4d a702b0f9
+          e8a16e86 050bdf93 30f29e57 7c18848e
+          be0ff49b 7680a9ac 81a9c0e9 0e8762a9
+          6921d59e 90fb8781 4f422430 b5efa1b5
+          97bcfaea 0a8449d2 4d0168af ae42b63f
+          6da36873 a8a9137c dc1ba353 90cdf4da
+          1367a0fa 44365317 14295559 5d1b3ce4
+          c3f83e2f a982ff98 0bd528d5 8b45a99e
+          dc4b05b4 9db81803 d1e6d420 4c36616c
+          6e0860f1 bc27df36 179424b2 94a870eb
+          6bcce431 e4a595c2 6dbb993c 2e8edd70
+          f8a18c8d c8f618d2 ffa8e7f0 3b8faabb
+          c4fe24da 5108cfb0 0580f9bb e8cb7de0
+          6835a2b5 3d844c84 fe61602c 2c0893c2
+          5933965d 17c0baad 9b46d47c f6653a53
+          0f092951 f506ef1e 89245573 98e94beb
+          24f0e524 e6e80f3f 1897782e a77b9783
+          df0fea9c 756450e2 d98bc971 db770ce1
+          75da6a96 5d41c1e6 842b28b4 f14ae80c
+          21c3fab2 3580623e fc72f78c ea8f3f7c
+          e8bf0268 afa9d615 3fffdc0a 961e126c
+          62baa840 d26c5efc f9d7a4f9 9c69431b
+          695ef8a2 85b303d2 667ef1c7 760a4e2e
+          079f3163 8f46affd 600ad915 a4d5209c
+          ea15d790 0eadf071 0f908678 eaad55ce
+          d57d56fa d28bcb6d a5253eff 0160d9c2
+          7f3c6b35 54c7b094 a8244a57 890af704
+          961f38cd 94aae180 d7e7fee1 db829f7d
+          71fdb5be e33be1d1 acc01953 de713268
+          105e132e 9f35a054 2f6efaa3 881fe91b
+          e27101d6 56dd186a f8e7abaf fea9a06a
+          c9bd1c7c 21b95b8e 68b779d1 9a1d163c
+          36753478 23f338bf f03d9409 98e91b79
+          90e64903 fd8a124f 9ded250f 0bafbbee
+          fd9a1a95 d97d7a9c b0e415f5 a0e569b2
+          71ae5642 77dc009a 930b7919 3b99baf4
+          45913725 9c389da0 49ee5548 34b07add
+          9a348791 0dbc80fe 89e03fb0 2f499f6c
+          4df4e091 05394188 5ab926ed 46e0112e
+          f2f4b244 af5d379d 937216da e0f8b786
+          78b456bf 017d20a0 5f227daa 47385550
+          23ccd2c9 3f059351 5a9fb9eb 619eb687
+          0491396e 16c225aa f2cc2c68 b858c494
+          aa9106c6 d93317fb 8c1977f0 66aff118
+          32ec5cc8 f3cfbdc2 1a1c37e4 1443195a
+          335e3b69 78a27dc9 08295ae3 be6fc663
+          6be04d17 cedd6e2d 28e94c1d ec22228f
+          1c3f122c 95d550ba e73093d3 c0319eae
+          47d713e1 8bdf5d40 7b6de8eb 8b9679a4
+          f4cc62dd e037a035 9b2baa20 62ec08ea
+          430f3017 5a4baaa2 8ce77eea ca9bce9f
+          eb8ee237 9ec6f362 edd34606 81676267
+          28dd9d85 fe4ddf05 85f988d7 289aa2d7
+          65a4f21a 0d7584c7 c91522ba 76a644a7
+          ac63d9e0 c76b2efd fa5b12da 68c2fce9
+          0a0e9cab 01de74e1 7c37de7c 39a73375
+          b0ebc065 aa0444e8 2dd09457 082cda4b
+          1a79de78 639e26b9 f715601c ea84a4e2
+          88c56fcd 61da0741 5a88d76e 6f6c029f
+          a42e4ce7 df98732f c5f396e2 a208eaa7
+          8fc81817 0a5a8b4a c06174d0 bf7381df
+          1a7af0de cd41739f db70ab39 6fe05373
+          b7a0b936 b2e4b94e b300adf8 c5f19828
+          a6fd199b a1348c77 d6d7539d 2649ce0a
+          50f0a0f0 f1066379 15357fe0 272d0ff4
+          a9885af9 e15c7715 0ed05ccf e039a9b5
+          089b6179 25287dbd 5d5644a1 c5b80fdc
+          5157e7c7 3b1b1b3c 38ca7e12 fc62213e
+          b1c386d4 9fe54d71 9f87c66c 918786d5
+          b90b403c 179a7333 75bb1c5a bbada919
+          05d50a22 136d8f81 d0d2ace5 81e755b4
+          4f8d974a 91d9f2e0 3499e9f7 37d0f73d
+          870c3b08 6e1e644e 86b5e013 3b388984
+          c844ffd6 3dafc2ec 455f33c1 47a022b5
+          75daecd4 26837017 e5e11125 ee0610cd
+          598ae616 684120ef f32112e7 d8fa4ba4
+          3c307427 fc7a3c10 6ef3a0be 56c2d938
+          99cce86e 00f19c68 6eea7088 9c587455
+          2158eab6 b7586817 e90eabc1 6b552b44
+          a9af9fe8 6e00f19c 786e9af4 ceb576f1
+          576b661a 1d4780de e2e800b0 03c00e00
+          ff8600e2 53d751e0 c9f1dc5f 1e002c03
+          9685f55c 5729fd0d 813433e6 aef9048c
+          8672a633 a7fedf80 87d6de5a 524e64c1
+          32b16cfe d38b4fce 0f7440fd 857cd76f
+          84f07f65 ed03d2e0 8e65214d 045c7b00
+          7815c4f6 ec75696b 106f4596 0e27d2e1
+          853b00ec 00f0ef0d 20eb79ee ccf507d1
+          f50abbfb e715416c 5f513076 3c275734
+          b4df0d71 ec256de5 958a26b7 6b029a13
+          cd6d8476 c41063c7 6b7aa71c 6e2f1d24
+          4792c477 3e2b0b08 6c74f7dc 784e34f7
+          8fee7a0b fe86b220 cc30769c 459f1f74
+          71f0c0c3 9692cadb dbfad700 250a6973
+          7c66e6dd 9ec3ef3d dd164235 eddfd7f7
+          f2a85159 edf2cb86 e141b95d 0f1f1fcc
+          fde1b735 9f32fe78 e60ec16a f2e7dce8
+          5cc8f9ae 125993aa 6bd2d9a0 f4f4b5da
+          7e037e69 4bcd6839 793ca16a d5ea27cc
+          972ff612 9d764f37 ffb6a6c0 2bd435da
+          947ec782 e7cc5ba5 888ea9fa 37e89256
+          f9b676e1 3a000000 0049454e 44ae4260
+          82   
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="Y" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000004f 0000005c 08060000 002f76e8
+          b300000d b6494441 5478daed 5d077454
+          551afedf 9b3735c3 24939984 f4322190
+          4655ba84 26602112 b1040224 a129206d
+          5dd61574 0f1c04d1 55505682 0882b425
+          60a4c82a d5234da4 48911648 83082195
+          d449a6cf 7b7bef4b 821e3786 bc9b9940
+          dcb9e7e4 cc3939e1 def77ff7 ffbffffb
+          ff7bdf40 711c07a6 ec4cbfc2 15cb67d6
+          5e38ff04 6b366a29 0a1c3a38 a0f5f288
+          a8f3eda7 4fff4c15 3bf81238 6bb02c14
+          af5b33a6 7ce7ce97 ad25853a 6487c461
+          36700014 23b9a7ec d9eb079f 39735721
+          7b0a28c3 f50cbf8c 21038f9b 0b4bc368
+          fc579473 ecc28bd3 625adf69 cf9e111e
+          cfc49d72 c61ab7a6 4f5e52b0 66c35bd8
+          0eca1976 201b58f4 21f5d1e4 461d3931
+          5034d966 78abf2e8 c938468e 16146374
+          9df343a3 b9593327 b5fc9215 e89d3265
+          aba3ad33 5cba187a ebd5693b d03a342d
+          71921de2 3a3bac95 464fca6e b4d23567
+          cfc6d214 b4cac00b 9bb232bb 5bef95ba
+          3b7aee9a b3a77bb3 76602851 2bd881f0
+          aa3d772e 9606d6ee 01ad35d0 a29ccdee
+          c699cd2a 874794cd a681561c 9cd5e241
+          3b871c9a 0290720e 2151ad6c 085a8e06
+          d7200f5f 17042ef0 5ce0fd7f 8087c4a2
+          dd843ed8 366e7d83 1df6d602 0f570a52
+          31f80c7c 0ca46a55 db051097 5b6211f8
+          c4f60099 b79a0840 c1e061b0 18851c42
+          134783dc c71be9ab 368a1d76 02891842
+          c7c48332 c81f585b ab852d07 9cc98c80
+          64db7cd8 b2d80e3b db4a61eb 1a2ef05c
+          e0b9c073 81e702cf 355ce0b9 c07381e7
+          02cf351e 1df070b3 dc191d73 9afe9383
+          57770004 9cd9e4f0 a9edd555 7f6ef0b0
+          c3b106b3 c8565e2e 76f4dcb6 d252719b
+          000fb773 704b82e8 c08a0309 67b3b573
+          b421684e adc0e7a8 7b7eaab5 3d0fb7a2
+          108214c3 d403d9fc b0656d40 19b33343
+          1d0d9e31 f37a0712 9ec40092 b6d6e8ba
+          3d10b8cb 76c45b2c 072229d9 3d1afd89
+          63031dcc 7752c3a5 9f7bd18c 40e34522
+          1e40d646 d487e730 78c260c7 de63b501
+          6bb701a3 74130c3d be7251b1 77efcb96
+          82bb0ebb a950b663 db73e6fc 7b219458
+          18f5e04e 3285c133 9b490400 8bc0e32c
+          4249df6e b181add6 08722f8d 60f0f085
+          194b7185 df9d05f3 163a0238 4b61812a
+          7ff1a265 42bd0ebb 8c58d58e 37c85a53
+          2b9cc038 ce4223df 3508f63c 04b7b1a8
+          04944101 3c184287 480650ba 79fbacca
+          6ff7f66b 297877e6 cf5b6cca 2f09a3c4
+          42a907c0 cdcf876f c35baaaa 91070a74
+          02b1b896 66b45e55 1c27fca1 ab6e6483
+          22c81fa4 1aa5f043 205e2883 28eff5d9
+          a9767db5 9414380c 7ec9a6b4 998c8c8c
+          edd59d23 409f7707 6c7aab20 f0305e8c
+          9757052d d66a8b04 af8b76b9 fcca0d3e
+          4b79f5ec 0e762bc1 b323c88c 59bf74cb
+          5fb8e00d a2245155 25cd7b7d 4e2a325a
+          24546ee0 c8510669 a15da770 28f9f12c
+          10f80e88 bdbc0b69 6948e82d c1621725
+          2973592d 141e3e06 fe4f0d41 dca74449
+          84004094 ac8b523f 9daffff1 8768a1ff
+          36ff9d7f bc61c8ca eb460bf4 5bfe9c19
+          79596842 3cd4dccc 83f2cb59 2022100d
+          d2505d2e 2d8fee7c 9548e3a0 05f30f1c
+          07634929 8425bdf8 1bf12c6c 13380b2b
+          cf9b356d 356b3235 db7ff43f 1c8b295a
+          b96a0143 10f07633 40e0c8c1 e0161c08
+          b99bbfe2 25178950 96c774bd 42bb75eb
+          71512417 99040a16 9e2358ab 1d72bed8
+          01aaf030 f01fde9f bfba2078 13e4088c
+          0bd7620b 3f5c36bd 59216734 d2797367
+          aee2ec9c 4c6886c4 cfe7d939 14fc470e
+          47c07d09 86820a9e 8284261a 4625d12b
+          1fef7991 964744de 76ebdef5 346b21f3
+          beda3b65 702b6d17 048e7e16 dcc3fdf8
+          9d159c7d 910177df 7f7fa9e1 eae5e007
+          fd6de18a f7a7e9cf 5f1d480b 4c12f846
+          80d4d30d c2272742 c9919350 7afa1a88
+          e404c515 e6cb9e3d 8f21ba2b a6b1c2d6
+          24267dc6 92b066bd ec283a7e 11cacffd
+          0ce193c6 02a3100b bef781e5 8ebdc6ec
+          f1cb9c19 2b9b8a7d 0ceedd65 cb960ae6
+          28aeeea7 43f24b60 a9ac82bc aff6034d
+          9ce301bc 26bdbae6 7e6deb35 2e7997a2
+          43e01512 efc37c81 5dffe6b6 aff95a57
+          97104794 3cf02654 7e7f7254 51eacaf1
+          7fa40ff2 66cff8c4 5e6bf110 7a69db86
+          c235e0e9 58442f3a c8fe3c8d a71b8aa0
+          aa67d13c 6e5d3a9e f67cfe85 fdf7c113
+          79785802 16bd339f f718020f c4c6586b
+          cc90b321 0d347d7b 8277dfce 6037126c
+          02f2a8db 6ffe7d75 e9968da3 38ebaf3b
+          602d2951 e64e49fe a8eac8c9 38a1a186
+          794e1d15 0c01f14f 43eed674 a8c92fe3
+          d721e806 f14111b8 f8bdf9b4 5cc16708
+          8a6b0813 f47963e4 f0ed15fb be4b20e1
+          027e8711 60c1a306 81df88c1 70e99d8f
+          c0545a29 9c906d75 a4ac88e9 744616d6
+          a1883518 18c3f58c 48f3ed62 9dd0e7c2
+          73314a39 747d7b2e 545cc980 9c8d5f03
+          a96dd819 342fc67d d1317def a4fbfbcd
+          fd86634c b939edaf f6ea71c1 5eadf7a3
+          085a8bbc 86423fd1 af4f45de 48c3d50f
+          d7e21584 8708bebd 64ae7f65 097e7d81
+          44f01cc8 7923674e 00a9d613 2e2f4bc5
+          3d3f2079 4f0357ff 8c97e7ed ce3f5dec
+          2109082a 6bb49f87 76ba3864 e5bfa6a0
+          ccc41185 2f5d6770 cea62f41 e1efcbeb
+          2992eccb 8730e240 ec25b49c 00b87a9e
+          f31fd11f dca33a42 f6ba6d68 33c880c3
+          cec0da81 0b59f9c9 2bbf05ae d166a876
+          7cca7e9f 1953deb5 111e33e0 30351655
+          41ee9674 f07b7618 a8634288 f45f8bfa
+          7b68c33c 2202917c 1a0937ff bd13f4b7
+          4bc9780e 6f029a0b e1b14cf3 72e2c1ff
+          d963ae11 69c01a0d a28c41fd 0fe9cffe
+          3ca4251c d1216514 2abe2311 ffade45b
+          58241d18 c1218679 4e21832e 6fcf81ea
+          1bd990b5 61179fc9 496d68d7 abdbb1a8
+          a3279f44 49c2d658 27b911d5 afb0876d
+          dc9acc78 b62be2ac 841e8876 3a2ffd5b
+          e47566d0 8d8bc7ae 4f94c905 f31c5a27
+          6cc26824 662d7073 fb7f0427 acfb5359
+          7125a128 d5addf94 d218707f 081e5fbb
+          4546e787 ae4a9d44 cc7f229c 7d6d908d
+          e48bba5b 0cf80e7a 1c6c4e0e 5f9ee786
+          f5018f2e 5190f5f9 36e43956 329ee3ea
+          2a92908f 3f7e4511 d325af89 aed61f0f
+          edd809fb 7d674d5b 4aca5922 a4e2ab73
+          8be0f657 df404842 3c2883bd 80488837
+          93e77079 18f4d273 9097b61b f4b78a89
+          ab086c6f fb575256 784d9cba a74907e1
+          1ed00ac1 85f8f5a1 030e559f 3a3f9488
+          ff1a24c3 ac249078 b8c395f7 56a38c4c
+          a6f09b2a d6455229 e2b9d97c 9b29736d
+          3abf7124 dd1216f1 9ca24ba7 53d127ce
+          0e12a954 9607f453 1fd4f590 b3615f6c
+          4d116bdd 0b89f88f aa5b2577 cb4e90a8
+          dd2178f4 0832f9f2 80100b1b 1fcf0bcd
+          5c5426f2 3c4772a4 8ce611b5 935720be
+          4f7a1070 cd028fd7 7f9d22f2 91fe23e6
+          3f7c3863 2eaf450a 7f3bb41f 1a0bda1e
+          1d85976f 4df09cdf 905ea0ee d105b290
+          9eb3d55a c8790e39 47d0071f 4c77ebfe
+          784eb3ec 6aeedcda c4a4033e d3272f21
+          257d2c17 ca2ee640 d1c1ef91 84190332
+          6f155f45 b40838b4 019e31a1 103ce679
+          9ee7aa73 0bebc295 7013bcc6 bd94dafe
+          d5d77634 3ba83801 ed5f5467 d21983fb
+          1f40fa6f 1809ff35 946f11af 258154a3
+          866bcbd7 82a5ca28 5c877175 c6aaa343
+          2062f664 2839760a 6ea6ed6b 11cfc9a3
+          c3cf459f 38338051 ab4d4e01 0f0f63c6
+          55ffab03 fafdc456 eb7d89ea 5f1bae42
+          c43c8012 ad1a954e 69509d73 176851fd
+          1744504d 773570b6 c661e93b a837048f
+          8d87d213 672077eb 1efe7724 4988ef24
+          8998eae8 23c7fb28 7bf7bd2e 88ce3982
+          73c77bdb 368fc81e 97bc4f84 1be184c4
+          4c3122d0 25c683b6 7777283d 751e8a4f
+          9c056361 119209f6 46fba118 1cb1520e
+          aa8e61e0 376c00b8 e982207f f741c8df
+          77b4ae71 4093f11c 9625a11f 7d90ec33
+          77de66c1 b990043c 3cf2664d 5b54b0ea
+          b3850c61 f9864318 13b4a647 24043c33
+          1414bede 60add2a3 c452810c 32f10733
+          f81a042d 91f0408b 954a9079 6950f697
+          4255e64d c8ffe630 5467e713 876a43f9
+          a54d78fe f3f0edbb a692fc7b 62f0b0fe
+          cb18f2c4 7efde90b c349ebdf 06718bb3
+          b15b901f a8c28241 d65e8b00 91018340
+          72479582 1ed5a796 0a3d4a0e 06301414
+          833e370f 7d96dd2f 014907ee 0acbc202
+          2fc79cbe d08fd168 6b5b153c 9eff32af
+          fb5debdf e7277b55 3551ffef f75aada1
+          938d9f48 a65540f7 85f32063 e55aa8cc
+          2982866f 1be2bfe0 a6850d06 7e1d4a54
+          1b75e870 bf76b183 2f93ced3 229d2fef
+          1459109a fae944c4 612cb4e4 edd1fa73
+          10517d0f 0f9fa635 5c5fc361 8b6f82f1
+          bf97b51c 38deeb50 d2095ab2 e42f2d01
+          aec5e0e1 a149483c e43b77c6 629b23ab
+          0667f6fa b0367cee a92dbef3 de5cd7d2
+          b91c5261 062e5bbe d8bd7fcf 838eaa1a
+          9c35b028 9785f85f d7ad593f d311f339
+          043c5a26 e350fd3b 51ece571 97b33c9a
+          c0619ea3 18da14b6 7e6392d8 d7affa91
+          018faf7f c33b16ea 56af9988 1eb265fc
+          e72caf43 9beafff6 5b7f530d 79f29ca3
+          e674e8ab 049e2f26 1cf6fdeb ec45f647
+          8cff309d a89f1ef2 a5ff8285 ab1c39af
+          c3dfc308 7af79f4b 54fd1e3b f0a8f01f
+          e6396990 4f8e6edd a66994c8 b1df0fe7
+          70f02889 94d3addf 3249ac51 e5939e7f
+          388ce7ea e8c3a2fb 746db2c4 3fa0c2d1
+          f33be50d 20794464 6168ea6a acffec0f
+          93ff307d f82f7863 81c73371 3f3a637e
+          a7bd3ea5 4918f79d ef9c198b 1e96fee3
+          796ed8c0 af03162e 5deeac35 9cfaee59
+          e07bcb97 7ac4f6d9 c7b63280 b86b230d
+          443cb776 c3547c73 ab4d8287 f59f7642
+          caeed6fe be29dcad d18e1dbb 5b1aa22b
+          75aa7dce 276d56f2 30c29662 9cff12a4
+          f35f19e5 38782883 63a9b60f de9f78b8
+          c07381e7 02cf059e 0bbc4761 50145032
+          29ff32b1 0b3c21b8 a1a7b2d5 d4c2ad6d
+          bbc05872 af556e94 920ce651 dd52bbc9
+          024547cf f3c78b14 ed024fa0 fb01f15d
+          6217e7b9 12860b3c d77081d7 16c1e31e
+          525b8565 b9360f1e c530e50f 2559cbe4
+          f7da3c78 cade7dce d10cd849 ffdb04e1
+          9e5ebf6e df7ec7db 3c788ace 5d73bda7
+          4e5e61b3 d4ddfdc5 f7e29cf6 83e6b7a2
+          4fcde867 37ba0f1d 7ec6e9de dd1a9484
+          bff5ac64 dd9af165 e9e92fd8 ee9584a0
+          8a41eaf0 3538b032 6aed1d75 5cdc9ef6
+          33666ea0 6572a79f 9cfc17eb c5920eb3
+          0cb34b00 00000049 454e44ae 426082
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="256"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          000000cd 000000cd 08060000 001bbcf4
+          69000005 c5494441 5478daed da4d6c14
+          e71dc0e1 77766777 4dfd9108 303484f4
+          900a5b88 4252f502 9444e294 e6d2268a
+          c4a1892a 55b98473 1c12a997 a455a434
+          ee014e3d 57552eb9 54e9a1c4 9736288a
+          7b8a12be 64d92848 016452d9 8e8cb11d
+          efe7e41d 2f348622 1c2c5169 d9e71123
+          a3dd592c bd9a9fe6 ffce9264 5916d6aa
+          5dbe5499 1ffbf0e7 cbe7ce1e 58999c18
+          0e853018 5f2e05e8 0ef5d00a 33a5ed8f
+          4cf51f7a ea93879f f9c578f9 b11f55d7
+          9e90dc8c 26ab55c3 f4bbeffc e6ea89e3
+          c76ab3f3 7b92fc4d 0b4897ca 6e1ce5c1
+          87cf6ffb edcba33b dffac35f 0a3d9bbe
+          8ba6b5b8 182ebc74 6474ee83 9323c534
+          bee8be02 ed78ea21 341a216c fde5b3a3
+          bbde7bff 58a1af2f 0e5fd195 b7dffcf5
+          6c0c26dd 2418b865 148b3d94 6217b37f
+          3ff95aec e4c5d5d7 aa572ef7 9c7962cf
+          e9e6c2c2 5092b64f 6cc5bab2 86f98cee
+          9ecff21e 0a376e22 790fc581 81c97da7
+          cf3f995e 1b3bb9bf 3eb73054 dcf45d30
+          db9ffa59 e81f7a3c 845ac3e2 d19dca69
+          b83e7531 fce7e34f 57c3c903 8a9d0c5f
+          1bfbc7fe 74e1d43f 0fdd1258 33848776
+          fd386c7e fa4008d5 6accab68 01e92ecd
+          1841a512 0a5912be fae8d35b 9e1d2f9d
+          feec505a fbeaeaae dba7b056 3dde6e56
+          5642b6fc 4da82f5c 0fb73d95 8607770f
+          1363280d f4e74fc8 da1dac7d 2f1edf4c
+          4e0ca549 9a6ebde3 a7d334d4 6230e7ff
+          f4e7d08c 635a52b0 a03ce0db 98561cac
+          e258b667 e468a8c4 70ee1855 31dd9a6f
+          fd2b77db 0c356bb5 78b44443 574413e7
+          acf61734 77ddedac 77bb2a14 e2211aba
+          643c2bac 7fa1a71b 2ab219d6 ab113aa0
+          907cdcba f78fdd7b 344992a5 03bd4bf9
+          4fab4e67 cf6359d2 5c5eeacd 7fdeb768
+          f22f78d2 877a9776 ff6bfc60 697070c6
+          aad3c9ea 33338313 870f8e37 ae2df625
+          e97d8ae6 e69da6bc 63c774ba 79cb9c65
+          a7a3a7b3 52a9be91 896943db fbac5ef7
+          3fd4e8fc e96c83d7 b1676220 1a100d88
+          064403a2 014403a2 01d18068 40348068
+          4034201a 100d8806 44038806 4403a201
+          d1806800 d1806840 34201a10 0d880610
+          0d880644 03a201d1 00a201d1 80684034
+          201a100d 201a100d 88064403 a2014403
+          a201d180 68403420 1a403420 1a100d88
+          06440388 064403a2 01d18068 40348068
+          4034201a 100d8806 100d8806 4403a201
+          d1806800 d1806840 34201a10 0d201a10
+          0d880644 03a201d1 00a201d1 80684034
+          201a4034 201a100d 88064403 a2014403
+          a201d180 68403480 68403420 1a100d88
+          06440388 064403a2 01d18068 00d18068
+          4034201a 100d8806 100d8806 4403a201
+          d100a201 d1806840 34201a10 0d201a10
+          0d880644 03a20144 03a201d1 80684034
+          201a4034 201a100d 88064403 88064403
+          a201d180 68403480 68403420 1a100d88
+          06100d88 064403a2 01d18068 00d18068
+          4034201a 100d201a 100d8806 4403a201
+          d100a201 d18068a0 6ba2494a a5baa5a3
+          d36df43a 4eeff913 5996d4a6 a77764f5
+          7ac9b2d3 c9ea3333 83f9f57c 5fa349e2
+          d9cde5a5 de89c307 c7439264 969d8e16
+          83c9afe7 24bd8fd1 dcfc458d 85c5be20
+          193a7e3e 8b7f8aff 8ff12c6c ec17c183
+          62dd68b2 562b1e16 8a2e98d6 5aedebfd
+          fb44b372 b7db57b1 5c8e7f69 84c4c369
+          ba209a62 395dbdee efa29a66 cdc6d777
+          7cabd108 e581fef0 9391a371 1b6341e9
+          926d4e0c a614affb fcfabf63 58cdc66c
+          ba6978f7 e4fcd8a9 5bde2894 4a21f4f4
+          c47f2059 0d07ba4a b31942a5 d2ee606d
+          30f188bd 4ca5bd4f fcf493db 37f9d72e
+          7c115af9 13e55ac3 02d29de2 9876fdc2
+          c5ff79e8 95f79254 af5cee39 b36ff7e7
+          cdeb8bc3 379f57b7 eab1aa46 586fb683
+          077883d3 fe5eb270 e36693f7 50ecef9b
+          dc7766e2 c9b4fce8 ce95475e 1df9fd97
+          bf7bf3bd b4d80e65 f544dff7 c37f036a
+          c61bc9ce 5747deca 7b49b2b8 cbcf6ad5
+          3075e4f9 3fce7d70 f258317f 78201868
+          f7526b6f 71b6fcea d97787de ffdbeb49
+          b91256a3 597db35a 0dd3a3ef bc74f5c4
+          f163b5d9 f9bd89e9 8ceebeb9 b4b7368f
+          6e3ffbc3 578e8eee 78ed8dbf 26954a7b
+          df9fddf6 3cb976f9 52797eec c303cbe7
+          ce1e5c99 9c180a85 b02dffac 86e89256
+          aaa11566 7a86774f fd60efde 7f6f7eee
+          85f174cb 96dada93 be0565e8 7e36c905
+          66d60000 00004945 4e44ae42 6082
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000009e 000000b8 08060000 00127055
+          50000029 6c494441 5478daed 5d079894
+          45d2aef9 26efec6c ce39b084 254a1049
+          8ae88a07 92410ffd 55104441 451445d1
+          33073087 f30c8044 0f044150 90a8a2e4
+          bc80bbc0 c2e69cf3 e4f477f5 b770a8e7
+          89b05bdf ce32e533 8feecac3 7477bd5d
+          f5767575 95cce572 c1c562c9 cfd3d46e
+          db36c094 76b29f29 e34c0799 1c82d8af
+          95d0bac5 c1fea911 f4fa6cfd c0eb0ffa
+          a5dcb25b 9bdcb91a da80388d 06a8ddbe
+          ad9be158 ea4063ea a16e2e87 231264a0
+          6de5c3b6 b91c50a1 0c0b3bab 1f70fd3e
+          bfa143f7 aa63622d 17ff01d9 79e0b92c
+          66287a63 debda51f 7e38c75a 559b2cc3
+          ffe9664a 72357d14 5e8ac280 b1e33f8d
+          796dfe9b aa98589b bb82ae7a c3bade05
+          cf3ef3aa f1d4d99b d9bce4ee a693f3fa
+          5005f99d 0a993cf9 ada8975e 5d2a68bd
+          fe033c67 63039cbb f3f677aa 366e7d5c
+          ae60bf54 bab79560 bb0d1c56 005de7f6
+          9b3a6eda 7abb3a2e dee46e73 287e7bde
+          88bc279f 59c580a6 13b46eae 0f3b809d
+          6dffa0db 86be9bb4 eaabd982 b75e045e
+          fe538fdf 5df0e67b cb956e3e c1df8a9d
+          c12d60e8 e0773b6e f97136c8 dcc756d4
+          fffc636c fae09b52 0515f833 aad366c4
+          c6f4113d e7b17b62 de787785 cc5290af
+          3dd9bdf3 09474343 924cd1b6 808776de
+          690573f2 0f3f74f6 193c24db 3dc6ec82
+          8c11b77c 58fdddf7 8fc8db98 2140cb27
+          d7ebcf75 3b91de5d a8dbb6f9 3a5b751b
+          041d8884 c8e9044d d5dad5a3 dc65c8ec
+          70a7abdf b56b34b3 766d4f1d 0c6388b5
+          ba6d5bfa 09753fef 1c086d58 04063e53
+          fa2f835c 0ebb5b8c b7f1c0be 8ef6066b
+          745b72b1 bf15c389 6303045b 596992ac
+          0d034f26 302b929b 13e7b258 04b7e041
+          e5a5096d d91020d6 4c1967da 0b328522
+          10dab8c8 e4727f76 b850b9c7 58af027d
+          30cca115 5043db17 0c10096e 3256d555
+          a00f9500 1ef18814 dcdbb304 1ef100cf
+          231ee079 c4231ee0 79c4033c 8f78c403
+          3c8f7880 e7118f78 80e7110f f03ce211
+          0ff03c72 1503ef7c 22be475a 8748a40f
+          817a922e a7f8f148 2bc1dd79 7d10838f
+          2cefd869 0108eadb 05c287de 082e9319
+          3257ac01 73651d08 728ff2a5 10a70340
+          13e40bed eebe1d64 5a0d946c db099507
+          d34050b7 31e0e1ae 52f9ea41 97140f60
+          3082a054 7a5caec4 2e1675e0 9d180ba0
+          f302d581 23a49e88 d4d5ba9c 6c66561b
+          b8d8075c 1ed4490f 3e97a80b d4899396
+          ff5c1da7 5a7cda28 b8c75465 f2ab837b
+          b479e0f1 974d6515 326b4eb6 5bccd570
+          3c55f000 af054c3b 07033742 321a8e87
+          4f1c0d16 b9a3bede 2d1e70da 2a2b68c6
+          e9127520 fb8d6e28 8147f78d 4ed7afdd
+          1fdd2c55 ccd56adc c4d5eae9 d645f89d
+          51a03241 f8cd0e9a 15c5b9b9 c409e24e
+          a3e5325e a40abd32 e005127e 976800f0
+          90814681 ce1638e8 808730b7 3b44fb8a
+          c0a322fb 325ec447 6d3a7d2a a8d51f32
+          6d363067 9e8b9251 2c0de24c 21175fbc
+          bb9a7403 b4c0b392 795abbfd 3f160f27
+          4d64ddf1 2bada525 89ad1d78 0e43a3cc
+          5a541427 23da9372 8ca5e297 61c5303b
+          69a5052b 4ed14c65 799c56ab 880266ed
+          307849c9 2a0cc78f f568edc0 b3646785
+          d9ca2ae3 29ead870 35a818f0 e4028fee
+          3b99b525 74b56632 e0219570 60b01203
+          950c7872 8d9acce2 e1b55ce3 bebd831c
+          8d0dad1a 788d07f6 f574dac1 8704002e
+          2c60a915 0f188cdf 392c16ca f39e4970
+          391c34da 40e0994c e22521db 650aad96
+          0c785868 d29c55d8 a371cfae 8ead1978
+          d5ebd68c a13cec2b bc754d16 c1c17463
+          26b3782e bbbd5650 8684d451 01cfce26
+          e744abc7 26abf4f1 263c2ab2 c9ca4055
+          f2c1bb0f b456d035 ecda195e bf6bff38
+          caf2646a 5f1fae0b d409ea86 02f4686b
+          b4ed3bd4 08baaedd ab48e2b8 cca2db0d
+          4670184d 7cb2ea00 7f52c5ca 9967afdd
+          fee3fd75 dbb7746a 7da70a07 14bef6f2
+          8b4ebbd3 8f2aa48f 65d0d441 62e4c661
+          3673dd50 7db73a36 ae5a705a 2c15541c
+          cf6eb480 b55634b0 dad02076 c000baf0
+          b5b89b75 b98f3ef2 99bda2bc 55dd6214
+          cd7b7964 edf69fa6 915500c5 4ade1a39
+          688245e0 596a6ab9 6ea8dcbc cb6a2d13
+          34098925 3222c53b d9a1d658 52ce7fd4
+          04073177 eb459b8a c3ac9ef1 4cd6a0dc
+          47a7cf6d 45078a88 a279f33f 15080b9e
+          b3030cf3 387ea0f6 f713993e d309eaa6
+          c5399e18 4903afee d7140baa d8f832b4
+          b65496a7 21379f7d 9b13148c 5fe8a2c2
+          f92290ba 5c0d40c5 aaafff51 b5fadffd
+          24f7b00d f59035f9 eecf9c26 6b386529
+          60ac8aef 1d170d32 9d964719 1a72f209
+          bf1c2c72 3fff3241 1914542a f7d1d602
+          81e511d8 e23664e7 82b3b111 40a900bf
+          ce1de993 41657c1c aa9c9933 179933ce
+          784b09bc fc271e9d 653c937d 1bf92d32
+          e3727ec9 edc58345 a381e924 8feb8602
+          f0ca207d 25e37865 822a2aa6 5a19e89b
+          4fe1f270 579b4a6b a1914d14 25a04b47
+          50f9aaf9 8048b1a7 c292afd5 c93933a7
+          bf215542 6ad59a95 3d4b172c 7d5d4e0c
+          3aacbcae 0df101df 0eedf82e 6c64d6ce
+          545e0324 1697614c 111c98ad 080c6c14
+          04add6a5 6ed7e10c 95cb4390 551c4ee5
+          265e1912 0441bdbb f36628b4 abcf5cae
+          164fb93f cd28f9f0 9d11d4a0 339fcbf0
+          ca9d3973 31b3325a ea563d0e 1b40709f
+          6b408ea1 14666d2a 0ea57230 92704b7c
+          e7d12139 1dbbfb08 18b9d675 bfe60899
+          9567d6a6 fac41930 e717f118 4bf89041
+          8ce46ac9 26ffdbb1 e43ffdf4 278d870e
+          84d281de 05398f3c f886b5ac aa3b7555
+          66342eda 606f0819 d8976f3e d441f589
+          d340193b f4ee73ed 81266f8f a78c1efb
+          c9dc1cc6 f38c7628 dcbe936f 01555830
+          c48c4ce1 bb819aef 612ccb69 7144e63e
+          34ed23a7 c542f29d 85af3c3f ba66db4f
+          0ff3d009 e57c5da2 9b8d1e7e 13e35901
+          dcdaa10e 501764d9 3032b0e9 075cff1f
+          e0f9de94 728291be 3c2aab83 c1dccac3
+          e9507df8 381f4d30 db81a1fd bb81dd0c
+          e4821cab e1c82fe3 0be6ce9e dae227fa
+          037b238b e7cdfb44 2e41796d 075bdbe0
+          be9d21a8 ffb57ccd 6b8e1ce7 3a90133d
+          6774d930 701c9ea6 eb71cdb9 0bc05386
+          4718f583 066d7152 f5396c6a 4398bbf6
+          3bb01497 32edcb21 76dc08f0 8e09a2e7
+          7b20c6f7 8adfffd7 dbb55b37 b76f31c5
+          d7d743d6 a4ff5bc0 2c6c1875 f314d4ab
+          36dc0fe2 c6333aab 9083b5b4 1c72d66e
+          bea0072a 7ee77b73 ca06b9af 9ff302f0
+          5002c7dd be82d2f8 e3f1dd52 6d80ec95
+          ebc165b6 70b29b78 d758b603 15f4a75c
+          811b01df dc993316 daabab5a e47c97f7
+          e4a38f9b 32728751 874e305a 21136490
+          f0f751a0 08f4e74f 1973566f 004b5523
+          4908e582 9b15c01c 78c75d2b 2f8ae888
+          e23f72ec 7e4d4cc8 01176177 577473b5
+          e9b950b4 f97b3e3a 5dfb4488 1d95024e
+          093acca2 d5339dcb bb3ee7c1 294f37f7
+          df5db972 79afb205 4b5f934b f0eac3c1
+          a86be42d 03c1a76b 270e8092 ef7f86ea
+          e3594039 16ac22a1 efdf67b3 ef909b33
+          7f073cb9 5eef0aba 7bd2bbd4 2dbf50e1
+          45db7743 5dea2f7c 61426f1c c8b84832
+          3824e830 8bcaa8fc ea9be72a ffbdecba
+          660b9d9c 3da3cb9b fdf81266 5d34e4a1
+          13c6ebfc 93632072 d8cddca4 37669c83
+          82ef7692 95a9b8e8 200fc1f7 4e79e7e2
+          c745bf3a cf843dfc d80675a8 df514aab
+          27131360 21fbcb6f c1565ec1 3948dc84
+          91e015e9 2fde1f92 fa5cf156 236ff6ec
+          45e68cd3 decdb1e2 3933a6bd 692dadea
+          4add7c1a 0f8a4a1f 0dc4df31 1a641a0d
+          386aeb21 7bd506b6 a60e9011 3e6a7532
+          f07b758c df1474d7 3dfb7e65 702efe41
+          1916668f 7af195e7 a9ef4ff1 82dc5cd9
+          c0c0b781 73104580 3f24de39 0604959c
+          bcb21402 c45a56d5 3967c6fd f35d8e2b
+          239b85af 3c37b6f6 87dd33c8 fbceba44
+          321f37f6 56d0c444 f21ff2d7 7f0786fc
+          4ad2985d d389c11e fdfa9b2f 08da5f2f
+          c2efb01f 3c69ea66 5db70e9b 9c16dab5
+          4237577d 3c138ab7 fec82d85 77720788
+          b96d0850 8f838f05 6f357edc fb50d987
+          ef5ef6ad 46e3fe3d d145afcd ff588abe
+          b318960a edd74d0c 9d30a93a 7004caf6
+          1c076a8e 89aede67 c8c0a501 63c61ffb
+          9db1f9dd 2f98598e 79ebfd27 99a53152
+          077431a6 54b0e527 a8ffe5b4 e8fa6fbe
+          01827a77 9084efa1 152e78f1 c58f8d27
+          52c3fef2 82d7d6c8 b326ddbd c4657384
+          92874e18 3dd14506 40ccb8e1 8cb60860
+          292c86dc 755b78b0 9c926362 6442d008
+          15b16f7f f0dc7f5d dffff64b bf5b6e3d
+          1372efc4 d71cd401 5de47b0e 17e722b6
+          8a2a9ec1 1277c728 d086f991 9f74f1d2
+          dcd1608c ca1879db 0a6b41fe 25d37197
+          c502676f 1ff796f1 6cee4d92 844e18d8
+          78e8c4df 0f5c260b 5f4b6bad 09a83bb0
+          633c36ec a1879ed5 f5e8597a c9c04389
+          79f3fdb7 35b16187 a9093e5a 1a53691d
+          8f3581cd 06cac000 4898388a ed58819c
+          ef21702c f9c53767 8c1eb9ca 70fc58c0
+          9f2e7603 23f0d3a7 3d53bb63 e763e4bc
+          ae297412 75eb20d0 77eec0e9 0ad296da
+          5379e42e d6c93c94 77d70edf 47bf346f
+          d11f7ab7 175f7cf1 bfff0f9d cea18e8d
+          3b54f9e5 57f7b293 9e92d24c 23f80cf9
+          55a0d0ca 419f9400 ead06090 b1635a4d
+          5a2e08c4 a743bcc8 b7e49775 aafe6ad5
+          187b7545 a32220a8 92ed00b3 e0e52503
+          a75370d4 d50ae6ac 2cbf867d bb7be73c
+          30f5ddea 4d5b1f56 48013a0c 9d748e85
+          f88963d9 0149090d 6967206b d54666e9
+          5ca4656a b8711084 baa4556b 466992da
+          57ffe1ba bafe241f 2d6bf25d b3ca96ae
+          7c8f7a31 c588bb1c 3a3d7c2f e83b7500
+          9795b9b0 4f9741f5 892c9022 108be109
+          74f70cf8 f50a7f9f 4a654888 53b4720d
+          602d2dd3 bb2cc033 5c040940 875108a5
+          4e0b9d67 4f034d54 04d8ab6a 20fdddcf
+          789e1df5 46b5336b 17f5c4cc 69316f7d
+          b0f07f6e e83f039e a3be0ed2 075eb7de
+          f8cb99d1 d48b8a6e 5e1b1600 9d1f9fc6
+          398bb5bc 12d2def9 0cac350d e40b7a71
+          880089f3 f96b3db4 269c3f11 de7bfe76
+          3cc8a7da 4f1e0b81 98ee64b7 43e6e295
+          50be3f1d a88d051e 027dfaf7 59db69fb
+          8f13049d f79fd1f9 3f3969fa f842e2b2
+          2f1e1474 ea3cea9c 390c4518 8aaa21f7
+          ab6fd982 3a40c55c 6ee2c491 fcee51b2
+          caf14d40 c3e83f7e 784e9d20 11e89a5c
+          6ce8c01e 10d8af37 ffb962f7 01a838c8
+          40274166 b39c6124 fed3850f fd19e82e
+          097828ba 6b7a95c5 bdf7de64 e66a1ce4
+          65e9f171 cec15350 b6733727 ccbe3dba
+          42e4d081 9c485fed c2432731 41103b66
+          38cff031 e5e643de 86ede2e5 3fe54670
+          711ae260 18b9cfab 6bf7f24b 0c605c9a
+          84dc3f7d 67c8e4bb 5e200fb1 c8c4c346
+          de37df83 e16c16ff 05de3d06 74890387
+          f9ea051d 5a7c4129 e7a113b9 9f0fb80c
+          46310cd5 6801ead8 21ea8161 e3258691
+          1fff42e4 ecd225ee bd8f5ed7 75ebb4d9
+          491cd015 3385ed90 b5723d38 eaea40a6
+          5643c2c4 31a00ef0 922465be 55583b66
+          f1a387dd 00de1d93 b82728dc b41deacf
+          16912576 5e1c3ad1 754fde81 d8f84b34
+          eaaffc61 b9af9f2b 71e98aa9 72bdb650
+          0abed798 5f09796b 37f177b9 aaf03088
+          bf7da4f8 48ec2aeb 5cc04327 dd1220fc
+          96c1fc74 83993dc5 3fec273f ede3014b
+          a65294c6 7ff4f114 860d478b 01af89ef
+          9544bdf8 c214e6d3 9d52f0bd b2fd27a1
+          629798e8 e0dfbb3b 44dedc4f 929479c9
+          5c2c5601 f0f782f8 3b46b1ff 50838d9d
+          f4b3576f 14372035 af631c33 e6f557ee
+          d70fbca1 e02f1b92 cbf9cef0 c7e66c0f
+          1c3be215 49f81e23 ceb9ebb7 83313b97
+          e754458d 180a7e1d a3af0ebe d7d40b2e
+          6edc3050 4784f34c 9edc35df 80b9bc9e
+          3cbc84eb cd30f056 f8e34f6d ba2c0f76
+          79009041 c282cf5f d6b68bf9 9e3a7b04
+          f99ec368 85ac7f7f 0dce8646 de872bf1
+          ceb1a0f2 d3b679be 87ca0e1b d41302fa
+          f6e2282c dbb9072a 8f9d05ea eb39d439
+          d3fd5e86 817f5cee b5c865a7 042a0283
+          9d894b56 4c962985 12ea3712 183f6bc8
+          2983bcaf 37f187e1 eae80888 9f30bc4d
+          f33d746b deb1c110 3d7a182f 6c693897
+          0df91b7f 20b7749c d729846a d43dc3c0
+          65dfe45f 512eaa7e e0f58531 afcfbb9f
+          2d8a4b0a be57ba3b 152af71e e260432b
+          103ef8da 36e97279 8a910a43 27a3d901
+          4f0fcefa 06c85ec5 4ef8661b 7de88441
+          2de2a939 3398eecf 5d91f1b8 d281843f
+          f6e47781 e347ce93 8cefaddb 02a6bc7c
+          eefea347 dd0a3e49 116d2eb8 8c77c4d1
+          c38780ae 633b6ee1 0bbedd0a 8d39e540
+          9d648a57 62feb7dc f049f44b afafbe62
+          af75e500 607cefd3 c52f6893 627e7212
+          830f773b 064cf189 a4135b91 eabc20e1
+          ce31a0f4 5693973f 6b29c113 7b408f76
+          109e723d ffb9fa50 2a94fc7c 843ed589
+          6d667554 706ae2e7 2b9e688e 74976679
+          f6a1080c b4272ef9 62924c25 9453f33d
+          0c98d69f 2b86826f b6f040aa 362e869d
+          fafe265e e2bb39df c3cda309 d441fced
+          a300542a b0169742 ee57df91 2724f07b
+          71b9d090 b070c9bd aaa86863 b3f0f4e6
+          1a9c7ec0 a0bc98f9 f3a74951 0900777f
+          c9cec350 7de028ff 3968c0b5 10767d4f
+          b78eef9d 3f28c58f bf0d5461 a1001666
+          d9f12176 8d81ee21 f679178b 6f739f9e
+          33cbefd6 e1bf34db 01b13907 18fee813
+          df040cbd f16df237 12327126 39cc1a98
+          0b8a7863 8bd831c3 409f10ea b67c8f87
+          4e6ee803 fe7d7a70 04966cff 096a4ee6
+          d0ba5899 c8eb7c07 f45e1139 f7f9c5cd
+          1a9968de 8132bef7 f9f267d5 51217ba9
+          e37b6805 acf526b1 2486c904 82de9bc7
+          f7145e2a f29218cd c1a7f4f1 a110c30e
+          4bb8891a 4f6540c1 e69fe91f 62b37128
+          83fd4f27 2e5ef1c8 6f9f27b6 2ee03151
+          45465913 162d9904 82ac8a3a 670ef95e
+          dd990228 d8b88dfb 2aaf8438 881b33d4
+          ad0e1ae2 eb2c05cf 3a117cf4 e0a8a915
+          1f62db68 1f6263f5 4eb684e6 f88ffe35
+          49d3be63 b3f74269 91a9f80d 1d9619f5
+          8f671e94 e44d2c73 45c5dfef e765b8d0
+          55045fdf 8f974093 e289e4e5 864e6246
+          dcc4ebc8 603671de faefa0b1 b09a3c74
+          6267ba0b 9b3eede9 c0db271e 6a110fd5
+          52038f7a eea5b5fe 7f1bf281 247c8f7d
+          72d66ce2 a7408cf2 c78ebb8d 274c3aad
+          ad1b74fc fef39a24 081b3288 4fa272df
+          6128db7b 823c9b18 539df4d7 74f93afa
+          d5373e68 316ad472 a44b0e09 8b963fad
+          8e0a3de8 9280eff1 1268abd6 f377ae72
+          5f5f5e12 438a1268 7fc5d269 82f462e8
+          44ad0273 7e210f8e 53671363 dd1cb9af
+          776ee2e2 650fcafd fcc0fd80 877c2f22
+          d29cf8f9 927b5d32 19493b83 dfbadcea
+          b45c28fa aea9045a 87768cac 4b5302ed
+          cfb52dfe 2bfef6db 40191622 66137fb9
+          1e6cf566 da2b3117 8f1ddae3 de7d67b2
+          578f9e2d daf1a9c5 e9aaef2d 7fcb887a
+          e11f33a4 086b28b0 04dab63d 62093490
+          b604da9f b9d88821 d7815fcf eefc4aac
+          68eb0f50 7bba4092 5a27a153 ef7921f8
+          be693fb5 b857a298 50e4332f acf21f76
+          d327e40a e7cda75d bc049ab5 ec7c09b4
+          51e01515 d06af81e 6e489f76 e13caf10
+          f968fdc9 5350b47d 0f7d0a3b 2f2796b0
+          2df69d7f ce23520d 01df9733 beb770d9
+          13eae8d0 a3e4f1bd a6126839 c8f7ac56
+          fe3e3771 e268fe50 46b22792 17854e14
+          5e4a9e75 22e875bc 5e4cf697 dff0fa31
+          94a1131e c2512b4a 123e5f36 45eee3e3
+          6a33c06b e27bc6b8 f73eb897 fd67bd4b
+          0abe7722 0b8ab7ee e444c6bb 537b881e
+          314472ab 877c3376 640a6813 e3793671
+          deda8dbc 6e0c7936 b1155c31 f3e74fd5
+          f71f5844 e88ce824 60dc1de9 e18f3d32
+          530abe87 51ffc22d 3f379540 9341f8cd
+          374060af 0ebce402 0698a93f f8bd41bd
+          3b42c8e0 01fcd45a be6b1f54 1c3a45ce
+          eb701cc1 7f1f3b3f ecd1d99b 49a35e2e
+          e25e5e2e 9b0d4edd 346861fd ee8353c9
+          53b679c8 c2173a3f fe002803 fdc15659
+          0d673f5f 09b60623 e9e99137 93f3f682
+          f65326f2 53ac3133 07d2df5f c43b6553
+          8e83a7b0 7748dc9d bc6bff10 6550b0bd
+          4d030fc5 929be375 eac6813b ac45c5fd
+          c9ebb699 c4206dd2 b4bb4186 79650e07
+          03023dd9 c3b26b58 ffcfde68 80331f2f
+          8586ec12 d203052f 8aa4d414 74dcb869
+          b0cfe09b b2c9230e 52701b75 5c9c5115
+          199161c9 a7071e8a 9211790e 3aac42ae
+          51314f27 a3cdddc3 8030d657 669bbefe
+          4c26d467 969017d8 71317eeb 737d9ff5
+          52804e32 e0e5cf7d e29efabd 47264bf1
+          3a4a9f10 06b1d8e1 46a50243 c639a83c
+          7a8abc5a 26be86f3 8e0b83c0 ebfa805f
+          f7ce10d4 f33854a5 9e23e577 c879eb7f
+          da3ba972 e5f29541 77de7390 dce253bb
+          da9a6fd6 25678c1d bf9f9ddc 7c803a64
+          c0c0d679 d614f06a 170f76c6 efd2def9
+          140c25b5 b4272c10 8d2bf633 4b9e791f
+          e8bb7502 6b7119a4 bff31958 88cbafe1
+          f598a0f7 3ed765ef 81ebb49d 3a57931e
+          f628bfcc 9c9da9cd 9efee072 e66a7ca8
+          b5cd4317 a35318e8 e2c487d0 5f7d0b26
+          063aa556 acf24ef9 41b78a1b 01db2be0
+          06508587 42c21d23 c4a70c84 76005b2b
+          d86b1a93 b226fddf a70eec9a de1681e7
+          349b2167 eaa4b7ad 2595bda8 131af140
+          11725d17 08b9a13f 576cf94f 7bc5d085
+          04d53b2f 2c3cb378 c6c26ac8 fffa3b5e
+          ebd9b757 778848e9 4ffe3c13 d7a0fed0
+          f109054f 3dfe689b 045ef11b af4eacd9
+          b9770675 9f560c12 7b450688 bc4ea9e4
+          a18bbc6f 76485751 f462a563 2d987d27
+          a172cf41 7ed0891a 7e0bf876 8c224fd7
+          c7b4ab92 8f17be51 f9c5d27e 6d0a7875
+          df6f6b5f 34efcd7f 51f769bd 5043eece
+          d1bc5b10 7f088da5 ce4cf40f a1ffe874
+          cbdf067f bd95d190 3c9079eb 78f935a5
+          5e439bbe 258e439d f3c8c3cb 4ca7d202
+          db04f06c 2545eaec fba72c77 d96cfee4
+          cd46b086 dcf0c1a0 4fee20b6 55dab085
+          97bea076 f5ff53e7 e7df0633 bee76a30
+          80262e9a b782e2e9 fad47caf d6909439
+          e9ff3e71 12f0bd96 051e6686 4cbbef0d
+          736e515f 725e870f a1af6927 d690635b
+          baeac051 2895e021 f425b95c b636b519
+          8550f8dd 76367017 040de8cb eb1adb25
+          e07b8d87 4f4cc89b f3d82cb7 065ed1eb
+          2f8dabde b4fd51f2 57efe7b3 79ef180d
+          a05283b9 b0887703 97b248f6 a5800fdf
+          8ad4a59e e4371ab1 6387f322 3dd4ef94
+          5157a59f 2c7aa372 c592fe6e 09bcba1d
+          5b130b5f 7ef533fe 48853275 bba94061
+          fc1d2341 151ac29f 3a624a14 b6551214
+          d06a4576 3e7770cd 46b09596 839ca76f
+          4990ae2f d69c5621 df33a6ff 12e856c0
+          b316152a b3a64d5d eab23b02 a528041d
+          993200fc 7a76e5a7 0becfe5d 732abf55
+          bad8df29 03db6995 d543ce9a 6f01cc16
+          d0754ce4 2fce24e9 e356676c 87f13da7
+          c1e026c0 733a21e7 c1fb5eb3 e4160da4
+          6e228761 08bf4ed1 10353c85 9b90ba13
+          e99264f3 5e516883 f1acaa63 1950fae3
+          6e6e7ec2 6e1a0441 bde83b58 62339d86
+          2327c7e7 cd9ef998 5b008ff1 ba51559b
+          763c491d afc3fb4f 958f9687 23645e5a
+          b05554f2 94779793 369bb7b9 2c1f165d
+          346464f2 3ac771b7 63074b5f 72cbc76b
+          107eb678 7ec5f2c5 fd5b35f0 1a0eec8d
+          2d7ce5d5 05d4f1ba 0bb581c7 0f034d74
+          14bf09e0 b581cbea 5a45a0f8 72422c0e
+          8b9d8758 1cb575a0 0c09629c 7504ef68
+          44fa5a4f 8cefa972 673ebcdc 98d6bc7c
+          afd98067 afa95664 4fbe7b89 cbee0c91
+          82d7855d dfaba9ad 920b4a7f d80d9547
+          3224bd12 bbe2d325 b657c829 87820d5b
+          783b2dec 6814750b 7d47231e dfab3325
+          664dbaf3 33a7c9d8 fa809733 63ea4bc6
+          33393752 c7ebc454 a7508819 338c3f22
+          47f7c46b 03abc0ed 85873676 1d85aa83
+          477841a4 88613783 5fe75849 ee731b8f
+          a68dcb9d f5d0e3ad 0a78a51f bc33acf2
+          cbf573a5 68f021d7 2a39afc3 ea50d8f5
+          8717b861 6eaa555c 893583ab 437e9abb
+          76336ff1 8edc155f a44951e1 9edf2b2f
+          583aaf62 d9e7035a 05f01af6 ed89ca7f
+          e6d94582 524ce4a5 e47562aa d32d3cbf
+          0e337af3 d76f8686 bc8a3661 ed2e0e6d
+          600c925f a999cca0 8e8e84f8 f1c3c5a7
+          99c459d3 18dfcb7d f491e5c6 5f4e0649
+          0a3c7b55 a59075df 3d8b9d26 4b38f9db
+          09e66e82 afeb0c21 83c50317 16b8c12a
+          f00a0db4 39416b53 9b96cb5b bd63843c
+          e0ba5e10 76432f72 978b3a66 7c2f21eb
+          5ec6f7ae 30be7745 c0cb7968 da73a68c
+          9c14ea78 9d98eae4 cf4eb123 d9995f01
+          e6bca602 377268b5 5762576c 21f079e6
+          567c9e79 8ab7088d 19350cbc 25a878ca
+          f95e6afa 58c6f766 4b02bcd2 f7df4aa9
+          5cbdfe79 725ec753 9d04ce75 14017ee0
+          32987855 285b8319 640a68b3 c2afd4ec
+          2e5e6900 b396055f 3de7b60a c671c90b
+          9e23df5b b46c5ec5 92850349 81d7b077
+          5778feb3 cf2d663e 5fa0b630 788a8d1a
+          3618f49d 3bb21f5c 3ca30333 3bdce976
+          e2b295c5 b8aba9b8 16f2d66d e2b14a5d
+          bb049ece cf03cbf4 7c4f99fb e8ac65c6
+          93c78349 80672b2f 97654d99 bc88f1ba
+          2829dec4 06f4486c 6a97c978 4fea4928
+          fa7e9f5b dcc336a7 b5293f98 0615bbf6
+          73b485dc 300082af 4d9684ef 391a8c09
+          9993fe6f c1e5e4ef fd65e0e5 3ff5d833
+          c68cec61 8204a94e ea206f88 bf630c6f
+          948cd59f 72567fcb 77ba4c76 f500ef42
+          d6f286ed 60cac903 5029216e c248d046
+          f8915fa9 e17dae21 357d74ee ac194fb6
+          28f0ca17 7f36b87c e9ca1715 12f03a5c
+          f0044c75 0a6396dd 6a85dcd5 1bc05cd9
+          e8965762 578c3d76 88b21bac 90bd7203
+          ef60a908 0ce0c5ba b13a8114 0591ca3e
+          5ff11ac3 c6a01601 9ef1f8b1 90bcc766
+          2f618a56 50f33ade e0e3e6fe e0d7ab1b
+          b770253b 7e86aae3 9957958b fd9dc2f1
+          41766631 146edcce 77a64f97 6488fadb
+          f5405ef0 bc89ef31 6c2c6318 096e56e0
+          a10fcf9c 7cf70247 bd214e26 4157196c
+          841c75db 2ddca736 9e3e0b05 9b76b6a9
+          20f19558 9be29d07 a0f6e809 0e80c8a1
+          43c0bf6b bc347caf de10cf30 b2f052f9
+          de25018f f9f05986 e3a74609 d4f53dec
+          c05f5c9d 4f7572d4 d4f1083e 79cf87ff
+          3ab88b3e 12f23dfc 7ecc5ab6 96560068
+          d410fff7 d1a00ed0 915fa971 bec730c2
+          b0f244b3 00af7add 9a9ecc87 bf4eeed6
+          9a529de2 31d52926 926768e4 aedb048d
+          05559258 3b54249e aa793d3d 9358baf5
+          fc077f8f 1f970485 bd91e35a 18d7cd59
+          f30dfb0f 2ba823c2 246b1add c4f75e2d
+          5ff4e99f becffd9f b5536ce5 659a5ffa
+          f4d8632d 2c95e4f5 7ff88d3d 21eeae09
+          bc3670c5 cebd90f9 c5b73c5d 883c7668
+          c20e95de f9fea346 af52c726 1cd42627
+          d72983ff 43670ca9 473596dc fc4ed56b
+          564db094 54f59522 1d0b3744 fcf81408
+          1f7e338f 6fe67df9 3514ef38 449e1ac6
+          db15f878 9fee72e8 585f4d62 52c36501
+          2ff39e3b 5ead58b1 e659f2aa 4ed8063d
+          26049267 4de36d95 4c39f990 fede22c6
+          f72cf41d a99942bd 7bf7589b b864d974
+          af2edd2a ffd79fb5 1615ca0b 9e79faa9
+          f2e5ff7e 8d5ce158 ef4e9043 f2ccc9e0
+          dd3189f1 72039cfe 6021afbb 2785d1c0
+          62eb1d37 ed98f147 b1ae3f74 b5556b57
+          f762a09b 2349aa93 a629d589 810e1b20
+          f32b3103 3de8d0d2 e9ba256f ebb869cb
+          c43f031d 8a2a32ca 91b874c5 eb21f7dc
+          f912f51b 09e4bc4e ab83a785 39eaea41
+          68aa4aa0 d0d13711 c44d57b3 f987e965
+          0b3e4ef9 4b1ccf51 5fa7c87f 62d63fd9
+          6494e46e 0d539d46 a5805752 c28536e8
+          75e78ac9 afc490d3 c97dbdf3 13172fbd
+          47191a76 e9549ded f0b87f7e fca2778f
+          ce1ba93b 9623f735 e45742fe d79b799a
+          98362116 62cf3711 24e67bc8 3d99f5ff
+          a7253757 77c9c02b 7cf9b929 e6bcd27e
+          d4269a17 82eedb59 2c48cd04 1be195fc
+          78509278 9d836d80 e8575f7e 40d7ab4f
+          f95fdef1 3ebe90b0 70f18382 46592ac5
+          057ee99e 549e2686 123ca81f 84f4eb4a
+          5e950043 2cb6eac6 0e05cf3d 35e79280
+          67387634 b0ecd305 2f509f1c d1d2e922
+          fc206ec2 089eea64 2d29e38d f09a8c08
+          3947099e 30eabdd0 e933b75e eedfa1eb
+          7d6d71c4 d3731e21 ef582e13 6f36f2be
+          de22368d 56c8c526 82d85486 f8d48d5e
+          aa72e59a d90dfbf6 b4fb53e0 15be3077
+          b6c36009 27ad82ee 148b5127 4c1c0d0a
+          7f7f7059 c52236d8 088ffa4a 0c6b03ab
+          23838fc6 7df8c933 d818e64a 2472ee0b
+          6b0386de f839f99b 58cc5aae 3373be87
+          59cbd854 26418aa6 3202d7ad 2e7fce63
+          cf63b5ff 3f045ec3 dedd9135 5b77cca0
+          e653783b 113dec06 31d509db a06fdb09
+          3527b3e9 5dacd81c d890b068 c97dcab0
+          f02b764e 32a592f1 bd4f662b fcbdcf4a
+          f146a2f6 743e146d f99e4f4a dfa9035b
+          e3c1e457 6a388efa bd472656 af5bdded
+          0f8157fc e6eb33d8 02f95296 89e53d5a
+          bb2742c4 d01bb99f 68483f23 491b74ce
+          31b139f0 23d3e7f8 dd3afc64 73fd9d9a
+          a40e7531 afbd763f e38c0ef2 806e5313
+          c1fa13e9 dc0587a7 0c86801e ed68afd4
+          649c2a29 4ade7ffb 093c2cfe 0e78e673
+          677deb7f dc3999f2 31364f75 0af4e6d7
+          3c58d5c9 5e5dc3dc c337ec44 e924bf12
+          4377e8d3 b7c7fad8 37dfffb8 b9ffee90
+          071ede15 3461d47c f23b54ee ea5c90bd
+          fa5bb055 56f1142a 7ea516e4 4dcaf7d0
+          88188e9d 1cdb7060 6fccef80 57b96ac5
+          487ba325 1ca8b85d 5355272c 3aad0a0b
+          619ab7f3 5e5ec6e2 1af22b31 74830a5f
+          5d61fcc7 0ba7cb54 aa160080 00f11f7d
+          f6b23a3a f4a0144d 04b13f5a de571bf9
+          b5a32a34 18126e27 2ef48d05 106c2e5d
+          d5aa2f26 fe1a784c e9b59bbe fd3be5e9
+          915775ba a99fd8a3 9517c4de 07e507d3
+          2579fdcf dca02b66 febc69ba 9ebdcb5a
+          ea3b9421 a1d6d877 de9fc2e6 6a2077b9
+          6c4d2b0e 9fe645c7 f1bb71cd c3d9da53
+          5a607c88 55bb7deb 38a7d128 5c009ee1
+          c4f110e3 c9f40154 9606cdbc 777c2844
+          f2aa4e00 c6ec5cc8 fb66bb24 499d3c74
+          327ee4bb a10f3cbc a5a5bf2b 70c2dfd3
+          c367ce78 9a3aa676 def2e57d bb83af35
+          ae79d4b0 14f08e0b 2673b958 0ac39c99
+          df9d1d60 132f00af 61f7ae3e 0e8b83e6
+          50d1e462 6346a4f0 6b1d97d9 0cb9ccc5
+          da8df405 b1d1eda9 a3428fc4 fd6be13f
+          a8828551 2fcffb48 d7296193 93fa4a0d
+          b396d91a e77cf52d 0fb1607f dc684c28
+          a0ca6291 f1d08aca 70f860bf 8b2c5e6a
+          5f32655b 31b1330e 7c3174c2 8ef95587
+          8f439d04 afc4c44b 755963c2 c2cf272b
+          4342c86c 90dcc707 12162c9e 2e78a9cb
+          c86f3530 6b39a308 2a0e1ce5 3ffb75ed
+          043eed22 490f1a86 e3c7aee5 c0c3aed5
+          a6b493d7 509d2271 7385f4ef c37b4ee0
+          ce2bdd7d 4092a44e 070f9dcc 78d2efd6
+          e169d4df ad1f7843 61d4b373 e96f3540
+          bcca2afd 791f381b 0cbcf65e f075bdc9
+          82ca6875 4da7d3ba 39cd2610 1c4683d2
+          5a549044 a17cdce1 da101ff0 ed98c4ad
+          5d634e3e 34e697d2 77a4666e ce7740ef
+          7531f3de fe142492 f027e67e e53ba0cf
+          62f25b0d b6d6c6a2 2a66f932 45abd7a9
+          3da8fc35 24e0438c d9ca2be2 1cb5b53a
+          c172ee5c 90adbc8e a4f6099a 749f7671
+          ccdde8b9 cfaf493b c3afa848 8b7363e8
+          c4475710 f7cf4f67 c8d4d2bd 16c2b04d
+          e2d22f1e 5706f99c a3ce5c46 90556350
+          d9e90465 a01f78c7 4693b85b ded3a3a2
+          3ed89293 1d2ad8aa 2a235c76 a79ecccd
+          24c6f150 b6cb6c81 faac1cda b2136285
+          2957ecdb 6fddafbb a6573948 2c9a76ed
+          eb625e9f 7fbfd30e 76d2662a 6ccd1b98
+          b7c16451 5028c127 21962ea6 e7020df3
+          b0e182f1 f8d1307c 13dde256 c725925b
+          afc870fe a3adb60e cce595a4 27598c5b
+          054d18f9 76c8d407 b7412b11 36969fd9
+          98dea48c a9e19a5b 6aeac052 55cdbd8d
+          5754b868 005a1a7c 4dfda84d 674e8709
+          32b93c8c cabc2bbc bd40ede7 cb7f3657
+          56892114 22378ba7 69754c04 864e9e6b
+          55a50730 71f45f0b 5f62633b ecb4927d
+          25db844e 30958949 d59aa000 50681440
+          d5ba58d0 6842046b 69490809 f0d8a494
+          3a2f906b 357ce6e6 8a6af109 1e050670
+          419de088 7befbd87 95c12116 6865c2c6
+          6465639b c9c66827 74794c07 55fcdf0a
+          a617fc50 14f64675 9b4e9f0a 128ce969
+          be24fb9f 4d4aeea5 e5a94288 426b6d2d
+          9962799d e47ebdb6 048c9d70 105aa9b0
+          b11d6063 dc467997 6badabe7 ae48ae56
+          83c24b43 66f12c85 057ee86a 7da92c9e
+          02ad9d20 c66d6c0d 06b205c6 ef668afd
+          a275576d 94e11857 b8080f19 76a3913f
+          85c42c65 b9564b76 c0902914 7a44818e
+          2c868499 1f9c6030 9f623291 b959b94a
+          68f4bd29 650fb472 6163dccd c6da4075
+          85e5305b 7948058d 815ca306 42d0eb10
+          7834f920 2e0c5e32 37cb9b84 38c18993
+          26001ec6 c8941121 673549ed 8b5b3bf0
+          708c3856 8ab81eee 7fa7b509 78ec07ae
+          1b3a51d3 010f2d9e 42d13463 c6f46d36
+          1a83c7d6 559bdc25 53d07ab9 5a3bf0d8
+          1871ac59 24575818 dab0db45 1e721e78
+          742ba441 e0d144d2 707e7259 d38c5dc0
+          1f7f5058 3ca430be be45e7b9 65eb469e
+          80632da4 72794ebb e302f0ae f461d35f
+          14391df0 9a1656ac 70e41227 4d75b870
+          382ac14d 848db58a ecbbd0cd 3655bd94
+          c90572e0 d19b02dc 65844c96 ed6693bb
+          008f74ac 1c78d0d4 414846e9 6a25f43f
+          4eba079e ba3e7d1d ee023cb2 b1cac48e
+          e0ae8b4f 1b94ac42 1277d2f4 a10aab09
+          5af769e3 e84e6375 3be0b565 eb7a558d
+          d5033c8f 7880e711 8f7880e7 110ff03c
+          e2110ff0 3ce2019e 473cc0f3 88473cc0
+          f3489b16 d2ce6458 ae0b6bb4 c96ccaab
+          acd7672b 15cc4a61 fae03a21 be3d55d0
+          810e73fc 1bc0702e 8797ae70 12a54579
+          e48f1402 5c078d59 7920d36a b86e284b
+          8990010f ab4256a7 a641d551 b15409be
+          ed14e41e fd4bc6b1 d8da5b6b ea20fdbd
+          85170c03 65f95f05 f52e9379 5865eb32
+          7cc27f74 d366399e 1413f448 ebd487c7
+          fe78c403 3c8f7880 e7118f78 80e7110f
+          f03ce211 0ff03ce2 019e473c e2019e47
+          3cc0f388 473cc0f3 8807781e f1000f2c
+          57c13cad 6e34d6ab 411f16c1 65b757b7
+          f559ba1c f62a371a 6b4d9bd7 87dd5e25
+          683b74cc 70b5e149 62da8f26 a94396bb
+          8c978d35 938db9cd aa0427c6 307756d0
+          75efb9b7 ed5a3a00 b94e95af edd4f98c
+          bb8c998d 35838db9 d0e568bb c6003127
+          f80efddb 7ea5bfee 2cef39d1 c6041b96
+          e8070dda a08e8935 bacb98d9 580d6ccc
+          eb9dd636 680818c6 106b8839 4115156d
+          0e7f7cf6 2b0e5bdb 9ba4a016 6aa29e7f
+          e55db77a 58c4c68a 63c6b1b7 35638018
+          63587b19 31c7c329 114fcefd 2270c4ad
+          efd84d62 95747727 11d8fdda 690343cc
+          bc79777b f7ed97e7 6e53c031 e3d8d91c
+          1a9da636 600498f5 466c318c bdcdb0f6
+          6fbebf5c 4d25619d 163314bf 31ef9ed2
+          0f3f7cd2 5a55dba5 a94cb65b 9156fc08
+          32707875 4dde1ef5 d2cbcf05 8c1e77d4
+          9d1556bd 615dcf82 679f79d5 78ea6c0a
+          d6107747 9da0a822 42d2421f 78f0ad88
+          a7e62e17 9a5ab55e 00de8500 4b7e9eaa
+          76dbb6fe a6b493fd 4d196792 6472c05e
+          67ea563c 3f279ba1 51269717 7bf5b8f6
+          177dff7e bb7d5352 d2640a25 b405711a
+          0d50bb7d 5b17c3b1 d441c6d4 435d5c0e
+          4714431f 960d155a 31deacec 70548111
+          13afaedd 0e048c19 bb571118 f42bd6fa
+          ffd9bc4b 463e5e72 82000000 0049454e
+          44ae4260 82  
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="Y" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000009d 000000b8 08060000 00f947ee
+          5300001c f3494441 5478daed 5d077c54
+          c5f63ef7 6eafe97d 936c36b4 4028a2b4
+          00822845 a98a8af2 7c147d96 3f028a8a
+          a88014b1 203c0ba0 4f69a2a8 08222a52
+          a4832048 2fd243ea a692bac9 6e76b3e5
+          deffcc6c 02585081 e4dedd65 bedf2fe2
+          06923b77 e69b73be 3973e60c c3f33c5c
+          89da9c6c 9565d3a6 ae35274f 74b19f3f
+          d79c9140 38fab614 7c1b2ede 03a5b288
+          c8745db7 dbf706f7 edfbb3c2 98648700
+          80c76281 ca4d1b6f ad397ebc 9bedd8c1
+          54e02106 1850fafc 5844469d d7a3b108
+          c2639168 745cf90f 987ad271 0e0714bc
+          f5c6e8a2 05f326ba ca2c29e4 2ffd6c80
+          eaa78f2c 547f2e62 e4a8b9f1 33672d66
+          b53abf25 5ce997cb bbe74d7b 75a6fd42
+          760f3c1c 8c3f8e45 58d09988 51a3f158
+          2c65d59a cba4f354 5743fac3 f7bf53be
+          7ef30489 0c7d53ea dfd68177 03b85d00
+          61fdee9c df74e537 e325fa20 bf7b87bc
+          575f196e 7eedcd4f 10d3e4ac 3200c6a2
+          7f9f779b ae58fd9c 44a703c9 f4e9d321
+          6fea4b23 8a967d31 5ba64284 63fddf25
+          e177c093 c77a36ab 1338abcd c17def39
+          ea4fedaf 5cf77d4a c65363d7 4914a064
+          64013216 a733ba30 6e7b5650 ef7ec799
+          dadc1cd5 8976a927 90b56be2 ef16eecf
+          6619abd1 64b53976 3215e98a 1aff68b3
+          1bcedcd9 f553cb4f 07464854 813516c8
+          caa5a3b1 68cb5a36 6dece22a 0f3cc291
+          5986dec9 5d694bb2 fcb8be9b bfb4d971
+          fe5c68f5 2f87fb23 2b177063 8178d614
+          f38db5ec dad10d02 1cb66347 bbfa4b5b
+          abf7ee6e cd393d61 c006e858 1c3fda95
+          75151735 65029870 f8ddece7 cf36f397
+          f6ba8a0a 9b04f458 9c3bdb8c 65a4d2f0
+          40b774e8 1dc3fca8 ade1013e 16e1d888
+          cb20f021 a76df519 c858a0a0 10189474
+          14947414 94741414 94741494 74141494
+          74149474 14149474 14947414 94741414
+          8d0fe1b3 e8ea93e7 19daf9a2 e3ca3359
+          4ca0920e bd24cfd5 bd234b89 273ae7f8
+          3ae231c2 9efa118c 749c1340 dfc40089
+          0f0e246f 99b3fa07 a83a6f06 564e075f
+          0c701e80 e4870682 3a39096a 32b221e3
+          abb5c04a 028c7478 5649d54a d0344922
+          a493aa55 f0bb23b7 14027b1d 756c3468
+          9a2503d8 1dbf75b5 81e45e79 0ebd99d3
+          454847fe 9f425c6b e776a3f1 707affa4
+          ab578a40 06251d05 251d0525 1d050525
+          1d05251d 0505251d 05251d05 05251d05
+          251d0525 1d050525 1d05251d 0505251d
+          05251d05 05251d05 251d0505 251d0525
+          1d05251d 0505251d 05251d05 05251d05
+          251d05c5 cd4a3a46 22f1a311 6129e9fc
+          9f7000ce fc7c86ab f18b9b37 c176f408
+          43491700 6fe82c2c 9071369b 5f34d75d
+          512ea5a4 6b40e0ab dbeb0bb5 3002d6a6
+          42ee5529 e8036fac addac61f 086f9536
+          46a45a6d 82918ef1 b2ee8a6f 08f8c22c
+          a3458329 f713d285 524bd790 ac2396ce
+          3bcd18a1 04337a0c 57ed8870 161505f9
+          fa6070b5 b5e02a2e 8a655881 c6830d70
+          4b77c9bd d6957e64 64c24817 3c809e6a
+          bbbe3627 2bcee749 67ad96d6 e664c70b
+          453aa6ae 0a22cf71 014c3a8f 07f52c47
+          5e98954a 052bc487 1ec3da8e 1c6ae1eb
+          a4b39f3d 1be72eb5 26325261 26232361
+          2f8f0b2f 2ce98479 1c535784 0f174344
+          7a4ea210 4e626127 52bd6757 0f5f279d
+          75efeedb 380fa76a 6c7d4f14 8e44022c
+          8e5fa20f bcdb23a8 edc1a4e3 04239dcb
+          ed9d558c b7fcab50 b38b9191 f8576f57
+          4989c297 4967d9be 6590502a 0b5b3946
+          e6bdd49c 73b984b4 749c60a4 c38b55ae
+          16971abd 827402f5 307657ce a24a53e9
+          f2a577fb 2ae16c87 0e465b76 ee19c80a
+          312d78af bca9f736 1e342ebc c0a47309
+          65e9f0cb 714e2731 e932bd0e 18011525
+          8b885734 fffd97dd 964a9f0c 8817bc3d
+          eb39cee1 0a114465 23824914 0a60e55e
+          4be7c1bb 35c2ad25 5cf8151d 82593a44
+          38778d9d 7c568404 112208e9 621dd985
+          1df3a64c fa3f5f23 5cf9372b db95ad5e
+          3b4ea214 48542182 49b51a44 3a3959d8
+          b9ac82ee d6380423 9dd7d271 e0b454d5
+          912e18a4 1aa5a065 fd25c875 15fd6fe1
+          2ccb961f 8dbe4238 5771119b fddc8479
+          c8c22985 921b9874 8ad01000 6ce910e9
+          6a2b2c42 c631ec2c 1a75ab70 2113f4c4
+          e28bc4ba 49f55a50 4684a195 93c00122
+          0f04e7be f4c2bb3c 76f33e80 fcd7678c
+          71e41676 67855ce2 903b24a2 80dc5682
+          248fa3a4 4c30a983 1692d52c 5ac15409
+          2a98730b bcb13aa4 29b489f1 84884282
+          45eb17eb 915343f2 df7a6d98 d884abda
+          b5dd58fc f1c7b3a4 0a10364e 860c9c36
+          d140bc8f 0b799eda b272928d 23046461
+          6115acb6 43c70aa1 de174f2c 5b6e1ef0
+          75694641 cd93057b d9dfb403 499982d9
+          b3e7da8e 1c0a138b 701eab15 b2c73ef5
+          1eefe283 840cd163 cf220fd5 81263e96
+          7cb6e6e6 83abda29 88a5c33c d3b4bda5
+          826524d2 12a15e18 872eec17 2bc0662e
+          202b585d b2119491 41c0097b 610b21ba
+          a7c66530 bffcc29b 62dd1555 34ff9d87
+          ac27d307 b34a619f cbb90082 9b370149
+          909e789c 8a93672f 5d1228c8 f39dce62
+          56a2d317 0937dade 8be9ca8e fd4a96b3
+          ac5e07e1 ed5b03ef 127ed025 c8cd566c
+          def59fe2 8f16dc25 f4b36b4e 1c0bcf7f
+          e3cdb912 99c00fe6 bd563ea2 f3ada4ff
+          dd651550 79e63c71 b7424191 682c62d5
+          6ddb1531 b839024d 78fcd265 474f82bb
+          b49c6c89 45a67500 995e2eb8 b6abd336
+          8c79cacb f36ab333 d5423e37 7bec936f
+          79ac8e38 46e0744d 8f0320b4 6d0bd0e2
+          4b0111e9 4a0f1d83 da72bb30 12c79b5c
+          e4569892 0b5956a5 2e66e452 ab60f132
+          bc602aad 81a29ff6 2173c382 3c3a0aa2
+          7b74068f 088b493c e8ae725b 8a79ea4b
+          93857a66 c9928577 59761f78 5422b05b
+          c5935aa6 5740fc80 3e005209 b172853b
+          f77963a5 82f85534 c9b54a8b 3434b498
+          55366d56 220dd717 0a696970 78a070e7
+          2f509399 43e81fd3 bb07e84d 51e0a915
+          c1cda2c1 2ff9fceb e7cbbffe ea96c67e
+          16b2a89a 9c9726ce 43032d6c d22e0f44
+          371befeb 074abc80 403a366f fd167094
+          548350d6 9604a4c3 82721509 c67296d5
+          689c8a84 c40b4292 0eaf94dc 352ec85a
+          f93d7035 0e906835 90f4d010 90aaa482
+          8ada7a9d 89acaf22 fbf96717 388b0aa5
+          8dd7e91c 643f3366 b2abb42a 851158cb
+          b9915b8d ead60ec2 bb75226e b57cff61
+          e4690e93 60b9600b 08c42f65 72d3f38c
+          5acde338 1da8535b 1f157a11 875fd872
+          3e1ff2d7 6f263351 d3340912 06de4516
+          1a628450 1ce6e2b4 fce993c7 36d633ca
+          bf59d5be 7ceda6e7 8576abd8 7be88c11
+          90785f7f 72bcb1d6 5c00595f aff706ca
+          05b6b69a 366d0fe0 8c71129d d1b46db7
+          4f8cb001 1e8082ad fbc072fc 246a1403
+          51777483 b0f6cd88 e0151a38 407b71e9
+          a733aab6 6f496ee8 dfed2a2b 95e4be34
+          f1036451 e5420e34 f65e5295 0c4cc3ef
+          03895e0f bcdd0119 2bd680b3 d22e9c96
+          bbec5040 7f7b4fc2 33423a5d 8f5e0725
+          4aa90504 766d3809 00a7b067 ad5a0bae
+          d232a474 a590f4c0 205046e8 048fdde1
+          9ee0dd9c 3e6bdcd3 ef7aaa2c 0dfaabcd
+          2f4e18ef c8ccebcc 0a7c3408 c7e41286
+          f4415ec4 e4d571eb 3683e58c 19045fc4
+          a0b19486 6872351d 3a1dbf44 3a55abd4
+          8b9a5bda fc248a6b 43fac65e 5405d9ab
+          7f40e2c3 0db2a808 44bc01de f0b5c02e
+          1f076a6d a7d30716 bd37f75f 0df53bab
+          77ef4cbe b8ecf3e9 1281d347 3d7680c8
+          cea910d5 338d7cae 3c7c1cf2 b7ee159c
+          70f5e4d7 a575d92c 37c4d75c 221d3ea0
+          113c70f0 0a9182f3 24505b7a f00c14ef
+          dc4b3e07 b76f03b1 bd3a8be2 66f141c5
+          fc3973e6 d41c3b12 75c39d6d b342d6b8
+          31efa1c9 a31772ab 0b1b0f8d 211412ef
+          1f885e48 02cec262 c85cf9c3 653f2702
+          42ee7de0 f32b9c8a 17e10fff fb076990
+          2a173ce2 340a5bbc dcb55ba0 26239bf8
+          5dc3c03e a06f1203 9cc06114 b24566ad
+          8dc97afa c9b778d7 8d6d95e4 cd9af688
+          f5f89901 426e75e1 d53f2b97 80e9e17b
+          914b0b26 5924995f 7d07b5e5 56c1751c
+          690f9a00 aa26f147 c2ee1fb6 e70fa453
+          1893acc1 7d7a2ff4 8894f153 b71f0a59
+          a883385b 0db01a35 e938895a f8dd0aec
+          82aaf61e 1a55b26c 51dfebfd 1df653bf
+          46152df8 f06da18f 78632b97 30a01768
+          5b3623fa 24ffc76d 50712253 14b74adc
+          3c1abbd0 a10fcc93 040579fe 403a8c98
+          17277fcc 48a144e8 05c5a5c6 20dd5375
+          a110f27e d84484af ca940889 83ef229a
+          40e8a516 b1bc2fbf f2be2333 e39ab7c8
+          78a44db1 a5f4581d 314266d1 601d177e
+          6b7388be cb7bf0ad eac469c8 dbf81348
+          443a8e84 1710b260 f5d9a831 e357fe6e
+          cd7619da db3a9646 3e367aae 183b03bf
+          09a36cdf 0f95474e 90cf913d ba424487
+          14d2a182 f24e8643 1d96e639 cf8c79f3
+          5a7fb6e8 fdb9f75a 76ed1b85 b5aa9062
+          5d151d04 c6070793 2880aba4 0c32577c
+          87dc2d2f 5a9924dc a6e871e3 5e532424
+          3aae4a3a 0cc39419 1f4a43d4 e9bc5b24
+          d6792bbb 40d6aa75 e02c2e21 fb845810
+          2ba3f4c2 5a3cdebb c0295fb7 797cf684
+          71e3fee9 8f957df5 4517f3e4 57970899
+          098c751c 3e52687a 7808c822 c2d06c41
+          9676e577 602fae12 3483e437 8443864b
+          9914fb4b dca4292b ff243af5 5ba065ad
+          35ee9529 933c2e10 0d58f03a 4aab21fb
+          ebb5a403 71479a86 0d22b537 84de26c3
+          96b7f0bd 05f3d287 dd371bb9 dae0abba
+          b6ca4a36 6ffad491 17468dd8 c07b5c21
+          429e74c3 031c7f77 0fd0b74e 2193a570
+          cb4e283b 7c1e84b4 b4bf9fb0 689cb8f8
+          e9b326b2 1aed1f14 39c3ff49 9c04d772
+          3bd5adc3 b7b6e367 86b04af1 c8874326
+          a6617d21 aa4f2fa2 f1ccabd7 8279e35e
+          908ad099 d8bdcbc2 b4d9ba2e dd36c813
+          e24f6b3b 74242ec3 917e5eea c8cc4ab0
+          1ed8dbdb 9159d001 078005d5 71385da9
+          8d099a3d 350a1885 02aa4f9f 83330b96
+          a1eee204 3de2f9fb be0aba23 6d51cbed
+          3f3ff1a7 ce8cbf4a 70ae7aef eec4d377
+          f4388afe 4588588d c7ab565c 68a7d533
+          8f81a689 89a4b99f 9ebf04aa d2f34411
+          c758725c cdc5e354 7c46841d 0779b016
+          529f7f12 e45111e0 aea88453 ef7c0cf6
+          a20a6045 2a8c86fb 8851a9f2 5bef3bd0
+          56d532b5 ec4ffbea 6a3fac4b eb9e13f3
+          dc732f88 b9a82061 14879bc4 9938ab15
+          18b50a4c 0f21dda2 1127e913 a7016197
+          f5675f42 138ec80c 0688ec90 474702b8
+          3d448ed8 f2c5231c b1726822 c44f9b36
+          fe6a84fb 4bd29145 c5d4994b b56d5bac
+          262b4791 22d938ce 559d590c e6ef7ff4
+          865112e3 2171485f afc5e1e1 a6053606
+          71bdbb92 dd1bdc0f c53bf740 c9fed3a2
+          488ffa05 20e64970 cfb46531 cf4e5cf3
+          979afd2f ff52ad06 d3e24fc7 48b4ca6c
+          5ec463a2 522ce677 1d848a83 c7c8cb45
+          dcde1922 3ab51265 9bcc2708 87de3ba4
+          65021870 1630c380 2d3d1372 bedf22da
+          4a95585e c40f5978 5046d282 8f9ffbbb
+          0af17fab d6b4b775 2c499cf3 f6639c1b
+          3ca25996 fa30caea 75e02cba 48f6138d
+          f70f0455 4cb0f081 6391418e 1086e0dd
+          9afb8051 2ac05355 05995fae 010ec910
+          46ac9b0b bc99c99e c4ffbef3 1f55abd4
+          8abf8d4e fc93df19 f5d4b8ed 91ff1e36
+          cd2da265 c16194da 321b64af fc1ec0e9
+          02696808 d1330c2e 61cadd2c 8cf3d696
+          c359380a 430c3279 1ce47cb3 0eac3925
+          a2ea38cc 0bc48f19 11231edd f98fc6f2
+          9ffee2c4 f73f7c43 93da7c03 6717efe5
+          70ccacec d80528da f6131901 7d9b9610
+          d7a71b88 b9d8117a 7063efe8 08a11dbd
+          c7394af6 fc02c53f 1f176d5f 95aca011
+          1f102f36 237ebcfe 8f0dc83f d65521a1
+          7cf29265 ff61b5ca 3cd1762b c0bb3f9b
+          bb6e0758 cf67109f 1b77f79d 1094121f
+          f0c4c3ef 17d43416 0c83ee26 3ace9e99
+          03d96b7e f4668e88 b4c8c33c 407c2844
+          bc780cf1 836b70d2 117dd7b1 7361e2ec
+          d98f221d 259abec3 3143cee9 26d9289e
+          aa6a6054 4a6f1845 a710258c 22c8e0e2
+          e3835a05 493b6735 2a928593 f1e5b7e0
+          b139c5d5 71484f23 3e3c8e78 91774d86
+          e35a9f15 3566fc96 88471e9c 29e6ca11
+          eb176b76 0998bfdb 400e6c2b 130c60bc
+          b79fb702 54a08551 78af4531 0ebd1b54
+          c678f2be b9df6e80 aa8c4260 452c668b
+          c73fecbe fe6f233e acbfe6f1 bb9e071a
+          dfff7096 ba55b3cd 62ebbba2 dd47a0fc
+          c061f239 bc6b2788 ecd23ae0 c228e4f8
+          e0ededc9 fb610296 ed3f0445 bb0e9130
+          92683ace 8137f30d 7b8c0b3e 9e725d46
+          e37a7e48 1a1ac621 3f3e9ad5 280bc4cc
+          46c1ae36 6bf506a8 2d2822d5 0212860e
+          00755c68 c08451c8 f1c1a428 48b8f71e
+          92ece030 e743f6d7 ebbd7baa 62e938bc
+          352965cb 92977c32 4a1e13e7 128c7444
+          df75ea52 9030fb2d acef38d1 f41d2e60
+          5d5143d2 78705ab6 343808e9 bbc1c022
+          02f27e1e 4621c707 d532481e 7e2f4874
+          3a72281d c7e39c16 0730225e 598733cb
+          0d33a68d d1df7157 c675cba3 1b6940f4
+          d3cf6c8a f8d703b3 c47469d8 cd569cc8
+          8282cdde 1091ae55 7388ebd7 5df0b315
+          8d22d287 f405352e 76836610 cea6ae3c
+          97276a78 046f7385 0deaf741 ec8b9357
+          dd9026bf d18618e7 7d3453dd b2e95631
+          f51d16d4 e68d3bc1 7a269df8 9dd8bebd
+          2038d5e8 b7611412 6c45fa34 b287f7f8
+          60c5a1e3 50b06d9f b8f13864 e194a6f8
+          c349ff5b 3cf1462f 6dbe61d2 4943433d
+          48df3d8a f45d9158 fa0e6b1c dec5916c
+          148fc542 b6879286 0d06b95e 0562c614
+          afd77d69 e3c3c836 1fd6a94e a457b356
+          fde0d5b0 625d1eca 13775f65 9c377fa4
+          3c36ee86 cd4b8364 ca693ba7 9913de7a
+          e331e412 78b1f41d 0ea3d8cc 65249c80
+          2b4c2a0d b124cc40 ee5af393 300ad6a1
+          92bae383 12a44f79 87133257 7c0bb5e5
+          36518e0f 5e6979e3 264d7836 a4ffe053
+          0d32560d d5b0e8b1 1336440c 1ffaa65b
+          647d57bc e71894ed 3b443e87 75b90da2
+          bbb6f39b 300a7661 8983ee02 6d8ba664
+          a6146cdc 0ae527b3 45d77138 5d297ec6
+          9b9f3498 8168c806 1a172c9c a64931ed
+          e0c41a64 c6bba2cd fe662338 705d6396
+          85847bfb 8326215c 946a50d7 3ab8e11d
+          5220aa57 77f2d972 ec24e46d da2d2ae1
+          70ba923c 2ae474f2 d2e5e371 2abc4f92
+          4e1a12ea 362d5d3e 9a91b1c5 626d49e1
+          6d21a7c5 4ec228bc a316b929 9d378c22
+          93f86c18 054f089c a6858b07 91e38317
+          4b913e5d 4b8e0f8a a5e3705f f13cd893
+          3e5a3452 9164aa6e 5029d4d0 8dd5754e
+          cb31bc3a f5718f88 96055b87 ca533950
+          b0693b29 4186dd95 e19e1e3e 1946c193
+          134f081c 8f938685 92b42d3c 611c17ab
+          444dcac4 2bffc8ff 8c9a143a 64e8a106
+          d7df8dd1 e0b8975f fd216c40 efd9a2a6
+          b9236f90 8fdc53d5 c93364d9 17dbbb27
+          84b449f2 2d7d571f 8f1bda17 74ad5a78
+          75dce61d 5076345d bce38375 ae5ed73e
+          75b5f1bd 0fe637ca a2af7196 922c247d
+          b4748ad2 18b74b34 7d87b351 dc1c647e
+          f92db84a 4a01940a 926dab8c d4fb86be
+          e3bdab42 43bf3488 baa33b99 9c58c799
+          d7ed1075 231f5faf 200d5265 36f9f48b
+          a758951a fc877408 f23883db b470f168
+          90b225bc 8895a0ec c516c858 be9a54a1
+          94c74442 b3471f02 a95a21ea fe2c5f47
+          b8d83b3b 407cddbe aa3ddb0c 1796af21
+          4522c53a f2599776 ee32be3f 7f942ab5
+          4d5923da 83c64350 ef7e59f1 53263f21
+          b6be2bff 350bb2bf fa0ee925 37689a25
+          43b3c71f 0619ce4b 13a15d78 02e2e7c6
+          dfd3158c c386a006 7aeb8e9c 5fb2025c
+          5535a2c7 e3a21e1f f96ac4c8 c77637ea
+          628f6fec 4a881c07 6707f57d bb62fdd6
+          89123588 16a8c53a 25ae6f17 48c0917e
+          b442c427 a82e2cfb 1a6cf965 de342101
+          b42716e7 b80670e2 d07e1079 7b17efad
+          35151638 fbd16750 9d51207e da79fbd4
+          0d2d77ed 1b20d16a 79ff261d 8233cf2c
+          3fd5adf3 b65a7341 37d1f40a 5f7f56b4
+          3322de00 443c39b8 caca2067 cd4628dd
+          7fe2923b 6e0cf261 578eb7e3 f4cde2c1
+          f8407fd0 241b497b 9ca5a590 bef04ba8
+          ca149770 245d4922 cf4fd9ba ad832ead
+          5b61a387 b578816a be566eda 603a3768
+          d07e34f4 e162a658 e3d56b64 5a1b487a
+          7830b05a 1df2292e a8387e0a ad1a7723
+          6b63f656 b294c10d e7ace1df 537f205c
+          1d1b06d1 3dbba0e7 de46ea8d e03d555b
+          46365cf8 6415d414 94894ab8 ba3ee18c
+          efbe7d4f ccb31337 09f148c1 48879137
+          73ea50f3 b459abc5 0c075c0a 09348903
+          d3f0c1a0 4e4af47e 132d342a cfa443d9
+          e1135075 210b9c95 d5d7b7d8 e0bd016a
+          994e095a a301c2da b7819036 2920d1eb
+          bcf7dca2 fe2ef9f9 20e47cb3 11dc35b5
+          a21e1dac ef8bf007 87bcde74 e5b75384
+          7aa6a0a4 23fa6e40 9f772b36 6e7b5674
+          e221578b 6ba2c4f4 be9ddc4d 4648416a
+          23b8c16d a9067b61 31f97294 5582db66
+          43e2df45 2a215d75 452695a2 55b10a14
+          a1c1a08c 8c04755c 34c8c382 9188937a
+          97abe877 dbcdf990 b77e1b94 1e3e4d16
+          0ca259fc fae1c069 e74d8d3b 53f71eea
+          2d0d0b73 0726e9bc fa4e7132 add30e67
+          7e611731 e351975c a0d3ebfe a2ba7580
+          d05b5241 1e1eea25 8af7920b ef17f7bb
+          fb05f8fa ff30bf75 c1f867ea 4b2ad4ff
+          aca3166c 886c17f7 1d86d243 2710815d
+          e2bad32b 741c30b2 9296dbb6 75d475ed
+          9e2de4b3 05271dd1 773fae6f 726ed0e0
+          fdc07842 c59eed64 c6bbebca 3504ab40
+          df340982 9a99406d 8845562b 08a40a25
+          3072691d 99ea4886 ab376322 6292d527
+          34e27e44 dfc716d1 65b581fd 622958b3
+          cd60397b 81900e5b 5652bb8e 059f801b
+          b9d5a477 660f8d99 f0e21aa1 9f2d0ae9
+          88be9b31 e541f3f4 d7574a04 0a575cab
+          f8c75658 86dca554 ab469649 89dca184
+          90cc1b63 bb1df46d 5b81abf8 22647cb1
+          06a9069e 4807cee5 42836907 37229dbb
+          c65db72a 04510f44 5f4dc785 f4bde3bd
+          e6ebb74c b8d12ce0 eb8168a1 48c3ab33
+          57597fd9 d7b5e2c7 ede3c5d6 77976620
+          0b978b2d 221eb9ac 767056db 7f532b05
+          1bb9a8aa 6ae282b1 55b39ccb 2637fc31
+          75457ec8 9f6c5df8 45063e07 9cf4a030
+          44ed372d 5ef6b218 840310ad eeb67784
+          4d8b964d 421d70c0 2773dd98 cb560abb
+          c54b5fe4 e823eb75 a7f84a78 397bf9ef
+          64de7c3e 5f71a17f 3471a420 769569e1
+          e2517243 8268a90f a2768fdc 10ef302d
+          5a32020d 6265a096 84f029ce 397106d0
+          a471c177 0f382b66 3b449f93 c1fdfa9f
+          8b7b65d2 1862ed78 4a8c46d5 717d7a2e
+          364c9bf5 99d86df1 09476098 f6fa8a90
+          be3d3fbc 592b6b36 fa0209a7 9d4706ff
+          6a5af2e9 84bfab92 79d3900e 6b23d3e2
+          cf5e5018 220e71b5 94240dbb 72200beb
+          1ae3bc0f 46221d67 f58526f9 8ce445fa
+          ce6e5ab8 740452ea 169ea35c 6928b8d1
+          248e19f7 7f2f840d 1b7ed457 dae453eb
+          2c2470cf 185e7969 ac875abb 06597d93
+          4b44d26e fd2a61f6 3bfff3a5 a6f9dce2
+          de30fdf5 cf43faf4 f8d863a7 bcb921af
+          4aaa9d07 a79b962c 7f9a552a 8192ee6f
+          f45df292 e5cf29e2 228e507d 77fd3a0e
+          7d3993e6 cf1fa56a 9152ee6b cdf3c930
+          26beebdd b468e948 44c02aaa efae4fc7
+          453e3e7a 72d8438f ecf5c5f6 f96aec1c
+          ebbb9386 57268da7 faee1a8d 1c9225da
+          36296b13 de9c3bd7 57dbc8fa 7207c64d
+          9bf56948 ef1e8ba9 befb67c0 c707599d
+          2a3779c9 b227a421 a1404977 3df24e22
+          41faeeb3 098ad8f0 e3bc9392 eaaf1947
+          52b438e3 9c398f69 6eeb58ec cb4d657d
+          bd2fe5f1 0956a4ef 46206d67 05aaefae
+          0a72f663 e4c3af45 3ef9f456 5f6f2beb
+          0f1d1a7c cfc013d1 639f7a86 eabbabe8
+          38d42f9a d629db12 dffd60a6 3fb497f5
+          978e4d78 fbdda5ba ceed3fa1 faee775e
+          d55bedbc d8b470f1 a3d29010 8e92ae21
+          1baa5442 f227cb9f 9185ea7f e55d946c
+          97dc2ace 647e6de6 93dace69 b97e3396
+          fed4c1aa 162dab93 e62f1881 66b78dea
+          bbbae383 43faff37 e68597be f7a776b3
+          fed6d161 c3ff7d2c fae92726 b86f727d
+          478e0f9a e2f7267d b8f01586 9500255d
+          63ebbb39 ef2fd277 6af71977 93ea3ba2
+          e3646c45 f2924f46 cb6262fd 2e98e497
+          a4f3eabb cfc74942 b4a76e46 7d474ea4
+          cd983e56 dff3cef3 7e397efe daf1aa94
+          5655490b 3e18c9b9 91bebb89 d2dc49ba
+          52afae1f c5bc38f9 4b7f7d07 d69f0720
+          7cf888c3 31639f98 74b364a3 60abae30
+          441d4c5e bafc7986 f5dfa163 fd7d20a2
+          c74ed844 ca53dc04 ab595c89 20e6f917
+          16291293 6afcf93d fc9e743c c7297ce9
+          f47c6343 1a12e2f7 efe0f7a4 03fee63a
+          b7c87b3c 0c251d05 05251d05 251d0505
+          251d0525 1d05251d 0505251d 05251d05
+          05251d05 251d0505 251d0525 1d050525
+          1d05251d 05251dc5 9f80c1b7 e7c8e5c0
+          caa4b433 ae03b4d7 ae957068 9a3a8a4b
+          c1763e03 9c45c5b4 4328e904 700d7200
+          f3c61d60 deb0c37b ab0e5b77 35130525
+          5d635b3b 7ad10a25 9d08cca3 5d401712
+          14947414 14947414 94741494 74141494
+          74149474 14149474 14947414 14947414
+          94741414 01453a77 ddd7cd02 27259dc8
+          e039ceca 735073b3 308ef7b8 cb28e944
+          863438a4 44a2d594 04fc65c4 3cce5806
+          4ed5a479 26259dc8 901b0c4e 554acb63
+          5c8097f6 c7774748 75ca2c65 8b940c4a
+          3a1f40e8 d0075705 7a4e259e 54fa1ebd
+          d6c86362 1dfefe2e 0c1f0035 7b3d168b
+          fc78dbe6 7b6a738a 3b485401 68e5b015
+          97494b52 7ffee516 cd2db7e6 534be703
+          90040539 9b7cb662 a434589b 43aee60c
+          14b3876f abb69352 fed58973 e63c1208
+          840b184b 570febfe 7d89e6a9 535fb36c
+          db761fc7 81066794 33fec935 f2c5b2e0
+          56b76eb5 29feb559 5342060e 391628e3
+          1450a4ab 47f5cf7b 4c96ed5b bbdb0eec
+          6bc77b3c f188793a 6c10fd41 ba21b6d5
+          305249be ba5dc75f 755dd3f6 04ddd9fb
+          24230dac a32cff0f d9edf4d1 f6562892
+          00000000 49454e44 ae426082 
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="512"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000019a 0000019a 08060000 00360685
+          63000014 12494441 5478daed dd5f8c5c
+          d57d07f0 7bc677ff 186305b0 89b18509
+          50e38616 92f2d03f 69aa2452 ab88974a
+          7d208112 a24aad40 4d28aa12 aa84b40f
+          7da89aa8 5240499a 9034a895 22112884
+          87483c44 a5a91a35 6ad2340f a8a1943f
+          819662e2 35c69850 dbe0fde3 3dbd7777
+          d63b9ecc ec9ebbde 333bb3fe 7cc4ee1d
+          76cefddd 7b7f473e dfbdbbb3 bb21c658
+          a49a3b76 ac08214c fcf4d16f ee3af9fc
+          0b6f0f65 b1b3fa70 fdb6ad7a db52bd85
+          620d62b1 7c12a13a 42b18e96 6ae7aaab
+          b65e9b47 bddea4bd ae6bcc54 6fd3d5db
+          e1eaede5 38573cbf e3a61ba7 c676ed9e
+          d9b2fdfc e44261d5 a0393557 1c79e0fe
+          8b8ffde0 87d71f7d e4a17757 e1f29ed9
+          a9572f3d 355f6c5f afa684ae 2b5b4f21
+          735db5f5 da3ceaf5 39d2ebba d489891d
+          db5e2cca c9ef5e74 c38ddfdd feabbffc
+          ad9d37df 72b4d852 ae3d688e dcffb52b
+          a7eeb9fb 23c71f7f e243d5ff ee6e8d2d
+          1e2a946b bd7759f1 027af568 3d6be7aa
+          abb65e9b 47bd3e67 7a5dddd5 2c989f5d
+          d81c3cff ba6bbfbe fbe377fe cdce9b3f
+          fc7cd16a a507cdc9 1f3f534c 7dfe0bb7
+          4d7de9de bfa8767b 6b6bf2f4 a94613e9
+          1f8d5e9b 47bdd6eb 85bad5a3 f9930b1f
+          38bced97 aefdf3fd 0f7fe32b 9357fdfc
+          ea4173f2 b967c79e 7aff6fdd f3c6ff1c
+          f8a3b1c9 334ed144 fa47a3d7 e651aff5
+          fa676b57 1f395505 cee4157b bf78f563
+          dfbe7372 dffe99ce 1dceb8cf 39f9ccd3
+          e1a9f7ff e6e7a6eb 90392fc3 e901b0f9
+          5459b1a5 ca8c3a3b aa0cb9a7 ca923e77
+          34a74e15 4f5cf70b b71e7fe2 d9afd63b
+          f4f80e92 cf187c76 a6d7e651 aff5ba7f
+          edeaff4e bd5114e7 5fbbffb6 6b1fffaf
+          fb8a2d5b cebca339 f2f0837b 8f3ff9ec
+          5f6e992c d6ff2510 006c7e55 76d41952
+          65c9a7ab 4c79dbd2 87178226 9e9a2ba6
+          3efbe93b 422c2ef6 e53200d6 2cd45f2a
+          2b765699 f2d13a5b 163e547f e9ecc843
+          0f5cf0ec cd1f7aba 1c2f76f5 0b9a385f
+          fd37ebd6 d49701f4 da3ceab5 5e577729
+          6355e156 9fdad588 b999e2e0 fe07be7e
+          cdce1b6f 7eadac13 e7d8bf7c e7fa627e
+          2164629f 9029caf3 b7c66d97 ee29ea60
+          ca70d631 634762ce ba6aebb5 79d4eb73
+          add72184 e2c44b07 8bb9e36f d661d3f3
+          aea6ca94 3d75b6ec b8e1830f 865327df
+          2c1ebf6c d757e78e fedfad61 ac77d0cc
+          4d17c545 efd817df 7ee7478a 627ada67
+          0c3e3bd3 6bf3a8d7 e772af27 268aa7ef
+          fe7271f4 47cf8572 a2cfa0d9 226cb970
+          fb7dd7fd f74f6e2b 670e1c18 8b31fc7a
+          bfbb99d3 67393f5f c42a64e2 f48c59f4
+          2f46afcd a35e9fc3 bd5eca84 556ac7d6
+          e4c4bb5b 131365f9 eadfdfbf 7bfa95d7
+          df36b675 f5f309cb db6c2f19 c8557b14
+          cf59afd5 368f7a3d 8cb5434a 8e954531
+          f3caeb7b 8f3ef2f0 256528c7 2eaf469e
+          b7b6a355 7b96e57a 9e7491b1 21235157
+          6dfd308f 6a0f5ddd b9b9a288 cd7eeea5
+          8e87b937 67b79dfc f1b3fbea 94d85374
+          fd8680d4 2af3b3b3 c5f4a1c3 6e4dd5d6
+          6bf3a8d7 9bb8d713 3b2e2c5a 6363cdc3
+          a6ca9630 3ebebb0e 9a1d6b3a 72752753
+          87cc7f7c e64bbd5f 7500c0c8 ab5f75fc
+          ce4fdd5e 6cbdb4ba 27999d5d 4b898beb
+          a039ef6c 4ea20e19 4103401f e375448c
+          e9030099 4cd64153 ea030099 2c7cd1cb
+          afd00420 1bdf5d01 40d00020 6800a0a7
+          c1bd1060 7ec567fd f0addafa 611ed51e
+          74dd01dd 6a0c2c68 c2c4d84a 3fe9e387
+          6f37576d fd308f7a 3d02bd8e b3b303f9
+          f196fc41 53ddc984 f172ee17 bff3fd77
+          8dedbae4 50cf8bed f8b506f5 df3958d7
+          6eb76be7 aaabb65e 9b47bd1e c55ecfbe
+          7ce89227 dffb6bff 1667e6ca dc773683
+          b9a30921 8eefbdec c572e7c5 af14006c
+          b8303e3e 53afcd9b e38e6629 616766c6
+          7d76e6b3 33bd368f 6a0f47af 575a93d7
+          9b579d01 20680010 34002068 0018bca5
+          1703a4bc f260e1db 4b9d031b bc5c6171
+          df3e7f9d 2dc67c2f 7cc8557b 14cf59af
+          d5368f7a ddb57ec7 94a5bc73 505cc32f
+          626e0dec 573747bf 241a6068 0c684dae
+          8f523678 515d38fd aef30389 fbaef4f2
+          bdf57e69 df206a8f e239ebb5 dae651af
+          bbc6249d 40d7badf e8a4ebc1 6583cc88
+          a19d4e61 8dbf2ea1 efebb9fd 4cc0a6aa
+          ad1fe651 af47aad7 8dc226b6 b3a0c9be
+          5e0c0040 56820600 410380a0 01004103
+          80a00140 d00080a0 0140d000 20680040
+          d0002068 00103400 20680010 34002068
+          00103400 081a0010 34006cac b2fd97a5
+          63c2d885 bf42dd39 30a61f27 d67fc3ba
+          f3ef5877 3d99ed02 73d51ec5 73d66bb5
+          cda35e77 8d89294b 79e7a0d8 68e95f1c
+          ec8e0680 bc773461 711b5206 87e56d48
+          dea9bd4b a57e9732 30cb85e6 aaabb67e
+          9847b547 f19cdb1f 0f294b79 f7a0d0f3
+          61ff7ddd d1009095 a00140d0 00206800
+          40d00020 68001034 00206800 10340008
+          1a001034 00081a00 040d0008 1a00040d
+          00081a00 040d0082 0600040d 001bab6c
+          6f63c2d8 181307f6 dd37c63e 4fc66c17
+          98abf628 9eb35eab 6d1ef5ba 3873d14f
+          5ed6e3f2 b6f149b7 cee63263 b32b17eb
+          00c3a2c1 9a7cb639 d10a83ba a8104c2c
+          c0b018d0 9a5c1fa5 ec789cb4 437b1b92
+          77eab8a4 90706121 d3c5878c 4d555b3f
+          cca3daa3 76ce6179 b3eac1bb 07859e0f
+          fbf36200 00b21234 00081a00 040d0008
+          1a00040d 00820600 040d0082 06004103
+          00820600 410380a0 01004103 80a00100
+          410380a0 0140d000 80a00160 6395ed6d
+          4c181b63 d7c0987e 9cc57d63 ecf364cc
+          7681b96a 8fe239eb b5dae651 afbbd6ef
+          98b29477 0e8a8d96 fef61d4d be16f68a
+          1a0086c2 80d6e4fa 28ad30a8 8b0ac1c4
+          020c8b01 adc9f551 ca8ec749 3bb4b721
+          79a78e4b 0a091716 325d7cc8 d854b5f5
+          c33caa3d 6ae71c96 37ab1ebc 7b50e8f9
+          b03f2f06 00202b41 0380a001 40d00080
+          a00140d0 00206800 40d00020 68001034
+          00206800 10340008 1a001034 00081a00
+          10340008 1a00040d 00081a00 3656d9de
+          c684b131 760d8ce9 c759dc37 c63e4fc6
+          6c1798ab f6289eb3 5eab6d1e f5ba6bfd
+          8e294b79 e7a0d868 e96fdfd1 e46b61af
+          a8016028 0c684dae 8fd20a67 51a0d1be
+          21985880 61d1604d 3edb9c28 9bd409cb
+          dbb0964b 0a091716 320552c8 18746aeb
+          8779547b d4ce392c 6f1aadff 5d8f93f6
+          f5620000 b2123400 081a0004 0d00081a
+          00040d00 82060004 0d008206 00410300
+          82060041 0380a001 00410380 a0010041
+          0380a001 40d00080 a0016063 9571711b
+          13c6c6d8 3530a61f 2756ea77 fd9ecc76
+          81b96a8f e239ebb5 dae651af bbc6c494
+          a5bc7350 6cb4f42f 0e764703 40de3b9a
+          b0b80d29 83c3f236 24efd4de a552bf4b
+          1998e542 73d5555b 3fcca3da a378ceed
+          8f8794a5 bc7b50e8 f9b0ffbe ee6800c8
+          4ad00020 68001034 00206800 10340008
+          1a001034 00081a00 040d0008 1a00040d
+          00820600 040d0082 0600040d 00820600
+          41030082 06808d55 b6b73165 705cdec6
+          b51c2cc6 b82e6372 1d5b6dfd 505b3fce
+          c15e375a ffbb1e27 ed5b36b8 bcb8f42e
+          2cfcd728 6de24a17 dbf95c08 214b9373
+          d5555baf cda35e8f 62afdb63 d61232b1
+          a36248d9 b7b5be97 b78230b0 23013024
+          6b727d94 3235953a 072dddd1 84869794
+          92da21d3 c5878c4d 555b3fcc a3daa376
+          ce6179b3 eac1bb07 35b9a3a9 79310000
+          59091a00 040d0082 0600040d 00820600
+          41030082 06004103 80a00100 410380a0
+          0140d000 80a00140 d00080a0 0140d000
+          20680040 d000b0b1 caf63626 8c8db16b
+          604c3fce e2be31f6 793266bb c05cb547
+          f19cf55a 6df3a8d7 5deb774c 59ca3b07
+          c5464b7f fb8e265f 0b7b450d 00436140
+          6b727d94 5618d445 85606201 86c580d6
+          e4fa2865 c7e3a41d dadb90bc 53c72585
+          840b0b99 2e3e646c aadafa61 1ed51eb5
+          730ecb9b 550fde3d 28f47cd8 9f170300
+          9095a001 40d00020 680040d0 00206800
+          10340020 68001034 00081a00 10340008
+          1a00040d 00081a00 040d0008 1a00040d
+          00820600 040d001b ab6c6f63 c2d818bb
+          06c6f4e3 2cee1b63 9f2763b6 0bcc557b
+          14cf59af d5368f7a ddb57ec7 94a5bc73
+          506cb4f4 b7ef68f2 b5b057d4 00301406
+          b426d747 6985415d 54082616 60580c68
+          4dae8f52 763c4eda a1bd0dc9 3b755c52
+          48b8b090 e9e243c6 a6aaad1f e651ed51
+          3be7b0bc 59f5e0dd 8342cf87 fd793100
+          0059091a 00040d00 82060004 0d008206
+          00410300 82060041 0380a001 00410380
+          a00140d0 0080a001 40d00080 a00140d0
+          00206800 40d000b0 b1caf636 268c8db1
+          6b604c3f cee2be31 f6793266 bbc05cb5
+          47f19cf5 5a6df3a8 d75deb77 4c59ca3b
+          07c5464b 7ffb8e26 5f0b7b45 0d004361
+          406b727d 9432a48f 0fa7df75 7e2071df
+          ca4a4f66 bbc85cb5 47f19cf5 5a6df3a8
+          d75d6392 4ea06bdd 6f74d2f5 e0b24166
+          c4d04ea7 a6075aed e23b6ff3 d6bbf14b
+          b573d555 5bafcda3 5e8f78af 1b854d6c
+          6741937d bd180080 ac040d00 82060041
+          03008206 00410380 a0010041 0380a001
+          40d00080 a00140d0 00206800 40d00020
+          680040d0 00206800 10340020 6800d858
+          65fb2f4b c784b10b 7f85ba73 604c3f4e
+          acff8675 e7dfb1ee 7a32db05 e6aa3d8a
+          e7acd76a 9b47bdee 1a135396 f2ce41b1
+          d1d2bf38 d81d0d00 79ef68c2 e236a40c
+          0ecbdb90 bc537b97 4afd2e65 60960bcd
+          55576dfd 308f6a8f e239b73f 1e5296f2
+          ee41a1e7 c3fefbba a301202b 410380a0
+          0140d000 80a00140 d0002068 0040d000
+          20680010 34002068 00103400 081a0010
+          3400081a 00103400 081a0004 0d00081a
+          003656d9 dec684b1 31260eec bb6f8c7d
+          9e8cd92e 3057ed51 3c67bd56 db3cea75
+          71e6a29f bcacc7e5 6de3936e 9dcd65c6
+          66572ed6 01864583 35f96c73 a2150675
+          51219858 806131a0 35b93e4a d9f13869
+          87f63624 efd47149 21e1c242 a68b0f19
+          9baab67e 9847b547 ed9cc3f2 66d58377
+          0f0a3d1f f6e7c500 00642568 00103400
+          081a0010 3400081a 00040d00 081a0004
+          0d008206 00040d00 82060041 03008206
+          00410300 82060041 0380a001 004103c0
+          c62adbdb 983036c6 ae8131fd 388bfbc6
+          d8e7c998 ed0273d5 1ec573d6 6bb5cda3
+          5e77addf 316529ef 1c141b2d fded3b9a
+          7c2dec15 35000c85 01adc9f5 515a6150
+          17158289 05181603 5a93eba3 941d8f93
+          76686f43 f24e1d97 14122e2c 64baf890
+          b1a96aeb 8779547b d4ce392c 6f563d78
+          f7a0d0f3 617f5e0c 00405682 06004103
+          80a00100 410380a0 0140d000 80a00140
+          d0002068 0040d000 20680010 34002068
+          00103400 20680010 3400081a 00103400
+          6cacb2bd 8d096363 ec1a18d3 8fb3b86f
+          8c7d9e8c d92e3057 ed513c67 bd56db3c
+          ea75d7fa 1d5396f2 ce41b1d1 d2dfbea3
+          c9d7c25e 5103c050 18d09a5c 1fa515ce
+          a240a37d 4330b100 c3a2c19a 7cb63951
+          36a91396 b7612d97 14122e2c 640aa490
+          31e8d4d6 0ff3a8f6 a89d7358 de345aff
+          bb1e27ed ebc50000 64256800 10340008
+          1a001034 00081a00 040d0008 1a00040d
+          00820600 040d0082 06004103 00820600
+          41030082 06004103 80a00100 4103c0c6
+          2ae3e236 268c8db1 6b604c3f 4eacd4ef
+          fa3d99ed 0273d51e c573d66b b5cda35e
+          778d8929 4b79e7a0 d868e95f 1cec8e06
+          80bc7734 61711b52 0687e56d 48dea9bd
+          4ba57e97 3230cb85 e6aaabb6 7e9847b5
+          47f19cdb 1f0f294b 79f7a0d0 f361ff7d
+          ddd10090 95a00140 d0002068 0040d000
+          20680010 34002068 00103400 081a0010
+          3400081a 00040d00 081a0004 0d00081a
+          00040d00 82060004 0d001bab 6c6f63ca
+          e0b8bc8d 6b39588c 715dc6e4 3ab6dafa
+          a1b67e9c 83bd6eb4 fe773d4e dab76c70
+          7971e95d 58f8af51 dac4952e b6f3b910
+          429626e7 aaabb65e 9b47bd1e c55eb7c7
+          ac256462 47c590b2 6f6b7d2f 6f056160
+          47026048 d6e4fa28 656a2a75 0e5abaa3
+          090d2f29 25b543a6 8b0f199b aab67e98
+          47b547ed 9cc3f266 d583770f 6a724753
+          f3620000 b2123400 081a0004 0d00f454
+          0eea4061 7c7ca6ef 73be79a7 b67e9847
+          b5075a77 a5357934 8326c630 73e0c5cb
+          e2cccc78 efa7fd4c c066aaad 1fe651af
+          87bfd7b3 2f1fbaa4 5e9b3747 d0b4aa8b
+          9e9b2b9f 7cdfbbbe bf525f3a 8376bd63
+          2e735db5 f5da3cea f548f6ba 5e9b07f1
+          0d94817d e92c4ecf 8e15000c 8f017d97
+          be1c920b f2d999cf cef4da3c eaf5689c
+          f3b0e619 006e9c00 40d00020 6800a02b
+          68e6b501 804ce6eb a099d607 003299ad
+          5fde7ce2 6c2a44f7 43009bd6 3aacf127
+          eaa0796d 4dbbcecd 15133b2e 2cdef9a9
+          dbcffe42 3a1e7b91 ba1f08d0 6bf3a8d7
+          c375cef5 5a5faff9 6b74a48c 3333afc4
+          c5f36a76 3e3116ad b1b162eb a57b443e
+          c0665687 4c8c6b09 bc5865cc e172f2aa
+          fdcf6c99 1c3b1ee3 ecf6c6bf d7ad3ef0
+          ecaccf18 d4d66bf3 a8d7e748 af9bc443
+          9d2d75c6 9417ddf0 c143e39f fcd80bb3
+          5347ae29 c656ddaf 88394e28 4fd9acb5
+          47f19cf5 5a6df3a8 d703ab5d dd048def
+          7ecb8b55 c61c2ee7 a7a7e7e6 4f4ed7bf
+          59f9da7e 39b2f0c1 56ab0813 1359d231
+          67e28611 ababb67e 9847b587 fe9cab2c
+          a8336195 c40955b6 7cafce98 b2b5756b
+          b1e30337 fdf3d4bd f7dd56f6 b9a3d952
+          7dfcc481 9f144f7f f6de85bf 73e0d6d4
+          9701f4da 3ceaf5b9 dbebfa6f dc9c78e9
+          e04236f4 73aabaa3 79eb076e fa769d31
+          65d85216 dbdff3be 6f4d7de5 be835585
+          3dbdce2a b4aabba0 e36f16af 3df1bc90
+          575b3fcc a3daceb9 688d2d64 43e89b46
+          ade25095 2d8fd519 13ea3b94 5845cf7f
+          feca3b3e 73e2f1a7 ee6a4dfa a4c12768
+          7aadb67e e8f5da6b cf9f2c8a 6dd75dfd
+          57d7fcfb 8feeaa83 66e1779d d50f76ff
+          c99fde3b 1f8b23f9 be8504c0 a6576548
+          9d2555a6 7cb1ce96 85bb9fa5 e776feee
+          2d07f6fc e1efdf35 7bb21882 3f9303c0
+          c8a9b2a3 ce902a4b 3e5965ca 4b4b1f3e
+          e3b737ef fec49ffd ed79575c faf9f937
+          840d00cd 42a6ce8e 2a43feba ca92bfeb
+          7cea8ca0 99b8e2ca e2eac7fe e9131357
+          ecfdc2ec 1b45e1cb 6800acaa fed9fd37
+          ea0cd9fb b92a43ee acb3a46f d0d426f7
+          ed9fb9fa 1ffef18f f7dcfed1 3f983b59
+          4ccdbf79 3a70dce3 0070fa1e 26ce5577
+          31554654 5971b0ca 8cdbaaec f8589521
+          b33f3b70 85df5f73 e4feaf5d 3e75cfdd
+          b71e7ffc 890f57ff bbb77e39 5b1d3aa1
+          5cf7d8f1 aa8ecd55 5b3fcca3 5e6fc65e
+          d7bf1660 6eb1e27c 1527636f d9f6bf13
+          575ef9e0 ee8fdff9 e59db7fc de8bfd13
+          69b55f94 766aae38 f2c0fd17 1cfbc10f
+          af3ffac8 43bf5185 cc7b67a7 5ebdecd4
+          7c717eb1 4e7fa133 f4e9ce7a d6ce5557
+          6dbd368f 7a7d8ef4 7a7e4bab 383eb67b
+          c70b55d8 fceb8537 dcf8bddd 77dcf1e8
+          d67dfb5e 2fdaaf2e 5b7bd074 983b76ac
+          fe89d0f1 9f3efacd 1d279f7f e1aa2a74
+          76551fbe b87a3baf 7aabef77 cab585e4
+          f2498422 ac6bb22f d5ce5557 6dbd368f
+          7abd497b 5ddfbbcc 546ff577 ec5fadc2
+          e5e5c99f bbfcb90b 7efb770e 57b9315b
+          6edf9e5c e8ff01eb 6a0a1a60 4668ca00
+          00000049 454e44ae 426082 
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000013c 00000171 08060000 00eec314
+          b4000064 c1494441 5478daec 5d079c14
+          c5d2af99 d9bc9773 ce702059 11242339
+          67447d9f fa4411cc 59101fa0 82113020
+          0aa8600e 04c93908 92511189 771ce172
+          ce69f3ce 7cdd3d7b b7770292 ee8edde9
+          a9f79b27 1ee7ec76 a87f5755 57d59f11
+          0401ae55 6c9595c0 308cba74 ddea1073
+          6a4622a3 8040f463 7ff4e8d1 c3a18781
+          9b10a1ce 97419f73 53efbada 67b8e0fb
+          ad8ea71c 3d79820d b274ad5b 5ef0ea37
+          b092d3e9 0058567c bff82135 73048d34
+          06f9fdd7 f0fe4b3e 83e7c16e 30005f59
+          a12ffc76 5902f010 833402eb 871f7ad4
+          e86125b0 4f6f852e e377591c 4f3e7a0a
+          907e5cf0 bf677cae 3234dccc 797a5cf3
+          8b98ab01 9e60b741 d1f7df05 551df97d
+          50c9ea95 dd10c8f5 b4e41447 f0027834
+          f42431ff 18616308 e33eef37 734a2851
+          f8f91fd5 b66cbed7 67f0c8ad fef74c38
+          a98e89a9 7d3fd348 639001ef fade6fce
+          cc80e29f 7f6a57be 75c320c3 e9e4aee8
+          dcea68c9 aff01744 9093fa3e bd15ba8c
+          5f59adf2 d765320a ed3ebf31 e3f77974
+          eeb425e0 ff1e2866 38c58d03 5ed1775f
+          c7e77c30 fff1eabf 4ffd07fd 6b08ab14
+          3f8a5134 9ab60957 98b3c6f8 0cd77f3f
+          8f5e861f 2bf9a351 e5e7b52b 70e2a485
+          214f3eb5 ad06f864 40ba05ef 772cae35
+          3f0f72e6 7fd0af70 d9e74f5b 8acb0720
+          f34dc328 1d8bafa0 689fde22 5d46569e
+          a82656f2 8f5c7dfb d63f86bd f0e2e280
+          ff3c701e 58eeda01 cf94920c b91f7d3c
+          2577d1e2 37d12206 b29adaaf ecf693e4
+          ce1b092f b01d2dae d2c7fbc7 8859ff7b
+          39f4b997 7264406a faf76329 5db73a34
+          6bf6ec77 2b8efefd a042d920 002703de
+          cd8c01fd 8937911f 14eadbb5 7ebdf98a
+          959f699a b7b83ae0 21b0539d 19d8ff43
+          635ad613 4acd255f 5506bc5b ff7e102c
+          08f8ec70 32f6c3b9 f723d03b 250352d3
+          be3f6fe1 472d2e3e fdfccfc8 1868c7ea
+          1acd2f94 01ef46c6 807e6247 c0a78e89
+          f8ecb66d 3b5e40a0 67be22e0 19939398
+          a4c1fd3f b3a4654f b9c242ca 80e722ef
+          17ecc0d8 2d908a40 af2f02bd 5419909a
+          e6fd08ec a210d8ed e25490c0 708d1b4a
+          9501ef06 c780fe8d 3700a862 c217b5dc
+          b2e3496d 8b96c2a5 80874c86 131d5a4c
+          ae3e797e 3177e553 4b063c17 da480ed0
+          dbd36ae7 f641de7d fb9b6540 6adcf757
+          fcb64775 aaf7dd9b 11d8f575 809dbc4f
+          5d5597d1 4fec08f4 f46d12a6 b43d96bc
+          043831a6 577b4d5e b4e2c7c8 ead3e7e7
+          701a68bc ab1b591a 5490d2e1 75ed9df3
+          fe5b93e5 d9687c41 f3fc309a 6f0c76b2
+          b8ba200c c3588630 ed2d846d 31353f26
+          8027d86c 903bf79d 67180102 1a2ff224
+          4b6308a7 0628dbbd fff9f25f 77f8cab3
+          d17852b1 778f77e9 ce3daf70 ea4bac17
+          595cd622 c02e2cf8 236c7b02 635c2de0
+          15af5aee 53f977d2 03ac5a9e 23b713bc
+          82567b0c b23e86cb 93d1a8d6 dd10b0d8
+          e2ae3d75 58169750 0f846908 dbfe0f61
+          1c4efe06 0546beca bd7b06a3 332b58bc
+          dcbd66bf bcb1fd7e 77fd8c26 7f3fc302
+          6338937c bf2537f7 5b6548c8 cd7f80d0
+          b84370b7 f75b0bf2 c170facc 7d8cb3d8
+          45dea7ee a2cb0cf9 ad508471 83fcc74d
+          f891c580 57f2cb8a bb3905c8 e2bea798
+          60caccbf a36cdbe6 5079361a 5e90b204
+          19d3723a e1799667 c3fd0463 1bb2f0ee
+          e68d4660 2d99194a 4160ba5d 8375278b
+          6b8b3fe7 e1d1419e 86861773 66463b64
+          2804ca33 e1b622b0 6a7557f4 2814c53f
+          7f1f6a2e 2c8f566a afebbaa2 29ae3618
+          f9fdd7fe 192cfa53 e1b75f25 22b37d73
+          83bd9c69 dc21b8cb fb0b972e 69c95eb9
+          f05fdea7 ae3e0664 e1598aca 238b7f59
+          1ec2324a 652cfa6d ad7c08b8 b7e01537
+          269f4994 67a211e6 56a94c90 67c18dd7
+          0fe7e499 ac7af3b9 94047c6a e1b88f7c
+          f7240161 55aa0879 161a4522 e529707b
+          83806554 aa500c74 fef27448 4682e529
+          681491e3 77125947 0c783a79 1e242372
+          2665e388 5e9e0249 880a039e 4a9e07c9
+          881c8b6d 244591a7 4012a2c1 80275706
+          4a47e4c2 c0c61139 4b5522fa 81014fce
+          bf934596 7f175947 2422f2ed ac2cb2c8
+          22039e2c b2c8228b 0c78b2c8 228b2c32
+          e0c9228b 2cb2c880 278b2cb2 c822039e
+          2cb2c822 8b0c78b2 c8228b2c 32e0c922
+          8b2cb2c8 80278b2c b2c82203 9e2cb2c8
+          228b0c78 b2c8228b 0c78b2c8 228b2c52
+          959a2e10 d75a1c2d d334baf8 fb1b82a2
+          50a669bc eabccbfb d44d7599 95db40c8
+          228b2c34 08c63a56 6ea0268b 2cb2d020
+          4c1d9796 b981ffb6 29be9ffc feebfc8c
+          86a43e94 691aaf69 3de57dea 4663902f
+          2d649145 166a4406 3c596491 45063c59
+          64914516 19f06491 45165964 c0934516
+          59649101 4f165964 9145063c 59649145
+          1619f064 91451659 1a4124cd a86e37c1
+          25d576ac da413f2e 8b2c948b c003f0e6
+          7ffc9001 e03432e0 b9a584f5 ed040a0f
+          3d5a555e fc01c741 e1e1a360 2e2e0786
+          9337bc2c 14839d1d 40edef0d 8177dd81
+          2c03bbc3 1a60c156 550df907 7e9701cf
+          bd5653fc 4778ff5e a0080f05 b05ac51f
+          a8d55071 2e158c05 e518fb64 91855ae1
+          09e0f941 f8f81100 668799a7 54822d3b
+          17f2f7ff 5e6bedc9 80e74662 339a4061
+          3020c0b3 397e6047 663c2fef 76596421
+          2e2dd285 6aa41f16 8b03f0ac 4467a42c
+          72344b16 5964a146 64c09345 165964c0
+          93451659 6491014f 16596491 45063c59
+          6eb530ac bc9c8d32 af9c3caf 32e0c9e2
+          722b692d af047346 badcb5bf 01c59299
+          09d6920a 00398d49 063c5797 cbb25731
+          d2c40356 0560cac8 63cbb66e 52c8dbba
+          e1a46ce7 36d69896 cde20a1d e999aecc
+          b5e98c84 44d2348d 78f1844b d698111a
+          7914b76c 8ed0f655 b02a950a 8ddbdae0
+          0745631f 442efa7e 349f1c83 a7569018
+          4d231a0f e6fc101a 6fee649a c6263fc0
+          6ed12adc 4a430f8d 4f29db65 0daa5178
+          3e395a14 8491f65a 129a46c9 62c0e5aa
+          2a186913 532ad0e8 34204b43 62029e4f
+          691e2297 b9e49272 2512c63a ecd2f220
+          419a466c 99e3c563 fef11ad6 59442b45
+          fa3bac9c 5ec855c9 6b9097cb 348d583c
+          1df32a39 9a467cab cfd47b3f 531b0662
+          dc4b17ae f5fd3c86 78bb642d 3cfba5c6
+          abc4530c d40e0595 a5e1c403 246a35b3
+          0aee323a c34b39ee 63671d16 9e346313
+          027fd953 4dca80c7 28143e32 4635e056
+          5228fcd0 3f749204 3cee3217 fa352ead
+          34233f52 b7f0ec97 2c1c2bed be508cb5
+          203f4886 a986136b 3e994f49 9e92ac92
+          bbc45010 ec76292f a784014f b8c2a585
+          845d5a6c bc162cf9 2c14e416 580d26f9
+          4b168649 f59e8b51 5e7a1723 e0467912
+          77696d92 8573d2f8 93b9ea22 4b6b1733
+          f1324c35 e429c224 4875689c 4af5cfcd
+          03bc4dd2 169e1503 9e45aa16 1e6fbd14
+          cb598584 0b110462 c1b605b9 a6b6610e
+          4c830104 abada554 93d358d5 652c3c8b
+          55ca169e 096b8651 a2ba0f3c eee4ca5c
+          7d91a5e3 a2009873 8be3aa0e edd7cb70
+          75f352b2 ea67bd21 25a38524 cbca8885
+          a7fca781 0776a433 124ece27 169e59aa
+          d68edd6c b9c4a5e5 d42ae902 1e87ccf5
+          d28a40e3 99d36d65 b86a80f9 54a91241
+          100225eb d2ea7497 bab4d2b6 f08cd275
+          6901039e f9120b8f d3a825ad a41c03ca
+          8adf76df 09122f02 6f0a29df b1ed4e86
+          916ee58a 52afbbd4 c23399a5 bca4d275
+          69717621 6fb25cd2 1182d36a 25ada4f8
+          46b17ce7 f6debcd1 08b2dc84 83800e4b
+          04783d24 7b438b3b 4de8b597 58783693
+          49f2165e 25483035 05af99ed 324aafd4
+          4b3cbc85 0c58737e 79afd2d5 2b4365d8
+          ba71295d b7dacf94 53dc8f91 a043808d
+          7f56cd00 ab54413d 4f005b78 06e3e5f2
+          f5a52038 65a31c03 5e154834 35c58629
+          e8f8fa5d 7d38ad1a 58a9778c e379bff2
+          5ddb07c9 b07513ee ecce6d03 c0ce074b
+          d5fb5120 3d506835 f5010fe9 8a5dba34
+          8d5634d6 721699ee 150ef493 9cc94e38
+          36712e5e 8d5b8b16 9753a981 d328a57a
+          8a896e3b 3ab80b57 acfcaff1 e471393f
+          e546fc9e a4d34ce1 8f3f4fe6 247aa18f
+          318ed368 c9e15fab 08584790 ae589185
+          c74873d7 58198e2b 63f577dc 59c9e935
+          76c955d4 62f31c01 9e60a913 c7432798
+          42a7458f 46da317d 0ee79059 7be6bcff
+          563f19be ae5f72df 7fbb87bd dadc4bb2
+          9d050531 7e476e69 6b3c20dc 0c14e90a
+          b1f02416 b71490ff aa0ef135 073c38d1
+          c0fa0c1a 6a52f979 540b128b e2e153ca
+          5a5525de 3ad5b5f0 f41ac742 4b5b69b1
+          9557bc7a cd4ce3c9 137243d0 eb1053f2
+          19b668e5 aaff21eb 8e916af0 1e1b752a
+          6f2f60d4 75627848 47701a97 b5ba5a7a
+          161e4ec8 5770e50a 6f1f1bcb 9b4c1681
+          17f225e7 d262c0ab a8162f2e 6a2d3c1e
+          5804762a 6f4f49bb b435569e cd60eb96
+          f3deec49 122f086f 50c97e77 ce445bb5
+          a53f4839 ce8bd441 171a52ff 369665d0
+          7e311222 2849bab4 825080f4 c052d33c
+          a0448a8b 6ab7d8c1 54542ae6 6ad4116d
+          70a0b47b 593b44a1 01c8fb61 d59cc2a5
+          4b5aca50 7675c99c f64262c1 373fbdab
+          907acf68 04749ae0 807fe80b 03e6d232
+          a433bc54 75031b75 a45b8ae0 f817492e
+          aca9a010 ea1d59e8 67ba8830 c9935bd4
+          803ea700 dfacb7df 5a683879 5c766dff
+          450c274f a8723efc 7031ab04 7f291f86
+          e2850507 9aa0807f a4a4b060 c8cd97ac
+          5e08164b 111e2f8b ff4fb059 0ba51aab
+          a8cec9ab 7f62a1f1 620b8fd3 b0541423
+          2005064b 7a4e9f94 5143a691 0b1c592e
+          dd27361b a48c1bfe aa6083de 8cc45396
+          70ac5eed eb096a1f 6fe402d9 eb1d8e06
+          a42b520c f5603dd7 24b6c803 8502397b
+          3a1d04fc df7f73d0 d80587b5 2799071d
+          5a8231bf 50e0ab0d 82c0b284 e6022db2
+          a0f6f321 8f20c131 5fee61b5 c8d24dcd
+          999e3973 da5d84b3 407e6a1f 3c4559af
+          4fef653a 9f319d55 d5f800d2 7d10a009
+          da902041 e1ed85fe cc139dc0 bac15755
+          0bc6bc42 a233521b 33c675ff 7beecf66
+          b55a0478 1c07eaa8 e81c3472 c919f2b8
+          98de5c54 4202b135 2d937053 50b4d8a0
+          8b0c059e 9e58bec0 28419333 f7c34586
+          137f7b30 0c03b288 243f055f 2cf6cf7c
+          6bee1246 052ae9de cbd6b776 3c1362d1
+          295867b0 48372c48 478c0545 44672418
+          d911788b 39131f70 2c39e9ac d63c4682
+          891a3874 67a93443 5566b613 f01c8bee
+          dd3c1e68 527bb491 f1e9dd3e 65ecf0b7
+          ed4623d0 0e7a78fc c6b34990 fdde3bf3
+          39052432 94801da7 62c0332e 06ea75c5
+          46ba518d 74c45e6d 95e40dad 801ba458
+          ade49e82 c50bcf9b cd5938be 2f82a1c4
+          1e1e988a b3171822 62820ac3 f03ce315
+          1fc3283d 558cc04b 70cc5778 70bf3cd3
+          85ec2773 de9a35b4 46e9697d b064bf31
+          e341e385 cc871865 8d2120ed 47b001a3
+          0b0960b4 c1810c63 e79dfa80 50a0e25c
+          2ae3f0f2 a4356634 264ea534 2ac32332
+          f1ba133c f71b3d36 5b1de85d 2148b0a2
+          169be895 a9e960af 74bab560 b3813a38
+          103ce2a2 80b70255 c2698045 2edc2779
+          0b3f0aa1 d6bc43d6 4dd6ebaf 25142dff
+          e5038596 9e61e30b 09ef16cd 80f3d43b
+          f95e3816 ecc89dad b8902a49 771617cd
+          aa43fdb2 0226dc5f 4a2c3cfc 7f9a662d
+          aa38bde6 bc14ab0f 70a30063 5e3154a5
+          65013878 3809d130 f263fcdb b506a0cd
+          b3c3a92a 4a88cd9d 3f77be31 f90c9578
+          67387d4a 99f5e6db 4b480a0a 35680724
+          33c1bf43 ebfab7b3 1c07d519 5948474a
+          24db5443 b05a4f08 76bbad16 f0048b59
+          e00dc624 c91ee868 a8257f9f 827a5c0f
+          362bf8b6 6e01da40 2ff2f754 611e3e04
+          d272eecf 7c6dea7f a933ee0c d5706ec2
+          c869680e fa0045ad 15b027e3 191b09ba
+          a808b4f7 edf5e277 c527ce80 20e10b3c
+          6550d085 9a6a2bb2 e48c5a8d ddda14a9
+          b2fbe193 abf4cc59 b0169600 53c34b6b
+          e741e1e3 0d011ddb 836003ea 04bb7225
+          6b36cecd 5bf86102 4de3cefd e0fd9e86
+          a4b4d7a4 9e6f7739 09ea7c3b 3098c7c2
+          91808a75 c156540a 65a7cf4a d6bac3c6
+          6ce0238f 9d65359a 3a808706 ae6bd7fe
+          8c541371 497a4a71 35949e44 46ac5251
+          6f3682ba 7504b59f 56d227dc bfcc4b40
+          eefcf99f 982f9ee7 68186ffe a205fee9
+          33de5cc2 69404dd3 3a63eb4e 171900be
+          ed5a0158 ea04ad91 2e949c38 0da6c24a
+          69c6ef44 4fde2298 4ca76b8d 9fda3fa8
+          35271905 5729d5cb 797cdd5e 70e877e0
+          ab0de8cf cecb0b55 60200477 eb44dde5
+          059913cc 7296963d 2879f0c0 17ed6565
+          921eabf9 c239c899 376f2ea7 8016d4c5
+          6d91e716 dab32bb0 1e1eb5e9 28580778
+          8311f20f fc2ed5fe 77a4adb1 3ac03bcb
+          bbdf80a4 4b002fe0 fe07b3b4 91811704
+          892a3e2e b1aa4a2f 8092bf11 d8d7a5a7
+          43a017d2 bb2be823 fc40a010 f4581d80
+          21e5e2cc dc051f74 045eba2d 64325e79
+          e1ff8c17 331f6654 74ad2f6f 01f08a0f
+          85804eed 91ad53a7 b410e940 c95f27a1
+          3aab90e8 8624ad3b 9ed40d1f d5b5bfc3
+          7c09e001 c70982d9 7c50da26 0d40ceaf
+          fbc05e59 e5bcc040 6e2de7ed 05d12307
+          21b39ea1 a3a9c03f 6c7e4e0b facc59b3
+          3fadfeeb 0fe9117e 0802e4bc ff4e7cc9
+          facd620a 8a40d7da b20a0622 870f0046
+          a375261b 63eb0ee9 40ceaffb a53d7c9c
+          86d37ff0 a1ba0739 eb74f958 f0ea3be0
+          9094fbc4 e193ac3a bd100af6 1dae6fe5
+          992de0dd be3584f4 b813ec66 a0525805
+          74baf0f0 c3afdb4a 8a255585 517dec4f
+          55fad4e9 8b198e0f a46d4df1 5e0eeede
+          113c6f4b fc8775a7 82fc7d47 a03a43ba
+          d61d690f 278055df b9cb81ba d919752d
+          3cf01e38 e4a0c030 5592566c b4c0d93b
+          f6832135 c3097af8 b606597a 11230682
+          77423871 036813ec ea559d4a 7a2effb3
+          4ffa4b62 3cb8836f 69095c98 38712acb
+          413fa08c dd43b0e0 34946062 dde18c84
+          dad64068 cfe3bd9f bd639f74 c10e8b0d
+          0dd5d733 55dfe18e 63f5f4bf eebff88d
+          1a7b511d e2f73748 58e1f16d 94b5ca0c
+          196bb780 60b6d6a9 beb003ab d342ec84
+          11a0f4d4 d0776b2b 9086a18a f4196f2c
+          ce5b303f 500a565e fee24f7b 561d3f35
+          9d5153b6 94761ca6 5042ecf8 e1e24585
+          cd56ebca 0a161b64 acdb02d6 4ab3646f
+          66c91ce0 0a8bf0e0 ed1e9dee b25e11f0
+          58bd1ebc 7adfbd9a 973adf03 5280d2d3
+          6990bd6d d725aead 363e1aa2 470d145b
+          29d016cf 431887ac a1b89cb9 f3e699d3
+          53dddaba cbfbec63 bfb4e933 972010d7
+          d0b68e38 af347c50 2fd02726 d4776595
+          4ac8ddb9 074a4fa5 e2124369 bbf368cd
+          03fffbc8 c64b3cbc 7ffec067 d8c8cd3c
+          3006a96f 0adcfb2c 7bfb0128 c715189a
+          3a577726 330474eb 0441dd3a 5019cf63
+          d16160ca ca7b3075 ca23f7bb eb182c99
+          1990f3de fb739141 435d0a0a 8ff6ac5f
+          bb6610da b72739c0 6b5d59b5 1a2a4e27
+          43d6d6bd c04afca6 1a5bb84a 5fcf14cf
+          1ebdf65d 15f0fc86 8f3aab89 08dc2b48
+          3c8e8573 8f043b0f a92b3680 b5a01870
+          375471c7 08e4891a 35183c63 43e88be7
+          89ae2d94 eddcfb61 dec28fe2 dd710817
+          a73cf21f 5346ce44 5643d9d2 21cb4e13
+          e80931e3 868975e3 35ae1afa b3adac1c
+          d2566d40 fbd92edd bcbbfaee ec728f4e
+          5d0c5705 3cecf37b ded9e92b 1a9a63e2
+          a0adb1a0 025257ae 17dba2d6 4948e63c
+          3d2176c2 7050e855 f4c5f370 6f38de1e
+          94fdce3b 1f59f372 dcaae828 efd38f13
+          ca76ecf9 8010f1d0 e4caf262 48227af4
+          10508504 3b2b2a18 b15352fa ea4d509d
+          552279eb ce61b318 82273df1 d36575fe
+          723f0c7b 75e666ce c7f3020d 8a8e6319
+          c5c7ce41 ee8e3d00 aa3aba6d 31833e21
+          1ea286f5 a3329e87 ad234b4e c1b0a421
+          839eb615 16b8c577 ae3ab84f 79f1a9e7
+          16318c2d 8886eec5 75c58e3c 91909e77
+          826fc7f6 c895ad13 8b51aba0 e8c0ef50
+          78f82489 5dd3e0d2 eb9a45ed 087c7472
+          d235039e bee39d55 9ac890af 044adc39
+          6ce9e1d8 46c5a964 12eba871 edf0c609
+          ead51502 ef6c4d5f 3c4f0081 d301541e
+          3bf97afe e285ed5d fdebda8a 0a21f585
+          e7a72283 a61feeee 4cd35261 25f78c0b
+          81c86103 c41bd93a 2928c68c 2c485fbf
+          43bc91a5 209e69e7 410879e2 e945987f
+          fa9a010f 4be07f1f fd1a1936 85346c18
+          bc19706c 2375e546 b016d589 e7e18d83
+          3649e498 21a08ff0 a7329ea7 548357fa
+          acd94b2a 7edbedd2 11b1fccf 17f5283f
+          72f47f9c 96b22542 5e98425f 9382a277
+          f6bac3d5 14260b89 db59ca8d 40437718
+          311d4775 c8a37397 1d57346e aef41741
+          93a6646b e32296f1 94583638 b6519d5d
+          0269ab37 89dd606b f2d0ac36 50fafa42
+          ec3d2390 9bc7d117 cf6309e6 774a9ffa
+          d22c6b91 6b9e7f05 5f2cf2ce 98f1fa12
+          04ce6a9a 6c3b3c54 dcf42272 685fd037
+          8f176f65 6b44a980 9c6dbf42 e9990cc9
+          a7a0d475 ebfd860e 9cefd1a5 db1513eb
+          b859b366 5d7e9fab 35b818ed 5cc9d61d
+          0f724ad0 d499e3c6 5e43b855 9f812dbd
+          eacc2250 79a8c003 6d20a1a6 51223a35
+          d5a12198 c4174a4f 5d24a725 73ebc6d0
+          e473845c 7ec69096 db59e9a1 deefd5ab
+          4f9a5063 fddee8cb 1bb00f19 2e853b37
+          7ecc42be d238101d 5a020dfb b4d69535
+          0104744c 84c8d143 8141ae6c cd7f80fb
+          5b969f38 833c964d 38af12ae 90432ea9
+          39c2dc34 8c527534 fe8b652f 2bc3c2f9
+          ebb6f0f0 a60c7c78 52ba262a 64096fa4
+          239b8930 9a2030cb d8f82b54 269f0346
+          e5bcd212 cc6608e9 db13fcef 48241b8d
+          32d75640 d6933273 f6db8bf3 bf58e4cf
+          b8c862e1 04e3f3f7 8fbbdf9c 57f228ab
+          a52c6e87 2c3b4d90 174463b0 e390e7c1
+          13d665b4 7f15602d 2e81d455 c853b10a
+          924f41a9 b5eecc20 f80e1d30 dba3d35d
+          b6ab382c 571685b7 3744bff3 de0250ab
+          721dec5e d2f7e0d0 8968335a 216de506
+          b05554a2 0dc4397c 07813458 881d371c
+          b4213ed4 c5f33041 33c3f3cd 33a74f7b
+          cf151a0c e04f2ffc facbb8f2 3dfb3fe0
+          682b1de3 45cd8d19 331854c1 8198b341
+          9c109621 96374e41 31e69452 9182526b
+          dda914bb c35f9ebe e16ade03 7b352abb
+          80fb1fcc f31d3660 bee39692 0a3a434e
+          054c557a 2193b166 3361b1c3 3476784a
+          c0666394 017e4cec 3dc390f9 cc32829d
+          1e8a4782 790858ac 45151353 268c1e2f
+          1a59b78e 66b162cf 2eeedcc4 498bd122
+          043bac18 6ad601e9 2213daab 13e37b47
+          3b06cc66 27dda25a cd14ec3b cc14fe79
+          8641162f 1df381fe 6743d65d ccdc77df
+          f0e8dc85 bfdabeb9 26831721 e76256ad
+          3c438b95 27c63071 87e4e350 b0ff30f2
+          1dea9810 b89554db 561031b0 27d8296c
+          188aac29 a662efc1 8f907515 75abbe03
+          b6303366 bc36156d c6feb471 5360c3c3
+          2b211422 87f577a4 a038fe42 ad02636a
+          3a646ed8 29c6ed68 71ed11f8 6be32257
+          053e3871 ef35e9f5 b5fc1242 ce6adfc1
+          fda6132b 8fa1289e 87364ec6 fa1d6038
+          9f463654 ad58ac10 36a037f8 b78d071b
+          6df13c7c 6b6bb387 a5bdf4fc 02dc7ee9
+          5648dec2 8fba95ef 3f3283a3 ad740ca7
+          a0e81c29 283abdd8 f6899c42 1cf04613
+          a4aedc40 3a01311c 251382b0 c86e878a
+          b0679f9d a5f0f1bd d6ed7b6d 12f7f9d7
+          1bb4f151 ab798a12 70f1c6b1 a10d7471
+          f95ab057 19ead4db 225357a9 80d87b46
+          8236d093 3a3e0cdc 6ec95e5a 35f2dcbd
+          a39f6cea cf2efae1 9ba0ecf7 deff42a9
+          06cae0ae 2605a50f e813629d 5d50f0c9
+          8cf665f6 e69d509e 92453c13 6ae6c308
+          e0d3a3cb 07010f4c bc668ad9 6b063c65
+          60101ffa f4333330 a2d2d481 02077eab
+          2ee641e6 ba2d6250 b84e7e1e 0e18c78e
+          1f468051 e0e9523e 5c9d52f6 ebbeb710
+          00b56e32 77aebc0c d29e79f2 53305b5a
+          d2d6d0d3 8e53506e 4f84e05e ddeae7db
+          69d450f6 f709c8dd 7d08389a f83ab02b
+          af549e8d f968c107 0a5fdf6b dfb7d7f3
+          19010f3e 7cc6bbcb 9defd196 96814fcd
+          bc7d47a1 f8e09ff5 e379e894 f5e9d006
+          c2fa75a7 af0a03bb b68ce07d febffffd
+          aaf8c76f bd1bfd34 afaa8294 b123a6d9
+          caabc751 47c46315 bba0448f 1deac81a
+          7004ee94 4ab0e415 42daaacd 20d804a0
+          e6104036 070e25f9 0d1b3855 7f7bc7ca
+          ebdcb6d7 2e0a5f3f 88fdf4b3 0f913573
+          88aaac27 466c2795 be762b18 d3329df1
+          3cbcf190 a5173eb8 0ff8b48a a6aede96
+          246033d0 f1dc830f 2d2ddfba a9d19c29
+          7c497176 d49051a5 bbf6cd61 29033b52
+          dd88f65f ccd821c4 a300ab23 7e823bfb
+          f07648ff 652318f3 cb09e526 35070002
+          3b6d5cd4 8fb10b97 acbb8173 fa7a265f
+          007d878e c698f9ef bd8810d6 44936b8b
+          dd565c93 98ba7c2d e1f324fd c6c8ecf3
+          c8ed5523 d77604a8 fd3ce88b e7e1b026
+          0f63b3e7 bcfe8925 33bd513e 236fdebb
+          dd11d87d a5d00207 94097665 437a7502
+          df3bdad5 7765d1a1 9bbffb00 141f3b0b
+          54e521e2 038055e4 854f9ffe 8a3234ec
+          baab766e c8080e7a e4b1435e 1d3b7cc4
+          5366d160 d7b62225 1bb2366e 07e01475
+          e27956d0 448441cc e841e447 d4c5f334
+          009507ff 9c74bccd 6d3f20f7 d6afc14e
+          f2ca4acc 27db3def b3852b15 6af0a10e
+          eccc22a7 6c04e982 62afd7bd b8fadc45
+          c8dcbc5b 3c706831 3c1caeac efa82133
+          821e999c 7d23258a d70d78f8 43386f1f
+          88fb72d9 6cc10e47 a8eb1387 5caabcdf
+          7e879223 479dada4 b0a0d3d7 af530772
+          1ad3d81a 9e41a0c7 571beebf 30f1a16d
+          99535fb8 db929571 53efb3e6 6443ca98
+          a18f66cd fd701d6f 328550c7 3a465250
+          54240585 f3d039bb a020cf82 afae162b
+          810c167a 5250c0d1 eb2e217a 6dcc870b
+          97deb0fe dee87fa8 6bdbce10 33ef9da7
+          91c95d45 156f006e 0dcf0b90 be660b98
+          b373fe41 f5c843c4 f081e09d 184127e8
+          8953d131 ebfd0f77 9c6c7fdb b719535f
+          ec69c9ca 54f35595 60af74c6 96eb56f2
+          88a68c9d fc3dfe3d 4b76b62e 73facb23
+          4fb46bb9 a57ce7be 2f901beb 471bd861
+          2382a4a0 0ce90dfa 66b14e57 d6918292
+          b5710754 5cc80596 b25b5986 e1f2c3a7
+          4d7b5615 1179c366 16733d66 61cdefd6
+          6c567b65 059ce9d5 fd65c3c9 93ef3750
+          d054a86f c036d6d4 ddfcfbf1 69e3d32a
+          0612273f 448ab76b f903542a c1909105
+          499f2c63 6c065363 9cc06e31 47985f01
+          292daf0a f44c6694 ca63c862 c9087ce0
+          814a6570 08de4782 631f91f7 97ae5fa3
+          329e3def 83dcb318 c166ed68 29a88cc0
+          692f3751 452134d1 fc34ca67 e0c3d2ff
+          f644a1f9 23ff11df 5fb3b7b4 1a28fdfd
+          2f4859b6 426c0ac0 dc7a3d68 9239c2ae
+          ac012068 c2e80712 7e5efdfd 95f0a8d1
+          010f8bf1 d449c5f1 366dd7b3 6a18dc00
+          27b1db00 5e0de845 0cee0e11 6386a053
+          d84aac3c dcb382d1 a8a168df 11e6fcf7
+          6b816df8 188b5bcd 11063ef2 3646b45a
+          842bb819 e4c074fc 5e03948b b92de091
+          1494002f 68f5dc63 82d2cf07 670188ef
+          572ac15c 50086716 2c054b49 6543dcca
+          bacd1ce1 ceebeab8 981f5aee d8fd7fea
+          e818b819 c0bb6988 d2b66a6d 8b9ef7ee
+          14816772 a88be7a1 4d97b3f3 0094fd75
+          aa7ee919 724130d5 6370773a a91eeb9d
+          a80a11cc f03f7137 6285f808 8e87fc3b
+          abadff7b d40ab981 642066cc 505006fa
+          8360ad43 a06db741 faaa8d60 2aaca42a
+          0585b8f7 76b81036 75daf375 c1ee2622
+          5237bba3 19087d71 6a86fff8 b14fd84c
+          40d7fd24 49851220 6dd52692 008a4fe1
+          da93c76e 87a85143 c0332e94 bea46459
+          6ed8950d eddd097c 6e6f5b3f 0545a582
+          bc9d7ba1 f8f879a0 ad7ed86e 026bccfb
+          6f3f15f4 c8e4c206 52d98691 c839efad
+          f36893f8 8160a3cf ca331694 3ba81e6d
+          a4679eb8 5276e03c f4107bef 083aa91e
+          65b93e57 d682bba0 8443f850 b10b4a6d
+          a809815d 55f239c8 dafa1bd9 6b5419bc
+          084b3cda 257e18fc d4735b1b d0466918
+          51c7c641 f3956b66 22c5de0f 9429373e
+          754bd0e9 9bb36d77 ad9557e3 daeae362
+          216ac400 19f064b9 b262f340 0ec598f1
+          c380d33b 5350f065 98bdb20a 2eae5a8f
+          2c1d1b55 29281843 309634fb 79cdebac
+          b6e19899 1af4c25f d3bc8531 eaadd913
+          791e8a69 cccfc3a7 70c5c933 8453c009
+          7a6608ea d90502ef 6a4b5f6b 7859aed9
+          92891cd6 4fec8252 37058563 2173c336
+          a84a2ba4 2e050563 08c29247 34892d8c
+          0daaa70d fa45d122 854dfbdf b9c0ffdc
+          f7046d7d e2709a00 2ee0beb8 620358fe
+          49f5888e f0e83143 411f1d24 c7f364a9
+          6fc89831 114f4b08 eed5b51e 81368309
+          b48f1c25 4d2b686b 618fb103 61c8b308
+          4b52a081 a9041a25 a5337cc6 9b2b742d
+          e2170a94 d595e218 8b21b78c 7081d6a7
+          7ab483c2 db0b6227 8c004eab 90dd5b59
+          88e01414 6db00f44 a1c350fc 414d1714
+          0518b3f3 207dcd36 314d87a2 c47e8c19
+          083b1621 0cf9a151 74b4315e aa894f80
+          c4759ba6 a1c53a42 9b72e3d4 8ba2bfce
+          42eeaebd ce541546 746d3d9a 2790068e
+          bcfda658 0e659182 62e3f390 6344229e
+          007fb15d bbc34be2 ad36485d b10ecc25
+          d5621e27 2d736227 ba720c61 c72b1843
+          1a453faf c44b7bd9 2f741d5a aa0c0840
+          58cd1fae d8b5f75e e0408bd6 f15afe63
+          97e0fbbc 29d716bf 1f1d2395 17d2c033
+          2612d4c1 41c8d5b5 89c7b4dd 0e1e71d1
+          602e2880 ea8cc21b ddcc6e3f 47f2fbc5
+          164761fd ba4070bf 9e2098ea bab26ac8
+          deb213f2 f71f17f0 651843c9 1c216861
+          040b5445 cc9a31da 7fccf88c 6bc19a26
+          691e703d 1231e3cd 5341131f 78de11cf
+          a3870088 c5b1191e 9dd2ebc1 5a5e4eb8
+          426b5c16 9cb6123d 6608e8c2 7da96b25
+          258b2876 9c82d23c 0cc287f6 23fd146b
+          351e7904 15679221 7bfb3e72 49415389
+          3ac60884 152f23cc 38d6a8ba d958347a
+          3512feea 8c6f7509 d14b1cc1 7a7a280d
+          55c05467 1533e9bf 6c24ff2e 0afa3bab
+          8d51fafb 3131e386 332ca67a e4e9a27a
+          a4fdc1d4 9e4abd9a 891d3f82 e1b45a06
+          59fd22c5 a242c1d8 ca2b99d4 559b18de
+          c2e37391 9a3941d8 c0e89bc7 fe88b062
+          49ada5db 48b8d4e8 7d28d4f1 cda0e5e6
+          6d2f332a ee6f9a92 92f14ae2 dbb5c2df
+          4f43fede 43ff283d 3383779b 961031b8
+          b76ce551 15b81353 50a286f6 015d5c74
+          1d221e62 7a40c6ba cd509d59 44153705
+          8e65daed 90123173 e6b3082b 1a3d5cd3
+          248d7734 cd122bc3 5f7ee511 9e87729a
+          f2f36aa9 1e37ee84 eaf3a997 503d86f6
+          eb05feed 9a91aeb6 b250e0ca e22e2877
+          b480a0de 5dc9fa3b ad023514 1ef81d0a
+          0e1e0705 65dd8b79 1b98a367 be3a31e0
+          3fff2d6a 92705353 8d2de28d b7ffd2b5
+          4c788528 374dac67 98eab1da 02177f5e
+          27f684e3 9cade131 214bccf8 e1a00df6
+          962d3d89 0b494109 f126443c e424ac6d
+          27a60463 4616e13f ae75f228 71813016
+          204c9889 b0e14093 e963538e b1f9caf5
+          9f6b1262 be1128eb 20825d94 aad47cc8
+          5cbb9564 cfd7a77a 0c82d871 43498a02
+          65ad1728 42bb9a14 9421a00a 0c705e54
+          e02e2866 0ba4ad58 4ff852a8 4a414118
+          80b0603d c284f94d 6a8034e5 87695bb4
+          84a8396f bd60b3c3 29da941b a718e41d
+          380645fb 8fd4a77a 349bc0bb 7d6b8818
+          d09ddcde c9224d57 36b47767 f0e950a7
+          0b0a3ef3 940ac8de ba0bca92 33e9aaa6
+          e0094547 26c28227 10263469 a66e9337
+          cff6bfe7 be92a857 5f9cc4db 986aeaa8
+          1e194cf5 b80d8ca9 19a40b46 4d1c03af
+          7ed8a0be e0db2a56 8ee7494d b711d879
+          370b8388 21621794 ba443c15 27932067
+          e741ea08 b491eedb 11064c41 5890ddd4
+          1fdff46c 0148eb23 df9e7758 df3a713a
+          6df13cdc dcd25a69 868bcbd7 016f3038
+          eb6ded76 60544a88 9b301234 fe7a10e4
+          789e3474 1bd92e4a 0f1589d3 b27aad93
+          88075976 b69232d2 4791b7f1 54116863
+          9d47baff 0ec280cd b7a266ee 964d75fc
+          f7cb1768 e2a2960b 94523de2 2e182443
+          b966d12d 56508507 43ccb8a1 24454190
+          e3796e6f c9902e28 43fb822e 3ea67e17
+          14f477e9 6b364175 7609553d ee48dc2e
+          2eea57a4 fb6fdd32 fdbb551f ac6bdd16
+          a2e7cd7b 1a797367 696b2545 e279bffd
+          21523dd6 8de7992c e0dbb13d 84f5b94b
+          eeaa2201 57167741 09ea5527 05059f6d
+          68bd0bf7 1d82c223 a7e8ea5e 2c90c84d
+          21d2f949 48f76f59 e0e6961a d37e23c6
+          1446bcf0 cc63bc85 3152a50d 0ea32e6d
+          f5663067 64d789e7 89bb2262 687ff069
+          19453d1f 86db821d 4941f121 2dc1eaa5
+          a0285560 bc980e19 1b763604 eb985bed
+          779b0984 b0298f3e 8174fee2 2df5b06e
+          ada9c341 f4fc8ff7 ea6f6f3d c36e2413
+          434d440f c7f32ca5 46b8b862 1df02693
+          333f0fc7 f3341a88 1d3f02d4 3e3a9c98
+          298b5ba1 1dcebd64 49680213 f1d47641
+          c104da06 23e98282 e3b8d490 15219de6
+          916eabc3 8217844d 7d7555ed 3ea712f0
+          1c12b764 d97c7554 f81a8132 378e452e
+          4d595206 646fda41 14c219cf b380262a
+          9cb40e62 1cee802c ee2138b5 28b4cf5d
+          e0ddaed5 3fba1773 a40b4af9 b91c12c7
+          a5c69345 53c0f97a 1f6eb979 f374754c
+          dcadd739 579814fd ed1d21f6 d3854fa0
+          c3f02252 6e9a9a44 909484dc dd87a0ec
+          e8894ba8 1efd3adf 0ec13d3a caaeadbb
+          18773805 a5793884 0fee4b42 13ce1414
+          15941d3b 05b9bf1e a62b0505 41bddd06
+          65c19326 4ed2b5bb dde01246 86abcc8c
+          cfc0a179 e14f4f79 1429375d c92aac98
+          be90ba6a 239873f2 48a99178 3462aa47
+          1e22470c 04aff830 19f45cdd 92416ba8
+          f05443ec 3d238190 ced4a6a0 28c15a58
+          0ce9ab37 81803b1a d39382c2 d8902b1b
+          3ae5d1e7 a3defde0 940ba99b 8bcc0fda
+          18310b16 edf6ec7c fb6c9b11 e8cacf43
+          18672eae 8234047a 02bed1e3 ea503dea
+          45aa4725 5226b935 bcaba29d 087851c3
+          fb833636 d2d90505 a71df13c a4ffb209
+          0c79e5f4 1068e37b 1aa4c3aa a0806561
+          d3677ced 62f6856b 49ccc70b df518706
+          6da48edf 560d5072 f28248f5 881392eb
+          c4f374b1 311035a2 bf087872 3ccfe504
+          5bdf819d 5a41508f bb486a51 ad205736
+          7fcf7e28 3a9a4455 e918d65d cecff764
+          8b4d9b5e 524746b9 9a43e542 1385dc38
+          8f4e5d84 b8654b1f b75b209d 36c5c149
+          a8b8db6d f989d397 f4cfc3ca 14d4b53d
+          d8e4d233 d7023b9c 8212ea0b 51a3873a
+          43110eb0 ab4eb908 999b768b 37b21479
+          2c360b18 83273dfc b047c74e a5828b91
+          b7b89c85 8727c8bb cf80ac90 c98f4cb6
+          1b81aa22 2b9c9b85 4b8d5257 6e024b61
+          312941aa 55225e40 4a35183c 6282e426
+          03aeb257 1d2928b8 db8dd2cf d79982e2
+          20d04e5d b90e6c06 0b5504da 38bd2cf4
+          b189af44 be35f7a8 e0824c55 2e194265
+          542a885b fce536af ee9ddfb1 d195924c
+          ac3c635e 19a4ad5c 8f5c585e 8c03919d
+          640785a7 27c4dd3b 1238ad92 309fc972
+          8b951b1d 3c617dbb 8077dbdb 9c9cb28e
+          1494ac4d 3ba0f242 3e5504da a47638d0
+          6f45c4eb 6f2e6458 d7bc9d71 c96f2538
+          32d3a3de 9bf7a62a c0770775 548f1a80
+          e2632990 b7eb37e7 ad2d168b 053c9ac5
+          43d4b0be 244e2287 f36e21d8 91149408
+          3105a50e 110fee82 52fae7df 90fbdb1f
+          24cf92b2 032025e1 bbef9e56 858603cf
+          bb663178 a3d134de a83b5b57 d451d182
+          ada2f840 d99e83e3 38257841 c3e8b85b
+          500462f7 b6e27c06 784687e3 2c7510ac
+          b5548f82 475c0c98 0bf3a12a adb0b18a
+          cf659ac6 7f11645d 0b4a9d1a 9a3f721f
+          a8902b4b 6838711e 063a9ccc 854570ee
+          eb9fc16e b2906ed7 94cc1183 5c594bf0
+          c407ee0b 79eab953 d044d69d cbd13436
+          c4802267 bd753164 e2034fa2 0915697a
+          291112cf b3da45aa c7e25292 b6235abf
+          98c05380 e8b1c340 1fe14f82 e6b234e5
+          9e145db7 c811fd40 17130902 4e41c107
+          11ee7063 e7217dd5 06301555 51d5bd18
+          879dbc7b 767923ee cbaf7f65 55aeedc3
+          33d78392 35bfcb34 521fabba dfa5ee67
+          580b0be1 788bd877 6d65d553 1ba02ca7
+          e6431a0b 3c1bf4fd f85636f0 cedb20e1
+          91fbd131 6a071e4f 12ce5fd5 a8a13239
+          8549faec 5b6461d8 1b3a30ee 5673d494
+          ef27fca9 9d5b09f1 0fdf4b12 c3910289
+          9fa1d140 ee965d90 b67a4743 a5a0b8c5
+          1c61f067 f51e5b5a acdf34cc b37b4ffe
+          6abadc18 78713def 778bbc6f 65602024
+          aedb344b e1e7b597 ba781e52 9ea23fcf
+          40feeefd 24d54124 b9c5bdc5 cce0d9b2
+          39440eed 23371868 22c15d50 74213e10
+          357a88a3 c6d99982 52999402 995bf650
+          d5df0e43 a6dd02b9 cd97af9c 5217ec5c
+          59dca6d0 c5b37b2f 73c8934f 4eb259a0
+          80262523 de920253 3dee82aa b3e7093b
+          7dad982d 10d2a707 04dc9128 979e35b6
+          6ef322e5 26ee82a2 f2f72356 b5782271
+          60abc029 281b8037 59c5b64f 9488dd04
+          42d08313 1ef3eadd 37c35dbe b35b2d4f
+          d8b49929 210fdefb 144f59f2 2d5634ac
+          4ca9cbd7 83adbc02 1885333f 0f5fffc7
+          8c1d4efa afc9548f 8d68dde1 14943edd
+          c0a75d2b 102ce6da c308035e e6faad50
+          9d514855 0a0aceb7 f3e9dbfd fdf8653f
+          6cac892f cb80d7d0 5f56a381 e88f17ad
+          54f8e93f a6ae353c da539548 a932d66e
+          a91fb7b0 d94019e0 0fb1e386 a1df6165
+          aac7c600 3b9c8292 58b70b8a e32fd46a
+          283e7214 0a0efc45 57e918ce b7f3f7da
+          1731ebad 190ce75e 59d56e67 802b7c7c
+          a0f9cab5 af727ede 8780b278 1e66a5c7
+          ecf405fb 0f136573 bab666f0 6edb0ac2
+          07f4906f 6d1b41b9 151e6ae4 ca8e0056
+          abaeed82 82ad1adc dd261d1d 4004ffe8
+          2a1d2b0c 7df69949 9e3d7aba dd6e73cb
+          8883d7dd fd8c61cf 3d37c96a 8152ba7c
+          5bd1bdcd 58bf130c 17d3ead7 dbdaac10
+          36f06ef0 6b1b2fc7 f31a18f0 a286f703
+          5d5c742d 37050e23 0808f870 771b7389
+          81aa1414 6ced06df 3feef9d0 175f3deb
+          969e92bb 4e7cc8f3 af9c4613 ff0c6dae
+          2d063c6b 95092eae 580ff6aa ea3aade1
+          7960144a 64890c07 4d802708 f2cdedcd
+          2b374e41 b9ab0d04 f5ec0260 aab3d154
+          4ac8ddb6 1b4a4e5c a0cb9545 53a0f0f3
+          5814b778 d90fac4e e7966370 5bc0c313
+          1ef7f957 df2b02bc 16d37689 81bbe656
+          9ccf21c1 72027835 f13cab15 d4a1c110
+          3b7e9818 e3936bcf 6e5cb971 1794305f
+          881a39d8 91075487 40fb5432 646ddf47
+          5d9d2ce7 eb7dacd9 8f2ba7b1 9e9e6e3b
+          0eb7be44 67f51e90 f0fdf297 157ede7f
+          0914c6f3 f2f61d85 e2437f5e d21adee7
+          f6b610da b78b4cf5 78c3a61d 265962c9
+          4590c2df 971c24e2 a473602b 2d23ae2c
+          6fb65195 8262b340 55d84b2f 4ef4ee3f
+          a8c2ad31 c3dd1702 2d4055f8 d45726a2
+          05a9a04a 296ba81e d76c0153 7a76fdd6
+          f0561b44 0cc3548f d124e622 cb75e21d
+          c2b7b07e 5dc18b74 41a943c4 83100edf
+          92576715 5365dde1 833368fc a817439e
+          7ee16f77 1f8b24ce a8e0279f 3d1e347e
+          e40bb429 370e965b ca1d548f 4653bdd6
+          f08c4ae4 5750fbea e578def5 28374941
+          8984f041 8e2e2875 aa298a0e fe0e0587
+          4f5015b7 c3e12265 80cf0f09 dffef839
+          abd7bbbf ce4842f1 757a48f8 eee7a5aa
+          10bfaf79 cafae761 e52b4fce 84ac8ddb
+          2f690daf 8e0c8398 b1434473 508ee75d
+          55705844 e5ad8598 f1238051 abeb11f1
+          9832b321 7ddd7651 63684941 c1713b1f
+          cfa4f865 df3dcd68 b4d23012 24e3e1a9
+          3510f7e5 37cf73be 9e2768cb cfc30d15
+          72771f81 923f8ed5 8fe799cc e0dba903
+          84f6ee24 bbb65745 3bb17c2c 6ae4a544
+          3cb8234a daca0dc4 9aa62905 c56a0143
+          e4ff5e7b d467c830 c9a47f49 2aecea33
+          785859e4 cc198fa0 85aaa249 5771f01c
+          7b5e69ab 3683393b 975824a2 120bc44a
+          89183e00 b9691132 e8fd9b31 63c62928
+          6d21a06b 2727110f b6e4940a c8defa2b
+          949e4ea7 ca95c5a5 63fe83fb be1e34f9
+          c9839232 0ea4b650 41939ef8 3370d4d0
+          693c85a5 67e6926a 929f477a b4d5c4f3
+          6c76e4f2 6b20e69e 11a0f4d6 c8f1bccb
+          08bea4c0 bd05490a 0af981a3 3e4fa582
+          8a934990 b3f300b0 34e5dba1 f960b5aa
+          b591afcf 9e873321 64c073e5 01e9f5d0
+          6ce59a4f 55e1413f 5217cfd3 00949d4e
+          83ec2dbb c4785e8d 98ada08d 8984e851
+          836add37 596ad00e 480d72ec d8a1a0f0
+          f571b66b 47f3672d 2e230708 2656a225
+          0585a477 2994e989 2b563ca9 efd44572
+          3b4592cb 882b0e62 3f59f42c a3559da6
+          2d3f0fa7 4b64238b a4ecd849 004d1db3
+          04b96901 5def84e0 6eb7cba5 67755d37
+          dc05a55f 37f06cd3 b24edc4e 4c344e5f
+          b3098c79 e554f5b8 b359c0e6 3b6cd063
+          3ec346e6 48523fa4 ba70be23 c714f90d
+          1b34192d a0812605 26f13cbb 0069bf6c
+          02736e7e fd781e72 d522470c 02cfd810
+          399e0762 dccee7b6 68081fd8 47ac93ad
+          494141ae 2c6ed050 f4fb69ea e2767e83
+          fabe9df0 ddf2ed92 3508a4bc 80f15fff
+          74c06f70 bfe9761a a91ef32b 207dd546
+          91fc8775 c6f3384f 0f889d30 02141e2a
+          aaa91e71 97689282 327698d8 54b5366e
+          a704635a 26646cd8 45158136 8edb814a
+          f16be4ec b7df6425 9282421d e0e17adb
+          a839ef2c 0025f70b 6dcd3131 4560f189
+          f390bb73 8fd3cac3 82dc367d 422c440e
+          ef47e235 0285f1bc 1a229ea8 11fd401b
+          1d59db05 051f0c38 813b75e5 7ab0569a
+          a821d0c6 e9388242 9dd7fcc7 1f1ef5e8
+          d849d2c7 a04bd334 3684a8c2 c241775b
+          e29e9275 1b462193 c69f61e8 a020641c
+          ee6de585 0cf08c0a 03757888 b32db99d
+          078f9828 b0141742 656ac1e5 6254929e
+          23ecce07 75690311 c30722cb c601760c
+          4308e033 d76d81c2 2367c881 c1dcbaef
+          df9473c4 d84cc007 8c1efadf c859730e
+          35940e0a 4d7092de c867483e 8d124f8a
+          ffd80905 252b7f9e 94bf7ced 76951670
+          662e157d 8109d5a3 85277c0b b7850583
+          d2c71b04 8ba35c0a 592f98ea b13a3317
+          2a338b49 9c9e0a57 160ddd23 dc17a246
+          0d1675be 86f90ab9 b5657f9d 80dcdd87
+          c9c50f25 d3c1608a 45df0177 7f1cf7e5
+          77ab050a cc7db7a0 696c08b1 959743ca
+          f8d1d3ca 77ec7e87 d392d3cd dd2908af
+          f933ec84 eab12524 4cbc4fd4 78c1f128
+          15602e28 02637e49 bdb40b01 6a9bf832
+          8d34805b f67eecbe 69827cd1 13882606
+          833f8363 57c8da2d 85d31f7d 01e6e28a
+          6bb995bd ee3570c1 7dc4a3b9 60794171
+          a4ed9123 bdf4ed6f 37bb8b2e df0c1e51
+          037858aa 8fffad38 79e79dbf 30ac6d44
+          23e655b9 1ce039e8 f4206654 5f081974
+          3730d63a d9c70ace d944b4e9 c7706bde
+          8f6b646b dc7b6cda a2bd766e e98f50f4
+          6732c965 6c943570 b539426f b51aa024
+          fed38f7b 853cf1cc 2977d3e5 1bc5238a
+          2a0301f4 eddadb12 be59f6e8 b9fb1ffc
+          0b597911 d40c1ceb b4802f31 4e43f0dd
+          dd44d6b3 9a5b495b 1de5a702 edfe212c
+          07793b7f 83e2bf92 e9ea8262 06a3dfe0
+          fecf06fe e7a15334 61004bd3 60317975
+          e581036d 90267852 356edcf5 42a784e8
+          518381c5 5d407899 da4cdcfd 2cb9b428
+          fefb3471 756922e2 417ba220 f4d9e7d6
+          70dede54 2d39552e 6dc99a55 e14963c6
+          ef576a21 a631f792 4bb9b482 98601b3b
+          b61f840c ee0760aa d30f1fe7 9f5d9e66
+          4fda461e 3af8f04d 35119512 2acf9e87
+          e445df01 8f5cfd6b 4c45717b 97566cd9
+          1ef8799b c34726ab 6362dd4e 976f148f
+          a801bcca 83fb99b3 63c7acb7 17170e63
+          14f45c5a e00b8b80 8e89d0ec 91ff88ff
+          659d045b dc1ebe2c 39eddfd2 52240578
+          787bb1c8 9d0f1fd0 0b94b86e d6e68865
+          6ad490b7 f55748fb 6587d8c9 9869d835
+          70c93942 6fb51b00 821e7968 62fc975f
+          7f450be0 5113c34b 7b6af234 4b5ee130
+          850ea829 9e274434 c15e1033 66188955
+          d5723320 cbae0a59 35177f5a 0756234f
+          932747f2 916cd555 9030f17e 27398fc5
+          0a217d7a 40656a3a 141d4db9 d68b0b37
+          df1c2070 5a60f296 7ef3a1c2 c7f3cfe8
+          799f9c94 5d5a0958 78384693 f6cc13bd
+          f3977cb9 85d58046 42d6cbbf 7e068e49
+          31e87f89 93ee03ef 0e6d9c34 83c8c2b1
+          5719e0cc 822fa03a abf04adc 0cd27569
+          05b11d54 dc84c110 d4b7a7e8 dee2df56
+          2ac15254 8ce6e54b 30155d35 35450a69
+          29e4fd02 e622d27b fe79dbce 9d7df577
+          74aa7065 5d6e083c 92fca545 e9da5521
+          398bbffc 9255030d e7b6d392 318b5d40
+          08d8d525 a26119c8 58b719aa 320aa922
+          a2a90b4f 84cc7cd3 2e305c48 238d0288
+          a0835115 1c083163 87a2bf67 29494d47
+          738180dd 5e5ed931 69e8d079 e68be725
+          3f5e4903 5ef51f47 20ed8517 3f46464d
+          3c4d7e1b 063bdfd6 31103eb8 2e110d43
+          38558bf6 1d81fc83 7f539582 71899223
+          c0b3555a 48050a8f ac5d928b 8805535c
+          a20322bc 6f1792b7 4847dc03 37fb445e
+          7d7ed1a4 ec77de7a 58063c37 960b131f
+          78de9c95 7b0f4313 61b2a30b 482c21a2
+          51398968 d44a30a5 e32e203b 449e1f9a
+          027797db f86a91cc 3c6bf30e 31be5913
+          cfb3d921 0c1d143e 2d22e969 a18586ad
+          40a097f7 e5d71f64 bcf44c1b 19f0dcce
+          c4e121ed d927ba19 ce9c9fcd 69809e0e
+          bf8e7162 a6327544 98b30b08 b2607883
+          91740131 9719c5b6 47b20087 ce83bcdf
+          7e87d2a3 c79de447 e8806035 1a7260a8
+          7cb454b5 c4e714e0 93f7c5d2 6586137f
+          7bc980e7 4652fcf3 0f7ed90b 167dc9aa
+          043d4d96 0c4e4109 e97e07f8 75be430c
+          c613ff8d 213b397b f34e284b caa4e306
+          f23a76bf c00b90b6 7a335872 0b9c6db4
+          2c16d044 4742d488 01623480 9203137b
+          427ca5a1 63d2e081 73cde7cf c980e70e
+          623e7716 d2a7bffa a942012d a88adb59
+          003ce343 2062c420 209d3deb 1048971f
+          3b297601 a1386e77 452547d6 aea9b012
+          81de46b1 99404db3 54746090 96f85ddb
+          93838416 0f01c7f3 cc39058f 65bf3be7
+          2119f0dc 40b2df99 f3b8293d fb5eaae2
+          7608df14 3a15c44d 18099ca7 de591b8b
+          532df20a 2075d506 d2f69da1 aa90f03a
+          5c397410 94fc7d0e 7277ed23 09d9e2a4
+          8a1d65a2 460e028f 982072a0 d0027a0a
+          e405e42f fdf6a3cc a9cfb795 01cf8525
+          63eaf377 e67ff5fd 3c056571 3b0c7891
+          c3fb822e 3ec69982 822d1564 b1a4fdb2
+          118c0595 24fd4096 2b997940 08b6b336
+          ef86aae4 73e4369b 086e89ef ed05b1f7
+          8c004ea3 046a08a1 c4f9f0c9 5df8e952
+          e3e99392 aa3b970c e0194f9d f0cdfde4
+          932fd142 e9a88adb 990102ee 6c05c1bd
+          ba392f29 b0204b05 5b2cd872 e16457f6
+          1acc3c3c 977672b1 837b27d6 d25ca203
+          c423311e 2206f722 3c18d4c4 f3d001c9
+          9bac1d93 060f9867 4a499601 cfa5c0ee
+          6c129c19 32f043c1 626f4b93 258373c5
+          74e1fe10 3d7aa8f8 839a3a59 b50a2acf
+          a440d696 3d5411d1 dcb432a8 00aad28b
+          2073fdb6 da246d22 e82009ed db03fc3b
+          34a78ae2 12b7b937 66e63d96 fdceec07
+          65c07321 c9797bf6 245366de 432c6571
+          3b4ec541 ecf8e1a0 0cf07516 c22b3864
+          a15492b8 9ddd64a3 8688a6c1 0c3d640d
+          e71f3c06 4587ff70 5661e083 84e5207a
+          dc30d006 7b839d16 4228dc14 1b815ec1
+          d73f7e9c 39fd6549 e4e7b93d e0654c7f
+          a95dc1b7 3fcd536a e9524cac 74e1037a
+          8057ab44 679d2cb6 4a181632 d66e86ea
+          8c223a4b c76eda97 13ff91be 661b98b2
+          729c9718 561ba803 03217aec 10847d8c
+          d83f8f92 f96095e0 93337fde 32e39953
+          1e32e0dd 4a57f6f4 497dcedc f9cbd082
+          78d1a493 3613805f bb38081b 7837c919
+          ab15e4ca 161e3802 05878ecb 292837a3
+          140a34ad e54648c3 a567f812 085f0061
+          20349bc1 b7431b08 ed23969e d1722f86
+          c32208e0 3b260f1d 3417878f dcda8277
+          479a46dc 1dc1989c 04494307 7ecc5754
+          8e6094d7 b5f7dc9a 82d06e03 41e3e709
+          cd26de0b 4a0f0fe4 daf2e423 19b51a8c
+          19d970fe bb55a491 25cbb9ee 18dce1fd
+          18f40c79 65c0b102 78dd9688 e659bca2
+          65d01ef5 8c8f112a 2f5e2464 e7ac828e
+          39623860 2c45951d f9b2c28b 7e63c69f
+          b89abeba 2a4da3db 597835ad 60b267cf
+          7cc09496 fd384d97 14787df1 f863c60e
+          064d6888 c8a94aba 7f706037 18e0e2f2
+          b5c83231 35a612d2 65e9a1bd 95bdf300
+          949f4a02 46a3260b 80810fb7 c98f9d30
+          92949ef1 767ab61f aeb72dfc 61c5c799
+          335f6de5 ae948e6e d70f0fff 4ed6ace9
+          adb3e7bc fb1b72db fc6ee006 d22d7bbd
+          e197e28c fff07e9d 8568a46c 0272b56a
+          69089102 66acda00 d9db0e36 54e91855
+          3c3eff26 b8779e36 c40f6e7b 761228bd
+          3cc9e510 da8302a3 d540e1fe 23c8a25e
+          cbe003a6 1154c235 e708013c 6f83dfdb
+          9e38d147 d7aa4df5 cde87243 e185a4fb
+          e1194f9d d421b0fb 9c55dd10 d8b9ade0
+          ce1ddecd c32172f8 00b1e553 cd96d568
+          a0b48640 5a8edb35 8a95579d 5d02e9bf
+          6c14a181 11691df1 81137857 4708eed2
+          8eaa5415 9caf88dc db4e29a3 87cf359e
+          3deb7eeb e9566097 920c2963 47bcc528
+          a00b4d7c 6bd86d52 7aa820f6 9e91a493
+          8750cb4b a100737e 21a4adda 487ec6c8
+          f9768d22 0a749014 fd711a0a 7e3be4ec
+          aa52537a 366a0878 4407d253 7a06e225
+          86e15cfa e359af4f ff3f19f0 1a51b266
+          4c9b8026 fa19aada 1b09a21b 113d7a10
+          e86222c5 b81dde74 9862d0ce 43faaaf5
+          602aaa94 e3768daa e18e2ec9 1b7782e1
+          623a308e fc3c1ccf 53203737 0e1d449c
+          46414fe9 1988f5b6 c52b577f 92357be6
+          6dee44fb e91e8087 26347bf6 acf8e25f
+          d62d5068 e9e2d2c5 ee5250d7 7610d8ad
+          93b34e96 58772ac8 dbb5178a e5d2b1a6
+          c13ccc81 5465265d 92ed46a3 d8061e8b
+          c5021e2d 122062c8 dd62e919 458700ae
+          b7cd9a39 7b99f1f4 49b7c9cf 730bf030
+          9c3caece 9cf9e632 34c14134 29197693
+          3ca20290 db3458b4 f46a82b4 b8742ce9
+          2c646ede 7335b219 591a5070 c3d08a73
+          d990bd69 07a9b5ad 0d2198cc 10d2b707
+          04dc9e48 4f2b2907 7a304ae8 9c3261dc
+          fba6f329 32e03584 e0893c77 df843790
+          1bdb9326 db8e948e 213709a7 3f28303b
+          bca3748c 5170602d ab4096c6 46e0cd36
+          b9e55353 831eb2a6 73771f21 1745b55d
+          55048184 18a2c70e 036d8837 b9d9a5c6
+          d0530054 279d7f3c 73da4bff 9101af01
+          24e39517 4654279d 7b85b6f6 46b8b578
+          d4d03ee0 91d80cb9 4dff281d 5bb319aa
+          d20be5d2 b15be4ca 912ec9bf 6c064b7e
+          2180d211 3cb5da40 15140031 08f470e9
+          192dac67 58703caf 64ddc605 39efce71
+          f9789e4b 035efea7 1f4797ac dbbc58a1
+          a5abdf07 768b7067 8ee03e3d c4d2b19a
+          9426cc3a 76f07728 3c7c826c 32596e91
+          d2a0c3d7 54500169 ab37a1c5 e29d5d55
+          30eb59fb d610d6a7 2b55561e b9d45108
+          7ee9afce 5866f8fb 984bc7f3 5c12f070
+          22a13933 5d953377 de128615 42a9b2ec
+          90a2e842 7d2066fc 08b186b3 36054505
+          c6d47448 5bb39de4 42c92d9f 6ebd6b5b
+          7cf42ce4 eddeefec aa4258cf 6c103eb4
+          1f78b788 a287f5cc 017aac02 3a9fffef
+          83ef992e 5e68b464 63c9011e 9e285b49
+          31240f1a 34c39c9e 3590a656 edd80dc2
+          b77f980c 5a89dc23 70a4a000 c7016f34
+          92964fd6 4ae3cdd6 c9cad250 0a8e2cbd
+          cc4d7ba0 eaec857a ac67b80c 8db09e79
+          d3c57a86 75b5eae4 9927d29f 7feafe1a
+          5d9601ef 5a5cd945 0b07579f 499ec6ea
+          809e9614 20963185 f5eb0ade c82daa6d
+          f9448224 9cc83a96 9c25a7a0 b8928263
+          426fa385 7449b657 549183a9 c6b5d544
+          4740f4a8 810ecb8f 16f7448c e7956dde
+          b12077fe 7badc005 eb6d5d0a f0f08990
+          b7f0a3f0 f4ffbdfe 059a3805 556087f0
+          cde7b618 081fd24f 2c1dabd9 2cc85ac0
+          bca939bb 0ec960e7 a2ae6d55 6a0164ae
+          df2ac6f2 6aac1ab3 19fcbbdc 09c1dd6e
+          a7aa0a43 4cd2b6fb a7be346d 69d51f87
+          3d5ccdca 7329c0b3 e4642973 de7b6f09
+          cb42384d 312aecf6 a87c75a4 7b31c9e2
+          b7d7b08e 29c84d20 be11249d 52e41414
+          9705bdbc 037f41f1 ef7f395d 5b5e2c3d
+          8b1c3110 3ca283e8 023d9684 9f3ba73e
+          3ee55d73 46ba0c78 97136b51 01240f19
+          f4b2392b 6f284bd3 0da4c390 8b193d18
+          d491e1ce 869ed85a b0d94913 4acc9b2a
+          2718bbb6 8263495b bd0dcc59 b94e426f
+          9b0d386f 4f881937 1c38ad92 aad233ac
+          c3157f9d 783275f2 c4fb64c0 bb8ce42f
+          f9ac4fe5 f1d3b338 316e478d 338b4bc7
+          427a7404 bfceb7d7 8fdba9d4 905b533a
+          26a7a0b8 be82e32e c9a5d522 0730be6c
+          aa25f4b6 8047cb66 1039a4b7 7881414f
+          3c4fc07c 1865bbf6 7e92b7e0 835632e0
+          d591bccf 3e0ecefc df1b5fa0 0952d104
+          76d8cdf1 8a0b81c8 9183c4f4 939ab89d
+          a3742c6b eb6fb265 e74eae2d 52f0d2d3
+          a990bb7d b7332159 10412fa4 4f4f08b8
+          a3055dad a4181018 c6e67ff1 d9179756
+          eeff4d2f 031e7665 0bf2d8ec 39731631
+          0cc4e109 a2c69345 ee8d42a7 82987b46
+          00abd713 f755d41a 0e6ca5e5 90ba623d
+          d864d631 f7b3f4d0 0195b57d 3f549e3e
+          5b279ec7 138b0f97 9ee9682b 3de34060
+          19e89cf6 e2f3ef5a 7273e806 3c6b6101
+          240d19f4 a225b768 344b13eb 9840bac6
+          42d488fe a06f1647 6ef4c4dd c110c5c8
+          58b705aa 338a49b1 ba2c6ea6 e03857dc
+          6227ae2d 3eb86a09 bd919bab 0cf447a0
+          3714b9bf 2c3dac67 1864906e 57fe7eec
+          a90b0fdd 77cbe379 b714f0f2 172dec5e
+          71f4f81b 1c65f976 a4e5d35d ad21a8c7
+          5df5e376 98756cff 61283874 428edbb9
+          b382a383 aa3aab04 d2d76e76 1e6400b5
+          a56798f5 8caa5b5b 41bcc92e df737041
+          fee285b7 510978f9 4b3ef5cf 7c63ce52
+          a51ab454 e5dba18d ae0bf723 9d728922
+          90ceb940 f84f0d69 9990be6e bbb82a72
+          e9987b83 1e52f082 c32709ef 45bd2ec9
+          561b440c ee0b3eb7 4551d54a 8a214c97
+          b6800b8f 3fbdac62 cfafb7cc 9f6b729a
+          46523a56 5a022963 467d2518 4cbdd8fa
+          148b4d01 7db78cfe 0ebb31d8 9d69f6e0
+          38d045d7 e95eccb1 84fff4fc 37cbc190
+          5b7a3557 56d27324 95f7d718 7595a919
+          e0d33c0e 94febe20 e0382d6e 25a55183
+          3e221c4a 8f9f44a0 77c5165f 929b233c
+          4ec10e11 a6e4335e bea3c76c 61753ab8
+          996a0cd7 a76944bb c05a5408 4943063c
+          652d2a9b c06a28ba a41044eb 2e62602f
+          f06ad312 84ba713b 8502b236 ee80f2b3
+          72e998a4 ac3cdc25 b9c20469 ab36006f
+          34915e86 8400c862 055d5438 448f1e48
+          eaa7698a e7e1504d f9e13f9f 3e3761f4
+          7d4cdd93 a1a9d604 5b5c4df6 a00fccfb
+          e4c3cee5 878fbeef 885131b4 3cbc1918
+          df36b14c e880de0c 58ac0eee 2bf4a855
+          4ce95fc7 99dcdf8e 30ac8a9e f9a0e541
+          0718830e 32267bf3 2e06144a 8608fe3b
+          b39909b8 ab2313dc bd0363b7 0023d033
+          278414a9 62efa105 85cbbe68 c938bcbe
+          a67a9ad4 c22b58f6 b96ff67b 733f57e1
+          b81d4582 d310d401 9e103b61 1421cdae
+          6df9844b c7f270e9 d81664ea 0b72e998
+          542d3d15 40ceee43 50f6f7c9 3af13c20
+          e5679123 0783674c 1055ada4 c4789e3d
+          e0dca38f 2d2bdfb9 ad49b1a0 c954cc5e
+          5a02e92f bf309fb1 dada52d5 aa9d1717
+          3866ec60 500707d5 b66a07c2 3a66279d
+          364c4515 7282b1c4 155cec92 bc092c05
+          45cea464 c27ae641 5a4929b4 74b19ee1
+          d6f0685a eeca9c35 e35d7b59 99b400cf
+          565408c9 23873e66 2faf7e98 a12c4685
+          4fee90de 9dc1ef8e f6ce7c3b 2ce8a4cf
+          dbb90f4a 8e9f97e3 76345879 48c18d79
+          e590b166 93d858a0 6ee95962 3c440eed
+          23b29e51 94b140ea 6d0ffef1 74cab8e1
+          f74a0af0 f23e9adb be6cdfe1 b9b47130
+          e07c3baf c470881c 36c069d9 11b05343
+          d5695c3a 26b38ed1 24f8602b 3a9a0cf9
+          7b0e385d 5b07e805 dfdd9d94 9ed94c74
+          cd8902c7 387f3bf0 49d1775f b59004e0
+          956fdbec 95fdc1c7 5f2bd5e0 45956587
+          5b3e7969 216ec248 60b59a7a 713b5b79
+          b9c86f2a 978e51e6 db8a4d43 3337ff0a
+          d5e72eca ac670e04 621821e0 fcc31397
+          21acd036 c1c735a2 85535901 596fce7a
+          9f3759da 5115b713 c45a595c 3aa68d8e
+          02b03876 30235e54 65aedd02 55594532
+          eb188d98 87bb2457 5948adb4 bdaaca59
+          7a863c00 55801fc4 8e1b867e 87a12a55
+          05c7f3d0 78bb20ac 78076386 db02deb9
+          b1c31ea8 38f8e764 dacaa478 e4968474
+          6f0f81dd 3ad58fdb 69d450b8 ef10141c
+          3a2ed7c9 522cb80a a3ea621e 646dd826
+          c6f26a72 d1d0c1e8 d3be1584 f7eb0676
+          0b5d73c2 9178de9f cf22cc68 d4785ea3
+          015ef18f dfb52edb b9ef23da c00e6f54
+          7d740049 37104d3d 67cb2763 5a26646c
+          d829978e c942402f 6fdf9f50 f2c75ff5
+          5c5b5c7a 1636a80f f8b68ca2 ab959403
+          f410667c 82b0a3a5 5b015ed9 e60d1ee7
+          1f7ae84b 56097e54 b56ac72d 9f344a88
+          bd672428 bcbd9cad da31eb98 c108a9cb
+          d791cc7b 396e270b d10b846f e9abb782
+          392797d4 528bee01 0f2cf204 621cac67
+          bc9dae39 41981180 b06329c2 109d5b00
+          1e5f5505 d9b35f7f 57b00b9d 8132c5c6
+          17159143 ef06cf96 cdeab77c 527090b5
+          690794a7 6491935d 1659c8d6 50a06d52
+          5c0569ab 36828089 9b6a5255 906bab8d
+          8a8098d1 8348e919 4da92a18 33107674
+          4118f236 c6129706 3cc16605 9c535371
+          f8af2759 da5c5984 6ffeb727 42489fee
+          24cda056 d49875ec 04e4ee39 22c7ed64
+          b9540171 97e49317 206fc76f 4e2e0c2c
+          b5ac671d c066a66f 4e10863c 8bb06402
+          c6149705 bcd255cb 9b976edb f3097597
+          14684db4 41de103b 6eb85841 51938282
+          dc14736e 3ec9b017 934d6505 97e53296
+          1eee92bc ed37a84c 4aa98de7 914e2076
+          3b448d1c 049e31c1 74f5cf03 47bb7c84
+          25085312 5d12f04a 37ae539f 7be8c145
+          9c1a02a8 0ac83b5a 3ec58e1f 463ada12
+          d784cc2c 023e9b8d 74cac0ac 638c4256
+          6c59ae00 782cf610 ec68afac 27399a35
+          a92ab8f4 90f3f284 d87b8683 42aba22a
+          55056308 c2924084 295f226c 69b0fcbc
+          06013c5c 0b97fde6 ac57048b d087362b
+          06dfca86 f5eb06de 1ddad476 2f661cd6
+          5deecebd 5072e2bc dcbd5896 ab2ba20a
+          a02aa318 32d66e15 1394eb74 49f6689e
+          4062c382 8db64941 a06f11ba 67bf316b
+          265f5ded 3a809739 735aaf8a 3f8eff8f
+          2a5e0a10 fbdbf9dc 160de183 fa38f964
+          b160d6b1 3367216b b35c3a56 4f84da87
+          713cce9f c9424acf f20ffe0d 85077e27
+          7ba8d651 427b2bb8 7777f0ef 90485daa
+          0ae1c3f8 f3f8d494 f1a386f3 a69bafbb
+          bb69c02b dbb4debb e0ab6f17 2bd44055
+          481e9fb6 2a1f1dc4 dd3b0a18 5c176917
+          fd0ddcfe c9565641 4ac73099 0bb52d9f
+          7008cae8 7c78633d c0bbe4e1 ebfcae60
+          a174ce1c a567e9eb 7680213d cb798981
+          63c21c4b 08bdb5c1 94959ee1 83400b4c
+          c9969d9f 95aefd25 fca6a7f8 7ada24d7
+          fc6e8db9 8dcb40ce dcdd6341 d5d1134f
+          73da8652 93bacbdf 6876c6cd bd5f10db
+          3e253c38 16fcbb74 7412f188 ac63c285
+          ef56a093 fa24a3d0 34eaf777 cd3912c4
+          4a134609 477d870f dbcb7978 1c440a9a
+          a56bd5a2 c27ffc04 fe72fb08 4bc9ea55
+          9aea13a7 fd91457c 5bd5ef87 ef3224a7
+          f5673908 6454b768 8d6fe11a 602bce3b
+          315268f1 f8c3c843 5030b575 d81a0d94
+          fd7d0a52 bef8117d 01fe669b 05bbd51c
+          91366b8c 6265bbe4 9409eae8 58e14afb
+          a851012f f5a94903 733ffd72 8b42d760
+          8cb26e01 78987c25 b44f4788 b9770ca1
+          dfabada6 d0a8a160 cf01e1c2 0f1b714c
+          8669a4ee d52e3b47 82991823 c7a266cf
+          7add77f4 b86dba56 ad6fc801 b3151741
+          c1b2a511 f99f2f7e c2743eed 194e03fa
+          eb1ca95b 031e9903 13081183 bb41d498
+          a14cbd34 27047a59 6b3741e6 a67d37db
+          56ccede6 087b01be 23874c69 f6e3ca25
+          980fa349 01af74f3 06ff7313 ee3d2c58
+          0d090de8 b6b93ce0 e1b89d47 4c08b47c
+          ea11646a 6b9cd514 98750cb9 21673ef9
+          4ab0194c b8ff99db 2adb8dcc 11764575
+          b7c52f88 5ffacdab 1e777533 5ccfc1f9
+          6fc097bf 78518f8c ffcdfc86 5543ec75
+          ec33b707 3c64d1e0 1e2ad0e2 b1fb199f
+          76ad9cb9 9d8ec6b1 c99f7d05 65c91937
+          037a6e39 47562394 26fef47d b7807bff
+          93742380 77435065 375443e6 6bafbe65
+          af6a50b0 73fdb014 2e1dd323 cd9b3012
+          384f8f7a dd8bed46 135c5cbe 162c9504
+          eca81202 762de2df 6db179e7 b35703bb
+          eb11857f 0084bf36 635ff47b 73fa2337
+          ef02551d 4408c397 0069ab36 81a5b8c4
+          d955c5ce 939871cc 3d23480c 99b69b5b
+          851a7c2f 4c7cf463 d3c5f31c 06baeb65
+          2ebb6e9a 46162977 da334ff4 2bd9b8ed
+          4345c3df caba2c05 21a12040 de6bcce8
+          01e07b67 7b1070dc ce71b230 4a2569f9
+          54f44732 4e411118 17fcfe8d f519c8e2
+          65b48909 5fb5dcb6 f3197574 cc356dc0
+          ebd9a478 537b76eb 59cae954 07cbb6ff
+          3a96e140 c75c3d80 e2ee1487 e433f005
+          86a5cc04 d6f252f0 ebd0bace 09c383d2
+          df0f547a 0d941c4f 76f49473 b93134ca
+          fbf13879 a32dde92 9591ef3b 7cc41fac
+          f2fad220 aecb3ec3 6057b66d b3a6f087
+          9fe72253 9aaafb47 1c880fec dc1a827b
+          7713c14e d45cc231 5a72f438 e4ee3e4c
+          ea6469ca b916ac68 ffa9d87d 2d376d7d
+          521d15d3 20bcc557 02c7b057 a6ff1932
+          e5d1c771 6c0b2899 663c48d2 25f9cf24
+          c8df7b48 cc06a899 17b31902 70e959f7
+          dba922f4 26561a32 b40a57af 9b55ba7e
+          75dcf506 caaf8ba6 91371a21 7dea4bd3
+          ec95d5ed 1dae2c1d 148bc88a d185fb31
+          d16386a0 89101846 c00f5676 2563cac9
+          67d27ed9 4c7e0f9d 07f45010 a2f1f376
+          288c786d c623eab8 78638d35 d6184f8d
+          20d05ba9 6f113fcf 516645c5 3c932950
+          0093b961 17537d21 0ded3995 63ff2147
+          c26e6722 470c623c 6383c91e 058aa81e
+          956a08ba 38f9f1b9 c6e424a6 d1681a11
+          a2b6ae3e 9ef4b282 a204631c 3762d51c
+          c48e1f0e 4a5f5f00 6b4dcb27 9630c9a7
+          afde08e6 a24aeae2 7656dce4 74ca23cf
+          85bff6fa b9a6fa4c 75742cb4 dcb27d26
+          a3551d12 28ca45c3 84deb66a 47976483
+          8174df11 5d5b07eb d98411a0 d0a9e862
+          3d43c865 aba81a9d fbd1dcd1 8de2d21a
+          5392e1e2 e34fbec3 a9404793 62636b22
+          62602ff0 6adda27e f7625c3a b6638fc8
+          3a465bb3 04dce4b4 45fcf761 afbcf663
+          537fb63a 26ce14f1 eaf4a790 81534553
+          8506766d 2b2fe48a 5d92b93a a72b2e3d
+          6b164725 eb19c222 a6e8a795 6f95efda
+          eed3e080 97f7e1dc 61d6d2f2 213435af
+          c4b111ff 760910da bf979397 82689d0a
+          aa925220 6beb5efa 4ac77862 58a446be
+          39e74575 4cec2df9 0ae1d367 fda56f77
+          dbeb3c6d b12b15d2 c3bd7f42 c99fc748
+          ce67ada0 bd19dcab 2b04dc4e 57e919c2
+          22c15e51 d522edf9 679ebfd6 b2b36b02
+          bc8adf7e d515feb4 e22d34e1 2c2d6708
+          7699d4fe 7a8841ae 2c835302 6ab2ddd1
+          9f6da5e5 7071c506 e0ad9495 8ee12a0a
+          1b638b9e f5da14ff f1f716dc caafd26c
+          f99a8f39 2fed76aa cad058f1 12277dcd
+          56d276ec 9fa567d1 e386838e 32d6337c
+          81517df2 ec0b25ab 57245ee3 145e658f
+          5bcc90fa f4138fd8 cbabdad2 d2e288e4
+          7ba19989 4360a70a 0912ab29 c8912206
+          d033d66c 86ea8c22 baac3b46 b478f56d
+          5bcc8d78 7dcef65b fd753409 cd6c612f
+          bef804b2 360ba922 af463a88 db8da5fd
+          b2112d48 9d2ec956 1ba802fd 494f4696
+          2ed63301 a9a547ce bcf7a7c3 3564095c
+          15f08a57 2df7c708 4a6a6505 3ab6168f
+          dc82b0bb ef029f3b dad5ef5e 8c59c7f6
+          1f8182df 4f5217b7 c365630a 6fddc166
+          3fad9eed 2adf297c e6ec0b61 cf4c7911
+          5fa0d094 0f84e379 38769cbb 731f804a
+          e54cc233 99c1ab5d 2b08efdf 9daa86a1
+          5817ab8f 9dbe37ed 99c99d6f 0af00464
+          d9e4cc7b ef499681 185a268f 2785db11
+          103ea4bf 58495197 75ec423a 64acdf2e
+          bab15425 dc91b85d 69e833cf 4cd624b6
+          30bad257 0b7d71da 77fae671 3f0934b5
+          4d62c47b 8becadbf 4155d259 27011016
+          b467c306 de0d3ead a2a98ae7 216f4b55
+          f0ed8fd3 4d29c937 0e78252b 7f0eaa3e
+          76e6095a f8297099 8ed24b43 aef9599d
+          d65927ab e080379a 2075e57a b0569ae9
+          621d6348 213b843d 35796ac4 9bef9c72
+          b5afa78e 8a86e879 f39fb3d9 219536b2
+          1b9bc94a 4acfec15 95754acf ec24193e
+          76dc0850 fbd2537a 86dbe4db 2aaa87e6
+          7ef05eaf 1b023cdc 9e3c67fe bb8fa2fd
+          1e4c8b15 838db9e8 5103401b 1de96ce8
+          89dd058e 83ec0ddb a1fc5c36 75ac63d8
+          72d2c647 ff1cfad2 b42f5cf5 3bfa0e1d
+          5110fecc e34f2160 e6198a2c 6fdc25b9
+          32ad0079 1d5b7109 413d426f 4d641844
+          8f18404f 83553446 4e095ce1 cf2b5f34
+          a59cbd7e c02b59fe a35f15b6 ee284932
+          c601f9e0 2eed20a0 6ba74be2 76a57ffc
+          0d399875 8c368a45 b48990e5 9416fbe1
+          87cfe13a 59d7d57c 16c25e9c ba591b17
+          fd499d2a 0c3a0c3d e47de51f 3806c507
+          ffa89faa 82f6b03f 65a567c4 ca2baf1e
+          943bf7ed 2ed70d78 799fccbf 9711209c
+          8689125b 3e0541e4 a8218e13 d17124aa
+          9460c1ac 636bb688 ac6334c5 edd068ed
+          16c61af9 d2b38ffb 0c1991ef ea5f5685
+          5cdbd805 0b6620c7 e42faa5c 5b46dc96
+          e9ebb681 29234bbc c400c71e 1678881c
+          31083ce3 28613dc3 561e03ca f2ddbb9e
+          a84d23bb 16c0335f 38af34a6 664d6228
+          48bbc0e5 389c4629 96e7787b 395b3e21
+          37165fda e0b81d61 1da32b05 85c12d9f
+          3c3bb59f 1f35f7a3 ad782edc 417c060d
+          ad8c78e9 d927790b 63a0c9ca c3e96296
+          322361c8 e3cd26a8 5d2f5b0d ebd948e0
+          b474949e 31c8c835 a6e78d28 fef98798
+          6b06bc9c f7dfee6b 2928694f 05e0217c
+          8b1cd21b 3c9ac73b 4bc7b0aa a81490bb
+          e337283d 71813a57 16275d2b 037c0ec4
+          7ef6c59b eee5df71 103df7a3 c31e9d6f
+          9f6d3302 5516398e e7959d49 879c6d7b
+          d0e2299c 63477b5a df2c96b0 9e51517a
+          862d5e9b dd2bf7a3 b9f75e1b e02153b0
+          6cfb9607 580a6a2a 48e9d81d 2d20a46f
+          cffaa563 c82da83a 9302d9db 1ca56374
+          b9b260b3 4279f063 9326eb3b dc6174c7
+          ef1ffbe9 e2f9aa40 bfdd3435 18c07b14
+          efd59c1d fba1e2c4 995a426f 22160b84
+          f4ee0601 1d5b5091 aa820d35 535ae6ff
+          1993cea8 ae0a7825 ebd78499 b28b06b3
+          12e720c3 e537da50 1f881e3b ccd155f0
+          1fa5633f af05bbc5 0e5475fd c3292808
+          e2c29e7c ec95c8b7 de3fedae c3d077e8
+          680d9efc e864b4c6 25549d54 2c2efde3
+          2175d546 b0e12ec9 4a47aa0a 8e3f230b
+          267acc30 d0a13d2f f5d2330c 78e6c2b2
+          9615bb77 def5ef80 270850f1 ebcefe60
+          b5f94ad9 aa210c48 0a0e62c7 0e055580
+          bf336ec7 8857fb19 6b374375 5629758d
+          01b07baf 0e0bf939 fcd5199f bbf538d0
+          3e8e9cfd de39afee 9d5ea1ce b5457bd6
+          90530ae9 6b36d702 9d68b68b a56731e3
+          8601835b 9b49b9f4 0c0d5bc1 005bf8dd
+          57a3ff15 f0f04629 59fbcb28 56e2568d
+          dd82ac98 3e9dc1bb 5debfa2d 9fd42a28
+          d87b080a 8ed0573a e69897d4 84af963e
+          af0a8f68 94eec54d 2dd11f2c 58aa0e0d
+          5e451bef 038e3917 fe7e1a0a f61fa95f
+          85613283 77db5610 767767a2 03d29e04
+          34dcf3a9 838c6793 955704bc d275ab7d
+          ccf9655d 1995a495 1a3ce342 206c601f
+          a7658745 a502c3c5 0cc8d8b0 83bed231
+          20443cb6 d0c7273d ee7577ff 3c29801d
+          1e83c79d 9d21e1db 6f9e456b 9e46d562
+          3a08bdf1 5e36a466 3853551c 961edefb
+          9e31c192 063de2d6 1695c795 efd8dae6
+          b28027f0 3c54ecdd dd5db058 03a5aaec
+          588f5925 0bd12307 02a7d739 4bc79089
+          8ffb69a5 ada2b074 0cc4b89d 57cf2ef3
+          623ffb7c 1ba39496 1fefd5ab 4f4ec894
+          479f4380 ced3b4a6 780fe3bd 8cd3aa30
+          3503dee3 e2c96627 8c7b9143 fba1839d
+          0141c217 9368c8aa e21fbee9 2158ad97
+          013cf4c3 92953f77 e138e9da 36f8862a
+          a0636bf0 6a9958bf 9a022979 deaffba1
+          3c259bbe 141484f9 aa00df83 51efcd7b
+          439a27bd 12225f9f b34e15e4 f7194d2d
+          d06b5cdb 0ab4a773 76ee75d6 da623123
+          d7b64d4b f0ebd042 da561e42 324b6e76
+          57a64e87 68169bfe 847e51a9 0256abb9
+          139cd577 927a30b1 b1d24329 84f6e981
+          06cc0b8e d259016d 04c19495 23e4ec3e
+          28203358 90eaf8af f080cd02 15c18f4f
+          9eec7957 5753cd5e 90daa30c 0e86841f
+          7e9a8e94 fba423d5 8a9a3566 5520e4a2
+          bd6d48cb 4413a1ac ddf70cc3 08a1bdbb
+          099c5a84 00498e1f dfcd58ec 779ad353
+          55b53857 837cc5ab 57789ab2 8ada8044
+          9b7ce293 ccfff6d6 a08b0c43 d6acad9e
+          dd9bbd63 2f58ca4d 842c85b2 b81d843e
+          fad02b11 33de3c25 48d8b7c1 63f3ec75
+          7765c8a3 0f4db19b c0425384 9690dd54
+          5b217bfb 9e7a1751 d8a3f388 8f01dfdb
+          9a49d6ca c31528a6 dc925084 6dcdeab9
+          b498be0c 997eedec 264ba014 5b96e375
+          e6b42c04 75bd53ec 8a52ebee 28c0989e
+          0525c7cf 10be00ca 5c594619 e0b922f2
+          9db94ba4 16b7bb9c 60c2e6b8 cfbf3ae8
+          dda7fb1c 9b91aeb5 c639b525 27cf42d5
+          f954a859 6b42eecb 3110dcbd 1339e825
+          1ccad370 9e9e6d6a e83e092f 2dbeb0b0
+          e5e7b540 7fe4408a bcb25660 bc9bc530
+          fae84846 b0d91cd9 76e8ef58 8ec93ff4
+          0763abb2 320c659c b2f8e6b2 d9cfab9e
+          53060442 cda127e5 a726a813 39fb9df7
+          5481defb 30e00345 dcb6bc99 67f2f71e
+          66a036db 1418c162 633ce363 198fa810
+          0693aa4b 71ecf8ae a6f8a71f 5af266b3
+          939716b3 98177eb5 b41927e1 fcbb808e
+          1d44329e 1ab39ee3 c05a5c02 a52793a9
+          4b30466e 1d1ffcd0 7d4f78f5 ba3b1728
+          13cfaedd 2d214f3d 3505017e 1955561e
+          dae3a567 ce822927 cf798181 635a7a2d
+          f8b66b25 d9c60218 d94dc949 89351919
+          a24bcb29 b0a9db46 8a03e6d1 3835815e
+          e093184f 8a446b05 2d7af9d9 f384449b
+          2aea49e4 cef9f6ef 392f7ed9 f75b1805
+          6d1c93a2 844ffddf 19bffe3d 5fb353e4
+          da8ab13c 1b14fd75 a2fe8dad cd0e7ead
+          1241a157 4a364585 d5a85b30 1a0d5b0b
+          78b69222 9560b535 97622817 67d97bc5
+          c780c2cf 0769bb98 8a458689 dcf89213
+          67249d87 7499b81d 2803bc0f 87cf9af3
+          06b03415 09ff43f9 d51a4073 b044e1ad
+          5b4b53aa 0a06bdb2 d3678137 189c6e3e
+          b27c34a1 41e01119 22c91a5b 7c71612e
+          2c8f28db b4debf16 f08a7ef8 d6df9453
+          1c28c576 50785d7d 5a24d4ff a182030b
+          7267abd2 3209ed1d 1d684752 502a439f
+          7d768a67 b71e06a0 5cd01cd8 137f59f3
+          8c60812c 5a5292f1 5e3764e7 83212ba7
+          9e5b8b0f 00cff858 69de5c20 84b3579b
+          f4c65327 436a010f 49147a24 d7cc1d17
+          482b3dd4 e081392a ea764065 39a84ecf
+          044ba981 1a771677 bc0df9bf 7ba685be
+          38ed38c8 42c4ab4f ffccc007 26bc400d
+          7135e616 b608507e f682b3f2 8258793c
+          78358f95 725a9686 d56aa36b 018fd590
+          7f919c7d 87dd154d 7000a87c bd9d6564
+          64c00c94 a75ca4c6 9d151b7a 7aae8a5d
+          f4e522b4 f032d2d5 31ff633f fb62259a
+          9b25b4f4 cec31e4f c5853442 dc5debd6
+          22cb4017 1c046a3f 4f495e5e e051da2b
+          2ac2c925 8d60b1e0 869fd152 0ce9600b
+          4f17160c 8c4e27b6 ca01f18e 5e3099a1
+          3a2b17a8 60b8e289 d79291f0 ddf26758
+          0f4f9ad8 1eaecde3 f1f00434 3753d11c
+          25d1e0da 628fc694 5f88bc9b 3267eb28
+          de0e0a2f 0fd004fa 4b12f0b0 2b5fb074
+          49248e5d 12c0ab3a b04f9209 c7784cfa
+          c830a867 ca711c59 6c735109 15eeaccd
+          0cf6ff6f ef6c63a4 baca387e ce9d3bef
+          b30bcbc2 be411ba0 2905faa5 89696c5a
+          ad556b05 d21605a5 d128b5f5 ad44b096
+          6a83a2c6 7ef08b4a 89c536a6 2968c18a
+          4d63b592 5008b68d 89524a8c 5882158a
+          490564d9 65778181 d9d979bb f7fa3c77
+          6781d959 6060e7ce ce7de6ff 4b6ec8ce
+          923b7bff e73cfffb 3ce79e7b ced4654b
+          564cba67 61c34d41 a914d226 491a7d83
+          b4129fe7 719fcf9e 49a9eca9 3317f6be
+          e064201c 56514a0e a4ce4076 0a857635
+          f26a998e 44da44de bda93d63 1dedaa64
+          b543baab 65fa0654 2e3924de f078da45
+          eba28faf bf61f34b db616b97 87347a93
+          b4fa4923 4c55e199 0be9e3a3 ee7f647a
+          6eac487d 78af758b 1bfec51f c5191e7b
+          9c198fa8 308fdfd9 a5b72d36 3cf1b51d
+          9526e694 e6b7a7ff e0c91f69 b3511e45
+          8f231e48 23d2eac7 a4d96ed5 0053555c
+          c3337449 c0443bda 94963b5b a9dd2dfa
+          b4e6992a f20c8fc7 6382cd4d 2a108994
+          96b49a97 c0ee11bf 41513ea7 d2d77d77
+          cdcac46d 770cc2ce 2a83b41a 22cd1e25
+          edc46f5d 9de93f35 fc206f64 209b9282
+          50734299 b190c8e5 dfc9e9a6 19e148c8
+          702c8bae d0753f81 195e4c19 e1d08529
+          29c5cd7a 38c3135f ca2ebc7b 7ddb8a55
+          7f878d5d 1dac1969 f773c9a5 2d6771f9
+          732965a5 062f6479 3cbe150e ab60222e
+          2e1970df 32393b34 39b5f7ad a0d1b779
+          6328db7b 3aae8555 3d9cd4b9 8dc7dbd5
+          8d647864 78f65046 15d24372 5377be69
+          87cd9333 7ef8e4d3 463c0107 bb4a5833
+          d2ee67a4 618fd4d2 76f835b3 b47b9ccf
+          f02846cc 48589914 33e2323c 5efd3939
+          183db36b 47dcb092 c98463d9 a6b8d7ca
+          78e7a258 b4749c82 1ad76d68 363ca153
+          5278126d eb7d0b9f 4bdc76fb 49d8d7b5
+          96b6b7f7 93869bc4 4e48e609 c8990c55
+          021975fe cecf4941 28e4c68c d01dcd4c
+          23126936 5420d0c4 3f886b53 6ad4001b
+          dec5e939 991f37b2 458627f5 6994eda8
+          4ccbfd9f fe2d6c6b 7c90865b 594ba919
+          9e9575e8 c85d580a d52d8982 c5316f91
+          4dca7332 e21cf622 0d8f1bd2 74df2a70
+          4a5cd0ca 66e92888 5cf3965f 1f8b74b5
+          ee69b977 f1bf6159 e334bc7b 171f202d
+          77db4257 03e62c8e 2b9dd240 70dc716f
+          a1eb41f3 9b644da2 0dcf8884 47ddadb4
+          7b57b30b 4a6449cb 9d3871eb adaf065a
+          a6c0b1c6 9b0e9086 a4e536c9 1b561786
+          463d99a1 2c8fab22 a1c33dc6 48861755
+          123d9d4b 5a7739eb d2fc3c3f 2877a110
+          2ac1f22d 4b97ed81 5d5529cb 5bba6c37
+          692a765f 2f2b932d 377a7ec8 27f795cb
+          281b5e44 e4380567 783c2565 d478843b
+          502bb444 3113b1e3 c18ecefd b0aa2ad5
+          401d9def 90a6ff93 9ae5f1f0 cee80ccf
+          08895e14 36cca56c 4c553e4c e9f57066
+          55cf6f84 788aa133 3a8df7d5 35547a7e
+          1e6b8acd 99feeee4 4f2c488f 770732af
+          7730f3cb f949cb6c 64d6f4fd e9038767
+          0522356d e39af423 6b285bfa 25bcd915
+          2709da17 7fffb57c 47d8e087 d122bd9c
+          333cb3fc 65593b9b 937ab9bc c3fc3ec7
+          c18228d5 344ed2f4 80d40acf 2e14ca93
+          846050ec 942d5e22 8167a989 5d204d07
+          caf79fb3 f305999d d7517ada 835f3e08
+          9baa2ea4 e961d6b6 310ccf71 0d4fac1f
+          144bdaa0 bafa2456 d7e6ef1b 5fa2cb86
+          a7cb4abf 7cadaea1 e61a456f 9adbafab
+          787bd61e dfeafd70 7ed2b4f7 0aede9db
+          7ee4e4dc bd194bce 6f04027e 8c854acf
+          1fe48716 22a7e0ba 1b6f8eb1 efa49d17
+          bbe459c6 c9e5f076 45d54dc1 d554e49b
+          b5b6658d 51151992 9fd21a62 0d6ff8ea
+          0c35baa6 156c781c 94495854 d5494a35
+          3ca76095 55467a24 c373845a 8264c3d3
+          63ac5b6f 17c42e76 06c3f3ce f044ce65
+          726cbba2 98916678 7257871c 6b0c47ee
+          534c9e54 95823f55 9dc1a2b6 8d617854
+          d20adeeb c564c3d3 8dd47bc7 6a642158
+          cab60b0a 5417dbe6 924066a7 b19d46cb
+          f074c36d 3fef5802 fb2e8fbd 044dc788
+          276c05aa 5b03c513 16696b4b 1cd32a9f
+          b3e928e9 5bf919e8 d2026ed4 19a5e2f3
+          66db533e b5048657 655853d6 d696368a
+          a7d598c3 3b1a8627 b0a495d8 a63c85dc
+          c0fdabfa ba1aa58b c88acef0 14323ce0
+          9fb21640 5b00c303 0000181e 00008607
+          0000303c 000080e1 0100000c 0f000060
+          78000000 c3030000 181e0000 c0f00000
+          a03246d6 c213b94d a333f609 456ed3e8
+          fea24a6b fd619bc6 2bea2e62 9bc6b1be
+          c4f1cfdf 7f4ddf61 e0354100 4023c05e
+          67485e1b 41a38d01 40cc5c74 6de6355e
+          a7f68bf6 7ae2aea1 e61a557b 2d336cd3
+          58517bfa ba1f691f c57235ce 8f871600
+          80860186 070080e1 0100000c 0f000060
+          78000000 c3030000 181e0000 d41253f4
+          c545234a c5624ae5 f3c31f84 c3d27756
+          07a062dc 5888537c 9881e10f 82c1e198
+          81e1f9ad 2587ff39 beeb2fca 4cc495b2
+          8bfb5307 022a3b70 4a190174 76d0e0a5
+          1dc500c7 c2f197ff a4946515 3f345421
+          3528fa75 0bd1195e f7eb7bcb 5e2f36c2
+          d49e303c d0e8d91d 1bdea9a4 3afa8737
+          ca928580 e0244fb4 e10522e8 d8005cba
+          a4a51889 3658668b 660700c0 f0000000
+          86070000 303c0000 80e10100 000c0f00
+          00607800 0000c303 00806b46 f4368d42
+          af01db34 4eccf91d f453ffc7 32b66904
+          003404e2 b7690400 80111a62 9b46c1e7
+          2ffb0e6c d3e8e9f9 35faa9ff af010f2d
+          00000d03 0c0f0000 c3030000 181e0000
+          c0f00000 00860700 00303c00 0080e101
+          00000c0f 00006078 000000c3 03000018
+          1e000086 07000092 0dcf860c 22b02001
+          b40597c5 66c3cb42 0711e421 81672046
+          84c4081b 5e1a3a88 e01c24f0 8c142410
+          c1201bde 69e82082 3e48e019 03904046
+          3b1a4e2e 77d2a9cd 861bc043 ec5cf604
+          54f04cdb 1ea8e06f d8e3c8eb 7a8df08d
+          730e0522 c19403cb f37363aa e8bc9b0f
+          40096f60 6d111e3e 8e0f6a3c f638f63a
+          a375e903 3da1a993 8ea8821b 37387c78
+          d894844c 5bfef061 de9e1047 f50fd2f6
+          a08d7ee6 df83bc8d 3cee2879 dd49c3ce
+          660b74bc a56ab399 07f068fc ce4aa5f6
+          43066f20 6ddfa17f 4e4209df a2c9e376
+          b3d71946 34aa5a3f f3c09b56 01aaf872
+          7c29ab74 7466d79e 4977df83 80f408d2
+          b69f34de cb5a430d 1fdeb0c8 dbc8e35e
+          67af33b5 69aaa63b ef7aedc4 2f9f3f41
+          c95f6785 4d8a6d1a eb4423ae 676337cf
+          7f29d4de 51fd9363 9b4617d6 96347e31
+          fbdfeefb c66857f4 d37abe06 c7fd6d0f
+          79dc4ef6 3af7d532 72bf334d b7ccdb6c
+          637aa5cf d23b8e46 f370d713 6bb7430c
+          6f218d5f 63adf15e 92ef2a20 45deb685
+          3d8e7f76 0d8f9daf f33b6b9e b51dd58f
+          092a7e19 9550daa2 c66cf9d8 877fda7c
+          e75d4908 e22da4f1 59d27a1d 6bceda43
+          111f405e c69e46de b6813dee bce13153
+          3fb7fc68 e7d71ffa 5e21a3f0 f8c20f77
+          ae214aee a6b7ed9c bde9c517 a0466d20
+          ad7f459a ef62ed41 dd27048a bd8c3c6d
+          0d79dbb1 918f4b56 4be97c62 edf39199
+          d337d869 985e5ddf b82ca5e9 ce7568c6
+          daef7f35 d4d99983 22b581b5 26cd1f22
+          eddfe336 8022f56b 76ec61e4 65cf90a7
+          6dbcf857 25861799 7d83337f e79fbf1d
+          9a39e317 f9b4c2fb 17f5d890 4354cae6
+          d43f666f 587f7ffb 8a55c720 4a6d21cd
+          8fb3f6d4 06fbb82d 607bf557 c6b27791
+          873d4d5e b69a3c4d 5dd2f05c d39b3337
+          377fe7ae 555d2b1e f90aa584 3d6efa0e
+          e39be0fa 959a20ef 36a413ff c02d2fcc
+          fbe3ef17 75acfcd6 7b106662 20ed0f51
+          1b2ca4b6 d8c86de2 604a573d 543dee30
+          0f79d609 f2ae47c8 c31e652f 2bcb199c
+          cbbc53d6 bfe5d7b3 ba9f5af7 b5c17f1e
+          f802fd38 c3080e9b 9f363d2b 799dd27c
+          c6d3efa8 ebf38f04 111b1de9 9d0eb6b7
+          ed68fdfc 83cf743d befa8d60 7b47f169
+          bb471750 ec135e4d 1bf1fdf9 8bdae77b
+          7b54f7ba a73edab7 e9b995b9 81e402ca
+          1e623a58 6c7cb331 fae984c5 b2538c11
+          ae7a2846 cc49b163 e159b3b7 76ad7efc
+          d9a95ffc d2914b16 49ce155e a275ac82
+          eaffcd96 96d4db7b 179c7ae5 e50f5143
+          7e24d73d 709deda8 84aaf28a c9fa126a
+          79f11df5 7c7e3ac7 50a8bdb9 4fa9e0be
+          e84d73ff daf1d863 db277f72 d1bb3c71
+          1286547f e7cf1e3b aa067eb7 755e72c7
+          b685e97f 1dbc83ac f083b9de b32df4bf
+          6292fbe9 04c6b26d 68950a75 b51e21d3
+          fbdb9425 9fddddb1 ea9bdb22 736e3ca3
+          0397bfd3 5cd1f02e a670ee1c 3772e8f4
+          abafb466 df3f3a87 ccaf8d3e e6832391
+          ef6de638 3bd2f93f 467bd45b 47bea38e
+          cecf33bb 38f5e605 3c07a877 0cd06de4
+          fd69cb1f 3e623435 a703718a 196d9405
+          1b0c69e2 cf5ff61d b6adac74 5ad9e7ce
+          c6fa366f ba9e5a76 26453ec7 c7143a42
+          c518317c da4f2732 960bc518 e101b67e
+          32b9def0 acebffd3 b278491f 7d4dce6c
+          6aaaf844 ff07311d d78eb499 ffcd0000
+          00004945 4e44ae42 6082 
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="Y" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          0000013b 00000171 08060000 000c1f0f
+          cd000047 2a494441 5478daed 5d079c54
+          45d2af17 26edee6c ce39c0b2 48109403
+          11414192 6411044f f1441010 0367463d
+          01415144 d4d33bc3 7dea9941 724e8a7a
+          0a028a28 396f0e6c ce3bf1cd fbbadf9b
+          8d802cec ceeecc7b f5ff393f 6076ed99
+          57ddf5ef aaeaea2a 46144568 2eec9595
+          c0308cae 74e3da70 4b6a6627 868710f2
+          76107979 9317475e 0cb400a2 f3cb90cf
+          68d13897 1bbf2d3e e32ac6b7 395fe5e4
+          755eb443 b6a14bca 39bf21c3 2b392f03
+          00cbc9e3 cb1f022e fafa502b 22578fdf
+          169fd11e e33baaaa e81f9a8a 1fbf0fae
+          3e78a833 d19150f2 ef60f2f2 71ea088b
+          7a706543 9197d5f9 ca27af02 a21be782
+          ee9c98a7 8988b270 469f660f c45c8eec
+          44c10e45 5f7c1e5a b5ff97e1 256b57f5
+          239337c0 9a5b1ced 10c1a7b5 05c43478
+          3a578069 2241577e 462b8c6f e17828e1
+          83820e1a 523afee8 3f62dcf6 a03b271d
+          d6c5c783 2b816477 35e38b50 ba7e9db1
+          72ff2f43 8b3effb0 3f79e366 7b5171a2
+          dd02c6ab 3100500f 2e4b7ed5 da20af2c
+          8637fc14 387ee24f 3e7d7a6f 0bbe674a
+          314314e6 aac9aee8 f34f9272 df58f660
+          f51f47ef 26ff0c67 35f24731 7c4b6db8
+          3f7d1070 d9e88d65 efeacf68 f9f80e32
+          187dd9a4 bf9ab481 bedf854c 9dfe4ef8
+          c30fefd0 c52720d9 b5e333d4 8e5dba61
+          5d68de9b 6f4caff8 dfeefbc9 1c25d5ea
+          1bd32c1b 0ef5e0aa 07b73b55 c426fd91
+          e7dda3eb 57918f3f f17ef0dd 53ced67a
+          41cd223b f3e99390 f7d63f67 e5bdf7fe
+          42325f21 acbeee2b 8b9e2c20 4f9f643a
+          c102995c 8d9fdff2 e879cf3d 15f1f8d3
+          3948766d ff0c52cc a1309fe8 c8dbe372
+          172f5e4a fed9a181 8ea01e40 1b7305f9
+          9bc32cbd 51e87d6d d705c92b 57bdab4f
+          4eb93cd9 994f9fd2 1e1f36f8 4d537af6
+          6ccd8513 8864d7fe cf00a295 909e0047
+          13de5872 77c4634f 1f46b26b db67b017
+          16c08991 b7cdadf8 f5e02b1a bd4b661a
+          f5e06ac6 27ef0884 f474f1d1 ef5eb3e3
+          dbc7f5c9 9d2c9724 3bd3c913 cc89db86
+          bc6b4dcf 99c57a5d d4e146b2 73936710
+          0560042b a427bcf1 daad118f 3d958a64
+          d736cf60 3b9f0727 c78e7aba f297834b
+          782f7055 d00bf5e0 6ac727ff 72d40068
+          e3a3deeb bced9b87 0c299dc5 0bc98e98
+          0a877b76 9e597de4 ccfbdca5 2711c9ce
+          8d269910 1e10c2fb 5f979ddb 87f90d19
+          6641b273 fd339c1c 336c48f1 a69d3b35
+          ae233ad4 83968e4f de1108e1 7977eb38
+          abfbef27 3e004e8e e1d58550 8b562e8f
+          a93e76e6 254eefd2 4944b422 68109ccc
+          ebcdb9af bdfc204a c3f5a8dc bbc7ab74
+          c7f76ff0 3a494750 4bdc1564 66288f11
+          3e7b99f0 5a7ceddb 12d98976 3be42d5d
+          fc282342 b0eb224d 08578023 8a57f6bf
+          9fff5efe edce4094 866b4136 957b44ab
+          ad2bc322 d1b9bf25 405d1f08 22bc369b
+          f25b1dd9 15affeda bff28f13 53581dca
+          c8e34067 d026c4e5 2e79790c 0ac38556
+          ddfe9ff9 926ddfce e450473c 4735c85c
+          115ebb87 f09b6408 f094f52a 7ffce136
+          b25785c9 87b8cdf6 f55d1d4b f0c4f1db
+          e519883b cbd49c3c 7597352f f7134d78
+          44cb0617 5dfbf55d 3dbeab3e a36acfee
+          eb458bad 3b6390e4 8f7ae009 cfc048bf
+          1541f86d 78d08449 5fb194ec 4ad6ac1c
+          7899e463 843bef60 5a10cdd9 f9d7976d
+          db1289d2 708147c4 3050f4c5 27371323
+          1ab5c4c3 40798d58 76031d26 13b0d6ac
+          4c8d2832 fd9a61d5 21dc1b81 9c8f4f4f
+          6030e8da dab01517 8160b6dd 0218abf3
+          4488ac4e 772379f1 7cf18a2f 222c85e5
+          711ac315 1d4db85a a3da4263 19258dcf
+          927f157e f6494ad0 c4c95b5a 83f09836
+          204d577f 466b8d5f f1ed4e43 f589b3c9
+          17d111d4 03771f9f 5876d6a2 f298e235
+          5f87b38c 4693407e db801b80 87bb5a40
+          93c28f77 4249b840 b63c1f49 e41b8092
+          f0c41004 bd5561f3 b69c39dd 819ee5d1
+          88368b62 f17cb05a 6d344ac1 25a0a5cc
+          7c510c1e 6b08b08c 561b4149 2e08c5a1
+          1884a208 5c023fc0 c3098fdf b028d979
+          a11c1403 cc02730d f428028f 8796929d
+          16e5a018 7801e01d 181700ad 3a056c58
+          94ec3894 836280b1 57942be2 e260e824
+          62ee9072 80738940 e08e8540 2090ec10
+          080402c9 0e814020 90ec1008 0402c90e
+          81402090 ec100804 02c90e81 402090ec
+          10080402 c90e8140 2090ec10 0804921d
+          02814020 d9211008 8487a3b6 744d732f
+          90630b39 377f8696 b611c456 8acd923b
+          ea81073e 038b6532 1088f667 0684ebe7
+          90c54a8f 08c49f03 75441973 c85fe57c
+          620b3937 1d5f6a1f 88ad145d 313e837a
+          e0d9e3e3 01050281 500590ec 10080492
+          1d028140 20d92110 0804921d 02814020
+          d9211008 04921d02 814020d9 211008c4
+          558257ec 933100a2 1dc061bd 90de393d
+          e01d2084 ba41f4c3 61266ae0 68a21e1a
+          f2238d32 f543b164 27da00bc a34321a0
+          673700bb 5d7e93e3 c052540c 85fb0f01
+          c3e17a47 a817d410 08b9b13b e8438201
+          04417e53 c343e91f 47a13a33 1f180532
+          8362c9ce 41c92e26 12a2268d 03a8a991
+          dfd4e9a0 fae809c8 df7388f2 1e02a15a
+          3808d985 f5eb033e 3dba0098 2df29bde
+          5e602b29 85ca73f9 c021d979 98996e27
+          3b563521 3a93497e 8ffc5ba8 9d580442
+          e5102c16 593f2cd6 06242828 b6f2011e
+          50201008 5500c90e 814020d9 21100804
+          921d0281 4020d921 10080492 1d028140
+          20d92110 08842ba0 f8568a62 db7eff76
+          9711b652 c4568a57 32bea850 3db8a865
+          a7b62ba2 78251681 6b467dba 80ad1411
+          08842aa0 8a568acc a5ff8dad 14ff6c70
+          6ca5d854 d08a6ba5 c8c82f6c a5884020
+          104a0292 1d028140 b2432010 08243b04
+          028140b2 43201008 243b0402 8140b243
+          20100824 3b040281 40b24320 1008243b
+          04028164 87402010 48760804 02816487
+          40201048 76080402 81648740 20104876
+          08040281 64874020 10487608 04028164
+          874020d4 0c6ca5e8 01cf7025 e3632b45
+          6ca57825 e3632b45 05035b29 2270cda8
+          4f17b095 22028150 05b095a2 873cc395
+          8c8fad14 5b7f7c6c a5e8797a 70513716
+          391f8140 a8014876 080402c9 0e814020
+          90ec1008 0402c90e 81402090 ec100804
+          02c90e81 402090ec 10080402 c90e8140
+          2090ec10 0804921d 02814020 d9211008
+          04921d02 814020d9 21100804 921d0281
+          4020d921 10080492 1d028140 20d92110
+          0804921d 02717560 b45a1482 0280ad14
+          3de0199a 3d3ecb88 b4ff444b 5a09622b
+          c50b613a 7982016c a5e8397a 7029f5c0
+          568a4ab1 3e8852a6 e5b0a55b 3672b887
+          b72e0a3f f9886351 173cfe59 b195a252
+          c88e68a3 5063e66c d9593a94 46eb82d5
+          e930dce3 e9fa01d8 4ad1239e a1b9e333
+          74f3d268 38065b29 b6f6f81a c0568a1e
+          a30797dc b490f315 05ba79e9 510cad0e
+          1f148102 2c7445fb e90e078d 563771f7
+          58c9e553 28b4a898 2e81bfa2 f4c2690f
+          4996af8a 027768d9 298fecbc 510cad8e
+          40140192 9d076c63 4d320698 368852b4
+          1fe8e184 2f2e6b24 bbcb410a 6732ea3a
+          9e542ed9 d179a46e 6c133b9d 9aee92f5
+          ae4cf39d 670d8630 e4a6565e 4a3c9780
+          5240b273 6fa34e8a d93531ec 5846b131
+          3b9a4754 b2615d8c 4cf288d6 40f56fbf
+          32e6b4ec 1046a324 c580fa98 ddc57e86
+          64e7891e ac289b70 0d2695e1 38f26215
+          39a9f431 abf6ee89 1545b5a5 8abb0ea6
+          e347832c 45a5fe8a 223b90f3 32a92e34
+          5204ba6e 14bc512a 96ec247a bbe03456
+          944f6339 e5723ceb ed758dbc 8811ad01
+          ced7af03 594bc14a db1c198e 9174a189
+          7500a282 4d3b45c7 ec1c94ec 1aee5462
+          ad65a74c 32607800 6b7e49a7 aafd7b35
+          80681594 6ddf9a40 2c6665c9 53ac4dc1
+          6ae0e130 4eb2131c 8a3dc053 b61b6b17
+          2e30cbeb c84e891b 18792c6b 494598e9
+          d0ef1d90 a65a0715 3feceaa9 44dda7b1
+          6b903c1c b1a17520 c7b9d1b2 f33ccb4e
+          140479f2 6a637664 e762784e 7a29d558
+          27de8957 e5de3dbd f190a2e5 301d3d02
+          f6b2aa01 0caf40f5 b8d8a64f 43dcc4b2
+          536a424a 436a57a4 654709af 614c82d5
+          f0c0f21a c53e350d 3c977fbb e3266031
+          5fbca5a8 f9e3f738 f3f9a24e 4a3b9c10
+          9de11cb6 e90105b5 eca8be28 93ed44aa
+          118a3401 a831e7b0 d99c9357 3f7bac46
+          039c4ec1 6447d6af 60b20fae f9fd37bc
+          36d64254 1dd83f80 a557c594 78725f4b
+          760d6376 44571c76 bb52a7d3 41c94e50
+          aa1bebb0 d91bc720 44072dd7 03ac560b
+          8acdce20 ebd7525c 1e57f6ed ce1b91ae
+          5a66fe94 6c5877bb 222f1988 2079388c
+          8687866c 477545a4 64a74ccb 4e50ac65
+          273d1db5 ecec4223 f31d780e 38835ed1
+          c993342c 59f4c527 9345ab05 49eb2a51
+          b2faeb24 4b4ee160 5689d501 29d9910d
+          9fa3e5e6 1df58a40 bd206a20 a065e789
+          969dc522 4f5e93ed 99371814 ada834c6
+          547de4f4 9d455f7e 9688b475 75565dce
+          d2c553c0 66372ad1 ca116b2d 3bada63e
+          0f95e888 83180682 c5aad42b b312d929
+          92caa5ca bd163b08 56eb0564 a731fa28
+          b91880f3 2a1078e7 bebee441 d16246f2
+          ba52ab6e cdd76155 bf1d9dc9 2ab53220
+          e1374e47 ac3a9e6f b466a80b 2b100341
+          a1391a76 c5925ded a4da6b6a 1a131b79
+          4febaffc c2202c59 cbd5c7cf cd2c5afe
+          450ad2d7 952177c9 e2271951 0c57f286
+          286df84d 7648c16a 03c16c51 aa6567a3
+          64a7d8c0 0eb5d06d 1555d0d4 8cd30505
+          2abf6821 add1ce83 31fba545 2f9b4e9d
+          44066b26 725e59d8 bdeaf723 b3588577
+          f2d0fa37 a9474a18 4e205e80 c32628f5
+          914d94ec 6a143ba3 0ec2e4a5 e54d2c3b
+          1174c4b2 63750c28 fdbe3cab 21937b2e
+          737ceee2 17efc4d2 00978739 2d95cb79
+          f9a57f31 b4dab3c2 4bbd69fc 8c17909d
+          b5ac42d6 09855a76 3c357e40 a17d63e9
+          c459cbca 417426d8 d204719a 38c97b7b
+          83d6cf07 2c259552 5e9a3b3f 434bc7e7
+          f5c0147c b6e275e3 cdb77c1f 7affccc2
+          cb554451 63dfd8da 5247a7ef 183d5da8
+          b1f5e7f4 9795b1c7 f68d7596 6417f5c1
+          818d3f84 88c05a51 29e76670 cad303f2
+          cb35f486 9c05141c aeb71497 123ab7d5
+          1f5210b2 d310cb4e 1be00fa2 622df646
+          1bb648dc d9989c57 5e59643e 73aa4dba
+          86792272 972e4eaa 3e7c7c91 d2dd5791
+          9019efa3 216eac5f e37be30c 0b96a252
+          e9e7ca54 03b0f04e 37f64a8d 578f68bf
+          46ad364b 691938aa 6b80a3e9 26cefa76
+          8c4e075e 61214cc5 a92cb77f 86d6189f
+          a6a298ce 663c90b3 68dedaa4 4f57ec6c
+          0ee1a9a9 95a239f5 2c64cf7f 61097100
+          429cff0b a3243d68 4a765aa3 0f687d8d
+          c0c86427 7f86dd0e e68242a5 ea017dd0
+          1aeadfd1 08be32d3 4f383966 67abac86
+          46774509 e1f9c445 abca72e1 75c016af
+          5efdbae9 e47123da 718d717e d96b13ed
+          26c71d4a bcf07f01 8837a30b 0e02decf
+          587fbb88 e8865063 9262768c 32ab9f51
+          7e2ba70c 50a958b2 239c2fd4 d8c05c58
+          d484ec1c e01d1b05 9c9e05d5 14f5a5a5
+          cb6c8e6e a7278e7d 86265b23 6414affc
+          2aecfc07 1fbeceab a8dbae77 4c64e3dc
+          53a21bb6 ca4a3017 9500ab4c b2b38976
+          7b19ebb0 982b4505 e7da5132 ab4ccb94
+          1b34d4ed 6e0ed005 06823e34 0844bb7a
+          16b9e4ce 1e3bf778 deb225d7 03966e07
+          6b7616e4 bcbc6801 03622ca8 2494492d
+          37dfc4b8 c615bc09 d99972f3 c15e6353
+          5ef45e94 1a26d934 21a1e56c c0c87195
+          da001f41 a9c17a3a 77d59939 84dbed75
+          311c6abe 73be3ee0 43ac3b51 6565df58
+          1d18b2ff 31ff4de2 ceaabe9a 71eecb0b
+          86541d3e 398dd1aa e3791d64 63d7051a
+          c12b325c daf01b92 5d656a86 2275811a
+          33ba307f 53f03df7 9959ef6b 7b58781f
+          7db952cb 01d0384c 4d5e3e58 4b4a9d95
+          59eb4dbe 802e298a ed34f667 ee2c70d0
+          ffcce4f1 b384ca4a f5baaf2b be34e67f
+          f4c91bc4 7d550de9 538386c6 aa3581fe
+          5239a75a a2735457 43c5b934 25eb4219
+          d0eb620e 8bc52a3a c402c59a ed2ccdb5
+          ab81ea8c 6ca2e40d 02127601 8cc49cd7
+          071ba51d 4f4d900a 051c3ebd a078f9e7
+          496a243a d16683dc 252f3d45 ac9baea0
+          a24c1cea d80476bf 46ce47aa 7d93e884
+          a5a0086a 720be98d 1ba5229f 1837d6da
+          aa27258a 5edcc46a 2d397c5c f6df6bdf
+          243b1b1f e0077e29 c9aac8b7 bbc0c0e3
+          2130f7f5 a5cb2c19 e9aa7bf6 820fdfef
+          5dfdc7c9 27189d7a 9e99ae71 5da017f8
+          7648a8b7 ea287816 4a8e9e00 c1e25072
+          710c6acc 89b2e1ea 70e42b5c b1a1e26c
+          3ad88a4b 1a5b7784 fc82afef 2e5dab02
+          95c5eb69 9caae64c fad8ccb9 4fdcada6
+          e7b6a4a7 6a32e63c fa06ab05 2f353db7
+          c30610d0 bd0b6842 8224af46 f67a5810
+          ab6ba094 18024a0e e738cce6 42a92503
+          35695983 5781a215 9bf09bb9 b80aca8e
+          9f02d034 b0d5ed76 f021aeac 6f628cb4
+          18d4e5cb 4957c9a0 64f5fa25 051fbd1f
+          ae0a8537 9be1dc3d 9366124b bf1f702a
+          9b6b2f1e 42fb5c2f 1d4cd4ed eb4417a8
+          11509d53 286ff80a f5eafc6e 1d92c7e8
+          f5c0b2de de103a7d 66b6a0f0 b8158d57
+          14fef207 885271c2 faab638c 5e076137
+          f5065582 d630131d 51b9af2e 596ccdca
+          54fce316 7dfa71a7 f23d0716 aae5f4b5
+          8ee42d00 81d7a680 5742acb4 c1cbfa20
+          4d3e14ee 3f48367a e5ba3534 6fda77c0
+          2d398c46 2397e963 f5faf34a 9f70ba73
+          55a66641 c5c93300 da06abdd 6a037f62
+          de1be3c3 d567dd81 948a0235 67d3a766
+          3cf1e86d 4a7e4ed3 c9134cda 430fbdc2
+          e920405d 4c47efc2 6a216250 7fd9c4ab
+          cdaf2356 5d4d7a16 941c3905 9cb2cfa3
+          4562d14b f74225b2 13052117 945a9ebd
+          8115434f 5df3feb7 e782c200 ac970122
+          6eedaf4e ebaed69d 5db76559 cde13f7c
+          955a2820 6fd9ab7f 7508703b a82cd548
+          b00284df dc1bbc12 63a58dbd ceaa230a
+          91f7fd6e 106aeca0 70999845 8b25bf8e
+          ec188ecb 21cfaef8 fadd1c31 e8ca8ea7
+          41e9e163 8dad3be2 da06f4ec 06015d92
+          4050e34d 2aead138 ec9d5367 3ef0bc50
+          56a6b8ca 28c52bbf 8a28fc62 c56b9c5e
+          5dd3ea20 44674c08 8588c137 4b49f575
+          202e5dd5 a9b350fc fb71a9a2 b572999e
+          6ce44643 8df75f7a d75b76c1 f7dc97ab
+          8f0c2e16 6dca576a 6ad6676f ff1e1c55
+          d5f549c6 b4120a31 eb63460d 018db756
+          9da92884 08caf71d 78a4e093 0ffb28e9
+          b9ec4545 90bdf0c5 85a2c51a a9a69c3a
+          bace39bd 06e2278c 02cec7a7 ee04564a
+          22269b7b e6966f88 d5e750f4 292cd563
+          6d90df59 bfc1c32a ebc88ef3 f333331c
+          7b520de9 177427ab 4a2f809c 6fbe6fdc
+          70842c00 afc47888 19315095 6447e75e
+          a30743fa 634f2dab 3e784031 1968194f
+          3d3ab2fa d8e9e9ac 415dd349 e3cfb1a3
+          06814f4a 3259db96 4656ddf9 5d3f42f9
+          c92cc9d3 51bc1c4c a6630eb3 d9514776
+          b45f24b1 6e4ea9c6 8a21939c f7dd3ea8
+          38461e59 d7f0b0c2 0a6103fb 4150cf4e
+          aa756789 07db2f6d ce238fda 4b4b3dfb
+          51c88314 af5aee53 b462ad5c d1444579
+          94821920 a85767b2 966f9236 f13a90b5
+          5e79f20c 64effc51 d9ee2bd4 396ce0d5
+          f3bab38c 33b7563e 8dd5e920 e4fe1927
+          05955834 529b45ab 0069ab36 81bdacbc
+          3ed1989e 53b31cc4 8d1f0986 105fd55d
+          2393d602 b180ca77 ef7baef0 d30f3b7b
+          b4fb4ac8 3a7bfebc e71d664b 8a9adc57
+          6ad11922 fc89fb3a 5aaef453 5bb38ee7
+          c0565a0e a95f6f20 646857c5 9d70fae8
+          81e3279e 649cf179 b6d68fe7 03038f8b
+          4a3f916d 006ac257 e79440e6 fa6df2c9
+          6c6d50de 66036d58 28c4df31 822c0846
+          75555188 0524f23a f04f7ff2 99372af7
+          eff5c8d4 5b6ad5a5 fffdc11b aa4f9c7d
+          9c53d13d 09ba5659 2d0b4993 c6822628
+          a0fe5042 caa903c8 5cb715aa b38a55e1
+          be3a5123 54571fad dbc86bff a28d8b3f
+          ca7be9cb d4a4dc9c 0e207fdf 6128dcfb
+          6b93d359 0bf85f77 2d44dcd2 473ad152
+          9d374b6b 9a0ae2f0 bca58bef f5c8157e
+          fca8b678 d58637c8 fc6a55e3 be8ab255
+          173dec66 30764d01 305b1ab9 af053fed
+          83fcbd47 402d27d2 f4b0551f 15722e68
+          e25de72e 20bbc0b1 e38b7491 41c7414d
+          c52c9d06 5dc6869d 60caca96 82b79272
+          5067df6e 87e81183 c1b763a4 1a094fa4
+          1b41c9e6 9d2f157d f9698c27 7d71a1aa
+          12329e7a e25187c9 dc574de5 bb688c39
+          b07b0788 18724b5d 3e9db48e c9265e75
+          26555ae3 34b15e35 fd9668d1 0f8ef945
+          131e5167 beb1b4ed 1c7dd152 dd0eb365
+          4ffdafaa e3c5f264 13283389 69ab3689
+          44062270 acdc9647 b08bacb7 418c9f30
+          5ae4bdb5 e4dfea91 49ad7507 166b64e6
+          73cfbe64 2f2f87da 75e2ce2f 8ae2afbf
+          4a29deba f305cea0 9e754c2c 3ad110e6
+          27264c1a 2b323c4f d6aa435a c340fe6e
+          afac12d3 bede28da 6b6c22cb a9670dd3
+          daa4c153 eedb275f 8994d747 ddde47ef
+          8e054dbe 7b9f2080 eaea75d3 1cb3b213
+          9990bbe3 fb068502 18102d36 f04e8a87
+          d85183a5 c30ab555 32670d20 9a33f3a6
+          9cbbefaf a3dc3dd1 987ebf9a a387d9d4
+          47febe44 a3035f00 50454086 869d189e
+          81f80923 411b1a2c d5eaab3b 906159c8
+          dab01d2a d3f281d5 a96bed12 5dadd644
+          45ef6ed8 7b86a58b 447a9137 8d036ed9
+          cbeab5c5 34c71640 3d2feacd 725a6072
+          77ed61ca 8f9c6018 9daed6c3 658875c3
+          840ee8cb 045f9fc2 382cea92 0b38e552
+          bafddba5 c55f7fe9 5fb756dc f0255457
+          41e6f373 ef23eeeb 18c92a55 c1dc504f
+          cd610526 6a487fc6 bf673706 cc96ba75
+          cbe8f54c e19efdcc f9dd0719 4eef5ccb
+          2a798936 6074a1fe 27fd6f1b 758a716e
+          84f4d528 aa11306a 6c8136cc 7f2fa833
+          280f0e9b 03d2576f 015b4949 bd8547cd
+          3922a8b8 3b468157 84bfea8a 0548adf5
+          ccd694f4 271f7f5e a8ac70db ef59f4d5
+          e791451b b72de455 943c4cab 9904744b
+          84c86103 1bc4e9c8 4bab0153 7a8614a7
+          a3f3a7b6 bee8f452 80212579 8b3e3ea1
+          912fd688 eca84c82 ee98bc46 505bba45
+          ad303400 35b92590 b57ebbb3 6db87395
+          d8eda00d 0984f8f1 23a5c5a3 ba263d84
+          402cd905 8f167cf8 c18d6ea9 f4562b9c
+          7f73d9cb 3c0b51aa 213a1bad 3cec0df1
+          13c71017 55db289f ce516382 d4151bc0
+          5e61566a 6bc43fd9 9d010411 ecc177df
+          b7f58275 dc24f001 beb70cfa 86d56a4a
+          40a59df6 e82964c1 fe2350f8 d3bec6b7
+          2b2c56f0 ebd10522 07dea8ca db15c49d
+          d566ce9b ffcfaa5f f6b95df2 42da037f
+          1b5773e2 dcbdaa89 4b39644f 2471c248
+          d04786d5 5b757473 a671ba4d 3ba0e26c
+          aeeae274 925567a5 ddc4020f fadd3ae4
+          c09f931d 7565478f cdd54505 6f75a8b5
+          8f32232f a4cccddf 105720ab 9ef028f9
+          db0588ba ed56f04f 89561de1 518bd651
+          65ea953d eff9c71d 55eed395 ac7ce756
+          dfa235eb 17b35af5 146fa265 9b2206de
+          00febd7a 34bef7aa d741c92f 0721ef7f
+          bfaa2971 b8f13e60 272e6c62 fc17faa4
+          0ec265c9 4e22bc91 63feeb10 c9fec1a8
+          5360b4fd a2adc202 e9ab3681 68b2d45f
+          271304a9 f65d0271 1db446bd aa1a6c4b
+          d61d7167 8b777cf7 6cc17fde edea1e5a
+          2f40eaec 590bc46a 73678657 c71cd04d
+          d6af530c 448f1c2a 5732a9f5 c0b45a30
+          676643fa ba6db2da b22a545c 2a0b9629
+          8e7c72ee ea8b8663 2ef626f9 e59fb4e1
+          41bf892a 3ca8a813 0c7101ca 4e6541f6
+          b66fa538 485dfcce 6a05437c 0cc48eba
+          558edda9 ccddd768 c12773e1 4b6f54ed
+          ddd3eeea 746efabd fdcce7b2 1e962a9a
+          a8601ee8 e6aaf533 40e2a4b1 c01a74f5
+          5dc2c866 4cfb6bd0 bbded6d2 1a500bf1
+          5f60d591 8dc0f786 9e6b02c6 4fcc6b36
+          d969e3e2 6d86c4d8 0f443ba8 1a9c541d
+          652f941f 3a7641fc 2e64c00d 10d2bb8b
+          fae27784 f785f2aa 21b9af2e 9cde9e5f
+          a3e6c821 7dd1ca35 cb588d3a 9a5cd38d
+          952606c4 df7e1be8 63a31ac7 e9781e72
+          b7ee82b2 e319aa8c d3c97290 0e26aca1
+          0fccfae0 9206cca5 7e10f1d8 d3ab4496
+          39abd683 8a5ae988 7607a4af dd0ab6a2
+          e2c6e928 44ba71b7 8f04afc8 00d55d27
+          a39b40e9 8e5d8bca b76e8a6d 17c5b758
+          2067e1fc 271c3596 3e8c461d 32a7564b
+          78ffeb20 a86fafc6 659bf43a 28fbed10
+          e4eefa59 b5713a69 4d109168 038d5b8c
+          7d6f3a78 c5641738 fece0a63 af6eef38
+          cca06a50 6532e596 4226213c 89e46a33
+          b2ed76e0 03032061 c2286035 9cbad251
+          6808d326 849e9d76 efaba2c9 d4e61f5f
+          f8c9ff5d 53b07ac3 b39c4adc 57ea3d18
+          3b4440ec b811e41f 0daef268 b560cd3d
+          0fe96bb6 c8d7e558 b52aa924 167be894
+          7b5ed777 ea0c574c 7654a923 9f7af673
+          9161d240 e5a0ae41 e181e350 f0c3cfb2
+          3b5b1bbf 2316866f b7ce1039 f846d559
+          770c9189 ada0ecae 73d3eeb9 bd4dddd7
+          43bf7399 f316bcc9 6bc05b15 160be136
+          8d8f5e8e d3797b49 7d5f6bf5 935e0d4b
+          5fbd19cc 8595aa8d d3493222 1ebd26d0
+          7747f89c 277fbe8c a3766904 de3eb1d4
+          bb47e7a5 820940ad 27b3b53b 074dbdc8
+          dcb20b6a cea6352e 07452cbc c86183c0
+          bf739cea e2773409 bb78d5da d7ca776e
+          f36f33f7 f5a505d3 6c05c543 55a1dca2
+          1cab8b1b 3b18bc12 e3eadd57 bad99235
+          98bb7d17 941c3a2b dded56b3 6e0a36b0
+          844cf9eb 8bba8444 b86ab2a3 a73c49ff
+          f9ef677c 80ef51d1 06aa0625 3b7bb595
+          eca49bc0 515ddd20 1dc54116 9b0e12ee
+          1c0d5a5f 83baaa1b b39247d5 e1ec94bb
+          fe29c9c4 d5eeeb47 efa714ac def812a7
+          922b6176 334068bf 1e1032a0 ef05e5d5
+          2b0e1d85 9c6f76ab a2bcfa9f ee073489
+          38267265 c4a34ffe da8ce5fa e7f0eed5
+          bb9ab0e6 42c126b1 a89aed3b 6961959f
+          c9859cad 17a6a3e8 63a2206e dcd00645
+          92d42313 7b51f9bd a933ee7b b4b6dbbc
+          2b50b5ef 67ffac45 2f7d46dc d71035c8
+          95260e1b e3432176 dc6df255 b0bae6d6
+          1ab01516 43daaacd d25d6ed5 c6e99c36
+          886087ea c477de7e 599798d4 9cbdf9f2
+          8898f3c4 6a62dd7d ab76eb4e 3276693a
+          caf7fba0 ece061b2 a53438e7 273b6ff0
+          8dbd21b4 efb56057 dbed0a1a d3fc6af5
+          b2b3f7de 75afe802 c2abdab7 c77872d4
+          88af6cf9 457f5183 fb4a2fb2 f3068d14
+          a7e37d8d 8dda208a 44bbd356 6e849af3
+          65521841 d544472c 5fdf3e3d dff31f31
+          a659cdc2 9a4576ba c40e62d8 d47be73a
+          886f0cea 8eded5ba 6e90be66 2b58f20b
+          1aa7a388 0e881933 1cbca383 54571d85
+          b8967ce1 f2d51f9f bb77f24c 87a9a6f5
+          886eef9e 7042742b ed25e5b7 a9223645
+          cbab136e 8b1d3304 bc93131b bbaf5a0d
+          9cfff647 28f9e334 707a503d 1c226444
+          3e3df735 5a8bb3d5 c88e22ee f5b77f33
+          f6e9f96f c18c42a6 d685a9a0 0232d66e
+          914ba2d6 55471140 13e00f09 1347935d
+          97575d75 1442785c d1f235ef 1fee7ecd
+          3be5dfee 086d91ce dbac9036 f3fe41c7
+          870ed961 2fab18ae 96203cf5 0a42fed2
+          05c26eee 2b8547ea a0d741e5 b15390b5
+          f57b6034 6a5740e2 e69b00c2 a7dd3b2f
+          60d4b8c2 66afcf05 0b1634f3 03189a57
+          f67be1d7 ab27721a f077fde3 b485c85a
+          60e011c2 abce2e06 8db7167c 3a25d5b9
+          1ab407af 548942b0 336527d2 a5dff350
+          195dd5f8 54116d05 e5bd4bd6 ae1c6f3e
+          79dc21da 852c7d42 4215a3d1 3a97517d
+          c1cd8ba1 faf7dfa0 6ccb96de a9d3a62c
+          2adbb6eb 7510ed11 2d70d718 4f5aa7d4
+          1bf08a0a 828e5327 03474324 82431e9f
+          e7c15652 06673e5e 01d68a9a d65e538c
+          a7e93275 f3395fbf ef3b7eb5 e2093e28
+          b8f95f44 bc825ae3 b44f45ea cc69930a
+          3efd7239 6f708990 44174f80 d89a9340
+          85ce6a79 e8fcf054 f0e99020 b91cd499
+          a5559f45 878339f9 fe275076 2c5d2a1b
+          e5813262 5a2a1b9a 7b4806cb f3be26f1
+          17d6dbfb 8068839c d0e9d3ca b4d1b1d2
+          c7d4125e f1eaaf0d 35478efb 11a24c32
+          9f393dc0 5666e9c1 32a06981 35e71132
+          6a2a2f86 78039d67 4d0163e7 6491a6d9
+          48d58589 8c44b29e ce7db202 0af61f03
+          5eef7e7a d0d67360 378129f2 f187fbc5
+          2f7be7f7 2b625df1 0a1b2b58 32d2e0f0
+          75d72d77 54954d66 3875939d b4011085
+          f6890f23 84378db8 717aead6 ca0d6b34
+          1ac69c97 0fc7dffe b0b57763 8f5364e9
+          168e583f b87891ad 9f696019 b6c22184
+          47c94874 b6418c1b 772b448e 180ca2d9
+          223a0767 a8fb7a7e e70f90b6 6a476b6e
+          9a1e4b76 84e8c0d8 bbc7c2ae 7b7e9d4f
+          d3bfaea4 37ca151d 5c3b1c0e d0c52540
+          d2c71f3e 63b7423e 20a4d48b 8ad47cc8
+          daf20db1 adf9fa59 96d25122 2176ec30
+          393954c5 778ca985 462b93d0 17670091
+          af7f012f bf57f773 35de04a0 f75e837a
+          24cb6d10 65ef405e 2f3a2d54 9e3a0b99
+          5bbe53f5 0d8986d6 afc6dff7 50dc6bcb
+          9630c4b5 bf52438d 9b3f7ffe 157fa83e
+          3eb1dc9a 7eb6b4fa e0b131ac a655b3ca
+          5c4d092e 199f5ab8 55e97960 080b04af
+          b81851ea c44e771c bb1dbce3 63c0565e
+          0695e7f2 5acbbaf3 4819b5e1 f81ef50c
+          d4a23384 fa41c769 7f25c46f 900fbca4
+          169f1cd8 2babe1cc 7f5780a5 b812380d
+          ce816001 7bf8ec07 a684cd78 e8b4c371
+          e5a77f57 9c9228f5 5fd4eb21 f6d5d73e
+          e27c7d56 8a0e95a7 a280f330 969e83af
+          dd06d475 65b49afa a976d074 9461e013
+          1ba2c666 db883fd3 259a13cc b3107fe7
+          28d08604 d5b54194 9a7bb30c 64acdf0a
+          551905aa ae66d2c0 7d657cfb 5cf7cfd8
+          c5af7f27 5ea59bc4 5e4ddb3a 0a5d742c
+          74f8ecd3 2708db66 02a8afc5 60d317b1
+          70197351 1593be6a 1323da04 86e118b9
+          7d1df93b ef6b64e2 278e6638 3dc73837
+          077ce14b 6a831839 b43fe37f 6d57b90d
+          2285284a 6d100b7e dac714ec 3dc4703a
+          9413d519 8d9ff150 cce25717 b03add55
+          b7db6cd1 6513ffe1 23b24326 dd314730
+          a9baea5d 1de84959 e9917370 fefb9fea
+          8b05d0e9 b258c1d8 b923440f bf4575c9
+          c6884bb8 646680c0 ee891035 bc411b44
+          c98ad041 f5b974b9 0d22abbe 36889770
+          5fada1d3 a7cef61b 34a44517 b05b4476
+          ac4e0f71 6ffc733d e7adfd37 ba684e99
+          f000d9db 7e80ca53 671a5737 260b3afc
+          d6016481 77506577 32448338 9d1d401f
+          6c94da20 32bca6be 0d22c781 50638234
+          da06b1ca aabe3688 17233a13 80ef8dd7
+          2f8e59f8 cacf2dd6 cd960ea0 8d8c82e4
+          35ebe672 46ef03a2 032747aa 8e62b643
+          faaacd60 afa8acaf 8e421634 3d418abf
+          6314e883 7cd4551d 05d1284e 472db684
+          89a34017 110e60b3 d57b001c 0b591bb6
+          42456a5e 6be7667a a6ac68f2 b0b7eec7
+          9817172f 66bdbc5a 6e88b4c6 97f21f36
+          a23a6cc6 030fd82d 50815324 170ba84c
+          2f80ac8d 3be4cac6 b5be0859 d8bac830
+          881b375c 3ed541e7 5f7d561d f180226f
+          ed07fed7 756fdc06 917849c5 7b7f83f3
+          3ffd8644 572fab92 8ecb97cf f61b3cb4
+          55823fad 562026fa 85457f84 4e18fd94
+          035d3409 3c59b0f9 7b0e42f1 2f072fa8
+          8e12d8fb 3a08ebdf 0bdd59b5 292f996f
+          ff6b6221 7ac46029 2da96179 75734616
+          a4ad959b d8639c4e 9655f084 d14ffb0f
+          1f75acb5 c66c35b2 637d7c20 e1dd8ffe
+          c3796b3e 11317e27 bb25642d 67acdf0e
+          96dc3ca9 0e996c9b cb652d62 460f0363
+          6218a6a3 a8c52523 b6892ed0 1b12278d
+          0386c672 1bb64134 992075e5 46b05598
+          5d7997da 736465a5 eeabe653 c2271f35
+          b7a2499b 929d64cd 848440c7 e5abe7b0
+          46ef2380 f13ba9de 98a5b85a 6eb64d77
+          f2ba663d 0270466f 4898301a 382f8d14
+          9b402859 7b654d8b bbfd36d0 45453669
+          83c841f6 966fa0fc 6416baaf 92494744
+          65f43e4e 78e4ef94 4f5a551f 5bfbbbfa
+          8f1c5311 f1f0ecfb ed16a8c2 9903a9ee
+          58c99154 c8fbe607 a91e591d ac56f04e
+          4e82189a 8e62078c df291834 5c117173
+          1f08ec73 5d93389d 0e4af61f 84bceff7
+          23d13941 78c34cf9 83f04859 ab1b1fae
+          f8c2114f fde340f0 d8db9e46 77b6dec2
+          cbd9f193 548fac2e 7e47c94d 4a47e90f
+          c13d9331 7ea760a2 f34b8e86 e8d14365
+          d7b52e4e a7014b4e 2e64acdf a6ee3688
+          4ddc57c2 1bcf10fe d8ef123d 748935e3
+          eb0b499f 7ef51e67 347c26a2 124be928
+          82c50ee9 6b3683bd bc5caa4f 269bec0e
+          29661377 c7283084 f90296bd 5798f212
+          8b5de767 909a31b1 5e86faf2 ea1c0ba2
+          c52a9557 b7145563 9c8eca8a f004e18b
+          358437de a1fce131 6427cda7 9f3f24fd
+          f7731abf 3b86f13b b93a4a55 46116491
+          9d5c8ad5 d435ebb1 81362c54 8ae748b3
+          81eeac42 b45736e2 62c70e05 43429336
+          88bc0672 b6ed82d2 63e9524f 62d5438e
+          d39d237c 318bf086 cb34c0a5 c673c0b8
+          3bca221f 7f6caacd 02d538a3 72fcae60
+          ef2128fa f9d7c6b7 2b2c1608 b8ae3b44
+          dcdc1bdd 59a5b8af 6680f07e 3d21b85f
+          6f0073c3 389d16ca 7e3f02b9 dffe2cb7
+          41c43413 20fc6021 3c319df0 45914b0d
+          0e573f48 f89c277f 0d1e31e4 694cb180
+          baabcd99 1b768039 2bb7fec0 424a4771
+          40f4e861 e0db2112 d3513cdd 5021f3e7
+          9b140e31 636f93aa e1d4b741 e4c19a5f
+          08e96b36 814370c8 d54d5056 40f86111
+          e1891f5c ee5db9dc 9af1f383 8e5faf79
+          571360fc 12e37772 714a6bb9 496ab64d
+          e336347e 23c12e48 711ddaac 87f7d662
+          3a8aa77a afb40da2 971612ee 1c039cd4
+          06d1792f 90a61d11 824b5fb5 114cf995
+          6a6f8328 cb8af001 e185ad84 1f16539e
+          f078b293 3ec4c708 09effd67 0ee36d38
+          8ef13b90 e234a547 d32177c7 7752fca6
+          de9db582 575202c4 8c182493 1dc6ef3c
+          4c7b65b2 a36d10bd 3a24364e 33d16820
+          77e7f750 fcc7594c 33914c3a 222b9ecd
+          4c78f73f b3083fb4 c94a6f33 433a70c2
+          e4e2e867 e7de4ffc f31a9c69 90167cce
+          377ba0e2 c8f1c6d7 c9ac1608 bba51f04
+          5f9f82f1 3b0f039d af903edd 207440df
+          0bf2e92a 8f9d84ec edffc338 9d13760b
+          d80286de fa60e0c4 c9596d66 64b4e503
+          86cd9eb3 3f70c8cd cf3ab0f7 ac247987
+          dd21a7a3 94963548 4711a52a b5b1b78f
+          0443981f d6bff314 43c50ae0 1d130271
+          e34741a3 a623b40d 627109a4 aedc407e
+          07e374d2 a66002f0 1f78d36b 1d57acdb
+          dac62ad7 86d60cf1 cb3baddf f2b6262c
+          7005129e 9c6c5c9d 530a1974 cee57ac7
+          f20f6c76 d0860643 fc8491c0 d09c2c74
+          fddd9be8 684b4d03 0f8993c6 00ef67ac
+          cfa7a32d 3509e965 acd90435 b965b255
+          a7764f9f 863079f6 bb98852f bfc87a7b
+          b7b57dd1 c60aeee5 4d0b7e3e cce8b527
+          50896577 b670ff11 28dcf34b e37414b3
+          05fc7b74 8588817d 40c0d359 37d65e59
+          81636e1b 043e291d a56b8075 d06a207f
+          d78f5078 e024c6e9 40aee527 b2fcf9e4
+          cf3f9b69 bc69409b fb2ced62 5407df75
+          4f71cc8b f3a7d3fc 1ad5af00 46be6191
+          b96127d4 a467cae5 dc6bc3b5 c4c2a3e5
+          80fc3a46 61fcce4d 61271e4a f05f3a4b
+          d7feeae2 74d485a5 71ba13a7 216bebf7
+          f2c92bc6 e9689cce 113064e0 234193ef
+          3edb2e86 c595b452 145bb1f9 a957976e
+          59d5fb77 579acf66 0d6fd08e 51956dfc
+          a8f76aaf b183b9e0 3c04f5e8 26758617
+          9db977ac 5e073ed1 1150f2fb 61106c02
+          fd5d6ca5 e826cf40 2d6eafc8 40e8f8b7
+          c9c09379 12e9f53f 11a4f9a3 55aacf7c
+          bc02aca5 5557731d 4c717320 9880f1bb
+          a5df9b1d 57ae7f8b d1b5dccc bd1a2e6a
+          b77029ef 1f00291b b7bcad8d 0e5de930
+          e1be47dd 1c5ae627 67c72e29 f9546641
+          46cac5f3 4a888398 3143a4ea 2822a6a3
+          b80528af b15a1612 278e026d 70805cc2
+          4bd228f9 2a208dd3 55651561 9c0ea45a
+          7e8cc8c0 bed8575e fd07df06 f9749724
+          bbab6d4b d6d297a4 e0463f31 f6e5571e
+          1679ee54 83feb3ea 6dc7a805 2677d75e
+          a6fc8f63 0ca3d7c9 3768a9b0 2c1626f4
+          a61b9890 ded73082 195bebb5 f74b74b6
+          418c197e 33e3dbb5 33991fab dc0691ce
+          974ecbe4 fff03353 f8eb316c 8328c7e9
+          1891d594 76fce4a3 078c37dc 6492f7f0
+          f6e19c76 3f080fb9 f7fec2b8 258b69fc
+          8eaab1aa 0fe6a93b 2bda1d90 b67a33d8
+          8a4aea2d 3c62ce51 4d8abb7d 047845f8
+          137716ad 85f604bd f71ad4a3 03440cb9
+          a5f18184 4e0bd567 d3206bd3 37d80651
+          365b581b 2d453f7c d0a344cf 8fb6f7d7
+          710b7209 9d3a7db7 7fbfdeff 106a40f5
+          815c1acc 369f2f83 8c755be4 a847adc6
+          d805d006 0541c29d a380e519 4c47692f
+          f7956c34 86105f88 9f301a18 da39ae36
+          ae40db20 565643ea d71bc05e 63930e9d
+          54bf29d0 7cbafe7d ff93f4d1 e75fb885
+          6eb9c397 e0030221 65e3d6b7 74f1516b
+          048cdf49 d7c98a0e 1c87829f f63569d6
+          6301bf6e 5d207270 3f4c4769 8fd89343
+          d618bae1 e8c243eb efbdd20d 89652173
+          fd56a84a cbc7389d d319217f 1c8e7fe3
+          cd2734c1 21eea157 ee221c3e 3048889e
+          377f36f9 eb19d5df 09656497 3693b843
+          35a9e972 3a4a2d6c 36881a3a 08fc5362
+          301da5ad ad3ab2c1 440fed2f e53f8ae6
+          c6d7c18a f6fe0af9 3fff8ef9 74cef56b
+          374365dc eb4beef7 e9d5c76d da33b855
+          8c2c74ea 0305f16f 2c9d6133 8345edf6
+          9dd46cbb d20269ab 3649dda7 6a9b6d8b
+          ce7494f8 096340eb 6bc0ea28 6de592d1
+          d8539738 881c3e48 ca7fac27 3a2d9832
+          b22063dd 7639e280 713aa0e1 28df3ed7
+          3f1d3a75 fa6f6ee5 31b99bac 42ee9bf6
+          836fefeb e761fc4e 76672bce e640ced6
+          6fa52e54 75f220d6 9d577c0c c48e1d82
+          d551dac2 a2b3cb6d 1013ee1c 47e6442b
+          e7d351d0 385d750d a4ad580f b64a33c6
+          e9a8accc 00fac4d8 af52366d 7d9f86a7
+          90ec2ee9 e78b52fe 5de7cd5b 97e993e2
+          d6e1fd59 a24fc483 a5dda768 75dbdaf8
+          9dc46d16 0b84f4eb 0d213774 4377d6a5
+          8b520ec9 254c1809 faa888ba 36884c5d
+          1bc49d50 7e2607e3 744e5989 0e3815f5
+          dc730f6b 42425bf5 1282222d 3b89f042
+          4285e8e7 9f7b9008 2e15e377 2055b54d
+          5bb3056c 05c55276 be5350d2 e28a1b37
+          02bca303 b13a8a0b ddd78881 374040af
+          9e176d83 78fefb5f 304ee75c a7763398
+          e2df7c6d 7ae8b499 a5a21b66 bfbb6d5e
+          5bc8d419 f9096f2d 9d2160fc 4e4e47c9
+          af80f4b5 9ba5d47d a6aed9b6 1df8007f
+          290d82d3 72988ee2 02a2a307 41d1a386
+          ca27af4e 0566b41a 3067e540 dada6d75
+          8aaefa0d b906c078 5df779a1 f7cfd8ed
+          b67ae4ce 322482db 65bcbefb 0207c6ef
+          24eba1e8 b793c492 d87341b3 1edfae9d
+          216ac84d 68ddb5a6 87211031 077841fc
+          9d6380d5 ebe59eaf d244b0d2 496cdaca
+          0d602dad 96caecab 1d0eb229 183a26ac
+          4bd9ba7d 19e7ebe7 b6dfd3ad c98e35fa
+          42a7cddb 971a9213 3738d41e 97a2a95c
+          44b1b2b7 7e0fd5a7 cf35263c ab152286
+          0e84c0ae 09e0c0f8 5d2b309d fc8abb7d
+          3818e262 ea6f49d0 0d97d740 d6966fa0
+          ec4426ba af4e5991 8d212d72 eedc8734
+          61116e1d 7472fbeb 599af008 21f2d967
+          67330c7b 4eedf13b 7ada67ab b14ae928
+          42554d5d 3a8ae4da 6ab5923b ab25d688
+          68471d6c a9a5123e a01704f5 b9be711b
+          4422e3d2 df0e41de 77fbf040 a2d6d537
+          8335e18d 25b342ee 9f91e7ee dfd523ee
+          a286dc37 3d3778f2 84597633 d6bfa3a7
+          b315a9e7 217bd30e b933595d b36d2be8
+          a222216e ccb07aeb 0471e544 478c3863
+          87488819 33bc491b 440d58ce 17481b8d
+          48dec7f2 ea523513 305ed7e5 a5d0990f
+          edf484ef eb315316 f3f2926f 8d3daf59
+          24625c4a 729ff27e fc154a0e fcd1c49d
+          b54050df 5e10dab7 07f69ebd 1ae52516
+          b1c6a887 c4496381 f5f66ad4 06d14136
+          93b4951b c1525c8d 6d10a55d 41725fb7
+          75f86ad5 abb4fa38 925d2b42 1b1b0f1d
+          57ac7d95 582cc487 53b93bcb c8961bcd
+          daa7d606 b53ae405 28079b62 c7de2635
+          7fc1038b 2b203aa9 e802487d 7b0d0971
+          525b4ba7 b4ebda20 961e4905 4e8fb292
+          f3e998dc b8a5afce d627a778 cc2af328
+          635cdf31 59885df2 ea6cb230 d3d5eea6
+          51ebc254 5809196b 36138bc4 263761a6
+          b0d981f3 f785f8f1 2380c574 942b725f
+          a3870f80 c03ed735 c9a7d342 f9a1a390
+          b3e3278c d3396137 8310f2d7 c9b3239e
+          7826dd93 ea587956 e4810896 08383be4
+          9ebb6712 81abde6e e1899551 fcfb19c8
+          ddf17dbd 75474194 d5d83505 62470c94
+          0f2b307e f7a7a0f5 e9c2fa76 83a89143
+          a4ab788d e274b9e7 21f5eb4d c465c338
+          9d64d411 f1f8744d 5e16b5e0 a50d1e67
+          2078a2c0 a3e62dda 49048ef1 3b3a81c4
+          dac8d9f1 a3647dd4 9583a2ba 6ab541f8
+          e00110dc eb1abc4e 7619a20b e89200f1
+          93c6c92e 6b837baf 0eb39910 dd7a3017
+          56609c4e 129624a2 ddc9eb36 cdd3c527
+          7a9eae78 a2cc75f1 0990bc7e f362f2ed
+          b7a93e7e 2735db16 89526e04 33b14224
+          0b8f921d 555af2c3 84c9e3c0 af533412
+          de2588ce 37391a3a 4c9d2427 0edb1ad4
+          a723afcc f5dba0ec 5806c6e9 9c1ba843
+          80a29845 8ba6e93b 247be46a f258c35c
+          9fd45188 7971e14c 3201e922 c6efc05c
+          5409e7be 580d4275 35213c67 fe1d71c9
+          381f6f48 9a722778 450621e1 35213a63
+          6204244f bb0b78a3 514e1cae 0d3fe9b4
+          90fbcd0f 90f7c301 4282282b 0ada3232
+          f4be298f 443ef38f d39efa0c edd64ab1
+          a5e3d3aa 13c69b6e aeb06667 9caef8e5
+          8f499c46 22eecb0d a098367e 17101e0f
+          60caaf04 7b452904 5cdb4592 8fe8b4f0
+          68977adf f818283f 711a6c55 166039f7
+          fbfe6df9 19b45c38 b5e89267 dc0d5a7f
+          7f1025a2 93998ed1 eba078ff 6f90be72
+          8b94c47d 89389daa da59d2e6 42864e89
+          ff4e78ff a3d768d9 a6e6e8a9 3b71c555
+          915d43a2 71a96bd6 ccf1e9ef 19ba763f
+          5bb16333 6f2b2cbd f90aeb89 b9fa18a9
+          cdc76788 85579551 00a2dd02 7ed72493
+          5f10a57c 287aaf53 1b1c047e 89b1507e
+          f20cd82a 2ccde965 da16c76c 6d2a23aa
+          1fd4a20b ec9e2459 741a3fbf 2644a787
+          f223c7e1 ec676b40 a43d7a39 c5afa1cb
+          7e066d83 c8689883 5d77efbf 5b9fd4c1
+          76a524e3 2e5c21fd ee957cf9 dadf75d5
+          035cedf8 96d4b39a 43dd5236 89566118
+          a369f68e e6aa5910 db737ca9 b7367157
+          e3c6df0a 91230713 ffd65a7f ba48ab75
+          e4e5c399 8fbf86aa f47ce00c edfafddb
+          5446f462 3f2dc219 76534f88 9f385a2a
+          c229d5a6 ab5d6bc4 a2ab4ecd 8093ef7d
+          4636839a cb1d4888 0a59437f fe19f2e6
+          501ebbe8 850151ff 5878b82d 74d995e3
+          730b162c f058cbae 16c4b476 90d5fc63
+          d9aeff4d 24168b6f 339788e2 2c3b5976
+          b2eb5571 2a1d343a 0df82427 9215eb3c
+          61247ff2 fe7e10d8 2599b8bc f9509353
+          2abb6a8c b22d3b9a 43c7f21c c48d1b0c
+          b1e38613 f97052b7 b6ba07d7 e9a02633
+          1bce7cf8 15584aaa a42b792a f10efef4
+          336cc40a 8e9839f5 91d8d7de dad656ba
+          8c965d33 91367bfa c8dcf73e daa8f102
+          f6125109 c55b7675 bf44f94d 6420fe8e
+          e1103678 807c23a0 76ae791e 1c362b64
+          6fdc09b9 dfed93de 6a60c928 c6b2a31d
+          ae68b56b 43843f24 4c1a0d7e dd3a1339
+          34c8a373 5a74a68c 6c38fd9f 2fc15458
+          d6dcc461 c55b7674 83d07748 fcb4f3b6
+          9df7e912 92da5c97 5d31bea2 c8ce929e
+          0ac7870c 5a643997 f18f4b9c a2a986ec
+          ea090f20 7ec26d10 36a8bf9c 5a519747
+          46cc3f62 e1941c38 04191bb6 4bc54169
+          cf0b8655 06d9d1ab 72b4a27a 689f6b21
+          66cc50d0 0606346e 682d119d 5e6a6a7d
+          e6bfcbe5 5c3aade7 cdb14be6 802c119b
+          058ea6ac f8a27fd0 a4bbcbda 439791ec
+          9a4378a9 67b5877b 74dbe2a8 310f66b4
+          ea26bb3a c273c857 a1a26845 144a7676
+          a1dee7d5 6ac15a58 04d9dbbe 83c27d7f
+          d09c3d51 72e318cf 243b7a63 44b083e8
+          131302d1 23063181 3dbbc91f 69171afb
+          fa061d54 1c3a0e67 3f5f0dd6 b29a2bbd
+          0aa65cb2 a3315f1b 531dfdfc dcc1d12f
+          2eded79e ba8c64d7 0c64bff8 7c42e682
+          c5bb793d 445e3895 ea22bbda 9dda21d0
+          1a6d7f81 b8db6f93 4e1d1b59 39c4ada5
+          a3551c3f 0339dffc 24969f4a a524c970
+          d4b565db 50d15a00 7af84089 4e1f6284
+          d07e7f11 c3faf701 ded79791 9eb3e11a
+          a7350079 0e0a7fda 07196bb7 8360b65e
+          4db56165 921ded23 41cbab5f 9b32a7db
+          1f27de76 075d46b2 6b06d21f 993526e7
+          5f1fac6b 12bf5327 d939ff2f 9a544caf
+          4625de3d 1eb4a121 f285f7da f967e4ea
+          1e841cc4 d263a720 7ff72f4c c5995410
+          4ca214cf 93d23018 f7223b6a b5d65e19
+          d487fb43 48ef9e10 d2f77ad0 06058940
+          8b23088e c6e36b35 52a275d6 e66f21f7
+          9bdd7233 724e4173 dcc239a0 713a5d62
+          c2cace9b b64cd677 ea2c22d9 7908d959
+          32d2e1d8 2dfd175b d3b39f65 bdeaa657
+          bd6457bb a009bfe9 c3022061 c208f0eb
+          d1558ee3 098d5c3c d1490a4c e5b90c28
+          fefda894 8c6c2e2a 9394815a 417527b8
+          4c1b929d b354ba94 5ae32c6e a0f1d180
+          31310e82 aeeb0a01 5d3b03e7 e72bd5a0
+          13054174 0e2e8fcf 32d289ab 39270f32
+          566d86d2 a3e764b7 9551e61c 5fd51cc8
+          71ba739d d7aebc31 f0f68905 eea4cb48
+          76cd80f9 d409fd91 be7db638 2a2a0739
+          e377aa27 3b695dd3 8a503c03 11036f84
+          a861b700 6bf4a93b ad159de3 3374129c
+          6d1b858a 4aa848cd 84aa73e9 507e3615
+          2c45a5c4 e2b38050 db9a8175 324bf308
+          f0f27320 d6ae0759 09a90547 0996d3b1
+          c0fb7881 4f7c2cf8 75880763 87443044
+          86ca16a9 cd569762 53f70cf4 3ffa3387
+          038a7ef9 1db236ed 04737155 6bf48e50
+          16d9d10d c4ca9823 9f7efcb6 d8c54b7f
+          688db24d 4876ed30 7ef6bcb9 89998b96
+          ece1f510 2e4f2b92 5d5d1c8f f083775c
+          38c48c1c 04feddbb c86c65a3 49f262bd
+          55e4bc14 2f979022 644848ce 5a560ea6
+          bc7c62ed 1583a5b8 14ace515 60afae01
+          bbc92c55 f475d805 6a5d5d15 d9311c47
+          88982796 971634de 06d0f81a 41171400
+          bac00030 84858021 3c14385a 4558b23e
+          9d566993 352c9279 66389ec6 e6185356
+          2e646fde 492cd453 b245caa9 688e9b43
+          760c3042 0d804faf aecf75fb f5c82bee
+          accb4876 cd89df3d 3a7b7cee 3befada2
+          f13bd978 41b26b18 a7a1ae69 e0b5d710
+          4baf1ff8 24c58952 2150ab8d 694a2232
+          f1815c28 9406fae9 9f946c68 f97281e6
+          75939714 48132f20 a0065f5e 6cc07417
+          3e83935c 696f5ca9 3f2e3d3c a116267d
+          9f9ea8d6 92dbc5c6 a7bf437f 9f65454b
+          4121e4ff b48f29f8 f937b057 5a5aeab6
+          2a96ece8 75306d4c eca64eeb 37dceed5
+          bd878064 e7e16467 cdcc8423 37f57dd5
+          9695fb8c 337e2722 d9351e91 baa4bc9e
+          8580eed7 88a17d7b 816fc704 46ea6fe1
+          24b24b91 579dcbc3 34fb2b5d 8175eda4
+          c63f5ba3 f4f359a7 e54988d0 949d0785
+          fb0f8a45 070e81a5 a486a93b 5c51fb1c
+          5f6c7c62 c0dbcc90 7dcdc6f5 7d03468f
+          cdf6045d 46b26b06 4c470feb 8e0dbc65
+          bb50567a 0ba341b2 bb946b2b d840a404
+          e1db219e 09ecde19 02ba76a2 a79bb275
+          4549afce 6d14afaa 36c7652d bbcbae58
+          a75bcd39 ad4b62ed d92b2aa5 02072547
+          4e913f4f 83add226 3a490ee7 f8d2e333
+          8285b147 fefd91b1 b1afbdb1 95e1388f
+          d16524bb 6620eb85 b989592f 2dd9c71b
+          20045c07 cf570429 b194ae0d 00ad9f0e
+          8c89f1e0 d731017c e263c010 1a02ac97
+          9e100d2f 130e4d52 965a0e3a ffbc80d2
+          1a4df085 64d774ae 1b5a88b5 561b3d01
+          a12ead28 27448b16 0b980a4b a03a2b1b
+          2a4ea542 c5b93462 c5554b3f a6ee6a1b
+          de02f1cc f19df974 be375cb7 a8ebdedf
+          e679a22e 23d9352f 7e3721f7 9df756f0
+          5ec0b9a8 6a98a214 8d560ba1 c9ba746a
+          34de1cf0 3edee015 19065e11 61d2c181
+          d6df0f78 8301786f 2ff2a75e 22409658
+          0917b514 18e642b2 6b7ab820 0872ec8f
+          fc29982d 60379988 629ac04a ac374b51
+          09d4e4e5 434d6e3e d82aaac0 5e6d9306
+          a3e5aa6a 4f849538 07ad3e38 994fce2f
+          6057e7ed 3b47785f dfcb8a64 a750b2b3
+          e6e6c091 5ed72fb1 9dcf7f9a 35802bca
+          242a56d1 6aaf9e89 72cf5009 b46439ad
+          28c26a35 c0f03c21 1dd9126b 3a873437
+          aed38cc9 e0151b2d 8acef2e7 8c4ec7a4
+          7db10a4a 8fa6d655 1aa93de0 a08da845
+          bb9dfc7f e4457e5f aab2ec4c 4191888d
+          853f6b80 8364f727 b099e07c ecf34fdd
+          18f3d26b 692e736f dc902b78 5011a880
+          b4915190 b275ebfc 134387de 2094150f
+          60542581 167a3fb5 24d3442d 051bb1c2
+          ac422335 6dba87d0 34174a5a 8ddc56f2
+          779ab662 2a2a034e d344bb99 067f32d0
+          1ab97108 d97d7544 ce9ef110 253a29c5
+          c8835a21 b614aa6b 0e4727d8 bbc775e6
+          b007674c b7dba010 35a0e50a 4449b036
+          874dba61 c1cbae65 d3d7c592 55a5bc3a
+          b6fe776a ffffbaf1 580015e9 a34be130
+          01684382 de8a7a7e de5a353e bf2a3b61
+          52c28b59 f0f299c8 87663e24 9800db48
+          2394bfe6 89e1cd07 05ee49d9 b6f579ea
+          dd882aec 52a5deb6 bfc45c88 9af7e22a
+          4d48e05b 0eecba85 5038ec56 280b9bf5
+          c00c9feb 7b9b4595 b6e35375 8f736d68
+          18a46cdd f63c1f1c b85b1450 2110ca04
+          eda61631 73daa3d1 0b5e3eae 6639f057
+          731aeb4a f7b2adc7 f7e9d5db 1cfed083
+          d333e6bf bc5b6380 6068f9f9 acaadaec
+          5de9c0e2 9fdf8750 bd8c5a7b 7c871918
+          4d90df87 310b177f 4ee3a354 07dac2b2
+          7347ae50 b565572b b4a8e716 9c8a9c79
+          ff236407 74e5cd0a 04a26dd7 b6000c1f
+          ec7f3865 f3d62734 a1a120aa bc9b3c7f
+          3547cfee d65dacc5 9fc1f310 f3ca6b2b
+          8ad7aeea 6b2faa7c 94d5b70a e129b27b
+          594b0764 1aff1b65 e4c2f1ed 56a88a7a
+          68f6549f 1b6eacb8 945e79ba 2e5fc9f8
+          aab7ecea f82e2008 3a6dd8f6 8c262460
+          3fc6ef10 9e0eda55 2d62da94 67a29e9d
+          7f10a581 6477018c 7dfb99c3 e73c7a3f
+          d9114b50 1a088f75 5fed009a 609fe5b1
+          4bdf7a97 d16a5120 48761747 c413cf1e
+          273be223 74674420 3c8fe9a4 db2aa73a
+          2c5f3b87 0f084479 20d9fd89 40743a88
+          7bebdf5f 69427cff 2d22e121 3c0c8219
+          cca17f9b fc80ef2d 83f07610 92dde5c1
+          f9182179 f5c667f8 90805f01 e377084f
+          213a1340 c0b081f3 923efef2 a7d6ae4f
+          8764a760 18fbdf5c 1df9e493 53ed5628
+          0511e581 7073efd5 0ec0fb79 6d889eb7
+          709974d9 18816477 25087fe4 f163617f
+          bbebef0e 2bca02e1 c6904b6e a5775abb
+          7eb6cf8d 37e15d6f 24bb2b07 a3d743e2
+          fb1f7fa6 0d0b781f 0f2c10ee 0abb056c
+          217f9d38 d377d090 5c940692 5d8b08af
+          c397ab9e e283030e 60fe1dc2 dd40e374
+          412387bc 9cf4c9f2 9d280d24 bb16c377
+          e0ad55d1 cf3f378d 568ec0f8 1dc25d20
+          da0038a3 6167d40b 2fbe0278 208164d7
+          5a089bf1 d0e1b0bb 263c86f1 3b847b98
+          74547bd9 f3296bd7 cdf4e9d3 17572592
+          5d2bbab3 060374f8 6cf927ba a8d0ffa3
+          155f1188 f604f132 1c4177dc 3ecb77f0
+          b0749406 925deb83 e721e9a3 4f9fe483
+          fc0e62fe 1da2dd8c 3a1aa71b 35ecf50e
+          9fadd880 d240b273 197c870c af8859f8
+          e2349b15 2a501a88 f6705f79 7fe3eee8
+          f90be7d3 cd178164 e752844c 9df147c8
+          84b18f09 988e8268 63904db6 3076e182
+          69debd7a e3ea43b2 6b03a119 0c90bc62
+          cdc7faf8 c88fa4f8 1d96fb44 b4855167
+          063178dc c83921d3 669d4669 20d9b51d
+          380e12fe f5fee3ac 9fcf41d1 8ee240b8
+          167453d5 c546bc97 bc72dd72 d6cb0b05
+          8264d7b6 f01f31ba 22feb557 a713d7a2
+          12a58170 1568323b 6bf43a90 f0f6bf9e
+          61341a14 08925dfb 20e8eebf fd1e3266
+          c4e302a6 a3205c04 7a1816b7 e4956901
+          63c657a1 3490ecda cf9bf5f6 818e2bd7
+          7ea8ef18 f75f243c 446b434a 331931f4
+          89e07ba6 1e4669b4 0caa6fa5 d82a3b06
+          2df8f9da b2c74edf 7dcf75a2 60be9661
+          c1959527 b095a282 65d4607c 86129d3e
+          29e6d34e abd77f48 0fc55a6b fd622b45
+          448b041f 38ee8ef2 84b7df9a 66b34015
+          ca15d1e2 35e50086 d16b8fc4 bebaf4ef
+          ad4974e8 c6225a85 f082effc eb6fc4e5
+          7812ddd9 8b033374 9a2d2896 6c9a3509
+          ff7c7346 d0844965 4874ade4 c662dfd8
+          d6036734 42dcd265 1f94edec 71a3c326
+          dccb6a3c 9a3b1857 0f8a7d63 2f3ea250
+          03103864 e0b3c193 efd9e76a 7dc0beb1
+          88ab86d7 355d2179 f9977318 8dfe8888
+          35631157 084a74fa 4e492b3b adddf02f
+          ced71705 826eac7b bbb3d4f5 08be73fc
+          03761abf 43df0dd1 ecb54314 52af3b13
+          bb70e11c cec7885b 25929d67 2cdab837
+          ffb53f70 c8a0b958 0e0ad15c f7d56e06
+          5bd0c471 3382eefc eb798cd3 21d9790a
+          dd011f10 0009efbe f72e59c4 5fe07532
+          c4e54037 c5804103 e6c7bff9 af1f501a
+          48761e07 7d8764b1 c3179fcf 6178dd71
+          40a70471 a9ad51de 0cb724bc f7c1eb7c
+          5030a699 20d97926 82efbaa7 24f89ec9
+          d36d16a8 4669202e e20400c3 6bb3923e
+          f9f84143 728a0d05 8264e7d1 885dbc74
+          6fe0a0fe 731d1694 05a2316c 661083ee
+          9a342be4 dea95928 0d243b8f 071f1c02
+          891f7ffa 6f868315 18bf43d4 19755680
+          80017d5f 8e7d65e9 56940692 9d62a08b
+          4b10933e fcf81146 a33989f1 3b04d04d
+          8f811f92 3efdfc25 4d6818ca 03c94e59
+          08be776a 51e87d7f 9b6ec7f8 9dca4d3a
+          6aeef385 891f7c30 4d179f84 c10d243b
+          65226afe 4b7bfc07 dcf00f11 3b7daad7
+          a8338318 72efbd0f 86dc3f23 15a58164
+          a7585097 a5e3f255 6f315a66 25e0d99b
+          fa8c3a32 e77e37f6 7a277ac1 4b6b501a
+          4876ca27 bcc86848 78e7fd87 41a33905
+          9852a522 938ea699 c0be8e5f af794613
+          1e81f240 b2530742 a6cd280c 9bf9c00c
+          e2d2e085 32559874 000e3b94 c4bff9ce
+          746d742c b64144b2 531722e7 bef0a35f
+          df5ecf3b 307ea778 3808bd19 6fe8f958
+          e88cd9c7 501a4876 ea7367c3 c22179dd
+          c6373983 66b588f1 3be5129d 05c0bb67
+          97ff4bde b0fd3360 51e590ec 544b7811
+          10f7fa9b 7f078db6 12f3ef94 e9be1282
+          fb35ea85 f94f6a42 42511e48 76ea46e8
+          ccd9c59a 605f0bed 0f8a5016 ecc4aa8b
+          9cf3f0fa c0db2756 a03490ec d0cda9ae
+          e6c12162 994f855a 769af008 9c5b3700
+          b6527483 cf70fe3f ce4e83cd 728c5ced
+          78b96c60 35b65214 ed76464d 7ae6aecf
+          80961d02 e1e1cc8b 40b2f31c c81d9214
+          efeaa8d5 97431fd6 4ddc586c a5d8fee3
+          3357a717 d84ad183 64e48e7a a6045dc6
+          568a0804 02816487 402090ec 10080402
+          c90e8140 2090ec10 080402c9 0e814020
+          90ec1008 0402c90e 81402090 ec100804
+          02c90e81 402090ec 10080492 1d028140
+          20d92110 0804921d 02814020 d9211008
+          04921d02 814020d9 21100804 921d0281
+          4020d921 1008441d b095a21b 7c06b652
+          f49c67b8 daf1b195 62fb8f8f 961d02e1
+          e1cc8b40 b2f31c60 2b457c6e 84ebdd58
+          6ca5d8fe e3632b45 8fe5196c a5e841e3
+          a3658740 20d08d45 20100824 3b040281
+          40b24320 1008243b 04028140 b2432010
+          08243b04 0281b84a f0280244 5b81d3eb
+          00bcbd80 e1adf21b 0603301c 87824120
+          d9219403 8670daf9 1f7e06ed a163200a
+          0ef93d9e 07535e3e b0b80a11 487608c5
+          901d4bc8 ee7f072f b828ca6a 29e9015e
+          204520d9 2194e4c6 5ee20748 74883600
+          1e502010 08243b04 028140b2 43201008
+          243b0402 8140b243 20100824 3b040281
+          40b24320 1008243b 040281b8 34b095a2
+          1b7c4683 ff47d1ad 14db687c b77c066c
+          a5d8fee3 a365e73e c07b0438 b1087463
+          150e95b4 5254edf4 a208dcc3 8dc5568a
+          ed3fbe9a 5a292aec 19b095a2 078d8f96
+          1d028140 37168140 2090ec10 080402c9
+          0e814020 90ec1008 0402c90e 81402090
+          ec100804 02c90e81 402090ec 10080492
+          1d028140 20d92110 0804921d 02814020
+          d9211008 04921da2 0968c933 078a41b1
+          c0b945b2 4334203b 2b8a41b1 b0a10890
+          ec103204 f2aa4231 2816d528 02243b44
+          fdce5f82 62502c8a 51044876 081934a6
+          938f6250 1e44da1b c66a2d40 4920d921
+          248d1041 b4d9cea1 201437ad c0e93555
+          ba8ec9a7 501aed0f 6ca5e806 9fc118bc
+          2078ca7d 6772de78 1b78cd65 9b51612b
+          454f7906 3b8036dc 2f3368c2 9d05d84a
+          b1fdc747 cbce0dc0 701c6822 220e90f9
+          c3533b85 4dadc362 f999bcec 280a7463
+          11ce5d2a 60f4ed47 b581dee7 44540bc5
+          40207319 3471d22e d6604061 b8831b8b
+          ad14dd63 7c7d878e 167d62fc e6eadf8e
+          a5307cf3 3ec2d522 6a0bcb47 b1e38b92
+          2971ded8 ff961d0c c7a31eb8 c1f868d9
+          b98bbfc3 b210f9e4 739f890c 98b085bc
+          e7c36101 305edbf9 b3a00993 ca501ae8
+          c6229a20 68c2e423 c69e5d96 53454178
+          725c8290 9d084511 4f3dfb0e c3f3280f
+          243bc405 e058200a b2401021 0f6f537a
+          aa890e60 370344cc 9cfa4cf0 5d53b251
+          20487688 4b597793 eece8a7b 61ee0cbb
+          052c4878 9e47748e 1a007d7c d4bf239e
+          79fe6314 08921de2 32885ef8 cae6d817
+          e6fe8d10 5e8d88c9 281ee3ba da08d169
+          e3a3dfbe 66e7aec7 f5094928 13243b44
+          3309efeb d87fcc1d c2e8bdf7 da4d2097
+          0a40b81f c7917971 9824d735 2ff2c107
+          675db3e3 9b39fa8e 9db0828d 3b1ade57
+          93d9edaa e3e4b61a df939ec1 74e2b8e1
+          fcbfdeb9 a7e8cbcf 66d8ca6b aee718e2
+          2cf13411 195c9558 21d63b65 2e1dbf2d
+          3e8371c5 c8522e24 755989d5 cdfb7965
+          e9131297 473cfec4 bbc153ee cb403d70
+          5fae40b2 f3906730 9d38c195 6cda38a0
+          74f55783 2d9939fd 6c05c55d 1c22f891
+          1f695a75 f7bb0823 b962fcb6 f80c178c
+          ef6019a8 d2460665 10c2db13 387ee2cf
+          e18f3cba c990dcb1 0c381ef5 00c90e27
+          b955b5cd 6a05d162 654a37ad f3b3a466
+          74607888 226f0738 5f5af2e2 5b129e10
+          9d0fc0b8 e801c406 93e0eacf 6885f1a9
+          0d475d52 1a482822 0497af4b 8c3b1b30
+          e6f642f2 1156de68 443df020 aef87fa6
+          d4ea5719 f13ec200 00000049 454e44ae
+          426082   
+        </hexdata>
+      </glyph>
+    </strike>
+    <strike>
+      <ppem value="1024"/>
+      <resolution value="72"/>
+      <glyph graphicType="png " name=".notdef" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000334 00000334 08060000 006d7267
+          77000019 34494441 5478daed dd7b8c5c
+          d561c0e1 73e7b1e3 b159e3c7 c660b00d
+          86865684 261017d1 4a0db48a a23651a0
+          28097688 2d120386 9a504214 444b1154
+          a5a28e50 2b522940 08d86ea9 ed041339
+          d04a4591 daaa2454 150db409 e2d10266
+          b10d7530 7ebf66e7 797bee7a d7d9b5cd
+          c35e7b77 c6fe3ee9 ecce9ddd bd337b66
+          feb83f9d 993b499a a6010000 a0131546
+          eb86de79 64d9f857 16dd7061 5739fc7a
+          dc3c2b8e 2971f4c4 31338e89 03a36be0
+          d78b1e1a 0000683b 8d38fae2 a8c491c4
+          b13d8e75 71bc15c7 a681ef2f d62ae1f9
+          0fdff7ad edd3aefd c3637e87 9263b142
+          f3d692bb c2fabbef 99512c87 4fc6cd4b
+          e3383fad d7a7a7d5 5a2e5e1e 1c616012
+          865e0600 003a4716 13ad81ef e990ed56
+          52eada98 148bcfc4 cb4fd52b e1c959b7
+          dfbae1f4 dbee6cdf a0597fdb 2d61e303
+          4b3f5a28 87ab5a7d 95cb5a95 eaa949d2
+          bf0294ad b6e4fb83 45b20000 c089923a
+          596834e3 68c4e4a8 e7caa5b7 72e3caff
+          d0a88487 a72fbee6 d5594bfe aa3d8266
+          ddd76f0c 6faf583d 3769ecbc 3946ccb9
+          b159c6c5 70298917 00006058 e0a4a116
+          eba312e3 e6e5b430 f15ba72c 98bbfa8c
+          7bbf3d36 41d37bc3 b561ebe3 4f2e482b
+          5bbed1dc 5b3d2bc9 859362c4 e43c5200
+          00c0fbc4 4d2b6d85 ddf9f1a5 d792f2d4
+          fba65cfe e965b3ef 7f78f482 a677f135
+          1fdfb47c e53da155 9d93e4c3 24ab3100
+          00c01184 4d489b61 476e42e9 d9fcc453
+          ff64d2a7 3ef1ec59 0ffffdb1 0b9ab557
+          2f08bb7e fcccedf5 2d1b16b5 f65667c6
+          98b12203 00008c34 6c5a693d 6c08f9ae
+          877ae65f 71f7d9cb 561cfda0 c96266f3
+          cad50fa6 8dfa95b9 aed06d55 06000038
+          aa5dd30c 3b4328ae e8993ff7 e61835f5
+          a316346b 17ce9fbc 79d5634b 43a87f26
+          c98792a9 0600008e 51d45462 d4fca8e7
+          4b575c7d f6f295db 461c3431 66c296ef
+          3dba264d 9b97c598 c99b6200 00e01847
+          4d3349f2 ff38f5ca 79f362d4 d4deeb77
+          73ef1b33 abbebf3c c6cca562 06000018
+          0d597bc4 06f96c6c 917bb326 39a2a059
+          7bd517c3 e655ab6e 4b436b5e dc61c1b4
+          020000a3 183585d8 220bb226 c9dae4b0
+          8366d7d3 4f9d9f36 c3757147 65d30900
+          008c41d4 4ccc9a24 6b93c30a 9ad7177d
+          39d4b7ed f866ae18 66994600 0060ac64
+          4d92b549 6c9443b6 cb214f0a f0dcf4c9
+          f3ea9bb7 2f8b7f3c de140200 0063a955
+          0f7b8b3d 93ae9bb3 71dbca83 82e7c02b
+          7a6fbc3e b4faaa7f 9c2b8471 a60e0000
+          186b599b c446b929 b6ca41ef ed3f6885
+          e6a793c7 cd6deea9 fe9da001 0000da45
+          ab11f6e4 2794165e b8adefb1 61b13374
+          63dd2d5f 0b4948be 91cb872e 53060000
+          b48bd828 e5d82a5f cd9a65a8 612b34cf
+          94923393 343c1f0a a1db9401 00006da5
+          11b6a749 98735135 7d7df0aa 61af412b
+          f44cb9b6 b965eb88 56679224 de4e2584
+          666abe01 00807df2 b1130ae5 10d29174
+          422e940b 53a72c8c 97ee3828 68defc8b
+          3b436bcf 9ecf8764 642f376b d44298f2
+          b15f0913 cf3d2784 6acd2307 000027ba
+          5257d8f9 d22b61eb 0baf857c 7104fb89
+          ad129be5 0bb15dee 9871c75d fbae1a7c
+          c9d97f24 c9ac7c29 bc14ab67 c248ee6b
+          b512c2af 7ee93361 dadc3f08 61e72e0f
+          1e00009c e8267687 4dab9f08 ffbbea9f
+          42a93cc2 7db5c2ee 66359cfb 5b69ba21
+          dbdcbf42 53ec99f0 c9d6ee3d f991ded7
+          248e66b6 3293c5cc aedd1e3c 0000a0bf
+          1192a3b1 a32414b2 768997fe 36dbfce5
+          59ce92e4 77e2d7bc a9060000 da58b628
+          f37b831b fd41f3f6 77ee0b69 adf6dbe1
+          80930400 0000b499 7c48920b 8705cd6b
+          8b6f9c94 f6d54ecd 3e84c6fc 0000006d
+          2c496bf5 e99b963d 347e7fd0 749d5cfa
+          b598325e 6e060000 b479ce84 90f655f3
+          af5e73dd 79fb8326 3acbcc00 00009d12
+          355d2797 3e323468 ce09434f 10000000
+          d0beb276 99955d18 3c09c08c 7d9d33da
+          7723de8f aea28703 00003a55 ad1e42ab
+          35dab79a 0c34ccfe a0e919f5 a0893153
+          dfb92bec 5ef77f21 f1ee1d00 00e83869
+          338493ce 382d1427 768f76d4 64ed72da
+          d8064da9 2bec7a63 7df8ef07 1e0d25cf
+          050000e8 38d5382e 583c2f4c b9f08210
+          2a7da31d 34c35668 4e1ff5a0 49d3902b
+          14fa63a6 abecc900 00001da7 12fa8fe9
+          b363fb51 96b54b77 7661f044 005d1e0d
+          0000a083 1486064d 31041faa 09000074
+          8cfcd0a0 71ca6600 00a05364 8b319386
+          864cb739 0100003a c8b0979c a5e60300
+          00e820e9 d0a00100 00e83882 06000010
+          34000000 82060000 40d00000 00820600
+          0040d000 0000081a 00000041 03000008
+          1a000000 41030000 20680000 801342e1
+          b8fa6f92 109a7b43 483dae00 0070a8c3
+          e5901f1f 8eab03e6 e32a689a 9510cefb
+          c9539774 cd98b9c1 d3150000 86abbdb9
+          61e60b17 5ff2547e 9ca0694b 692ccdd2
+          99b37b05 0d00001c 2c29141a e971f672
+          a6e3ee3d 3469a351 f0540500 8013e358
+          d9490100 00004103 00002068 00000004
+          0d000020 68000000 040d0000 80a00100
+          00103400 0080a001 00001034 00000082
+          06000010 34000000 82060000 40d00000
+          00081a00 0040d000 0000081a 00000041
+          03000008 1a000000 41030000 20680000
+          00040d00 00206800 0000040d 000080a0
+          01000004 0d000080 a0010000 10340000
+          00820600 00103400 00008206 000040d0
+          00000082 06000040 d0000000 081a0000
+          00410300 00081a00 00004103 00002068
+          00000041 03000020 68000000 040d0000
+          80a00100 00040d00 0080a001 00001034
+          000080a0 01000010 34000000 82060000
+          40d00000 00820600 0040d000 0000081a
+          00000041 03000008 1a000000 41030000
+          20680000 00410300 00206800 0000040d
+          000080a0 01000004 0d000080 a0010000
+          10340000 80a00100 00103400 00008206
+          000040d0 00000082 06000040 d0000000
+          081a0000 40d00000 00081a00 00004103
+          00002068 00000041 03000020 68000000
+          040d0000 20680000 00040d00 0080a001
+          00001034 000080a0 01000010 34000000
+          82060000 10340000 00820600 0040d000
+          0000081a 000040d0 00000008 1a000000
+          41030000 081a0000 00410300 00206800
+          0000040d 00002068 00000004 0d000080
+          a0010000 040d0000 80a00100 00103400
+          00008206 00001034 00000082 06000040
+          d0000000 081a0000 40d00000 00081a00
+          00004103 0000081a 00000041 03000020
+          68000000 040d0000 20680000 00040d00
+          0080a001 0000040d 000080a0 01000010
+          34000000 82060000 10340000 00820600
+          0040d000 00008206 000040d0 00000008
+          1a000000 41030000 081a0000 00410300
+          00206800 00004103 00002068 00000004
+          0d000080 a0010000 040d0000 80a00100
+          00103400 0080a001 00001034 00000082
+          06000040 d0000000 82060000 40d00000
+          00081a00 0040d000 0000081a 00000041
+          03000020 68000000 41030000 20680000
+          00040d00 00206800 0000040d 000080a0
+          01000010 34000080 a0010000 10340000
+          00820600 0040d000 00008206 000040d0
+          00000008 1a000040 d0000000 081a0000
+          00410300 00206800 00004103 00002068
+          00000004 0d000020 68000000 040d0000
+          80a00100 00103400 0080a001 00001034
+          00000082 06000010 34000000 82060000
+          40d00000 00081a00 0040d000 0000081a
+          00000041 03000008 1a000000 41030000
+          20680000 00040d00 00206800 0000040d
+          000080a0 01000004 0d000080 a0010000
+          10340000 00820600 00103400 00008206
+          000040d0 00000082 06000040 d0000000
+          081a0000 00410300 00081a00 00004103
+          00002068 00000041 630a0000 00410300
+          00206800 0000040d 00002068 00000004
+          0d000080 a0010000 10340000 80a00100
+          00103400 00008206 00001034 00000082
+          06000040 d0000000 081a0000 40d00000
+          00081a00 00004103 0000081a 00000041
+          03000020 68000000 040d0000 20680000
+          00040d00 0080a001 0000040d 000080a0
+          01000010 34000000 82060000 10340000
+          00820600 0040d000 00008206 000040d0
+          00000008 1a000000 41030000 081a0000
+          00410300 00206800 00004103 00002068
+          00000004 0d000080 a0010000 040d0000
+          80a00100 00103400 0080a001 00001034
+          00000082 06000040 d0000000 82060000
+          40d00000 00081a00 00004103 0000081a
+          00000041 03000020 68000000 41030000
+          20680000 00040d00 0080a001 0000040d
+          000080a0 01000010 34000080 a0010000
+          10340000 00820600 0040d000 00008206
+          000040d0 00000008 1a000040 d0000000
+          081a0000 00410300 00206800 00004103
+          00002068 00000004 0d000020 68000000
+          040d0000 80a00100 00103400 0080a001
+          00001034 00000082 06000010 34000000
+          82060000 40d00000 00081a00 0040d000
+          0000081a 00000041 03000008 1a000000
+          41030000 20680000 00040d00 00206800
+          0000040d 000080a0 01000004 0d000080
+          a0010000 10340000 00820600 00103400
+          00008206 000040d0 00000008 1a000040
+          d0000000 081a0000 00410300 00081a00
+          00004103 00002068 00000004 0d000020
+          68000000 040d0000 80a00100 00040d00
+          0080a001 00001034 00000082 06000010
+          34000000 82060000 40d00000 00820600
+          0040d000 0000081a 00000041 03000008
+          1a000000 41030000 20680000 00410300
+          00206800 0000040d 000080a0 01000004
+          0d000080 a0010000 10340000 80a00100
+          00103400 00008206 000040d0 00000082
+          06000040 d0000000 081a0000 40d00000
+          00081a00 00004103 00002068 00000041
+          03000020 68000000 040d0000 20680000
+          00040d00 0080a001 00001034 000080a0
+          01000010 34000000 82060000 40d00000
+          00820600 0040d000 0000081a 000040d0
+          00000008 1a000000 41030000 20680000
+          00410300 00206800 0000040d 00002068
+          00000004 0d000080 a0010000 10340000
+          80a00100 00103400 00008206 00001034
+          00000082 06000040 d0000000 081a0000
+          40d00000 00081a00 00004103 0000081a
+          00000041 03000020 68000000 040d0000
+          20680000 00040d00 0080a001 0000040d
+          000080a0 01000010 34000000 82060000
+          10340000 00820600 0040d000 00008206
+          000040d0 00000008 1a000000 41030000
+          081a0000 00410300 00206800 00004103
+          00002068 00000004 0d000080 a0010000
+          040d0000 80a00100 00103400 00008206
+          00001034 00000082 06000040 d0000000
+          82060000 40d00000 00081a00 00004103
+          0000081a 00000041 03000020 68000000
+          41030000 20680000 00040d00 0080a001
+          0000040d 000080a0 01000010 34000080
+          a0010000 10340000 00820600 0040d000
+          00008206 000040d0 00000008 1a000040
+          d0000000 081a0000 00410300 00206800
+          00004103 00002068 00000004 0d000020
+          68000000 040d0000 80a00100 00103400
+          0080a001 00001034 00000082 06000010
+          34000000 82060000 40d00000 00081a00
+          0040d000 0000081a 00000041 03000008
+          1a530000 00081a00 00004103 00002068
+          00000041 03000020 68000000 040d0000
+          80a00100 00040d00 0080a001 00001034
+          000080a0 01000010 34000000 82060000
+          40d00000 00820600 0040d000 0000081a
+          000040d0 00000008 1a000000 41030000
+          20680000 00410300 00206800 0000040d
+          00002068 00000004 0d000080 a0010000
+          10340000 80a00100 00103400 00008206
+          00001034 00000082 06000040 d0000000
+          081a0000 40d00000 00081a00 00004103
+          0000081a 00000041 03000020 68000000
+          040d0000 20680000 00040d00 0080a001
+          0000040d 000080a0 01000010 34000000
+          82060000 10340000 00820600 0040d000
+          0000081a 000040d0 00000008 1a000000
+          41030000 081a0000 00410300 00206800
+          0000040d 00002068 00000004 0d000080
+          a0010000 040d0000 80a00100 00103400
+          00008206 00001034 00000082 06000040
+          d0000000 82060000 40d00000 00081a00
+          00004103 0000081a 00000041 03000020
+          68000000 41030000 20680000 00040d00
+          0080a001 0000040d 000080a0 01000010
+          34000080 a0010000 10340000 00820600
+          0040d000 00008206 000040d0 00000008
+          1a000040 d0000000 081a0000 00410300
+          00206800 00004103 00002068 00000004
+          0d000020 68000000 040d0000 80a00100
+          00103400 0080a001 00001034 00000082
+          06000040 d0000000 82060000 40d00000
+          00081a00 0040d000 0000081a 00000041
+          03000020 68000000 41030000 20680000
+          00040d00 00206800 0000040d 000080a0
+          01000010 34000080 a0010000 10340000
+          00820600 00103400 00008206 000040d0
+          00000008 1a000040 d0000000 081a0000
+          00410300 00081a00 00004103 00002068
+          00000004 0d000020 68000000 040d0000
+          80a00100 00040d00 0080a001 00001034
+          00000082 06000010 34000000 82060000
+          40d00000 00820600 0040d000 0000081a
+          00000041 03000008 1a000000 41030000
+          20680000 00410300 00206800 0000040d
+          000080a0 01000004 0d000080 a0010000
+          10340000 00820600 00103400 00008206
+          000040d0 00000082 06000040 d0000000
+          081a0000 00410300 00081a00 00004103
+          00002068 00000041 03000020 68000000
+          040d0000 80a00100 00040d00 0080a001
+          00001034 000080a0 01000010 34000000
+          82060000 40d00000 00820600 0040d000
+          0000081a 000040d0 00000008 1a000000
+          41030000 20680000 00410300 00206800
+          0000040d 00002068 00000004 0d000080
+          a0010000 10340000 80a00100 00103400
+          00008206 00001034 00000082 06000040
+          d0000000 081a0000 40d00000 00081a00
+          00004103 0000081a 00000041 03000020
+          68000000 040d0000 20680000 00040d00
+          0080a001 00001034 000080a0 01000010
+          34000000 82060000 10340000 00820600
+          0040d000 0000081a 000040d0 00000008
+          1a000000 41030000 081a0000 00410300
+          00206800 0000040d 00002068 00000004
+          0d000080 a0010000 040d0000 80a00100
+          00103400 00008206 00001034 00000082
+          06000040 d0000000 82060000 40d00000
+          00081a00 00004103 0000081a 00000041
+          03000020 68000000 41030000 20680000
+          00040d00 0080a001 0000044d 27480a85
+          86871500 004e8c63 e5c271f5 00252154
+          dfe89d9d 361a054f 57000018 aef6e686
+          99d931b3 a06953f9 72082f7c e292a752
+          cf550000 3848d632 f9f1f1cb 7174c07c
+          7cad64a4 fba20600 0078f763 e6e38993
+          02000000 82060000 40d00000 00081a00
+          0040d000 0000081a 00000041 03000020
+          68000000 41030000 20680000 00040d00
+          00206800 00003a21 68eaa602 0000e820
+          f5a14103 0000d071 0683a666 2a000080
+          0e521d1a 343be248 cd090000 d001d203
+          83a6624e 0000800e b233fb52 18d8d83d
+          5039c9a8 dd7c9284 56a3b12f abe41400
+          00749cec 583e3ba6 cf8eed47 59d62e3b
+          8606cdfa 38e68cee 7f5f0bdd 67ce0a1f
+          bf617e48 f29e0c00 00d069d2 6608279d
+          715affb1 fd1804cd e6a141b3 2b8cf67b
+          685aad50 9cd81d26 cf39cf33 0100003a
+          55adde7f 6c3f0641 f3c6d0a0 79278cc5
+          4901b27f bcafea49 0000001c 6ed06ccd
+          2e0c9e14 e0d5e02c 67000040 e704cd86
+          fd4153db 51fd79bc 4ad00000 009d9033
+          add830ff b93f683e bcf4bb2f 26e34a2d
+          49030000 b479cc84 d82ecdd8 302fef0f
+          9a69572f da9b7415 d7052f3b 030000da
+          3c6962bb bc1d1ba6 b63f68f6 5d9d3e1b
+          bf36cd0f 0000d0c6 1ab15d9e 1edcc80d
+          f9c18fe2 a89b1f00 00a08d65 8b30ff36
+          b83178da e650dfbc e79ff3a5 183823fc
+          90cfec35 6bf95257 0813bb4d 350000d0
+          df065923 1c95f7b7 a4a115db e55f0e0a
+          9a9977dd f18b8d7f 7d4f6f5a ad9e13a3
+          2677a4fb 2fc43d6e 7be995d0 5cfdc458
+          7c622800 00d06e62 ccec8c8d 50288c3c
+          669271a5 b5336fbf 75fde055 499afeb2
+          939e3b7d ea9dcd2d 5bff34e6 4ce9486f
+          23494268 5442683a bd000000 30201f3b
+          a150cede ba3f829d b4425f7e ea942573
+          deda72d7 2183e699 52323b49 c37f8542
+          9864ca01 0080b6d2 08bbd324 7cf4a26a
+          da3b78d5 b097969d fa4737f5 e6268ccb
+          3e64b365 b6000080 b6111b25 b6caf359
+          b30cbd7a d80a4de6 a793c77d aeb9a7fa
+          48ae1026 98350000 a01db41a 616f7e42
+          e92b176e eb7b6ce8 f507bdf9 bf67fe97
+          d7e42794 5fb44a03 0000b485 d826b151
+          7e1e5be5 8707fee8 a0159acc 73d3277f
+          b1be79fb d25c318c 377b0000 c0586ad5
+          c39e62cf a4abe66c dcb6e6c0 9f1df2f4
+          cc933f7b d9f79324 fc246d5a a5010000
+          c64e6c92 34b6c9d3 b151d61c eae7875c
+          a1c9f45e bff0fccd 8faefe61 5add7be6
+          483f6c13 0000e0f0 6b26064b 69fcda9e
+          7973bf30 fbc1e53f 3bd4afbc eb076866
+          7fd035fd 43df6dd5 c22e3309 00008cb6
+          d822bb63 93dcf76e 31f39e41 9339e937
+          2e5a922b e696a7cd 50379d00 00c06889
+          0d528b2d b22a36c9 bdeff57b effa92b3
+          416b17ce 0f5bbef7 e80fd2b4 7979920f
+          79530b00 001ce398 692449fe 0753af9c
+          77e5d9cb 57bee7ef 16de6f67 033b58b4
+          79d563b9 b459fffd 18356553 0c00001c
+          a398a984 507c62ea 95577cf5 fd622693
+          fb203b8d 3bdad633 7feee7e2 8e97b6aa
+          617b484d 34000070 344b2684 d81a3b62
+          733c1cdb 235b99d9 fa41feec 7d5f7236
+          d4daab17 845d3f7e e696fa96 0dd7b7f6
+          56677b09 1a000030 e2966986 666e7ce9
+          cde2d499 0f765f7c d192b397 adf8c07f
+          7b584133 a877f135 e76c5abe f26f6242
+          fd668c9a 9343e2c4 ce0000c0 e1964c7f
+          cc6c0fb9 d2bf4f5b 38ffcf66 3fb0f4b9
+          c3ddc511 054d7fd4 dc706db2 f5f127e7
+          a6952d37 35f7563f 92e4c249 316bacd8
+          000000ef 1d316968 c6efbbf3 e34baf27
+          e5a9df9c 72f9a757 cfbeffe1 23dadd11
+          07cda075 5fbf31bc bd62f5a5 4963e7cd
+          ad4af582 2484720c 9b92551b 0000e080
+          90a9e6cb e376e5ba bb7fd6a8 84fb4e59
+          30f7f133 eefdf688 763be2a0 19b4feb6
+          5bc2c607 969e5928 87afb4fa 2a9f8f71
+          734692f4 9f452d1b c57db7e6 71040080
+          13215ea2 d6c0a8c7 e4a8e7ca a54dcdb4
+          bc66c6d7 167f67c6 9fff65ef d1baa9a3
+          163443bd b5e4aeb0 feee7b4e 2b96c3c5
+          71f35371 fc6e5aaf 4f4babb5 ecac6a83
+          23396048 1e0000e8 ac6c3970 64019326
+          a5ae5a52 2cbe162f bf1cc7bf d62be189
+          59b7dfba e5f4dbee 3cea77e2 9804cda1
+          bcf3c8b2 fc2b8b6e 38b7ab1c 3e163767
+          c7312d8e 53e2383d 8e530762 a67be0d7
+          b3cfba29 788e0000 40db68c4 d1174765
+          607b4f1c 9be2f845 1cdbe2d8 18c7abb5
+          4af89f73 1ebaff85 0f5d7575 7534eed4
+          a8050d00 00c0d1f6 ffef0bc3 1bf3b9a0
+          34000000 0049454e 44ae4260 82
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="X" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000278 000002e1 08060000 00ccfed9
+          b10000d3 81494441 5478daec 9d079c15
+          d5f9fe9f 99dbeff6 ded95d90 2a224d51
+          90a2803d 768d3196 fc8cc658 628f464d
+          515334d6 24d6688c 266ad4d8 3b7614a5
+          2808d2fb f6decbed 73e7fecf cca2c17f
+          c29d0b52 76ee3cdf 8f236d17 76df39e7
+          7d9ff39e f7bc478a c5622084 10420821
+          c9837d6f fd432d0f dd97baf9 ca6b273b
+          3d3840fc b2523cd9 e2c9174f a978d2b7
+          3d0ef148 db7e2464 5f10daf6 04b68dc5
+          76f1d488 a7491bc6 e2a917cf aa7000ab
+          0ffc64be 2f75caa1 b418217b 81c0fa75
+          b6e5078e 9fe8f060 9cf8e570 f1e48927
+          4b3ce5db e247a678 5cdb3edc 458b91bd
+          4c64bbd8 a1d1b32d 763488a7 75db8f6b
+          23017c55 79e76d5d 45975fbd c7bf2069
+          4f64f0ea 7e7da354 7ff79fcb c4449c2b
+          7e799c78 c6c72291 c2583822 8b90296f
+          0b9cdb3f d8ee4742 0613da04 51b7fd18
+          fbe6d731 4465b7bb 163679a1 f8f52742
+          f0bd39e6 85e73a32 8f399e16 23e43bd2
+          fbc94758 7dccf185 4e0fa688 5f9e2a9e
+          8950d572 3510b46f 8b21f276 3143660c
+          21833476 7c3b6e68 3fc6a04a 4e478be4
+          702c11bf fe5808be b74bafb9 a2baecd6
+          3f0c5e81 5773d565 68fefb53 13ec1e9c
+          ab060327 c442612d 3ba76508 b56c9c8d
+          538f24e1 f4d5264f 543c8af8 49c4e6f5
+          6c911cce 1784d8fb db88471e 6ac9f9fe
+          0f682342 12a4fb8d d7b0ee87 e70e11a2
+          eebc9812 3939eaf3 ef276d1f 433401c7
+          38429227 7e285aec d01ec9e5 6c94dd9e
+          d715113b 0acf3f7b 7df9bdf7 0f0e8157
+          75c98572 fbbf5ffe 3e94beab d46078a4
+          24c12d7e dbc98948 2c2af842 624a056d
+          a9294b14 b8ef1a76 cf1defe7 9d7b3e6d
+          43c80ee8 78f1dfd8 74d125b3 1c52e85a
+          a5af7fda b618e2a2 9c23168a 1df83a76
+          c86ee746 d8d3eecd 3de3e4e7 2a1f7c54
+          dd27026f eb05e74a 9d6fbc77 5e2cd879
+          b51a0857 48325239 2109f966 c246632a
+          faed19a9 2ba372ea 5de5b7fe eab5829f
+          5c42bb10 f2b5b0fb d73fb1f9 eaebe7da
+          63be5f28 dd7d13b7 c5103b2d 43ac9e28
+          d06287ec 716e95dc d90f651f 37f7af43
+          1ffbe7de 13785bce 3ffbe0b6 a79fbf43
+          8a852748 36a453d6 11b20354 a862b2f6
+          dab3d2de 519d1937 965d77d5 d6c29f5d
+          4dbb104b d3fee4e3 459b2fbb f24e29d0
+          7b8c1076 1990f52d 5842c87f 841e6251
+          f4c95ee7 97b6b4fc 1b320e9f ba68d83f
+          9fdb7302 6ff3d967 a0efb3a5 372b9d0d
+          e7aba170 89989832 df022109 09bd881a
+          450d3c69 b756fcf6 d74f165e 712d6d42
+          2c47ebdf 1e42edef effcbe14 ecba45e9
+          ecae946c 70d22a84 c4157a6a 4c410324
+          c7df73ce 38e9e6fd 9efaf7ee 17789bcf
+          3e1d1dcf bff238a2 ca699243 4fa51342
+          7676ae2a e88427fd ce2137ff f2f6a2ab
+          7e4e8310 eb88bb47 1e40d5f5 bfbc3ad6
+          d77da364 478ec4f4 002189c7 8e28fa00
+          fbb339a7 9f74f97e 4f3d1fdc 6d026ff3
+          0f4fcfed 7ce195c7 6350e68a 1517fb0b
+          11f2dd44 5e2fbc19 4f0cf9d5 0dd7155d
+          737d8816 21492fee 1ebacf51 75d36f7e
+          17ebefba 48b62383 16216497 445e4882
+          fdfdecd3 4e3a6fbf a79fef30 fa78dbcd
+          37df6c24 eed0f9c2 cbcf01d1 6385b863
+          036242be 23922c16 49c1d001 3d8bbe48
+          b7d963ef a64d9d4e a390a4a5 e5c1bfa0
+          e69737ff 26d6d775 85ec401a 2d42c82e
+          c70e3b62 eab0c0da 0de302eb 57bd947d
+          ea19ca2e 0b3c4ddc 75bcf0e2 bf00f524
+          21ee5804 4bc8ee9a a862b114 0b86c6f5
+          2d5e1a92 6dd14569 d366d028 24f9c4dd
+          fd7f42cd af6fbd46 ededba4e 7652dc11
+          f29d9121 c762b1ca c0da7545 c1f5ab5f
+          17226fe7 05dea633 4f41e74b 2ffd5afc
+          f40216c2 12b24744 9e530d84 c6f57fbe
+          b43116ec 5f9d3e6b 368d4292 86c63ffe
+          0ef57ff8 e3a96a4f e76f85b8 cba54508
+          d94db143 3be01ac3 88c0dab5 cec09a95
+          1fe79cf6 fd1d68c1 1dd0bf78 e1644475
+          71e7a139 09d9438b 31278a22 ddbd1705
+          376faca0 35483211 aaae2a0b 7774fe44
+          1be3b406 21bb3d41 902634da f9ba56db
+          517cf95f bfb9f9dc 33a174f5 fc5172a0
+          84662464 cf627361 4af7079f 5c5dffdb
+          dfd01824 2968baf3 3674be3e ef62bb0b
+          2c302564 4f893c07 4a8556bb 4368365b
+          c202afe7 dd79e7a8 fee021db 2e752684
+          ecc9492a c31d6e6e 3ddeffd5 9733690d
+          920c04d6 ae3a34d0 d0788a18 dbdc0122
+          648f050f c89a56d3 345b4202 6feb85e7
+          21160c5d 2bd9f5fb 0009217b 019b1365
+          7d8b96fe 5fc36db7 d218c4d4 34dd7307
+          ba3f5c70 9ec38961 b406217b 58e3d9e1
+          129aed32 a1dd1c86 02afe385 e77e2814
+          e14866ef 08d9ab93 d41e6a6c 9ed6bf70
+          c1545a83 9899fe2f 161d1aa8 ad3f5c1b
+          d3b40621 7b3a78e8 59bcd142 bb9d1e57
+          e0555d76 91762dc6 d560bf3b 42f62e31
+          3d8b57ee 5bb1f69c c63b6fa3 3d882969
+          fecb3de8 5bfce50f ec4e5422 467b10b2
+          57b0c12d e6dbc5ba 86db91c0 6b7df491
+          61b16068 04b37784 ec838598 1d8e607d
+          e3c4ae57 5fcea135 8819e97a ebf5ac60
+          75ed81da 58a63508 d95bc103 b2d06e07
+          080d377c 8702cf9e 9375a1f8 404e4c42
+          f605316d 9ea2d291 977b228d 41cc8823
+          2fef5831 8647307b 47c85e17 791ea1e1
+          ceff9f02 aff6866b a1fa7c27 8b0f6253
+          6342f611 b203b9be e5ab8e6e 79e0cf34
+          063115ad 8f3e84be c54b678b 315c406b
+          10b2d705 9e4368b8 53342df7 cd6fc562
+          034badc5 925429bb b04a48be 145a8a90
+          7d364911 f56369da a4fd8f18 bb74751f
+          0d42ccc2 da598764 767dbce4 75871787
+          318347c8 3e40854f 0d61ff43 62b11a3d
+          61f0f5ef db73bc47 88e0c253 4f84ec4b
+          06b66973 6d995907 d118c44c d83232c7
+          8a80924f 7147c83e 4b10d885 969bf3f5
+          2fff5383 2749b3b4 394a0b11 b2cfc913
+          0f9b1e13 5321bb5c 87881f0a 690942f6
+          dd3a4b3c 477e4be0 35ddfd47 c4c2e1c3
+          28f00819 14a4484e c7389a81 98858e67
+          ff05dfea 7513253b d2690d42 f6a1c093
+          a483bf25 f0aaaefd 45762c1c 2980a4ed
+          0e1142f6 25224822 b8a9bab0 e3d9a769
+          0c620a9a eebdc3ee 5bb7a958 e6113d42
+          f669f888 85c3f9cd f7dd9bf2 8dc07364
+          38797305 21836586 8a20e9df 5c95def0
+          db5f17d3 1ac40cd8 b3737244 004963fd
+          1d21fb32 7800b170 c4b6f5f2 ab0ff846
+          e009860e fc112164 9f13d327 a6d79e9b
+          378ac620 26a1423c ecc040c8 be177992
+          23c33966 7b813702 60068f90 4184473c
+          25340331 91c04ba5 1908d9e7 685a6ec8
+          f602af94 36216450 e1124f11 cd404c42
+          e1b64509 2164df53 babdc0d3 eebee416
+          2d218307 b7787823 00310b99 e2e1110b
+          42f63d9a 962bd95e e0e553e0 1132a860
+          068f9889 1c0a3c42 06a7c02b a6c02364
+          d04d5216 ad13b390 4f8147c8 a0891da9
+          db0b3c07 051e2183 0e361e27 66c14113
+          10326804 9efdff17 7884100a 3c427605
+          de634ec8 209b8f32 27272183 165efb44
+          b8182184 ec2c19db 0bbc34da 8390c1b9
+          0a23c404 78690242 060d8eed 059e4a7b
+          1032e8e0 c54f8463 9510b24b f391b757
+          10420821 84241914 78841042 08211478
+          84104208 2184028f 10420821 8450e011
+          42082184 100a3c42 08218410 42814708
+          21841042 81470821 84104228 f0082184
+          10420805 1e218410 4208a1c0 23841042
+          08211478 84104208 21147884 10420821
+          84028f10 42082184 50e01142 08218410
+          0a3c4208 21841042 81470821 84104281
+          47082184 104228f0 08218410 4208051e
+          21841042 08a1c023 84104208 21147884
+          10420821 14788410 42082184 028f1042
+          08218450 e0114208 2184100a 3c420821
+          84104281 47082184 10428147 08218410
+          4228f008 21841042 08051e21 84104208
+          a1c02384 10420821 14788410 42082114
+          78841042 08218402 8f104208 218450e0
+          11420821 84100a3c 42082184 10428147
+          08218410 42814708 21841042 28f00821
+          84104208 051e2184 104208a1 c0238410
+          42082114 78841042 08211478 84104208
+          2184028f 10420821 8450e011 42082184
+          100a3c42 08218410 42814708 21841042
+          81470821 84104228 f0082184 10420805
+          1e218410 4208a1c0 23841042 08211478
+          84104208 2196c24e 13989498 f82f66fc
+          6192a4fd 8fe62284 10c21842 814706f7
+          bc1493d2 91e28164 b70fccd2 1dce4c19
+          6a308868 30cc094a 0821e41b 7167733b
+          21bbdde2 e76a3c79 8798a220 e20b0c08
+          3d428147 f62c9120 b0ff95e7 2075ec28
+          2014def1 0766a4a1 f1a91750 f5ca4770
+          78683742 08210331 a4f4e869 283efb34
+          a0a76fc7 1fe872a2 7ff57aac b8fd1138
+          194328f0 c8de410d 0b611710 b3341c47
+          e0391d50 c5ea8b10 4208f956 0cd16283
+          164382c1 381fa40e c41a624a 78c88210
+          42082184 028f1042 08218450 e0114208
+          2184100a 3c420821 84104281 47082184
+          104228f0 08218410 4228f008 21841042
+          08051e21 84104208 a1c02384 10420821
+          14788410 42082184 028f1042 08218402
+          8f104208 218450e0 11420821 84100a3c
+          428886e4 74d008c4 1c81c4e5 a41108a1
+          c0238418 074ca07f c97269cb 8fcfa631
+          c8a0a6ea 920bd133 7fa1ac8d 59420805
+          1e21c460 6646fb7c 36df8ae5 b40519d4
+          04567d05 a5a75792 184d08a1 c023bb01
+          55056231 c30f9364 1912ad65 4e242d93
+          c7b40819 e4c3548c 5189b1c4 6cae458f
+          0d866831 468b3584 028fec45 121077c4
+          f4d8b63d 8410c258 4328f008 49a28536
+          218c2584 104e4a42 924ce0f1 282d612c
+          21847052 129264f3 93fd27c8 60c7ce58
+          62226262 e5286dab c1e3f6ab 25041eb7
+          824c84f6 b262d1a8 f1e4147f ae1fb290
+          06263531 1d5afd5d 1acd4006 39a9db44
+          1e315520 4920ec8b 18a2c51a 0a0453ca
+          846f045e 84f630d9 224c1777 b1dd3389
+          c960450b 9ae93403 19e46863 94a504c9
+          1969b6c5 1a623222 db0b3c85 f630d9b4
+          fbba4d8a 9180d373 f1b41705 1e217b8c
+          340a3c93 2171f19f e428db0b 3cd64f98
+          4ee0c58c 13784200 ca769b78 586a6152
+          b4fabb1c 9a810c72 b2c15a51 f3c40e11
+          0bb498a0 c586b881 411380b1 6db18698
+          0d797b61 c72d5ad3 09bcc41a 1d338367
+          6a588347 cc803646 59836722 b45b4712
+          6a742ce2 8c1ae106 9f098950 e0995de0
+          25329185 c06326de b4708b96 9801ed90
+          05b7684d a5f090f0 166d2cc6 9b2ccc2e
+          f0b8456b 3698c1b3 02dab657 1ecd4006
+          39c5e2f1 d00c66c9 0e889060 b3e94f22
+          9d187855 9929e116 adb927e9 4e9ca265
+          0acfacb8 b6054f42 063385e2 71d30ce6
+          416f9f65 4b24af13 6301b739 e1295a73
+          eb3b3501 7db7ed90 85be52a3 cd4cb8d2
+          764a0e3b 051e19dc 62c1e9d0 041e0f59
+          98e99dd9 12147831 6ed152e0 91bd8e56
+          f86a5887 a7893a66 f0cceb84 1d90c275
+          8df9edcf 3c496390 4149e7f3 cf22b8a5
+          a644b2eb 07828839 168edb04 9ef1166d
+          8c872ccc 8afcff0b 3ce678cc 12f8b1dd
+          d17523ed 46816766 81875055 4366ed0d
+          d7327892 4149fdcd 3749fe8d 5b3324e6
+          efcce55b 24597f0c 03cdb658 c308623a
+          bed5072f 4a7b986c 1196c855 65da1c95
+          6d09d65a 9041b9d2 96e174e4 e5e7d218
+          643062cf cdcb92b5 fa3ba607 cce35634
+          bf62b743 76d8133a 64a1c71a 62b2c881
+          c0f6028f 872c4c86 1a0a6f9b 7852dcc9
+          293b6cdb 1a5ad266 26453b68 51413390
+          41ca70f1 78690673 a1c5044d e419a407
+          f42dda18 b768cdc8 b7327861 dac36412
+          5d8d1a27 f03481a7 65f0c4c3 8350a645
+          0b9e6369 06328805 5e0acd60 a6e0a109
+          3cbbfe24 94c1639b 1433f2ad 43163e30
+          c7632a54 25ca3e78 d6406b22 3b9e6620
+          8311c96e 2b073378 e6433b60 a195ee18
+          e93b3586 6888f91f f34978f4 6c2ff0b8
+          c96e3681 f7f5295a 03f12639 b6add488
+          59714936 7918cd40 061b7d9f 7e8c484b
+          fb38c9c6 0c9ea9a2 bf96c1b3 69edb3ec
+          88abf0a4 6da76815 6ed19a90 d0f602af
+          1bcce099 67d52c26 5e34144a ac064f88
+          3bbdd682 6fd79cc8 9094aede 217d9f2d
+          e0491932 a8d8fca3 b36cfd6b 36ee27b9
+          d822c55c 0a6fdbc2 df611417 243dc668
+          b1868d18 ccf686bf bd451ba2 4d4c4622
+          279bb4d7 fc75bf23 624e31ef 04821bab
+          53369ef6 bd225a83 0c261cf9 0585b284
+          142e1e4d b86eb47d dd00df68 8f163c45
+          6b4ebe75 c822487b 9829ea0b 7d178e40
+          8d1a6dd1 c6f434bc 369179c8 c2c4ab6d
+          1b521c05 8587d118 6490a11d fee1f6ac
+          d95c8af0 2936b70b 368f3bbe c093b66b
+          74cc0c9e d9f8560d 5e04dcc4 338fbe13
+          934df107 10d36b23 e26fd1da 5d4ec8e2
+          e1db3535 e9e2a5cf a619c8a0 4296b531
+          994d4398 f0d57d7d 8a366e60 186893a2
+          b5e4a2c0 335b5ae0 db7df098 8335197a
+          e16b2c81 abca6cdb 1a1d53e0 9958d1c3
+          a5fa0393 026b57b3 0e8f0c0a 02ebd74a
+          6a5fff41 2282f004 ad19059e d309c9e9
+          4cecaa32 116ba8ef 4c47dff6 02af9bf6
+          3055c047 2c1219b8 ae2cdecc d30e5938
+          1d901d0e ea3b33bf 6ebd0eaf 267fcdac
+          a985b406 190cac3f 66768e7f e58632d9
+          455b980d 7dddef76 41f28897 a71a6cd1
+          0a71a7f8 7ccce099 55c86ffb 3104e678
+          cc3549b5 1629c69d 8e7571a7 3dc4dc1e
+          59b2c3e3 c8cf9f4e 6390c180 3d2f6f92
+          24c3c3a8 614e6c5a d98ecb28 83f7f516
+          6d84a768 cda7e17d db0b3c3f 6d621eb4
+          3ba2c3dd 7dfa418b b8336f5b 06cf4681
+          97042f1d e9b170f8 d4506d0d 6d41f629
+          e1ba5ac4 82a1e3c5 98cca235 cc17fab5
+          8a1d7dd1 afc68c7c 8e2ef0a2 91083378
+          e613782d db0bbc16 308367a6 600f5513
+          777a0d5e 9c992726 b09686b7 89872fd7
+          e4afdc0e 47704bdd c1ab26ed cf4d31b2
+          4f593dfd 20a77fd5 c6d9920b 1e5ac364
+          915f6b72 ec96c4e3 4a600748 12714641
+          b837f81f a540cc42 707b81c7 3e782613
+          7851ed8c 4554359e cd4eed14 2d354152
+          bc761bd2 ed59d927 d112645f 62cfcc3a
+          5c8cc52c ae1acda8 f0f04d5d b6a1c0d3
+          32785a12 416502cf 7c6f19cd db0b3cde
+          6461a640 bf4de029 a1044a27 6303e978
+          59e61b4e 02d284a8 3f53e9ec a025c83e
+          41e9ea44 4c891e27 7e9a416b 9830f28b
+          186077bb f4c7708b 567cb01a 0e236ab0
+          514406a5 c0eba1c0 33b3c8d3 9cadcf0f
+          e84d28e3 d7e1e94d 2ddd60b3 63b3bf73
+          3bece1da 86495f8d aa48a335 c8be60d5
+          8431eee0 bacd274a 4e6ecf9a 32f24785
+          c04b4b13 4faa1078 6afc2c82 1285120c
+          51db9993 eeed055e 0fed61c2 d5b41078
+          7ab3e378 024f4ce2 0181e7d6 53edc4e4
+          c8c892bd 2917a93e 1f6d41f6 2aaadf07
+          d9e3b950 8c4166ef 4cabf0b4 16294efd
+          89db4755 c414ad15 57a4b78f 3633e35b
+          8ec5be75 c882193c 93a1493a 351836be
+          2750cbe0 b99cb00b 81c70c5e 12bc771b
+          52234d2d 972c1f5a cca3d164 aff2d598
+          61b6e0e6 ea2b2407 059ea905 9e5697ed
+          b01b57f7 88d8a232 8367cab7 2cbbddff
+          396421d9 6c3c6461 4214bf7f db3d81f1
+          4fd2dabd 5ed8bc1e c38b2f88 69d47dae
+          e4749ec3 4bc0c95e 8b1862ac 893177a6
+          187b39b4 86a9f51d 1c69a970 a41a6fd1
+          46431184 7bfb29f0 4cf68265 af3734a9
+          a5f73f57 954deef0 778adf8c 318767a2
+          182f665d b847eb85 1736a8c1 5385c0f3
+          c091e265 8e365990 91a6b4b7 5ffb6529
+          632dd93b acd8af14 a19abaf3 253bd269
+          0df30b3c d9b0064f 0b1d5121 f2423c60
+          613e82ff 0915ff21 4cbb9848 e069cd8e
+          7b7b07fa e1c59b80 6a4cef83 a7d5e171
+          8b36a928 17cfe534 03d94b9c 299e8960
+          473453ab 3b59c40a 6dc10fbb 3dfea93b
+          49821a0a 8b18d3a7 c71a622a 0ddff2bf
+          045e0d98 e3310fe2 cd457afb f563ec46
+          872c1ca9 2962d596 c2979b54 0a1fde68
+          4fef8d5f 96e616d3 18644fb2 6278993d
+          d2dcf27b c9864c5a c3c4915f d5b27776
+          3d1ec4cd de6d1378 5af62edc d94d496f
+          3e81d7fd bf049ed6 188f555a 6689efba
+          c0eb4ba0 064fbcd2 b4343833 3298694f
+          baa91ccb 5683c15b 6908b227 1163ec21
+          31d64a69 0993bb8b 28e0cace 86334be8
+          f4682202 2f8c5067 9f76b08b 9868ba62
+          0719bc66 3083672a 81170ac4 a0f4fb8c
+          1bdc69ad 52523cb0 b9c08316 493508e0
+          5003fe33 9757161e 4f63903d c157a32b
+          67281d1d a78a48e1 a435cc2f f01c1969
+          8965f044 48514321 44c2b1b8 f9033228
+          055ecdff 1278095c 8b400655 7c176f2b
+          d8da0ed5 1fc0c055 153b1678 ceb434b8
+          b2d2f549 4e92c96b c75222cd 2d7f5e5e
+          599465e8 b409d929 7137d416 aaae7e5c
+          0cb22c5a 23095c85 789ce9a9 b0a77ae3
+          0b3c5942 4c88bb50 772f777d ccf99a9b
+          ff97c0ab 05b768cd 25f0c413 0924d02a
+          252a049e 58b93933 29f09274 2054845b
+          9a1f8e2b f209d909 568cae44 a8baea69
+          31b6ca69 8de489fc da095a7b 6a9ac109
+          5a196a20 a0270f28 f04cf99a 7bff97c0
+          e316add9 e2ba787b c1a67644 b52bcbe4
+          78757851 38d2b50c 5e264fd2 2627dadb
+          3ff1ab11 153fa029 c8ee205c 5f779688
+          eedf133f 65055632 447da1e7 ec4ec09d
+          9b23fee7 8c5fd623 bc897645 59a8ab9b
+          2768cdc7 0eb7685b c00c9eb9 049ed60b
+          afb7c7b8 175e5485 233b0bae 9c2c2af8
+          e4c515ac aeb9e7ab 91fb5580 9dabc877
+          60e58123 474155ef 113ff5d2 1ac923f0
+          9c59a970 a6a7e9ad b38c3207 d14010c1
+          f60e0a3c 13be6af1 74ed48e0 31fe9b08
+          d90ef4d7 7722a2dd 4b1a6f7b 4e5bad39
+          1d706565 e9ab3866 f19254f0 db5118dc
+          b2e589af 468f42b4 bf9f0621 3b171922
+          11ac1a3f 0681b51b ef134ea2 801649a2
+          77ab9fa0 cdd47772 b41d9df8 81458622
+          045ea0b9 8b2768cd 27ef6291 0e7fed7f
+          093cf19b cde05d16 26537840 341245b8
+          a35bbcc0 88e19565 dae47666 a6b00e2f
+          99459e13 d37deb37 5c67d3ae 22226467
+          c68ec301 ffca757f 901c9849 6b2459dc
+          5701774e 365c99e9 f15ba468 31241246
+          a8a3134a 486506cf 5ce24ebb a124327e
+          f5aa8eff 1278e396 7fd924fe 50a2c433
+          9953164f b0bd1351 5fc0609b 362ac45d
+          1adcb9d9 1478492e fbed2e5c bf6ac2c4
+          f13405d9 19561f3c e930c98d cbc44f1d
+          b4467205 092dac7b f27361d3 7be045e3
+          0a3c2d96 84da3a59 e7614289 27d96c0d
+          9efdc7aa ff25f052 c64f5024 bb9db759
+          986dee8a 5918686e 4144eb87 27dbe20a
+          3c777e0e 3c8505ec a691f412 0f59be15
+          cb1f5e75 d041294a 6707ed41 8cc5dd94
+          299efe2f be7c42fc 348dd648 b2a81f01
+          dcd91eb8 0bf2019b cde08085 0ca5af1f
+          fd8d4dcc de998f6f 1db0f896 c01b1809
+          b17af0a0 85b9049e 6da0175e 34101093
+          377e064f 4e4b87a7 a840d781 acc34b72
+          8de7c194 dea54b6f b467e7d0 1864c76e
+          a1b7176b a64d93fb 3efffc11 316686d1
+          224918f5 f5fabb8c 6d072c0c b66f8400
+          8cf4f723 d0d81c37 5f400625 8a7836ee
+          58e001ad 1478260b e476c0df da8b704f
+          ef403acf a0edb83b 274b3ca9 8829b45d
+          b26377e1 d2d53366 cc014fd5 921dc5f3
+          f474742f 5c78a6cd 8333698d 24157822
+          a2a79697 c1539827 248091c0 93a1f803
+          0874fa79 c0c284af 5a3cf5f1 041e3378
+          6643d20e 5aa8f037 b640d5fa e1c51378
+          8aa2b74a f11617b2 0ecf0a43 434646ef
+          82050fae 39fcf0a2 484b330d 42fe8bb5
+          73e61e60 77e2766d 3d406b24 277afd5d
+          411ee4cc 8cf8f577 b20cb5b7 0fbebac6
+          81f8c065 a1195f75 5b3c81b7 413c0cfd
+          2643eb90 d25f5b8f 7057b7be 02db2162
+          72bbf273 913aa4c4 b0151249 0eec1e0c
+          ef9c3fff 8fb6f474 0670f29f b55e4707
+          d61e7594 a7fb83f7 ef966c28 a3459234
+          e28b68ee c9f2c095 9d655c5d 2f6247b8
+          ab07fd75 0dbc14c7 a4d35a3c 5be2093c
+          66f0cc28 f0ec0302 2fd2db2f 227a9c38
+          aeaa905c 2e788af2 e148b5eb 47e749f2
+          e374e2fb eb4f3ded 0cc462dc 742103b1
+          3c33436e 7ff7dd1b ed5ecca5 35921735
+          a26dcf16 ebdd13b4 1d9cf883 c28e489f
+          96c16bd0 630a31db cb861a6e e9d9bc43
+          8127feb0 5efb205a ca5c68b5 12a12ead
+          3165ab71 3fbc6814 9efc3ca4 89491f0d
+          d3769618 1f7638ba de7efb77 6b8f3f7e
+          a8a19327 9618121b 4e3b7da6 cb89cbd9
+          332199df f2c0a515 69951570 15e4c517
+          785acc10 b1416bb9 1514b184 f5772643
+          bc675b46 aa3af2c5 7fb7ef50 e08d78f6
+          e96a393d 55e1a437 a57a87af be1191ee
+          de81a3f0 3b4289ea 933d7df8 3036c4b1
+          d0e47778 51d9f5d6 5b77ac3b e594b470
+          5d0d6d62 61d69f72 4a4ec72b afdc2b84
+          7f3aad91 c4d35ee8 3957ba1b 29258590
+          5c065718 8998a174 75a3bfaa 967b78e6
+          f4f131c9 edaece3e e5747587 022fe7fb
+          6705658f bb9e375a 980f2da5 deb7b90a
+          a18e2e11 cde36fd3 ca5e0fbc 45f9b07b
+          6ddca6b5 96c83ba9 f5f5d77f 2c16eb6c
+          646b4122 4d8dd870 fae99e8e 975fbe5d
+          8c8503e9 e5931b6d 87267d58 29dcf9b9
+          c6dbb376 3b02aded e8deb099 dbb3269d
+          de42c02f f92f5df0 df8120a6 1db4e03e
+          8e09059e afa91bfe 86a681c9 6cb04deb
+          2d2c40fa 880a6ed3 5a4ce4b9 1cf8f9d6
+          6baf9f1c 0bf8690f 8b21c9b2 d4f4c20b
+          670871f7 638abbe4 9feb1a19 a387c3a9
+          0bbcf8b7 57682bfd 404b9b10 79fd90b9
+          fc33a7c0 03561a0b 3c60d9b6 0f26a6f2
+          de03fd8e fa6beaa0 74f5c43f 4d1b51e0
+          2acc47f6 d8d10327 e1e9ecad b31070a0
+          b8edb9e7 6edff8a3 1f1586aa ab6810ab
+          c4fb4818 55d75d3f d1edc04d 9cefc98f
+          2ad6f829 c5994829 2b85e474 186ccfca
+          503abbd1 5f5bc706 f8e6e5bf 9a1cef48
+          e0ad0533 78a6c426 e671cffa adf037b5
+          ea472777 eced637a 4a3ea5b4 08dea20c
+          dd1910eb acec9d5e cc68fcf7 f397a9fe
+          7e370d92 fc84ebeb b0f9c73f ce69fee7
+          3f7f2704 fe705a24 f9890a9f 9eb9ff48
+          78f272c4 0030c8d7 8858a165 ef7a376e
+          d50ed212 532a7a44 23ad1deb 0c059ef8
+          a02aed83 6931f3a1 df6ad1de afb74c41
+          281c7f9b 3612813b 3f0f5907 8cd19d01
+          9b5a5a4b e4b91db8 b4eece7b 8e8cf6f5
+          f0cd27fb eb0ef85c 0d4f3e75 b9cb8ba3
+          99bdb3c0 fb56c5fa dd6d43fa d072d832
+          0cae27d3 62841080 da013d7f 4b1fb767
+          4dfabe6d 19a9a1f2 3bee6a36 147865bf
+          ffc31639 3d354047 60529127 e66befa6
+          cd0836b5 008e38b3 351a852d 3d1599a3
+          f6832bc3 cbabcb2c 86dd81cc e6279ef8
+          f5964b2f 1d11aada 4a8324eb c2deef93
+          eaeffed3 112e3bae a44fb706 5a5d75d6
+          e84aa494 150f94df c47bef22 46849a5b
+          d1bb6113 0d67de05 bb2aa7a7 6f2abce2
+          9a98a1c0 2bbef686 802d23bd 41a842ba
+          03330a3c a710789b 1bf46ee4 b01b3433
+          52a2f00e 2945dec1 fb43d10e 5b309763
+          1d9f2066 b7c78b49 0d4f3e7d 79b8a921
+          85164942 71d7df8b 2d575c51 def0d7bf
+          fedae664 4b148b04 7b9d9cf1 63e1d4b6
+          670d4fcf cae8af6d 40cfa63a d89c349f
+          49094155 3fff9f7a e07f7b06 7585f83f
+          cf579a10 ad416524 a0a0afaa 06d1ee9e
+          f83df1a2 51d833d2 9135760c 9c696ede
+          4f6b4191 e7b6e3ff 5a9f7cfa a4685707
+          5b9b2693 c7afadc1 d6abafce 68fadb63
+          d70b217f 0897ebd6 40cbde65 8c28466a
+          45d980ef 37e87d17 edee45df d62a847d
+          11363736 2f9a565b 9db8c003 16ebaa90
+          987205a7 1db6e85e b3197d5b aae31fb6
+          d826f2bc c585c89d 3c1a51ed 8d338b67
+          29ec4e78 1a1efeeb f55bafb9 769cd2d1
+          4683248b c7afadb6 d73dfad8 1942dcfd
+          942723ad e3fbb577 9d7fe841 70e6e51a
+          1fae70bb d0b7b91a 9dab36eb 31838b00
+          93be7615 8ad2d9b9 286181a7 74772d15
+          9fc40c9e 49d10e5b 045a7bd1 b3710ba2
+          7e5ffc96 298aa267 f172c68f 83339db5
+          7896730e 035bb507 343dfec4 2faaafbb
+          2e3754b5 85463139 d1ee2eb4 bff8ca14
+          971d5751 dc59e8bd 6b8d8df7 2b42fad0
+          0a480e83 d628b28c a8cf8f9e 0d5be06f
+          efe5e10a b3a21da8 494f6d2f b8e4f28d
+          090bbcfc 1f5fb4c9 9e9e12a4 a237b1c8
+          b3015dab 37c22756 68701964 f1541529
+          434a5138 63221b1f 5b57e49d 51f7f727
+          fecfbfe2 4b0f2d62 5ec2b535 a8b9e9c6
+          a2da3ffd e9468713 a36911ab 4c62ad91
+          b584c2c3 a6c09993 35702779 3cdc2e11
+          1baad0bd 7ee340a9 3663bd39 5fbb0ac5
+          919fbb7a c86d7722 6181577e d79f038e
+          fcbcea58 94b7d299 56e0094d e76fec42
+          cfba4d50 83617dc5 b6e3a55f 14b6b414
+          e44d9e80 94d2cc81 ad5a6239 91e7b2e1
+          b2eef90b 0e55da5a 691093e2 5fb93ca5
+          e6c1872f f27a712c b377d641 09025963
+          872263e4 7e0965ef d440508f 0dbefa2e
+          66efcc4d 30168d2e dde1abde a1c38f46
+          3f163f04 683ff3a2 15cd76ad d988fe0d
+          9b8db378 62c5e710 2bbfe223 6688096f
+          e71db516 c4e1c290 dabfdc77 55cd4d37
+          94866a78 cb85d988 7676c8dd 0b3e3b4a
+          08f5ab29 ee2cb438 d3b6e952 dc289c39
+          158eccf4 04b2774e f4addf84 8eafd6e9
+          3b3dacbb 36312a02 d1bebe8f 765ae009
+          348117a4 05cd8b76 ecbdbfbe 031d2b56
+          23eaf3c5 3f51abc6 20bb5cc8 3e707fe4
+          1f3c062a b76aad17 288428f0 7a717ced
+          a37fbfb0 f7c3f7bc b4887950 3ada517b
+          f36f46d4 dc71d795 42a8a7d1 22168af1
+          21a068c6 848193b3 466a4dc4 00d51740
+          e7ca35f0 3575e93b 3dc4b4e2 0e729ad7
+          977dd229 cb775ae0 a93edf6a f117f046
+          72b38b3c 87568bb7 09dd2bd7 014e835c
+          bc58f9d9 52535078 f861f014 6640e58d
+          c4961479 2e1b7eea 5bbdf668 a5ad856b
+          7b13106e a817e2ee d799b5f7 3d708510
+          e8d399bd b3508c17 3eda539c 89fc6907
+          0bdf9daa 97dbc445 c4809e55 ebd0bd66
+          0baf2533 bbaf56a0 baca4ab7 563ef458
+          78a7055e c69ca39a e4144f35 622cbf34
+          3392d074 c1f63e74 2e5f05a5 abdb58e4
+          a9aa7e85 59d93147 4012ea90 5bb5d6c3
+          e1427ecd 3d7fbeb2 f697378d d6ee3125
+          839bee77 de74d4dc ffd0d95e b644b1dc
+          624c6b99 5076dc1c 3833338d b76685ef
+          573abbd1 b6ec2b04 da7a99bd 337d7047
+          7f4c51de 8cf7213b 1478c3fe f12fb8ca
+          cb3e8d45 5887676e 2f00d85c 2208acdb
+          82b6854b 07d4ba2c c5157892 ddaedf51
+          5b306d1c 54b64db1 64e0d032 41b58f3c
+          7679e7f3 cf72bb6f 10a36dcd 06366f9d
+          ea947111 c59db5d0 b6660b0e d91f99a3
+          47e83e3b eec10a49 12be5f42 fbe2a5e8
+          11b1408b 094cdd98 7d00a057 0d06deda
+          2581a711 f5f5bf23 7ee8a325 4d2ef4c5
+          5b567c0a dabe588e fe759be2 df51abbf
+          f828e414 0f4a8f9e 8db4a145 acc7b3a8
+          c873c838 27d4d078 86d2dec6 cd9c4148
+          a4b90975 b7fca6b4 fab63b7e ee74632c
+          2d62a1f9 2916dede e22c941e 3b572fab
+          31de9a75 c2b76133 da967ea5 c70249a6
+          0dcd3d00 a0c5e8d6 d42953d7 efb2c04b
+          993879b1 ec71f750 e99b1f9b 07f0d575
+          a0f9d325 88f60acd ee3088d9 4a14f6b4
+          540c3de3 043852bc bcc6cc82 08d1e0ad
+          befb4f97 d6fef286 8942e4d1 20838c8e
+          679ef4d6 dcf7e04f bc5e1cc7 ec9d8562
+          bb565cef 7261e899 27c2217c b4f17db3
+          36fd905d d3270bd1 5bdda6c7 0262f231
+          1046d83d 7ce897c3 9f7901bb 2cf0463c
+          ff6ac83d 62d82af1 97b1122b 09d0eaf1
+          7ad66f41 eba26588 697df1a4 385bb57a
+          c488c153 5080b213 8f101f6b 674adf6a
+          4e6460ab 7642c35f 1fbbbcf6 c6eb3323
+          ad2d34ca 2041e9ea 92c36d1d 273a645c
+          4e7167a5 49a93d32 8a8e3818 2965a503
+          fd4d8db6 666d36b4 7df6b9f0 fd5bf5ce
+          0a2429e8 52038157 8c3ec830 51ab0683
+          af6a7f19 ed697eb4 9e4791be 305a172f
+          43dfda0d 091cb888 41121f93 3be940e4
+          1f3486d7 985954e4 b9bcf861 fda38f5f
+          d6fce77b 181e0601 da556475 37ff7254
+          f56d775c e4742383 16b10e6a 14c81c55
+          8ee2c3a7 4376bbf4 9ae9b868 5bb3eb37
+          a375c972 ddf7cb36 da301944 beec71b5
+          bb478c7a f73b0b3c 57e5d0b7 24b7b38d
+          d99be4e0 ebaddac6 77e6eb27 aa6037d8
+          aad5eaf1 84231972 d231f014 6573abd6
+          a20ec52e e19ca83f 304b1317 64dfa174
+          75a2f657 37a6d6ff e5c12b3c 5ecc64f6
+          ce42d350 f85e47ba 0765df3b 0ab2106e
+          867577b6 81add986 f73f417f 0db76693
+          661c8410 f18c1dbd 6ce42b6f 1a363233
+          1478a3de 78b7c37b c0e8a5e2 2f65fe26
+          4982b5cd 0d746fac 41dd9bef 0decbdcb
+          06edcea2 aa700e1e 0cfbe169 b0b95cdc
+          aab52076 0f46d4fd e9be4b6b aebbba50
+          a1c8db67 34dd7d87 bdeefe87 2f727971
+          21c59db5 fcb67635 51e549c7 c2535810
+          bfbc4643 fcb92a9e 8637de43 cffacdba
+          cfa7df4e 1a3ad450 e8c5443e 30a1b334
+          b17044eb b5d249bb 26075ffb 86ae55eb
+          d1f6c922 c4b453b5 09380c6f 71214a8f
+          3f5cf809 1ec1b262 8011a2e2 8486479f
+          b8aafe96 5f72ab76 1f10edef 93842f9e
+          669770ae def38258 67fa8995 78dee431
+          c83c6094 d0790675 771a4e27 3a172f43
+          fb576bf4 56571247 4bd2f861 c9e56871
+          1616cddb 6d02cf9e 93fba6e4 b4b77205
+          903cc876 20dc1d40 cb8225f0 6fdc6a7c
+          aa563814 c96e43c1 a1939133 6e241b20
+          5bd4b9c8 12ce861a 3b49f5f5 53e5ef4d
+          71e7eb47 dd2f7e5e 5073e73d d7d93d18
+          478b5808 e16bdd79 99283ff9 38fdf4ac
+          a1b8130b f6506313 9a172c46 a8ad1fb2
+          83264c9a a1104220 65d281ef 8e7afb83
+          849a9725 e4a447bf f7715fca e4f11f8b
+          bf3c4413 2791c873 69f578ed a87df37d
+          a8fe40fc bb6abf16 79363b2a 4e3b1e1e
+          e17028f8 ad87c38d e2fabf3c 7469f515
+          970e53fd bcc9706f 51ffeb1b 1c750ffc
+          f5329707 c772de59 cc4f8bc5 f7d0b34e
+          4eec5085 2c0b371d 45ed1bef a1bfba69
+          a0a13149 269a628a f27cc263 27e1c5bb
+          a268a769 5b69df24 731e4ea0 6f6b356a
+          5f7e1b31 bb0d896c fcd8bc5e 54fee014
+          c876f6bf b5224e0f 66343df6 cfab6aae
+          be8c0360 2f100b85 c4ac94ce 10cefa22
+          5ac36a2f 5f42f151 b3903aa4 0c926cec
+          9c632e27 9adefe08 3d1bb70c 3433e6d6
+          6c728d06 9bad464e 49f962b7 0b3cc9e5
+          7e4fac0e 3680799b a4427302 dae9acae
+          35ebd1f1 c912c065 bce49324 49389c52
+          941c3593 a540561d 37c06990 6de7c422
+          110e803d e9d12311 545ffdb3 e175f7dc
+          77a1c383 5c5ac44a 2f1f481b 36044533
+          0e85a4ed ae18455e 21ee7a97 afd66f2c
+          527c61ed 4c064922 d400da52 a74efee7
+          980f3e4d f8731216 78fb7fbc 0869d30e
+          7a56fb47 68ea240b d6c21184 ba7c685c
+          b010feea 3ae37a3c 5d18ca28 9c390d99
+          2387d280 16c4ee41 5ed3437f bbaceae2
+          1f4f8c45 d93b678f c477358a ea2b2e96
+          9b1e7cf4 3aa70733 69116b61 f3b830ec
+          ac53f5da 67c3ba3b 210095ee 5e34bcf7
+          0902addd 7a8d3549 b6402dd5 8bb8fbcc
+          ce7ccace 154ac762 dad1dc26 5a3af9d0
+          0a71030d 6da87d6d 9e766a7a a043baa1
+          3094f5c2 5f57167b ad5a54e4 4d6c7eec
+          c96bb75e f8231a63 0f507dc9 85687ae8
+          b16b849d 7f486b58 4ddd0395 da359119
+          69897dbc d381fa37 df87afae 9e99bb24
+          440d2098 71c4b48f c67cb470 a7ce41ec
+          94c0137f 79b7f847 3e14ff18 0f5b24a5
+          ca03faaa 6a50f7ea 3c3ddd9f 08ee9c2c
+          549e7e02 ebf1ac2b f24e6a79 e2e91bb7
+          9c77168d b1fb57ec 8788ff6b 8675d318
+          567aef40 cea40390 b5ff28bd 1cc6108f
+          1b6d1f7d 86ced5eb a04654b6 44494e6a
+          63d1e8b3 bb10d277 62dc6975 0092f40f
+          f1d31ada 3b19e389 76355d14 9dabd6a2
+          63d11722 acb813fa a4f4e143 5138f3d0
+          c49c1149 36dc88c5 ce046287 d114bb8f
+          aa4b2ecc 6c7ef41f bf1202fa 405ac362
+          132a271b 95a71c3f 106f8d10 1f136969
+          47cb679f 23dced63 f62e0989 0680cc23
+          677e39fa 9df9cbf6 a8c0d350 83c15531
+          6029cd9e a4224fab c7ebec43 d3fb0b10
+          6e6a06ec 09780c21 ec8a674f 476a4519
+          8f5c5810 2142f66f fbd70bd7 6cfdf139
+          ecb8b51b d87ae179 687ef489 6b6c8ee8
+          91e03948 4ba1ed84 685bb37a 4b944456
+          e4e2d9fa dc2bf035 b6b2ee2e 79a9852c
+          3f25399d 3b7dc075 a705dea8 791faa99
+          b3a7ff23 1a4435ed 9ea4224f 8c0a7f4b
+          1b6a5f7f 67e0bec3 048ee76b 7723567e
+          ff44d8d3 5269400b c625d9ae 1cdffae4
+          b3b76d3e e74c5ae3 3b125394 b3c4f37f
+          42da3164 5bcaf14a 283ae230 a4565624
+          f6f15e0f 9ade9d8f fe9a7ae3 4318c4bc
+          fe0058ae 06fcefec 9263ded9 4fb0a5a6
+          21160e7d 28c6d352 ae2d93d5 d188478d
+          a167c316 d4bffe5e 42ad5334 dcb93918
+          72d23103 2b4bfa1b ab8d197b 2ca29c1c
+          0b058ea3 31769daa 4b2e18d2 fecc8b17
+          d9dd28a6 35ac25ee 52cb4b51 347b4662
+          35746241 ddbb6a2d 5a172f85 e20f0ef4
+          bc234947 3488a6ec a30e7f7e e4abf394
+          bd22f034 46bcfc96 923577d6 53d100ea
+          f80a9257 e4298130 da97af44 d7b2af12
+          3e74917d e018144e 9f02c946 9167356c
+          6e5474be feeef55b 2f3897e2 6457c4dd
+          c53f46cb 634fde0a 84a68af9 c7e5b385
+          70a4a660 f8796740 4ea42446 96a1fa7d
+          a87fe303 043b7a28 ee923806 c76258ae
+          86c32fd8 323277e9 afd8a5a1 61cfc941
+          2c129ea7 c6f025dd 50128f2f 313a42dd
+          fd687c77 3e948e2e 11c11368 9d223ea9
+          e4e82390 525acc1b aeade790 64c48287
+          b63ff5dc 0d9bcff9 3eedb113 6c3dff6c
+          b43efef4 3592143e 594c216e cd5a85d8
+          80cf1c72 c2517064 6624b6d5 ea71a3f6
+          b577e16f 6c12538e abe86425 1a4063f6
+          31b39f19 fedc4bbb dcb56497 b5fff07f
+          3d1fca3e eaf067c4 17d1c057 91c4315b
+          8ac1dfd4 82ea97df 42424b45 e1a06c6e
+          372acf3c 0936af9b 593ceb2d 0aec6a28
+          7caad2d1 7636ad91 384a57c7 a4682874
+          9eb05f3a ad612171 27cbc89b 3201d913
+          0e10115d 35fe1ce1 5b5b3ff8 049d5fad
+          811a5178 04277917 cb5a95d4 ea5834f2
+          a2232f7f 97ff9a5d 16788ea2 62f11528
+          afa9acc5 4b7e3fa4 a8e859bf 194def7d
+          0ca4788d 3f4155e1 292a44f9 f14742b2
+          cbacffb5 18b21b85 bd1f2fb9 7ceb4f7e
+          348cd630 a6ead20b bc3def2d f8addd8d
+          d1b486b5 82b8a730 0fa5df3b 32b11653
+          361b424d cd68fe78 11227d7e 8abb2426
+          1a405bce b1739f1f f6f7a703 dfc9177f
+          974f1efa e83f03d9 47cffe9b 1a442d5f
+          49723ba2 68308496 4f97a0e7 abd57ad7
+          f444445e b65899e6 4c3c505f a5329367
+          a9f122c5 14ff84b6 7f3c73db d69f9c47
+          7bc461eb 05e7a2ed ef4fdda8 467cb379
+          6ad642a8 42afb95c a838e538 d8b545b3
+          d12a5813 8042e055 bdf03a82 6d9dac7e
+          49f278ab c6f0a9f8 ffb3ce92 d2efb6d8
+          fe2e9fec 2aafd0b6 64de8faa 58c0d544
+          f20fba70 772feadf 781f4a6f 9f717d9d
+          7058b218 1c65271e 8d94e26d 29668a3c
+          eb0c179b 102bb1f0 316dff7c e6e75b7e
+          f4031a64 0704abb6 7c5f2c9e ce13f672
+          d21ad620 26c49de4 b4a178ce 0ca4ee57
+          092809dc e5ec72a2 eee537d1 bfa57a40
+          0c32de26 2d5a0bba dce38e7c acf2fe47
+          fabfebdf f59dcfdf 54fce9c1 60e62193
+          1e0dfbb1 85832ef9 459eafb1 19352fbe
+          9170164f 3b1d36f4 cc9361f7 7a74c746
+          2c345cec 48554391 0b02ebd6 4ca635fe
+          9beacb7f 5aecfb62 d5953617 4a680dab
+          a8bb013f 9a36a414 f9874f4b ec5085f0
+          b5bd6bd6 a3ed8b15 5023518a bb248fb1
+          8a8a4592 c3fe81ab 72e877fe ebbeb3c0
+          730f1f81 614f3db3 30f7b8a3 3e14ca93
+          243b42b4 75add980 d60f3e4d ec2a3345
+          81a7bc14 25471d0e d965472c 4a135a09
+          215e8605 376cfd8d d602846c 2fee2e42
+          cb237fff a31ae89b 24bc3043 b655f49d
+          58e4bab2 3351f983 936173d8 757f1a3f
+          e04b503a bb51f3f2 db50fa7c 1477492e
+          eec27e6c ce9e7ad0 23436ebf 7bb7a8a9
+          ddd241c7 3d6c78c4 96e27e40 28cf351c
+          8016d078 e1081a3f fc14be4d 5bf4ba10
+          43220af2 a61f8cec 716320d9 2566f2ac
+          840c5bd4 ef9bd3f6 f813bfd5 aee02203
+          fdee5a1f 7dec6788 464e921c e0f56e56
+          11778ab6 e0b1a354 2c765d45 05ba5f34
+          c4e140ed 1bef22d0 d44a0326 395a822c
+          f77bc7bc 39ec89a7 3e738f1c b59bdcef
+          6ea2e457 b7aecc3b eea897a3 2184f9aa
+          929f704f 2f6a5e7a 0b6a2081 433e5a3d
+          9ecd8eb2 938e8157 38369eaa b5d8c2d4
+          0eb71a56 ffaf6fc1 c773680d a0efb34f
+          c6abc1e8 6542dcf1 5e3fab88 3b55bbce
+          5146eee4 03917df0 78209440 987439d1
+          3aff3374 ae5c3b90 b963f224 899da4be
+          35bbc691 95fe0ff7 f01191dd b7bede4d
+          78c78e8b 3972321f 56a26c9b 629501d9
+          5fd3806a ad1e2f91 5b2e1405 ceec4c54
+          9c7a2c9c 19295023 34a19590 9d288eb4
+          b6df5a7d c5c55956 b643cd35 3ff3861b
+          5afe28bb c0163216 c35b5a84 92e3e640
+          966d099d 9a0dd637 a1f1fd4f a086e82c
+          939d6808 6adef1c7 bc5674cd 2fbedaad
+          7e7777fe 658557fe bc21f7d8 a3fe21be
+          d84ebe32 2b446da0 7df96ab4 7dba44df
+          4a302418 42eac8e1 289e3515 76af43df
+          b2209619 2b52b4df 37a5e5a1 876fa9be
+          ec224b9a a0fa8a4b d07cfffd 3746fbfa
+          666b5bd7 1c14d640 5bcc3a53 bd283b76
+          2e1c5999 fa62d748 dc419650 f5fc6b08
+          75f73173 97e468af 5b8962a1 b330ef61
+          efb8f1bb b58069b7 0abc9409 93e02a29
+          7c5e7cb1 f3d8a7c7 1ac4a22a eadffe00
+          fe9a3afd 8e444322 11e4cd9a 86acfd47
+          0ed4e371 bbd63a8e cc0e59bc ef1f76be
+          f8dc1956 fcfebb5e 79e164b1 a8b940d8
+          81e2ce2a fe516839 bbd789c2 9987227d
+          cc707d91 6b1c9565 d4bff93e 7a37d7f0
+          9e590ba0 84d09377 dcd14f17 fcf4b2dd
+          de4f78b7 0f9ffc0b 7eda9535 f5e02742
+          7ed452e4 5963f511 eeecd7b7 6ad5449c
+          97108436 97132527 1e859492 0266f12c
+          866c4776 3418f955 ed755756 5ae9fbae
+          bbe1dac2 687fe0d7 b213051c 05161177
+          db5aa2a4 0f2d43fe ece9895d 456693d1
+          bb71b37e 5b05c59d 45e26714 efb88696
+          bf9032e9 a0ddfef7 db6ebef9 e6ddfa17
+          6a9d9753 0f9adc18 aea9cef5 6fda3255
+          b631c19c f4835438 a2507b2f a2a10032
+          0fd04eff 18a4e514 05f69c2c b8d352d1
+          b7652b22 fd612dbb 432c3158 c4e80847
+          f2fb162c 29507d3d 2f661c79 74f28bbb
+          9b7e2e35 dc7ef75f 634a788e 447f681d
+          812716af dee21c54 9c79125c b9392292
+          470ca37d a4ab075b 9e7e09e1 ee3ede56
+          61017117 f2636bf6 6187fca6 e4daebd7
+          384bcb76 ff827a4f 7ce1de71 13822907
+          8c7e38ac 602107a9 4506ab03 6859b414
+          1d5f2cd7 af31881b c6b441e1 0b207ddc
+          18141c36 0536af93 872eac34 56c4f090
+          1d38a5e5 c13fff34 f9c5dd75 68f8c35d
+          378905cc 59e2a137 b4089a3f 73a4ba50
+          7cc47478 87550081 a071b417 0be3ba37
+          df83afb6 95d93b0b a084819c 638e7e6e
+          e8030f2d 489d72e8 1ef937f6 d830ca3a
+          e194eabc a3e63e29 be893ebe 4aab7835
+          11d0def8 10818646 1856066b fb174a14
+          79874f43 f6d8e1ba 7f637f3c 0b618353
+          72ba6eaa ffcd8d63 92f9db6c baf7ce13
+          8498bd86 196aeba0 3573971d 36e44e3a
+          00d95326 ea8b5943 71271eed aeeff665
+          abb413e7 24d917b9 dad6ac82 c56993c6
+          3fee1d37 7e8fa537 f698c04b 9b361de9
+          074f7e45 7c13af73 dd6a9141 2b4653b0
+          bd07352f bd8da856 8f6794be 8d44604f
+          4d41f1d1 b3915a51 c47a3c4b 0d16b11e
+          08044beb 6ebdedce fa5b7f95 9e8cdf62
+          c3ef7e33 4c76b96f 17623693 2fdc2aea
+          6e60a19b 3eb41485 471e0ed9 9ed86d15
+          feba06d4 bf3d9f87 ce2c8212 466fde51
+          73ff9e71 f4b19bf6 e83a7a77 d7e07dfb
+          6fb7f9c2 1bd635f9 ab1a66da 9cc8e66b
+          b540dcb6 01fea66e b1748822 63f8d0f8
+          276b3501 1851e0c8 cf85d3ed 425f550d
+          14ad1e8f 670c2d23 f2641b86 77bdff89
+          5376e0dd f419b392 47dcdd76 6b6aed2f
+          6f79488a 29d3c1ed 36cb8ce7 a858d7ba
+          f3d230e4 84a390a2 f93f7fc0 60a12b21
+          d2db8faa e75e157e b35d3b84 442c304e
+          4221bc54 7cfe797f cafde1b9 7ed30a3c
+          57790552 274e6808 6ddee208 5555cd94
+          6470f85a 0011acd1 bfb50e9e c23c78b4
+          2b790c33 790adca5 c5880503 7af3e458
+          44650d8a 95449e1d 933bdffd b8cbe6b6
+          7d917ed8 4cd37f4b 8d77fc01 3537fcea
+          2ebb0be7 51dc5987 8196282e 141d3e15
+          b9d3a600 c104eaee 622aeae7 7d80b6c5
+          6bb47b9b 49b2bbbb 81fb6637 654d9d72
+          53fef93f 5eaf6924 d30a3c0d 675131a2
+          9dad35ad 6fbd37d4 e9c418be 628b38bb
+          28e0ab6b 46fa880a 38d2528d 459e2cc3
+          535c8050 7b3bfc8d 6ddf4c06 62099167
+          b33930b5 73de475b 44805c9b 7ac85421
+          f0cda98c 9aeefe23 aaaebbf1 c70e377e
+          4f716725 8737e0f3 b2c78d40 e989470b
+          7726c7df 9ad5c55d 0c9d2b56 a3eef5f7
+          f5453149 7ea20a22 99b38ef8 63e59fff
+          f45cda94 a97bfcdf db2b2e28 65fcc4c6
+          ec2907dd c7de7816 8ad976c0 dfdc89ba
+          37df87e2 f319abb5 7018f6ec 2cb1fa3d
+          0ca9e5ec 8f674132 1d1e3cb0 e5fa1b4f
+          936ce6db a38f45a3 68baf76e 6cb9f617
+          a70b7177 37eb8ead 851a1671 ae3c1f85
+          7366c096 96a6d717 c71577da 365d5b07
+          6a5eff40 8c1d16de 5922266a ef3c8279
+          79a79ef8 5ccaf849 7be5dfdc e3193c0d
+          57c550a4 4d9ad0e4 5fb7c119 aca9992e
+          f39a1e4b a0ad4a7d 759db0b9 65a4550c
+          11a2cfbe e33b18f5 dc7504ce 825cd89d
+          4ef4d7d4 b21ecf7a a4086d77 b83d2df3
+          c3d4430e 693295f3 164e6dc5 d469735c
+          1e3c2a82 772e5fa5 b5c49d23 dd83d263
+          8e40e6c1 1380defe f80b5a31 56a2fe00
+          6a5e7b07 3d1beab8 356b0975 a76fcdd6
+          644e39e8 17b93f38 6ba5ab62 eff479df
+          2b024fc3 51541c95 a15437bf fcfa010e
+          27f6e31b b7c8b8b6 0fd4e3a5 14e7c353
+          906f9cc9 1302d053 52a49db0 844fabc7
+          8bb21ecf 62abdc94 b637de19 ebcacfae
+          f67df955 55eae449 a6f8ba5b 1e7af0b0
+          bef7df7a 587cfd15 7c8bd641 6f892256
+          25f9d326 a070f661 90220ae2 1e85d5b6
+          6ea351b4 2e5a8aba 7716c1e1 a10d2db1
+          0850f4ce 2277eff7 e0fdcfa4 1e3275af
+          3504db6b 024f23da ddd51b5c b3a62b58
+          d374b8cd 8934be76 4b046cbd e9a7bfa9
+          4dbfb2c7 91911edf 016a7fe6 b0c39397
+          8350473b fc0ded03 028f5b5e 9641f886
+          b2b657e7 1dd6fec6 1b2dceac cc0d6987
+          1c32683b 24b63efe 047c4b17 cddcf4d3
+          4b1fb23b 319a6fcf 62024f04 eecc3143
+          517afc5c e1db3206 6eab90e2 384341ef
+          962a54bf f096fec9 2c59b242 10d4af20
+          5e38ecf6 dffe2e63 ced1ed7b d597ee4d
+          81e7aa1c 8af42907 d5f6af58 698f34d4
+          cf90b855 6b8df16d 07021d7e a1f00348
+          ab2c87cd ed8e2ff2 1405b6ac 4c385353
+          e06ba847 b8c3c7ab ccac26f2 1cc81682
+          e9e8d6d7 df093b73 7236a64d 99e21f6c
+          5f63cb83 0f62c345 3f39bef3 f5371f70
+          7a318a6f cd5a415b 6b89e229 48c79063
+          e72265d4 7e80cf6f b8351b6a ef14e2ee
+          6de1d73a b4850cb1 c03889f8 d1983669
+          e2cf734f 3d6dc9de da9add27 024fc351
+          58147566 676c6d7c e6f9510e 27467204
+          5824600b 81e6ab6d 83c3eb42 ea901248
+          0e47fc7a 3c21f29c 85f9b0d9 64bd3f5e
+          d4afb01e cf7a3885 c89bdbf2 dadb699e
+          c2dcde60 554dad77 ccfe8322 9bdbf2f0
+          c3251b2e b9e42cb7 17778bb1 5dce5765
+          2db4cc9d cded42c9 ec69c899 769058c1
+          860c1ca0 0c351442 d3870bd0 faf91ae1
+          0769432b a0468194 4907df31 e2ef7f7b
+          3275caa1 d1bd1e77 f7b6c0d3 883437f7
+          0556aca8 0dd5b71c 293b90ce 61608d95
+          8c465f95 568f5700 77419ed0 7109d4e3
+          15e5231a 0ca2bfba 516f3bc0 7a3ceb21
+          168207b5 bff2f611 cdcf3f1f 72e76675
+          861a5bba 3cfb0d13 63612f2b 7e311e3b
+          5e7dcddd ffe9c7d3 d75f74f1 2f3c5edc
+          80184b4d aca7ee06 ca4ef20e 1e8be2a3
+          8f806c17 e3301abf 254a4cf8 aeee956b
+          50fdea7b 5a769a15 27162118 c282518f
+          3cf8dbb4 c366b6ef 8b7f7f9f 083c6dab
+          36e3b0a9 8d3d8b97 849596e6 592268b3
+          0b901534 9e565f1c 8c21d8d1 89b48a12
+          3832330c 03aae472 e9f578e1 ae76f8eb
+          db07b278 f48e9643 04c54ca7 13c7b5bf
+          366f44d3 73cf4aee 9c0c35d2 d9dded2a
+          2b8beaa7 b3f7643c 5722e89e f74e6eef
+          47ef4f5c 73ceb967 75bffec6 5d2e2fa6
+          80dd2d2c 8926eed2 caf35172 ec6cb8cb
+          4a804030 fed6accd 86407d23 b6fefb0d
+          28be2064 ee445802 2580c6d4 71075c92
+          7de2895f ededadd9 6f626e6c 1f5e7ed7
+          fbc1bbf9 cbe71c75 afc783b3 381caca2
+          f2c4c0f7 0385d3c7 a34c2f4c 4ed74f95
+          c5137948 4d41ff9a 0dc241be 3cd07685
+          6d05ac3d 84065a0e ac0c03ff 1875d7ed
+          5fa64c98 d42a7ebb 5b3c7de2 f1a92ad4
+          d4891361 cf4efc76 44a5bb1b fd4b9762
+          5b0b274d 316ae71b f3c493e5 5fb7a660
+          fd65571e 2356a167 39bdc8e0 7da1d616
+          7776af1b 43bf7f3c b2671c32 d01225ee
+          ca4446a4 b70f75af bf87a605 2b06b666
+          397e927f 9c081fe4 1a79e075 a39e7dea
+          2f9ed163 23fbcc57 ee5381f7 c97c6cfe
+          e9c50784 d6ad7fc3 e6c1100e 0b6ba00d
+          39350454 9e7e240a a64f81e4 70c61779
+          5a4477bb d0fee962 54fd7b9e f8dc200f
+          5d9081ab 8cfdfadd eeabc52f 578a67b3
+          786a8537 ed1c76cd 15bd5927 9d9c70bb
+          ecaeb7de b06db9ed ae6c21e2 72c42fb5
+          305c249e 43c53341 fc332942 d8f12278
+          abfb2d55 1b037694 cc998292 636643d6
+          ea88e3f9 2d9b0db1 4804cdf3 3fc3d617
+          3f604b14 0b110ce0 83099f7c 747edaf4
+          59b5fbd4 47c6f6b1 d70aac5b 8db5a79c
+          71ba52b5 ee6f92cc 7a3c2bad 841da96e
+          0c3bfb14 64ec3f4a 1b88f123 a8e62cc3
+          61d4bffd 011ade5b 02c91665 3d1e3112
+          7e09a30d 2507451c 8983766a 3667d248
+          949f721c 5cf9797a 8155bc41 189365f4
+          ac5a874d 8f3f0755 89f29098 55c64900
+          2dce9123 8e1df1b7 47bf4c3b 6cc63efd
+          5af67988 f48c1e8b 918fffed bd50084f
+          80c96bcb 203b8150 77100def 7cacd7a7
+          685b19f1 678d7090 2929c89f 7a10b2c6
+          56889531 6d48fe37 9a48b37b 00e74e3c
+          dac753dc 911d2e48 c380b728 1345d30f
+          85aba870 a0ee2eae 839311a8 6b40e307
+          0ba0f829 ee2ce37b 54849c23 46dcb2ff
+          abafacda d7e26e50 083cdd28 51b5db5b
+          59f16735 84cf3944 ac3213b4 5a16a07b
+          53035a17 7d01a5b7 4fcfd2c5 251080ab
+          b4042573 66c05d90 a13b5d42 08d9c341
+          1bb2c389 a2595391 367a38e0 0f181eaa
+          50fafad0 baf00b74 adaf838d 2d512c43
+          2884d747 3dfbd48b ee91a307 450a6250
+          08bcb469 8761ecbc b7b6da4a 865c2382
+          76078789 85449e1b 68fa6819 3abe5ca9
+          5f4ba65f e5b32334 a7eaf723 75d40894
+          1e73b85e bb175369 4642c89e f3516a44
+          42c16107 2267d2f8 8134afc1 5564b168
+          149d2bd7 a2f9d365 ba7fe3be 943510da
+          a5d63364 c88d6a38 d23a58be a64153c5
+          e41e311a e33e7aff 73b9a8f4 26317f82
+          1c2ed640 d36cdaf6 45e37b0b d0bb7e93
+          5eb76278 7f8f70a0 d963c7a0 68e644a8
+          8a4c074a 08d92344 c340dad0 02e44d99
+          045b663a 103138b7 a33566df b4150df3
+          e6eb7e89 57915944 dc451090 0b4bae1a
+          f7e1fb9b d3a64c1d 345fd7a0 2a53770d
+          1b1e19f7 eebce7c3 413ccb21 631d6407
+          e06ff7a1 71fe4204 1b9b8cb7 6a150572
+          7a1af2a7 1e8ccc31 65dcaa25 84ecfea0
+          1dd5ca48 9c28993b 13de6115 c6757776
+          3bc2ad1d 68f9ec0b f8dbfa75 bf46929f
+          580caa9c 57f48f03 177cfc91 d030832a
+          dd30e8ce 21460381 4e675ede adb12836
+          71e85807 ed0463d7 aaad68fb 6c29a2da
+          9d8e4622 2f1482bb b418a547 1d014796
+          4777c684 10b27ba2 b626f06c 42dc4d43
+          d6fe2385 bf3128a9 d2b66695 08da977d
+          85962fd6 f12a320b 110962c5 b84fe6df
+          ecaa1cd6 35d8beb6 4127f052 264ec6f8
+          258baa90 927d7e4c 859fc3c7 3a0ed5e6
+          061a3e5c 828e652b 12dbaa15 222f6dbf
+          a1283fe1 28e18ded dcaa2584 ec9e4443
+          18c81d3f 6ca0eece e542dc15 a4d612c5
+          26a36bf9 6a34bcf7 311c2ed0 1759256c
+          a9f039b2 b3af55fd 8196c1f8 f50dca4e
+          62420963 fcf2a55f c65c1937 8b89a270
+          185903bd 1e4f3c8d 1f2d44ef 9a7580c3
+          608f432b 76161f9f 3966248a 664e4034
+          c4821742 c8770cda 22e2b8f3 d250346b
+          1a5c8579 40d8207b 67b72350 5b8f9685
+          4ba1f8d8 9fd34249 89a8d028 378eff72
+          e922efb8 0307e597 386887a2 abbcd23f
+          61e5f2c7 2241bccd 91641db4 fe78fec6
+          6eb42c58 8250a358 14390cae ac50a2b0
+          6766a0e0 b029c818 5ea43750 2684905d
+          0cda5055 19a547ce 42da7e95 c6e2ce66
+          43b4b70f 2d9f7e8e ceb535fa 2e04b1c6
+          4811dae4 7da1519e 115a65d0 1e0a1dd4
+          6b0d3518 ec94ddae cbc5a46b e678b20e
+          5ad3d9f6 2f37a165 d1e78885 c2f15ba7
+          6884c37a 3d5ed909 470a076b 63eb1442
+          c82ea184 24141f3e 0959e3c6 0c6c27c4
+          6b89a2fd b92ca163 c56ab42e f91276de
+          916da585 40add026 17098dd2 3698bfcc
+          412df03c 234763e2 fa753551 c57e1676
+          eee62162 726cc259 367fbc08 6d4b9619
+          6fd56a44 2248ad28 47c5c9c7 420d73ab
+          9610b293 09850890 31b400f9 874e863d
+          233dfe3d b31a2e27 7ad66c40 e3079fea
+          37eb706b d63a4345 68928b85 36a9d334
+          0a05de77 c0555e19 9bb865f3 e74a50ba
+          85e3ca3a 68ce526b 7fa26dd5 f6aedd00
+          b80d96c7 62a52dd9 edc8dc7f 240aa61f
+          88283b29 12421244 4fd48975 61e97173
+          e0292bd5 178c7111 be26d2da 2e16a05f
+          c2dfd4cd 96281642 d3224293 7c22b4c9
+          a04f3a99 62cde11a 52ee9b54 537d7f34
+          80b738bc ac83568f d757d38e d6058b11
+          6e6937ae c7132b6e 4756268a 674d434a
+          791e541e cf218424 802a1684 e5271f85
+          f46195c6 993bad64 447c4ccb a78b85c0
+          5bab9794 106b2034 c842a145 1ed43489
+          2962a859 0c2bd96c 9d629175 a9f8693f
+          879975d0 ebf1966e 408b1079 df38d7b8
+          cb2b05ee a27c549c 748cbeaa 663d1e21
+          246ed00e 01d9e387 216bec28 486ea776
+          cac220e3 e0d4af56 6c59b84c f818f643
+          b11011f1 b6cf165a a4dd2c5f b069049e
+          b3a81813 abb6d647 02f83ec7 99b590ec
+          31e14c3f 47dba79f 1b6fd56a 8859985a
+          3904e542 e4a921da 8f10b203 57a1b544
+          c94945e9 31b3e12e c8d54fe5 c717772e
+          f83657e9 d9bb484f 50bf6691 5844dd05
+          70a6d020 759a16a1 c0dbed51 5e82bba2
+          5299b475 eb7c61e8 3b39dcac 24f084df
+          ed8fa055 88bcfeb5 9b008fc1 9e482c06
+          d9e944f6 01639077 e858d6e3 1142fee7
+          42502be3 283df608 a4141702 5183cc9d
+          dd8698cf 87d6cf3e 47cfc646 c83c356b
+          257177a7 d01eef68 1ac44c17 0c9beedc
+          8fbbb2d2 3f796bd5 6d6a18f3 39ecac83
+          b6dddab7 b505cd0b 1641e9ec 32aec753
+          55383233 50327706 3c459988 f12a3342
+          c8762862 e1573c67 0ab2f61f a51f9a88
+          db124517 7876fdbe ecd6c52b 61e3d6ac
+          65105a63 bdd01c7f 14dac367 b6afdd94
+          07bbed79 b95d4a14 178b9ff6 71f85904
+          ade59470 aa1dcbd7 a369fe67 03b57846
+          2ba9980a 4f7e1e86 9c70a47e 2a37469f
+          4c08c140 e62eb52c 1705874e 823d2dcd
+          b8ee2e35 053d5fad 41fbe75f 8ac562d4
+          a49193ec 0261a135 4e159aa3 c38c5fbc
+          2987a9cd ebc5e48d eb374502 389fe3cf
+          4268a355 8da26df1 52747cf6 3990e28d
+          afdab43f 12423073 c47e283b 7e0eebf1
+          082103ae 4168b48a 538f85bb b030fe3d
+          b31a2e27 82b50d68 fe6411fc cd3d6c89
+          622184c6 b854688d 0d9ae6a0 c0db6b5f
+          b50cf7f0 91d1491b d6bf2e5e c0dd1c86
+          d641abc7 0b7707f4 4317be0d 9b016f02
+          f5786e17 72271d88 dc8923f5 13738410
+          eba20484 b83b790e 522bcaf4 9d01a358
+          a39dccd7 5a3575ad ade6a959 6b89bba7
+          85c6f897 a6350cbb 3750e0ed 7edc2346
+          86266d58 f75bc966 5bcce168 1d6421f2
+          7ab734a3 f9a34fa1 f6f50fd4 cf18883c
+          477a1a4a 8e9a0577 760a5ba7 106251b4
+          055ed6fe 95c89978 0064972b fed6acb6
+          3b90e245 fbe26568 5fba424f fb49bc24
+          c71a8904 9b6dadd0 1657088d e13775ac
+          34fb8b70 8f18d513 e98f5e8c 18ba392c
+          ad32fbb4 91aba26b f506347f bc50bff0
+          3b11cfeb 2d2a40f9 a9c752e0 116241b4
+          79efcaf4 a2fcc423 e1ccca34 aebbf37a
+          d0b77a3d 9a3f5b82 706f505f 58122b0c
+          14a84253 fc4c688b 0eb37f2b 49512a3a
+          7ef9972b 94202ee2 6db5d641 b669ab71
+          052d0bbf 40c7e2a5 62a5ed31 3e452184
+          60e6a8e1 28993b4d dfa62184 5807edea
+          c3b2efcd d6177a86 688bc660 086d5a6b
+          a6aa168a 3bcb0c12 fd74f5d5 42537c9c
+          14713219 be09eff8 0938f08b cf5f8e86
+          f000d80e c332684d 4643ed7d 7ad351ff
+          969ac4ea f15c2e14 4e3f04d9 e386e997
+          8b134292 9fa858d0 15cd9a84 acb16306
+          c49bd162 d0ed42c3 7bf3f553 fb921c33
+          aed523e6 47bc6639 c5f382d0 127f139a
+          22299444 d21cf64e 997c50e4 80c50b6f
+          91335297 3293672d 91d757d5 84960f3f
+          452c1018 70de0622 cf919e8e 21c7cf85
+          33cd038e 1542923c 6e8b39ee 2dc946d1
+          e1d36037 3c792ffe 2c3d0d3d cb57eb2d
+          51a2a130 6fabb00a 2a6a94ce c095424b
+          f892e55b 4aaa6e3e a9530e6d 53dafbaf
+          102faa9d a3d52202 4f1ef0e0 9dabd6a2
+          59883cad a541224b 356f7101 2a4e3bd6
+          b0430221 c4ccea0e 7aa6bef2 8ce3e1ca
+          cd19f88d 78b8dd08 d6d4a1e9 a30508b6
+          f6706bd6 2a280846 c3b868ff 4fe63724
+          d3b79574 ed1ac77c f0fec268 043f8f29
+          e0069c55 449e4d6b 7d1041eb e2a5e85e
+          b612484b 35de8291 24648e1d 2d56f507
+          b3750a21 498a08da 1872c22c a40dadf8
+          46f0c55d 2d2a0a9a 3ff8143d 1b6b07a2
+          23b76693 7f0da020 2634c32d fb7ff4c1
+          3b69d367 26d5f796 74022ffd f0d918f3
+          fe3bffb2 e7653fc1 d3921612 79622407
+          db7ad0f4 e12708d6 d6eb3534 8683df61
+          47f11133 90be5f09 af322324 c9d03277
+          99a387a0 60eac162 11281b6f cd66a4a2
+          f5d325e8 f86ab550 86d181dd 0192dce2
+          4ed56ec6 cafe4868 8607d267 1d9174df
+          5f520ee1 8cd94786 a33dddb7 c422607f
+          3ccb28bc 81a76f6b 031adf99 af3b68c3
+          e694c2a7 3b32d351 71cab1b0 7b9d86bb
+          378410b3 446ec0a9 b54439e9 18d85352
+          e2cf6dbd df5d0afa 56ad458b 107891be
+          a0de509d 58609844 502bb4c2 cf846648
+          ca6b4f93 768d32fc 99971a44 c0bf560d
+          a1916976 8b683c6d 91aeaae8 5cb9064d
+          1f2c1868 9d62b8cc 57915251 86f2538e
+          d3efa724 84981f6d 2e971d3b 1bded212
+          18aedcec 0e447b7b d1f4eec7 f037b6eb
+          2d9848f2 27048436 104a1e57 09adb036
+          59bfcda4 157859df 3b11235f 7ae933d9
+          8e5bd400 22147916 99b75a7f bc40445f
+          89f7ae58 6d7c5fad 4654d5af 322b9836
+          81228f10 b38bbb08 f4b99c23 e6b46133
+          630daf1b 8def7c84 eef55503 6290b122
+          f9c55d00 8ad0067f 161ae125 4d2b50e0
+          9951e49d 7032863f f7c23f5c 6545cfc4
+          226c8861 99f92b46 75a8a307 0df3e623
+          dcda0e38 1338592b 49283d66 3652cb8b
+          d83a8510 93a2ade5 bc45d928 39fa08c8
+          0e47021f ec45e7c2 2fd0b6e4 4b210c15
+          b644b1c2 18110b00 a1093e15 dae02e4d
+          23243349 5f469a75 d2a92120 7ab378a9
+          cbb832b3 ce0a4da3 afba0e8d 6fbdff8d
+          803372f6 8eac4cbd 1ecfe675 1926fd08
+          21832d72 0f4cfd8a 538f1fb8 8acc6812
+          bb5c0835 b7a0e9fd 8f11e90b b0258a45
+          6283d002 4d42135c 2bb441d2 b753b3c4
+          39a1b23f dc5965f3 daaf57fd e2c552e4
+          59631e6b f5788a8a f6652bd1 f2d1a740
+          6a8ab1c3 5714a48e 1886b2e3 e60c0c13
+          8a3c42cc a3efc47c 2d9e3b03 e963460e
+          1cb28a1b f96411e8 236878f3 3df81b5a
+          b92d6b11 71273440 bfd00237 094db0cc
+          0adfb225 045eee0f cfc5d0bf 3dfe912d
+          d5758778 c1414e66 8bcc67ad 3f5e4841
+          931078fd eb36001e b7f12785 23c83f6c
+          0a72278d fb262b40 0819e4e2 4e053286
+          57a078ce 0c209240 0b54e10b 5a3f5b82
+          8e156ba0 8a85205b a25842dc 2942033c
+          25b4c0e3 9a26a0c0 4b22727e 70362aff
+          fac85f5d 15652fc6 c2bcb1d6 32f35a8c
+          f070771f ea5e7b07 91ee1ec0 6e7c9599
+          e60d4abf 37172925 85147984 98005756
+          3aca4e3c 5abf6b3a 1171d7bf 76039ae7
+          2f821a89 b2258a15 16006131 462aca3e
+          171ae08f 9a16b00a 965ab7e4 9e756ec0
+          9e93febb a882e5cc e25944e0 49039aad
+          6f6b2d1a de7c5f8c f8045e7c 340a675e
+          2ecab41e 5a5e37eb f10819b4 915b5bc4
+          c9283972 26528655 186fcdda 6444fbfa
+          51ffd687 08b57771 6bd61241 400c0b05
+          b522f6df 243440b5 95be75cb 25a60b7e
+          72e97a47 66ea2f55 3f9a39b9 2d32bfb5
+          51ae423f 29d7f6e9 e7896dd5 0643481f
+          371a45b3 a743b6cb cce21132 08c59dbe
+          709f3c0e 79874c06 0241e3cf 7139d134
+          ef23f46e add13f5d 620c487a 7127627d
+          40c4fc7b 44ec9f6f b56fdf72 022fff27
+          17a3e2de 7bdfb167 a5ff252a 5e3c459e
+          7546ba56 6b53f7f6 87f06da9 019c0ee3
+          cff1f985 c09b81ec 7163064e 5f51e411
+          32a8c49d b7281fa5 c7cd35be b546c3ed
+          42f7b255 68fe6c89 7e008b75 77c92fee
+          448c5744 ac7f4ec4 fc47b4d8 4f816701
+          f27e7401 caefb9eb 7e5765f9 8bb108d8
+          dad62af3 5d4c78a5 b71f75af bc0da5af
+          3fa1abcc b44f2af9 de914829 2e1cf835
+          451e21fb 5edf8979 a8954f94 8ab9e9c8
+          c936de9a 75d8116c 6c46edab 6f430db3
+          df9d25c6 88d6efae b27ca188 f57f1031
+          3f60451b 58760d93 f7a30bfb bca3f7fb
+          8312613d 9e955674 1a3d9bab d138efc3
+          c456fd8a 02b71077 25c7cc86 333d8559
+          3c42f679 e4865e36 913fed60 646ab755
+          0442862b bb584441 dd6bef22 d8de49fb
+          59c4d78b d8de2862 fcef45ac df645533
+          583a499d 79f471eb 5cb959bf 8ffad142
+          91679d89 af65f29a 3e5e828e 652bf495
+          bd213e3f 320f3a10 85d3a7c0 e6b421c6
+          33d884ec 1b6db72d ab9e396a 3ffdb60a
+          6d6e1afa 6ea703ad 9f2c46d7 aa755f1f
+          922749ee e3454cef 17b1fd3e 11e3dfb5
+          b2292c2d f00a7f76 1586dcf6 fb571db9
+          390f8a01 e1e7c4b7 8e03d09e 9a97de46
+          b0ae09b0 25b05fe3 0fa068ee 2c64ee3f
+          0a924dd2 fb6e1142 f6b6c203 dc79d928
+          39fe4848 5a1dad51 4adde180 6f7315ea
+          def97060 ead3c727 ff105110 71950f79
+          41c4f67b b5184f81 6761f22f b85888bc
+          dfdd2706 c42b6260 b01ecf2a 1a4f38fa
+          48bf0f35 afbc8da8 9e0530f0 fcaa56f4
+          6347c9b1 73e02d61 3d1e217b 3d708b45
+          95dde344 f1e187c1 3bb40208 19343496
+          6584bb7b 50f5e21b 50836166 ee2cb278
+          57c25896 3eeda0bb 446c0f59 dd1c3c47
+          a48bbc9f 7665ce9e 7e47248c e5b486b5
+          445ed7ba adfa4d17 0915d785 42f0540e
+          41d1e1d3 e0cc4a65 168f90bd 28ee249b
+          8c9c0907 2077d654 a0bfdf50 b0c55415
+          0d6f7d00 5f6d130d 6811a201 b4baf272
+          ee4c993c 650dad41 81f70d9e b1077ee5
+          cacfbd53 0da283d6 b0d00470 000def2d
+          40d7ea75 c64d9035 45d8d78f 9c291391
+          3f65126c 6e07ebf1 08d9e3ea 6ee087d4
+          f262fdd4 2c4261e3 cfb1d9d0 b97c35da
+          162d1b48 ce337b97 f488d8dd 6fcfcd7d
+          60c8ef6e 79a9e8aa 9fd32014 78ff411b
+          10e5bfbb e5795b6e ee9fc540 f1d122d6
+          410b00d5 2fbe8140 53abf156 ad96e98b
+          46517ce4 2c648e19 a16715b8 554bc81e
+          d4772ae0 ca4e47d1 9c99b067 651ab744
+          110bb550 730b6a5f 9b3710e1 28eeac30
+          46547b49 c96be5bf bfe59efc 9f5c4a83
+          50e0fd37 f9175e82 f23fdc7a bfbdb8e4
+          55316098 9bb18cc2 d3eeabf5 a1f6f577
+          a1f8fdc6 1faf4421 793c289e 3b132925
+          f9dcaa25 640f8a3b 9bdb8ebc 8327226b
+          cac48153 b3069359 0d4550f5 e29b88f4
+          f4d38016 2112c2ea dcd34f7c 40c470be
+          740abc38 22ef828b bb0ace39 e3de7008
+          5fd11a16 d27836a0 73f926b4 2e5a0a55
+          49e0ac4d 2000ef7e 9528987e 089c1929
+          50b91c20 6437ab3b 2da12e23 73e47e28
+          3e7226d0 e74b28c3 deb66419 bad75731
+          ba590435 8c6e575e ce5dced2 210b690d
+          0a3c43ec f9854b5d b939778b 81d3456b
+          58079b07 689c371f bd1bb718 1fbad002
+          8dcf8fdc e9539033 f940d81c ec8f47c8
+          6ed5772a e02dc945 f1d14740 72bb4524
+          570de764 5f4d2daa 5e7c1bb2 9df6b388
+          b80bc899 390f95ff fe96278b afbe9e06
+          a1c033a6 e8aaeb50 71db6fff 2565e6dc
+          a34610a0 45ac4354 89a2fa85 3711686d
+          4f2002c5 f4ed5a6d ab3673d4 b081e9c4
+          7a3c42be bbb8138b 2567ba17 f9d3a6c0
+          3b42ccad 40d0f073 22bd7da8 79e9cd84
+          2ea821c9 b1009073 f2dfacb8 fdb77fca
+          bf907577 14783b81 d61f6fe8 1d7f7854
+          ceca7b4d c4715659 5904ed02 f260472f
+          1ae67d38 508f67b4 25145160 cfccd49b
+          20a70ec9 e7562d21 bb21704b 76193913
+          c7215fcc 2b08e166 340fd570 18f5efce
+          87afa685 872a2c42 2484cd25 575cfc90
+          88d5adb4 0605de4e 9377fe4f 5a4aafbb
+          ea4f9120 56d31a16 9a140ea0 7de93a74
+          2c5d2902 47c4f813 84104c1d 331cf987
+          1e0467ba 875bb584 ecb2bad3 1e09e9fb
+          95a378ee 0c7d6e19 7f4e0cdd 6b37a2e5
+          e3a5901c 34a12586 89823e47 56e6edb2
+          37f5435a 83026fd7 0de4722d 766464dc
+          2582364f e758e9bd 3b81da37 de417f75
+          adf1076b d9857e3f f2664e45 ae76f9b9
+          64e3c95a 42762570 8bc591b7 281b45b3
+          67c09e9b a3974018 116c6b47 f54b6fe9
+          0b336289 31128979 33eeafbc f3f6c78a
+          aebc9606 f97fec9d 07601cd5 b5f7ff3b
+          33dbd57b b78aabdc 7b917b6f f4920049
+          80d00224 21040810 1242420b 01420990
+          9084de42 33ddddd8 72ef45b6 655b9665
+          f55e57d2 f699fdee ccfa4bbe f7bd7847
+          808bb4f7 fc920dc6 d2be279d 3d73cfff
+          dc7bee39 24f0be3d 293ffb25 729f79f2
+          ad8039f2 49e6585e b2082718 d4639f80
+          36caccd3 da06ddc2 1eb51e4f 51903a7b
+          1a62f373 d92264a0 7a3c82f8 86e24eb4
+          19113f6e 34a2470f d36f89c2 122b1f4b
+          ac4e7ef8 25bc9d4e 3a9ae5c1 47d89a1a
+          b0446fca 7be6c9b7 936eb885 0c4202ef
+          bb9378fd 4dc8fbcb 33af29e6 c82fa81e
+          8f238d27 015d954d a85eb51e 7e47570f
+          eaf17c30 26272065 e664d832 e2a1d064
+          6382e861 e4565f02 4b8e0621 6df65426
+          ee5ca19f 37f635b5 7ca279d7 5eb41fa9
+          80209209 79c0eb46 75cee37f 789cc5e4
+          62b20609 bc3327f2 aebda12a efe9279e
+          650e564a d6e007d1 0c346d29 42eba162
+          284cc0e9 8a3ca70b 51238721 79ca7848
+          761a6546 103d4161 8f56645e 9a362106
+          26937e4b 14f6f5ee aa1a547c b616a285
+          ecc7450e 108057b4 d97ec7fe b896ac41
+          02ef2c38 5860b368 b13cccb2 4d3aaae5
+          08031379 e5cbbe82 b3ba56bf 3f9e8acb
+          8de45953 91386134 1378d43a 852042ae
+          ab6a4b94 680b12c6 8e82352f 1bf0ea2c
+          af2cc972 37b7e0c4 3b1f69b7 de091e9c
+          04b21cb0 bc3ae0c5 e79625df f673b207
+          09bc334f f2cdb761 c0cb2fbd ef938d4f
+          808e6af9 1178ffae c75b094f 4bbb36c8
+          5c6f7741 2565fa14 c40ecf81 eca5e220
+          82384de0 8681fd47 1d459634 771ad0d5
+          adfb30fa bb9da82b dc067753 27093c4e
+          bcc4a798 360ff8fb 4b7f49bc eec67632
+          0709bcb3 46e28fae f70d7af5 efaffb7c
+          e272b206 470f8a04 388ed7a2 7ee336f8
+          3bbbf42f 5d787d30 a7a72079 ea24d8d2
+          62b42328 8220fe27 b207881e 9283e419
+          93b51ad6 903be44c dc057c7e b41d3c82
+          faf57bb4 9bee44f8 e375a169 d03ffefa
+          308bbd54 774702ef ec93f083 ebca06bd
+          f9ea53cc f16ac81a fca08e32 abfb7a07
+          3a8a8f21 a0ceabd5 abc773b9 11336638
+          920b266a 2d1ca875 0a41fc07 35e9b167
+          c42165fa 64989213 b5a6e17a b81a1b51
+          f9e51a08 66b21f37 180c4f05 6499fadd
+          91c03b97 ab93b291 fdef6fc8 109cad35
+          4ca8957d f8059c35 754cf1f5 e0f1f178
+          91326da2 76e942f1 90fd0842 454d7644
+          93a01dcd 468f1eae 3f8a4c14 e16d77a0
+          f29315f0 75bae868 9613bc2e bc3ce8f5
+          7fbe9a78 dd8d54c9 4c02efdc 117ff50f
+          0383de7c ed638f0b 7f226b70 24f0d813
+          23bb656d 17c1d3d4 0248925e 2200984d
+          482a9880 98a1d9ec bd644382 50939db8
+          91f94866 c90ff4a6 c5080264 26005bf6
+          ec47db91 2aad5c82 e042dc1d 18fcf69b
+          ff48f8c1 752dbaa7 250409bc 331be845
+          245cfd83 ce21efbd f32a7344 da3ee6e9
+          a131026d 872bd0b4 6d1764b5 285cd4af
+          c7b3f6cb 40caf489 b0264650 3d1ec135
+          6a92133d 2803a9b3 0a20d86d ec2f42f4
+          123a15d7 9dd535a8 f8740344 0b6de470
+          828fa5c6 bf8360d8 671048a6 90c03b1f
+          224f9460 304ac799 23fe91fd 6b3d5984
+          1f247300 d5abb6a3 e3d8f160 61b85e86
+          e9f12266 ec48a4a8 c5e4a07a 3c824fd4
+          96285284 84847123 611b90ab bf7bc7d6
+          58777d03 ca3ef89c 25d5d454 9217dc2e
+          3c32e48d 5757c55f fe3d5a29 49e09d3f
+          e22eb858 c97ff3b5 f5cc211f a251393c
+          a97b6801 47ed8fd7 5d510518 75ce8d98
+          0834f865 244e198f 946963b4 db83e42f
+          046fa8bb d7295327 20513d9a 75eb14a5
+          8a22e4ce 2e346cd9 09674d9b 36598608
+          ff7595c5 d277f3df 78edb584 ab7ee036
+          1869c030 09bcf3e9 8f2613e2 bf77b53f
+          ffadd73f 7239f102 056d8e3e 7b11f0b6
+          b950b3aa 109ec6e6 6007fe50 c83244bb
+          1d4993c7 23667026 6427d990 e0079f0b
+          88199ecb fc7f9c56 e212725a 8520045b
+          a21415a3 ae701f44 131dcd72 e223e543
+          fff5cef3 f1dfbfba 9ac41d09 bcde23f2
+          aebcaa65 d8fbefbd c81c7403 5984a307
+          88ad412d 456568de b9178a56 8fa7d304
+          d9ef8735 274b3baa 35c55aa8 1e8fe002
+          c50b44a4 c62075ea 44985353 823def42
+          c19e2367 4d2d2abf 5cad3e34 b4dbcd45
+          20656e11 c0afc4e8 e83d2ca6 92a22781
+          d7bb449e 3121be84 39e86306 031c6411
+          7e162541 9251bd6a 1bda8f96 9cba7011
+          221a9d3a aa8d1d35 146973a6 429d8712
+          a0a58c08 63d47a53 75b73ba9 603ca247
+          e6eb8b3b b696aa3b e2aab8f3 76b8e9d6
+          2c0fcb28 5b325d4e fc71e8eb afae889a
+          31cb4f16 2181d7eb 889854a0 0c7de3b5
+          0d4e27ee a78c93a3 c5491d37 ebf7a2ea
+          8bd5e83e 5101584c a1e7cf2a 0ad49b61
+          da78a6a9 a382ad53 c85f8830 4d80d47a
+          d384f1c3 9138698c 36962c64 46238a50
+          d802dabc 7b1fda8b abb41d72 22fc7d84
+          7de49f0c 79fdb597 13aebaa6 4bb0dac8
+          2624f07a a1316d36 247cff6a df90375e
+          ff906523 ffa4a0cd d167cf02 91b3be03
+          755f6f82 b7a11930 eb4426bf 0c29260a
+          c9532620 aa7f1ad5 e3116189 ead75103
+          d291326d 32a4a828 ad44e1f4 815e5d30
+          03683f74 14b5abb7 b3674aa1 c48703fc
+          6e3887be fbce9f13 afbabac2 60a2f973
+          24f07a73 32c21c94 396ad3d0 f7def933
+          73dced64 11be445e f3bee368 debd1701
+          8fb707f5 78326c39 99489d3d 0552a411
+          0a1d4c10 618422b3 47c00ca4 4c1e0b5b
+          5e766871 a76eea99 4d7056d5 a16ae53a
+          c81e374d abe02260 025e05b7 59f2f276
+          50dd1d09 bcbee1b3 4623acf9 438e31c7
+          fd1d4b4a bd64114e 3e77415d affca859
+          b35d1b88 0e4947e0 a9f5786c 498b1d3a
+          08a9b326 6b3dc2a8 1e8f0817 64b6f2a5
+          ce2d40dc f8513028 3ace6d36 c2dbd88c
+          da758570 d5b641a0 8d9cf05f 2f837577
+          7fca7ff5 9fcbacf9 43e9ba19 09bcbe83
+          256f8032 e4d57faa f578f7d0 94158e16
+          2d8905b6 6eb575ca d7e82e3d 09582da1
+          039b2c43 305b903c 691c9226 0d578f2b
+          e8588ae8 e30f41b0 254afcf0 1c244d1c
+          0bc162d6 76ab4f1f 85d4423d 05ed878e
+          a0694f09 f5bbe344 dcb1d8b8 6bf02bff
+          fc7be20f afed1423 22c82824 f0fa0ea2
+          3d0249d7 5ee71df2 ca3fde66 8efc1a05
+          6d8e1e2a 13d055d9 8cbac2ad f0aaf36a
+          d500170a 4581313e 0e29d326 22b25f12
+          6417d990 e8bba897 866c49d1 489d3119
+          e6e424a6 f6746a0f 4c26741c 2941d58a
+          4d3004fc 7434cb01 3e379cf9 6fbcfeab
+          a4ebae3b 699048d1 93c0eb8b 598a2022
+          e9c737b4 0e79edb5 27640ff6 9345f8d9
+          c1505b3b b4ec2d41 f38e3dc1 b610a1e6
+          d5aa3b7c 4ce4d9fa 65227d6e 01448ba0
+          1dd71244 5f431bc1 c7fc3f6d d62444e5
+          0f0c3633 3edd0eb6 faf7560b 3c750da8
+          5bb7119e d64eba35 cbc9fae8 51707bf4
+          f4691b59 8ca45164 24f0fab6 37c7ce9f
+          57e29171 1ffb2355 58f1f2a9 ab8dfafd
+          3e346cda 11acc753 bbb2873a ab3fd53a
+          2576d810 a4cd9ea2 d52f1144 5f0bdc7e
+          0fb45283 f8b12361 908ca1a7 55b06742
+          7674a261 cb0eb41f a964890d 9990071f
+          7139f146 fe5f5ffa 504a4c22 714702af
+          ef2346c7 0486bcf4 e25ae6d8 77d1512d
+          479fbb89 65aa2ddd a85d5308 674febf1
+          ac566d94 53fc9801 5a1d13f9 0bd157f0
+          3b81e8bc 34a44e9f a2b5000a d9d0584d
+          765842d3 76e0304b 82f6b33f 0740b5ca
+          e18fecc1 eec12fbf fc68f24f 6eed16ec
+          54774702 2f1c8c6c b723f9d6 dbe4412f
+          bcf04fb7 13ef53d0 e6e8b337 029d6575
+          a8dfb40d be9656ad 15444814 05a6f838
+          a4cd9d86 88f47828 6eb221d1 fb514b0a
+          44e6eba9 3327c1d6 2f434b56 4effcd2c
+          c9b159d1 7db202b5 5f6f82bf cb4547b3
+          3c6080c3 23e3fe84 cb2f3b4e c6208117
+          76a4dc7e 7be7c017 5efcade2 c151b206
+          474f187b 35ef3d86 96ed7b99 800be8d7
+          e33122fa 65217dc1 74ed4661 800e3288
+          5e8e5a52 903ebf00 b123f283 bbce4a88
+          9d6a8b19 de8626ad 29787765 4bf06896
+          8a57c25d dcc1edc4 c3839e7d e66b8399
+          cee249e0 8529c937 df749c65 31bfa45d
+          3c8e1e32 a37ab3d0 8bfacd3b d05e540c
+          98f56fd5 1a441171 2c58a6cd 99a2d535
+          11446f0d dc6a2941 ecd06c24 154cd04a
+          0c42b744 11b424a6 75f77eb4 ec2b09ee
+          dcd15ac8 83b8fb70 c0d34fff 33f58e5f
+          28ea8916 41022f2c 09c83206 3cf5e40a
+          0fcdabe5 e8430f76 f477353a 50b77e13
+          5c15d580 dda65f8f 67b12079 da24c40d
+          cb819fea f1885e88 3a67d696 1c83f4f9
+          d3618a8d 0d3dad42 c56246fb 8162d415
+          ee82e2f3 6b979188 305ffebc 281bf0d4
+          d30fa4fe f297ed64 8d738bf8 d0430f91
+          15ce6532 2349889c 320546ab 6d67f38a
+          b5a34523 06925538 f9ec593a e56eed04
+          7c5e44e6 6469022e e42d43f5 0165df63
+          4d884547 4929fcdd 5e0a8844 af4a5c54
+          fa5d3807 b12387c1 a0de9208 d512c56e
+          87bbb21a 959fad40 57651324 3aa9e360
+          d183c7e3 c18dc356 adda46c6 38f7d00e
+          de7922ed de5f7973 1f7ffc97 010527c8
+          1afc083c 35d0b5ec 3d8ce6ad bb821dfc
+          435d1d54 8322fb7a 447616b2 16cffc1f
+          419520ce 73e0569b d52279ca 28248c1e
+          a125ae21 93159309 fece4ed4 6fd8828e
+          d21a1277 9ce075e2 89bc871f fe3ce0a1
+          3a131278 bc89bcfb ee3beef3 e017ec8f
+          34669e97 07cec802 a3d38bfa 4d3bd0a1
+          d6e3d9ac a1dfa006 4d4140fc b851489d
+          35410baa 74544b9c 6fd4692b 513969cc
+          27a74250 cb0d421d cdaa890c cb4c5ab6
+          ed46f39e e2604e43 3e1cfee2 ce85d539
+          bf7fe8e9 f4dffc06 06bdba63 82045eb8
+          11608b62 f643bffd d2e7c223 640d5e3e
+          f453f578 0d1da85d bb11eeea 3afdfe78
+          eaa50b93 11697367 20666086 360a8a20
+          ce9b0bb3 9c43b21a 91b97426 2cc989ba
+          6506b05a d179ac94 2535dbe1 ed746ba3
+          fc88b0a7 8ac5b63b d31ffc9d 834c71fe
+          a01abcf3 883ab920 6ae62c08 01dfe6b6
+          759b2608 12fa5366 cbcb67cf 32dc3607
+          13f91e44 0ec88560 34ead7e3 994cb0a5
+          c4a3fdc8 71281e1f cdec24ce 0b6a8291
+          b9b00009 13c6c020 ea1ccd5a ccf03434
+          a1eacb35 709ca885 44e28e0b 7c2edc30
+          6c7de146 b2c4f985 42442f20 e3f78fca
+          990ffcea 4e83513a 49d6e047 e0297200
+          cd7b8bd1 bc6507a0 d630e9d6 e3011139
+          d9c85c3c 33e4861f 419c3571 e702e286
+          652365fa 14082ce1 08d9d098 25b0ea29
+          4563e156 b41f3e01 41bd2044 096c78c3
+          d625bf0b 2f65de7f f7c70185 1a7892c0
+          2334321f 79e2a8dc e9bf1b0a e8008e97
+          878fc547 7fa71b75 1bb6a2f3 f011fdd6
+          29a72e5d a8a3cc92 a78ca629 17c43945
+          f103e6f8 28645db8 00a2dacb ccaf7f34
+          dbb6fb00 9a761d64 ef0dd00d f0b07710
+          6d777775 c6afeefc 55d6634f 6a275404
+          093ce214 a9bffcd9 32d98317 40890f37
+          d9ae6006 dc0d0e54 aff81a9e 8646ed48
+          2bf422aa 68db7f99 4be72122 27058a8f
+          cc489c1b 5f350806 642e990e 6b5acaa9
+          9db8102d 51981fbb 2a2ab551 64ee96ae
+          e0b40a22 ac31984d 0d6977fd fc17594f
+          fcb99bac d13ba01a bc5e44cc 8245501c
+          2deb3b37 ef9cccb2 dd3c3ace e027cdf2
+          767421e0 71236af0 c060e6ab 7306ab1e
+          8f599313 d15e7c0c 01af9f8e be88b38a
+          5a779754 3012e973 66c0a057 2fcabe2e
+          7b3ca8fa 6439f3cf 9310a9ee 2efc51e0
+          832cdf3f 74e3f615 648c5e15 5a88de44
+          bf3fffc5 9f72c7ad 770b16cb 09ea79c6
+          49e6abd6 e3f91434 abfdf1d4 7a3c93d4
+          a3f7a997 333216cc 20031267 3776fb00
+          7b5612f3 b5d930a8 7577a1c4 9d21d8db
+          b169e376 b4159f08 261e947c 84bbb80b
+          281ebc92 78c34d2f 913148e0 113a643f
+          fb5291c1 28fe5ef1 83ae98f3 f220b2b8
+          e973b851 bb6e13ba 4b4e9c6a 9d12e20d
+          ea0e1f0b b4c9330b 103f6628 02d44991
+          380ba86e 2659cdc8 be74014c b1d1ba3b
+          cbb058d0 75ec38ea 99c0f332 7fd666cd
+          12e1eb1f b2368a6c 5bf26d37 de99f3e2
+          dfc92024 f0889e10 7be9156f 1902788d
+          3d403259 83879532 d81fcfdd ec40c567
+          abe06deb d0dfc953 83ad2ca3 dfa54b61
+          cf48d2fa 9311c419 754b9638 a4cd998c
+          a881fda1 6dc58512 782623bc 2d2da85e
+          5308778b 2338ad82 4e21c27a cd12acd6
+          eaa45b6e 7830e7c5 7fd0952f 1278444f
+          c97df935 24ddf4e3 7ba1603d 1379b44c
+          72827ac2 d555598b bae56b98 600b9c9a
+          02101a29 c28eac8b 17b17f5a 28a01267
+          2e7eb3d4 327a603f adc1360c 3ae24e6d
+          89c2be5c bf6a031c c7cb4376 fc21c203
+          c5872e31 d2fe54ce 5fffb98e ac41028f
+          f886e4fc f5154fd2 8faf7b40 b0da4a28
+          707322f0 44b6707a 6434ef39 88d6edbb
+          b55d11fd 95564154 fe40a4ce 2860efa7
+          479a3803 e24e61ae 171381dc ab2e0e8a
+          3bbd9e66 46096dbb f6a179ff 6116f8a9
+          250a07e2 3f6010b0 2c7aeefc e7c81a24
+          f0886f2b f25e7e6d a7292de9 4f8a17ad
+          640d4e1e 4a755e6d 970755cb bf86aba2
+          461bd4ae 8bd787b4 05331133 74101990
+          38033e28 2163d16c 98e363f5 bf99f9a7
+          bbba1655 2bd7c1d3 d24da3c8 c25edd21
+          2058ad7b 127f78cd fd79afbd 43f62081
+          477c1722 c64d7c55 300aefb1 acc94bd6
+          e0e4c194 004f4727 4e7ef839 fc5ddd80
+          d8832d11 5941f6e5 17c0969c 4447b5c4
+          7709e048 1c3b1c89 0513f4fd 4814e077
+          bbb55164 eea636ad af23f95e 98bb8717
+          d5a6f494 87725f79 bb96ac41 028ff88e
+          e4bdf92f c45f73f5 fd0683b4 29e0a736
+          c85c70aa 86a9bbaa 16b5cbd7 a9493374
+          0b9b0201 18a32391 79e17c18 236c6443
+          e29b076f e666b6b4 24642c99 0bf874ae
+          66abfe28 4a6858b7 111d2527 b5490654
+          7b17e6fe 21a3db60 14fe611f 39e62bb2
+          06093ce2 4c89bc57 deea8cbf e6fb0f1b
+          4cb6e324 f138d178 42b01eaf 71e71eb4
+          ed3d0848 3dd8c5f3 cb881e35 0c0913c7
+          68c76c04 f14d505b a2e45c76 01a48888
+          1e7cb384 ce232568 dcb20b7e a797eaee
+          c25fdcc9 0683b422 eeca2b1e eeffee47
+          64101278 c4191679 85f63143 ff227bd0
+          42d6e044 e449ea80 771f2a3e 5f09775d
+          63cf8e6a dd1ead7e 2a6a609e 76bb9120
+          7ae66c06 244f9d04 7bff6cfd 7e772cd9
+          f07774e0 e4c75fc2 d3de4dfd eec21d75
+          7756b216 c57defb2 7bfabff9 2fb20709
+          3ce26c60 cac87a51 30193f62 d9144d21
+          e525eeb2 a7d4a7d5 e37d06d9 e3edd151
+          ad419290 7de952d8 12e3a926 8ae81111
+          5919c8b8 60beb60b ac2704d5 0e3e555f
+          ac86a7b9 4df34f22 ccf59d07 75f671c3
+          ffd2ff8d 7f959335 48e01167 8901ef7e
+          8484ab2e fbad41b0 14b2c04d 87b55c28
+          3c6822ad abbc1ab5 abbed6fa 13e8222b
+          30317197 b670168c 91361279 4448cc31
+          d1c8fbd1 152c93e8 41de288a 68deb213
+          2d078ba1 f8641278 61afeee0 32988caf
+          9992535f 236390c0 23ce3279 afbfd714
+          3dbbe051 c58d12b2 063f224f f1ca68d8
+          bc038e23 c77a76f4 eaf5226e dc28c48f
+          1e0141ed a747228f f86f41c0 6c42eadc
+          e93d6b89 c2c49dab b61eb56b 0a217753
+          dd1d07e2 4e0e28c6 f5f1575c f4bb011f
+          7c4af620 81479c0b c4a8a80d 06a3f155
+          16b4dbc9 1a9c683c b509b2cf 8f8a4f57
+          c0d3dcd2 4391e743 e6850b10 919dd9a3
+          a918046f 3e25227e 443e9266 4de9d9d1
+          ac2ca362 d957f076 74d2ce5d b8134020
+          10301d4a f8de25bf eefff687 34ed9a04
+          1e71aee8 ffde3224 7cffd227 15bff17d
+          f620d2bc 5a8e7037 b5a2fcc3 2f98d8eb
+          c171daa9 7abc7e97 2c82392e 46b7769e
+          e00b4b52 bc9600c0 dd83169b cc8f6abe
+          5a8daef2 2a35f2ff bb950f11 9ec86e34
+          c62e9afd 97bcb7de 3f40d620 81479c63
+          d4fe7889 575ff63b 26f23607 1ba5117c
+          64d60138 ca2a50b7 6e73cf1a 8fc932ac
+          9919485f 300ba628 bb36868a e0dd8798
+          5eb3dbb4 8b385a4b 143de52f 08709e28
+          47d3f67d 90bd7eea 7717fefe 21b3c4f0
+          3383d9fc 0a198304 1e71be44 de1bef35
+          245c79e1 132cdb2a 256b7082 3af79d89
+          b6fac26d e86441b7 47d1d6e3 41c294f1
+          881d3618 8249a27a 3ccec59d 5a939938
+          610c2207 f5d71280 d0fe6680 e272a1e4
+          9d8fe077 bae86836 fcfd23a0 f8c43509
+          575e72d7 80f7a9ee 8e041e71 9e03beb0
+          8265d86f b23fb9c8 18fc207b 3dda2833
+          5f6757cf dee0f5a2 dfa54b61 cf4ca3a3
+          5ace1304 755a45e6 258b359f d04512b5
+          ba4f5f7b 27d98e8b 75453894 70f5e57f
+          e8ffce07 5d640d12 78c479a6 ffdb1f20
+          f107573e e2771ba8 03255799 76009e96
+          56ade8bd 47bb784a 00069311 fd2e5c08
+          4b628cda 999ee0ce 6700737c 0c72afb9
+          1cf0f7a0 6e5e14d1 b4713b5a 8b8a1150
+          bf9f8e66 c31abf0b dd89d75c f14eff37
+          ffb58dac 41028fe8 2d22eff5 779172ed
+          550fb307 74335983 a378cd44 5bdba1a3
+          a8dbb0a5 676ff0f9 611f908b 94a99360
+          8cb4523d 1e5ff900 449b0529 d326c392
+          92c404bf ce87cf92 06575d3d aa576f80
+          ac5ec220 71c703ef 31bff813 9981041e
+          d19b608b 71ce3fde 284fb9f1 ba6798c8
+          ab2083f0 24f214d4 ae2e4477 752d7a74
+          f6ea7623 69ee74c4 0c190841 34503d1e
+          174ec216 7b49406c fe40edb3 57dbe7f4
+          644dd14a 00baba49 dc71008b 1bab937f
+          70e58379 afbf4b2b 02093ca2 d7693c49
+          0a40913f 634fa77a f389fa16 7184ecf1
+          a0ec9d8f b57ff6c8 577c7e64 5f71016c
+          996950c8 53c25fdf b145c192 188fac0b
+          16c0d013 712789a8 fc7c25ba abea4005
+          9be18fcf 8593c93f fcfe8bb9 afbc59af
+          b65522c2 03fa24c3 8cec175e 96038af2
+          44fdebef 0c345af1 03b2083f b89a5b51
+          f9e90ae4 7cef12fd 1d174581 60b3226b
+          c93c94be bb0cce7a 478ffa26 137d50dc
+          298039ce 8a7e172d 841413ad 5f7b6730
+          c0515c82 c66dbb83 75774458 e377c393
+          7acb8fff 96fdec0b cb0d2633 a9791278
+          446f45b0 5a91f3e2 cb5e16ad ff58f7ea
+          5bfd4d36 4ca22338 5e227900 4d7b0e21
+          222f1b89 e346e95f bcf0fa10 91938511
+          77dfa61d f312e1bc 300810cd a61e5dac
+          503c5e94 fdeb53ed 9f449863 d0ee5e2d
+          6309df73 82c54a6a 9e041ed1 ebd7729b
+          1dd9cfbd 7844b0db 9fab7de1 6fd9460b
+          52c82a9c a0c8a8fa 74052232 d3604949
+          d6ddc833 a881df66 25bb7192 00f4e09b
+          50face47 f0765287 0c1ec49d d789ad29
+          d75efd87 7e4f3de7 218384a1 16201384
+          276244a4 c282f727 2c3bfb3b 1548f3b5
+          68fb5d5e 94bef521 a08e32eb c9fc5935
+          f0d32bfc 5fbad140 40f3ce7d e8385a4a
+          75771ce0 77a13afd b69b9fcd 79fea512
+          312a8a0c 42028fe8 4b643cf8 b027fd86
+          1f3de571 e23d1279 7c3dd5ce ba66947f
+          ba02b2cb 039a2b45 f4046775 2d2a3e5f
+          ad4d4921 38480403 78dd6014 3f17a3a2
+          a93e8304 1ed1d790 e2e290f5 e4339d99
+          bff8e99f 58b6b691 2cc20f6a 61bdabae
+          51bfd719 41a8fee2 9751fec9 57f03bdd
+          640c0ec4 1d4bfa3f 48bff69a a7d27ff3
+          7b3a9a25 8147f459 91171b07 6bffdc93
+          4a007564 0d5ea235 205a2464 5fb20882
+          d54cc76d 44c860af f8fda8f8 6c05baca
+          ab69ce2c 07f8ddf8 3ae367b7 3edcefe9
+          673b8c09 89641012 78445fa5 f9edd751
+          f9c89f6e 319ab190 acc1070a cbc9b32f
+          5b026b46 1a8bdf74 3c4b844e 06d46b94
+          eee66604 fc940870 b13e2870 d8060f6c
+          94e213c8 1824f088 be4afd4b cfe3e4dd
+          bf5a2a37 d5dfc532 f368b208 078bb70f
+          48993916 f123f261 a0da3ba2 2741c028
+          216be902 f64ff217 1e60c9fe eccac79e
+          baa6e5bd b7c81824 f088be8a bfa96194
+          bba1e9d7 82094964 8df0479b 56101f89
+          b4793320 98cd6410 a267b044 c0969e8a
+          9ccb2f40 805adf85 ffc72d20 4aaeabb9
+          bfecae7b 2e6ef8db 8b649030 86fae085
+          29cd6fbf 6eaf7fe5 addb4d66 4c206bf0
+          a0eed8c2 cd5e79d7 5c0a933a ad2054dd
+          9dfa2549 04d4fe77 a240b368 c33a9ab3
+          97acdeb8 71013e7f c81bd571 6346c051
+          5689c6ed fb215064 086b0433 12dd750d
+          f7faea6b cbd8bf16 914548e0 117d45dc
+          bdf92aca ef7de036 5f43fd95 a2052259
+          84037dc7 6278f6a5 0b11d93f 47ff5285
+          4982bfdd 81eaf73f 81bba903 065a05c2
+          d72f64c0 1c1b89f4 85b3604a 4808f646
+          3c5dd037 1a91b978 0ebaab6b e1ac6ba4
+          ee3a610e 4bfec7d5 bff1ee2f ad8306df
+          157fd50f 5bc82224 f0885e4e c38bcfa3
+          ead1c796 f8ea1bee 14ada0ee 959c04f1
+          98610390 38799cfe 6e9c1ab5 d9abeaab
+          b568dcb6 178a7a24 47851a61 ec1cd076
+          e3024cf4 e7fef86a c0ef3bbd 8fb0ef31
+          c6c6a0df c58b50f2 cabb5042 8841a2ef
+          631020f9 cacb2f3b 79f7bdc5 0145f953
+          c235d792 5148e011 bd1957f1 c1a1eeba
+          86fb4d36 a4d2d11b 1f01dc92 10cd82f2
+          62082653 e8be77ea ce5e6404 9ad66c40
+          eb8143da 5f8934a5 8c8b04a0 ada818f5
+          abd62365 d16ca0b3 fbf447b5 b28ca801
+          b9c860df 57b16c15 edee8639 a20d11be
+          daba5f96 df7bff71 a5b3eb93 a49fdc4e
+          46092328 770f239a df7acdde ba72dd9d
+          46132692 b8e343dc a9355659 172f8225
+          2549bfa9 b1d5824e 16e8eb0a b76ae3cc
+          0c7478cf 05eae7ec 77fbd0b0 6907ba8e
+          9e601981 45f73d49 53262076 f8006aa1
+          c8c11ac2 445eb2bb a6ee3e67 d1bee164
+          10127844 2fa449ad bbfbd5fd b778ca4e
+          5ec9b26e cabb3958 98d5babb d4d9d358
+          201e12b2 b62af8a4 b347dded 41edaaf5
+          7035b452 7d156f22 4f9d5ed0 da8e9a2f
+          5743eeea 62513d84 ba67aa4e 309b9075
+          e14298e3 a8ca8387 b5c468c2 d8b63585
+          f7b4bcf7 661c1984 041ed18b 6878f945
+          54de7bdf 5c5f7dc3 2f583616 49bb77e1
+          bf20abc4 0cce45da dc693dbb 05cb0276
+          d517abd1 5156712a e29319f9 52786a42
+          1040c789 72d4aede c0567e1d 07906558
+          d25398c8 5b04832a 06694d09 6ff79020
+          ba4b4b2f 3d79f7bd 3f697ef7 4d320809
+          3ca2b7d0 b166e550 777dd3ef 98b8cba4
+          85980f4c d191c8ba 6821449b 4dffd6ac
+          dd86d61d 7bd1b87d 0f027e85 c651f11a
+          c4d5cf9d 89bc26e6 0badbb0f 00aaef84
+          c2eb43dc e8e14899 3631e862 b4b68475
+          d2c8e287 dd575bff f38abbef b9b8f195
+          97c92624 f088f34d f33b6fa0 bbe8c81d
+          92119369 01e60025 f8d4662c 990b6bbf
+          0cc0ef0f fdfd4623 dc5535a8 59bd1eb2
+          db43e28e 80bfdb85 9a358570 57d70292
+          4e3587a2 2075de0c 440fcc09 eefad21a
+          13ee222f d95dd7f8 60fbf22f 86914148
+          e011e753 dcbdfb06 caefbceb 66f7f113
+          971b8cd4 ef2eecd7 df40f089 4d299888
+          84896301 8fced801 83018adb 8dca4f57
+          c059d744 b199f8f7 d1bcabae 11d55fac
+          d28e6243 16643281 27454668 bbc5c6e8
+          48ba74c1 81c8938c 18e13c5c 727fcbfb
+          ef503d5e 1f477ce8 a187c80a 7d90a6d7
+          ff89ca5f de35dbd7 dcf68c9a 7551f40e
+          ff85578d c3115919 c8beea52 0892a47f
+          346b35a3 6ef50634 6cdd1d7c 3fa573c4
+          ff2bf21a 5b209824 440e1910 7a275856
+          604c4e84 2818e028 3981801c a04b3ae1
+          ec1e2204 6f7d6b9e 63e3468f b95ffa46
+          dbd01164 943e0a2d f97d55e0 bdf272a6
+          a7b9fdf7 4cdc6590 b8e340df a999b5d5
+          8acc8b16 6a3b2ada ce4b284c 26388a8e
+          a06ed376 2d9e93b8 23fe7f91 a7363fae
+          fd7a33ba 8e1cd78e f243e272 2369ea44
+          248c19ce c49d41bb c14d846f 32c9e28a
+          cdd7d8fc f3f29ffe ecc2e6b7 5f279b90
+          c023ce15 2d1fbe07 6f7dcbaf 4409e349
+          dc71b0de 2aea1829 11a9b3a7 2272e820
+          c0ed0efd 065180bf c3a1dd9a f5773ac9
+          80c47fd7 7886603d 5ec5672b e1777406
+          5be984ca 30d81bd2 96cc85bd 5fdabf85
+          0011d622 2fd1d3d4 f668e3df 5e184a06
+          2181479c 035a3f7a 1f6537df 7ca3abf4
+          e4750613 cc649130 5f67d5b8 ca045bc2
+          d8914855 5ba2743b 43d74ca9 3b337e3f
+          aabf5c8d ee9afae0 691c1da7 11a77717
+          7495d7a0 76f57a7d c5e6f5c3 9c9488cc
+          85b3618a 89a45d3c 1e449e84 21bea6f6
+          075b3ff9 30960c42 028f388b b47cf01e
+          4edc70fd 647f7bd7 c3ea8819 caa0f958
+          646da949 485f322f b8c3a257 776734a2
+          71eb6e34 6cddfb9f 084e1021 149e7a7c
+          dfb06517 9a77ed07 2431b41a 74ba1035
+          722852a7 4d826891 b4316844 18bb8709
+          a2b3e4c4 85276eb8 e1a76d9f 7d4c0621
+          81479c2d aa7e7b5f 8acfe17a 9289bb14
+          12771c68 3b163c4d 5176a42f 9c055352
+          82b68312 12514077 69396a56 6d088e21
+          237147f4 50e429b2 82eaafd6 c255531f
+          faa856c5 e345f2ec 02c4e40f 623e67a0
+          9bb5619e 60b27863 f1b775de 597aed8f
+          16b72efb 906c4202 8f38d3b4 7df99901
+          fec07d54 77c78fb8 53ebee92 a68c47ec
+          a4b14077 7768c1a6 b64471b9 51fec972
+          f8baa8ee 8ef8861a 4f1d65d6 d6892a26
+          f214bd1a 4f4581c1 6442dafc 99b0a725
+          532d1e1f 222fd6d7 e17cb6f2 bebb0691
+          4148e011 6756dce1 f8f72fbf ce5d56f5
+          13830926 b24898af a70ab49b 8a314306
+          2075de74 a087824d bd11d959 56433766
+          896f27f2 44a0f5c0 31d46fda aebffbeb
+          f6c09697 8df439d3 608cb441 a1a3daf0
+          17791272 11101e6d 5fb59cea f148e011
+          678ab29b ae1da674 fb9f146c 3053b61c
+          fe0ba9fa b267a620 7dc95c08 569bb663
+          123a321b d056548c ea951b21 18c984c4
+          77080812 50b3723d 1cc7cb42 1fd5aa5b
+          7edd4ec4 4e1a8784 f1a3201a 45ba7411
+          ee098049 9d575b71 e1f1cb2e fe79c79a
+          95641012 78c477a5 63dd9a24 c1627b96
+          3d5c7124 eec21f75 27c41865 43eacc29
+          b00dc805 5c2e5d71 e7ae6f44 c5b2af20
+          d02c13e2 3b4771d5 0703a8f8 6c15bc2d
+          6d3ac908 5b907c3e a4cf9f89 e841b9c1
+          37d31a15 d6c9a760 8351eef6 dd7deca2
+          c5f33bd6 ac229b90 c023be2d 8e0d5f1b
+          8f2e9eff 6b4f65dd 2c834425 f361bf7e
+          2a806892 90306e14 e2268d61 eabe137a
+          2303fcdd 4e542d5f 0b776b17 5daa20ce
+          8cc613d4 d6290da8 5ef53514 26e042c2
+          be2ec644 6bf578b6 d4783aaa e543e445
+          28aec03f 8e7fff92 5c320809 3ce25bd0
+          b971038e cc9b7359 c08f9f09 66f63951
+          661cf60b 271403a2 07e5207d f11cf6e7
+          5367b521 05a182a6 6dbbd1b4 fb181dcd
+          126714d1 0cd46f3a 8096fd87 4f35633c
+          9d1a0c1e d5460c1d 8ce4a913 618cb050
+          eb140ed6 2a830919 6244d4d3 9d5b36c5
+          904148e0 11df9023 0b660d60 ebea539a
+          b823c27f cd6441d1 961e8f94 59532146
+          46eab744 6171d571 a21cd5ab 0b2159c8
+          7ec49947 f5abf28f be447775 8d8e2f32
+          67ecec42 d2ec6948 183d1c6a ad00d5e3
+          8537eabc 5a4f75c3 85c533a7 ffbc6bfb
+          56320809 3ca2a774 efde192f c5c6fdc3
+          20219dac 11fe284c cb491166 a44c9b8c
+          a8d1c37a d412c5d3 d286f20f bf80ecf1
+          d3d12c71 96a23820 bbfda8f8 722dbc1d
+          8ed0e502 ea2e9fac 2065ce54 440fc864
+          4e0daac7 0b77f160 86c03ee2 fb0f1714
+          cceedab5 830c4202 8fd01577 fbf6980e
+          8e9ff880 afb97506 b5bb087f b439b382
+          88f851c3 90387502 d0d9ad33 8acc00bf
+          d389da75 9be1ac6b d36e3d12 c4590b10
+          46a0fd50 b9361d45 717b42fb a6c70373
+          563a5266 14c09a14 4347b57c f88785a9
+          88770e4f 99449b11 24f08850 380f1e40
+          d1987117 0926fccc 40819b03 75c75eb2
+          01d183fb 6945eada 35589d96 28ea9cd9
+          d6fd8750 b7618f56 27451067 1bc90ad4
+          aed988f6 a3c799bf caa71779 eadf3bba
+          10337604 12278f63 ef33d2a5 0b0e3088
+          4896e2a3 5f741e2a 8a226b90 c0234ec3
+          8111a3f2 58d07e1a 2248de71 807a346b
+          4d8945ca cca930a5 a5683b20 ba494075
+          1d2a3f5b 4de28e38 b7b90813 6aea940b
+          57538bfe 377bbc48 99310571 a386c1a0
+          86183aaa 0d771561 90db3a2e 2a1a3ef2
+          0ed7d162 2a182181 47fcffb8 4b4ba28c
+          b196d7d8 8a9849d6 e023600a 66231227
+          8e41f484 515a917a c8e32fc1 006f7b07
+          ca3f5d01 d9e5a369 15c43945 3d5170d6
+          b6a16675 217c0e1d 5f956518 ec362433
+          91179597 a6253244 98236a35 79bfdb3f
+          64e83416 cbc81e24 f0887f27 bce527a5
+          bd0306dd a7b8dcd3 a8609e07 75a7be04
+          248e1f8e d43953b5 632dbdba 3bd9e541
+          d38ebde8 28a98181 5aa210e7 23865b80
+          c6ed87d0 5a7438d8 1f2f94cf 3a5db0f5
+          cf4672c1 4458e223 102091c7 839a1099
+          8f7cbc6f c0a06432 06093c22 1080a7a2
+          dcb02727 779664c5 fd24eef8 4061b131
+          b27f3a92 a64e04cc 66fdba3b 45d64647
+          557c5ea8 d54311c4 f9147915 9fac4077
+          650f5aa7 743b1137 712c1226 8d654989
+          18bc594b 84370624 4891d2df bdd55536
+          3206093c bef59ddf 8fddd939 d92c68bf
+          41d6e0e5 33679a2e de8e1426 eeec03f3
+          00974737 503aabeb 51f6fea7 104d643f
+          e23cc76f 7594992f 80ca2fd6 c0dddcc2
+          149f1032 81555fc9 0513103b 7400143f
+          8d32e324 ae5db83b 33eb4e6f 6d0d0d4f
+          2481c72f fea626bb 64c2cbec 8fa9640d
+          0e507730 58404c9e 3609715a 4b149df1
+          6282005f 4707ea37 6c85b7cd adde5623
+          88f31f34 d4d62925 d568dabe 077257b7
+          561f7a5a 7c3e1813 1390327d 0aec5989
+          548fc709 92158fec 4ecf98e4 abad3568
+          429f2081 c713ccf1 cdbbd3d3 af67417b
+          1e59830f 64af01f1 a3062371 d238eda6
+          61480c06 286e379a 7717a17e fb21ed68
+          8c207a0b 46e68fb5 abb7a0fd 58a9d6e0
+          58af1e2f 72f86024 4d19cffc 58a4fe78
+          fc88bccf 77a5a727 294e2719 83041e27
+          b06cc657 572b32c7 9f2a5af1 1732081f
+          284ccf45 0e4845ca cc0218e3 6298c2d7
+          dfcae8aa ac46f5ea f590a825 0ad1db30
+          04a347d5 67abd15d 55135ae0 a9b8dc48
+          9e361129 051310f0 53b13127 c489225e
+          6602cf4e a62081c7 05fe9616 ec4a4b4f
+          62d9cd3f c81a9c68 7a193046 99903265
+          3c22d4ba 3bb74edd 9d24c155 df888a65
+          cb21bb65 6a8942f4 4e8d2732 ddd6d489
+          fac2edf0 b6b50362 881a02f5 2211fb7a
+          52c178c4 8ecc637e 4df6e3c2 474cb868
+          5752d2cf fd4d8d94 a692c0e3 00458962
+          f9eb6bec 4f39640c 1ed41db4 6efe6a67
+          ff04b5df 9dded12c 0b82beb6 0ed46dd8
+          82aeca66 1a4546f4 6ab4d629 3b82ad53
+          025e6fe8 7a3caf0f e68c3424 4f9d045b
+          6a8c769b 9ce0c047 ac786c57 52f2187f
+          6323e90d 1278e18b afb1c1bc 2b35f51a
+          e6f00bc8 1a7ce077 0309a307 21a56022
+          201943b7 44311810 f0f9d076 f8281a36
+          eea7ba3b a26f0411 1350f9c5 6a384a4f
+          6a178342 e2f62066 643e52a6 4dd47600
+          03d43a85 1791f705 8b7d49be fa3a3206
+          09bc3014 77f575c2 ee8ccc02 d1ac50dd
+          1d27a83b 14b69428 244e1a0b 536ab27e
+          dd9d246a fdc5aabf 5aab054d 82e80ba8
+          25048a3b 809ad51b e0ae6b00 8c213a71
+          ab372afd b2f64c24 178c87e2 21fb7142
+          3c4b6e5f 30188d11 640a1278 6185e764
+          19f6e4e6 258ba24f 6d8942cd 2e3840dd
+          9950c73b a5ce9986 e891c3b4 22f39018
+          2578ea9b 50bdf26b 783b5cd4 1285e85b
+          81842524 edc76ad0 b87517fc 1d8ed0f5
+          78b20c21 22028913 47237a60 3a641279
+          5c205a71 f1eeccac 5bfdcd4d 74364102
+          2f8c8c6c b5462a2e d7933020 8facc107
+          6ad04a99 3a1e89e3 47c120eb f48560c1
+          5071bad0 b47b1fda 0e97d3ee 1dd12791
+          cc01d4ac db8df623 25c15bb6 a16ed67a
+          bdb0e7f6 d36ed61a 234dd41f 8f138d27
+          045c0fef c9c91de5 6f6c206b 90c0ebfb
+          f81aea4c 7b87e45f 2d597025 40c3c8b8
+          10772e20 6e442e92 a64c8060 b36a3b16
+          a7450d82 82c082e2 7154afd8 0ad1444d
+          41893e8a e6ca322a 3f5f85ae 9232c0a4
+          3334d92f 236ef430 a4ce9cc2 9e111a72
+          c1898f98 e1ebfa64 cfa0c1e9 9e13c7c9
+          1e24f0fa 2edeba1a ec1d3a7c 309ced4f
+          31c7a611 f11ca0ee 44481112 92268e85
+          352b8305 319dad09 1604bb4a cb50f5c5
+          2a35e251 0a40f4ed f82d019e 56276a0b
+          b76a2507 21ebf114 857dbf11 8913c720
+          7e423efc 2eb21f17 3e2220c5 d7defeb4
+          60b74792 3548e0f5 499c870f 62ffe8b1
+          69e86c79 dd20820a 4b3940dd 81502f56
+          642c9c89 d831c3b4 314d21b7 258c127c
+          2d6d68dc b60bceba 0e6a8942 8405eacc
+          e496fda5 68deb50f 01b5e7a3 4e3d9e29
+          214ebb65 1ed92f81 eaf138c1 68c125fb
+          468dbdc5 d7506f25 6b90c0eb 7b8b9cdd
+          1ee36d68 789c89bb 51640d1e d2d2e0d1
+          6cdaccb1 da8e8441 bb5a18a2 0784da2f
+          cceb43f3 ee7d68d8 5e0c91da 801261f4
+          2c180c32 ea0bb7a2 edd011fd 29177e19
+          110372d9 b353a025 39d43a85 031731c0
+          1468adff edfe3163 47ab275d 0409bc3e
+          83b7ba4a 3c3863d6 6223d5dd 71832aee
+          22b21391 307134a4 e8e8d075 772a6633
+          da8f1e47 f5ca4d6c b1a3a359 22cc020b
+          136abe4e 2f6abfde 04a73aca cc1ae2e2
+          64200003 7bc58d1d 8eb4f953 e1f3d0aa
+          c985c893 102537d6 feab68d2 e42c67d1
+          01320809 bc3e20ee 2a4ee2c0 b4e9c3fd
+          3595cfb0 2c85ae83 73803aa9 42b28948
+          9f3f53bb 19a85b77 c7829dab bc4aeb1b
+          e6eff640 a0962844 38061723 d055d680
+          86cddbe1 6b69d5ad c7132c16 248e1d85
+          8461b9f0 d17c7a5e 7c24d355 59f547c1
+          668b216b 90c0ebd5 74efde89 8373e665
+          c995e52f 0b262491 45f8c0ef 05526717
+          2076e860 1802c11d 89d3c282 9cbfb50d
+          f59bb6a1 b3b44eab 572288b0 443daa35
+          06d0b4ed 105af71e 8456901a eab8d62f
+          c39296cc 9ea529b0 c6db6994 192798cc
+          b8f4d0e2 a5377aab 2a694384 045e2f5e
+          cf4cc6d8 ee13271e 10cc9840 d6e02380
+          f95c40d2 f821489a 380682c5 a4df1285
+          05b9d6bd 455ad033 48013a8a 22c2fb11
+          514b5165 1f6ad76d 84e3f031 c0663b7d
+          02a4febd 1240d4a0 3ca4cf9f aeed8c07
+          a8770a0f 3e62f695 96dc7770 eefc09ea
+          09184102 afd7e139 598aa357 ff7081d9
+          8c6bc81a 7c20bb81 88f478a4 cd980c53
+          427c6871 a7462a9b 159dc74e a076fd16
+          283e2f4d ab20f808 3246c0dd dc8dba0d
+          9be1a9ac 0e5d8fa7 b54e9110 3f660452
+          678ed166 39531214 fe8816c4 7b4b8ebd
+          7668f192 bcae5d3b c82024f0 7a91b83b
+          7e0c872f b8789ce7 f0e12758 3662278b
+          843fda28 32f6f4a4 cf9b16ac bb536fcc
+          9e6eb741 fd7b26ee 5c2cb8d5 ac2d84bb
+          d1a1053d 82e049e4 b51dae44 c3b65d08
+          a863fb42 b54ef1cb 902223b4 d6293103
+          d221533d 1e1f22cf 8adccee2 23bf3718
+          1047d620 81d72be8 dcba19c5 975f91eb
+          3e7cf879 e6a05964 110e508f 663d40da
+          dc49881b 99cf849e a0537727 42e972a2
+          79eb4e6d 1499da0c 96762508 ae1e192d
+          d2c868da b64f2b51 d0a65c84 aac76309
+          93353d15 e90b66c0 1869d48e 6b89f0c7
+          6cc665c7 6fb9ed06 cf89e354 8f4702ef
+          fc13f0b8 ed8ea283 374b564c 266b7022
+          ee9c40e2 c881482e 98a4ce19 d66f8962
+          b16841ad 7ef30116 d39453c1 8e20380b
+          3646b575 8a0bb5eb 37a3eb68 49f0a836
+          543d1e13 80d103f2 903a772a 142fd98f
+          0b1f1160 71eedd7b 57f1e557 16b88f1f
+          258390c0 3b7fb88f 16e3e47d 0f5c6836
+          e146b206 2782dec7 f45a8c0d 6973a6c0
+          9c1817ba 99b15677 6743e7a1 63a86341
+          cddfeda2 a359827b 91d755d9 8486cd3b
+          82ad53cc 21ae91b3 c4c9c0be 9e386134
+          12c70fd6 2e34d1ce 77f823d9 90ecdabf
+          fff9a357 5d33a873 cb263208 09bc734f
+          e7e6421c fbe18f26 77eddcf9 b020219e
+          2cc2077e 26f03297 ce42446e 76b0b62e
+          d4d1acd9 acb54469 d8bc0d5d 55cdc169
+          15742b90 e01926d0 d4be8f2d fb8fa379
+          dbee5351 28441862 09942936 862554d3
+          10911a07 85469971 90456b22 2fbf63cf
+          dedfca8e 8e043208 09bc731f e85b9a32
+          da77eff9 bdd1863c 0ada7c04 2675187a
+          ead49188 1f3d0206 49d41945 16accb6b
+          dcbc1dad 074a8373 6669f781 20b4dbe3
+          b2db8bfa 8d3bd0b6 af08b085 9e72a1be
+          ec99e9c8 583c537b 86a8750a 1f22cf6c
+          c265958f 3d7183fb e861aac7 238177ee
+          701d2eb2 57fff9f9 db9803ce 2571c707
+          6a4b9498 41fd903e 6f3a445b 0feaeeac
+          66b4ecdc 8bba0d3b d97b3dd4 128520fe
+          1fd4dd6c 774b9736 afd655a1 b64eb19e
+          5eb929ec ef450171 23f2913e bf805aa7
+          f0224e24 583a376f bee3d875 37cc6231
+          970c4202 efece33c 7800c76f bcf952c7
+          c64d3731 07a46586 87645261 f1c52420
+          63d10c98 9312426f 21a85fb3 dbe02aab
+          64e26e0b bc6dddc1 a3598220 fe67f031
+          028ed21a 34b0e744 eeee068c 52880c4b
+          d1eaf192 a74d465c 7e36fcd4 3a858385
+          97b9840d a95d3b76 3c7efca6 9bf31d9b
+          0ac92624 f0ce1e8e 8d1b507a cb2d531c
+          db77fc86 395e02ed def1815a f7a3d6dd
+          45f6cf86 ee1991c9 087fbb43 bb54d15d
+          d940e28e 204e837a 9b3cc084 5bd3aec3
+          68deba4b bb6d1e72 6b4e09c0 141d898c
+          a5b3618e b5b1f792 0d391179 235bb7ed
+          78c05356 9a480621 8177d670 1f3d9cae
+          3a9ad986 8124eef8 40adbb4b 183f18c9
+          0513b50e fb21ebee d4be5ea2 84a6cd3b
+          d0b2ef68 5008d213 4610a70f 4026f68c
+          75b951bf 711b3a8b 8a8108ab 6eeb94c8
+          7e59c85a 3a87041e 4722cf6c c4a50d6f
+          bf7793f3 d001aac7 238177e6 7116ed8b
+          68fce8d3 9f32475b 4045be7c a00e3bb7
+          a5c52363 d16c0866 b3764c74 fa458839
+          45841d0e 16a4d416 10fe6e0f 0cd41285
+          207483b7 bacbedaa 6fd7a6bc 781b9b01
+          4b886d6f 35c11204 96748d46 d2949190
+          5d64421e 908cb0b4 af5d777b e94f6e9b
+          cf623119 8404de19 1477fbf7 e2c46db7
+          5fd1be66 ed8dccd1 a85c9e87 b8a3051e
+          23fa5d34 17d69464 fd37582d 705756a3
+          7acd7ab8 9b1cd412 85207a8a 2178b3d6
+          71bc12f5 6b3769a3 caf45aa7 a8b7d833
+          16cc4264 6eb29688 11e1bf1e 9b6d4873
+          6cd9fabb b2db6e1f dab97923 198504de
+          774775a4 b29fde3e b663cbb6 7b998325
+          d0ee1d1f a89df353 674c40ec f0fcffac
+          30a77d8a 0404bc3e d4afd980 ced22a1a
+          454610df 54e369ad 53fc68de bd1fadec
+          a58d32d3 c114178b ac0be743 b299b58b
+          50041722 6f4ccb96 6dbfeddc b231892c
+          4202ef3b d3f1f5ea 8c962ddb d5babb41
+          24eef840 66e22e6e 782e5266 16fcbb0f
+          57486c56 346fd989 96fd47b5 4043a3c8
+          08e29ba3 ee7a7b3b 82a3cc9c e595da14
+          9890f578 8ca841fd 913a670a 198f2391
+          67927051 fb86cd37 3b8bf651 3d1e09bc
+          6f8ff3c0 3ebb63fb eedb9943 5d4ce28e
+          93054466 59626c04 b22e5c08 2932427f
+          b5b15ad0 5d728205 a52df03a dc5ad138
+          4110df0e 7527afbb ba01b56b 37c2dfd9
+          09188da1 9f3f2580 14b575ca 884134e5
+          82132413 2cad2b57 dd7ae2f6 db17b318
+          4d062181 f76dc49d 5a7777db 45ad2b56
+          5dcf1c8a 0edc7809 309280cc 25b3614d
+          4dd1dfb9 3319e16d 6b47f597 abe16a6c
+          a3ba3b82 f8aecf9f 10ec3bd9 76a8048d
+          2c69823a 31c6107a f9152d66 642c9a0b
+          4b720c1d d5f29084 9faac7eb d8bcedd7
+          65b7df96 dfb57d2b 1985045e cfe9dab1
+          15276ebd 7578c7d6 edf73047 4aa6dd3b
+          3e508bb5 13278d66 af7141a5 16b2eece
+          a0059efa 35855a71 b8168328 0d2088ef
+          1e942440 76f9d0b0 65271c45 47424fb9
+          3815f1ad 6929c85a 3a97bd97 421a4722
+          6f6ccb96 ed7f6879 ff1daac7 2381d773
+          9ade7825 b575dbce 3f30071a 45e28e93
+          05430622 b35391b9 684eb015 83de076f
+          36a175d7 7e34edd8 0fd9eba7 51640471
+          26039311 f0b475a1 6ac53a78 9a9a0193
+          4eed832c 236eec48 24154c40 c04ff6e3
+          45e49944 2c719694 dd4afdf1 48e0f508
+          e6282677 45f56daa e390b8e3 65a5008c
+          5136f4bb 6c09a488 08fdef37 9be1aeaa
+          43d5576b e173b8a8 ee8e20ce 02ea716d
+          77751d6a 97af6139 9712ba75 8a0afb9e
+          ac0be623 3237839a 20738264 86a565f9
+          ca5bca6e b979298b dd641012 78a1c45d
+          114edc74 c3052dcb 575fc71c 87dad472
+          1349808c f93311d1 3f27f4a4 0a155184
+          dfed4635 0b3a9ed6 f6603363 4a0408e2
+          ac08bc80 5741ebbe c368ddb9 27f4ac5a
+          2d510bc0 c09ecfec cb97c21c 1b49cf25
+          0fb979f0 a836b57d ebce5fb3 d83db87b
+          ef1e320a 09bcff22 eef6efc3 891bae1f
+          d2b17dcf 43261b32 68f78e97 1502881d
+          361849b3 a7025e9d 8ea96aa1 9d6040e3
+          bacd682f 3e8e801c d0abff26 08e2bb88
+          3ca33a2e d08b9a55 85c1d629 7afdf1d8
+          c26dcbce 44fac259 548fc791 c863317b
+          74fbf63d 4fd43ef5 38cdab25 81f7bfa9
+          7ee4c1e4 f69d7b1f 638e328c 323f7ec4
+          9d352509 39dfbb08 f078f5bf df644247
+          f131d46f dc0ad945 75770471 4e441e8b
+          529e9636 547eb60a fe6e2720 ea842d96
+          a8254e9f 8cd891c3 c8781cad e59288f9
+          fe36c7cf 5c478ba9 1e8f04de 7f701f3b
+          2ac95dae 3b58c2b7 84c41d3f 88362b32
+          97ce8314 19a9ffcd 4609dea6 66547db1
+          1abe2e57 705a0541 10e740e1 057769ba
+          4e56a1e1 ebcd41c5 a7b775ee f620fb8a
+          0b604b4b a1a35a5e d673b51e 6fe59a9b
+          4aaffdc1 052ca693 4148e0b1 75e0f831
+          1cbfe67b 0b5b57ad bb46b450 dd1d378e
+          2f49489e 321e3163 47007e9d 6b772c98
+          28b2a2ed 20b86a1b 83ed50e8 689620ce
+          9dc613a0 dd56afdf bc031d87 8f06fbe3
+          e9057c2b 4be02e5c 0063949d 441e0f04
+          8f6a531c 3bf73dc0 627a7fd7 d1628a73
+          5c8bbb12 26eebe77 d900c79e a2278c36
+          64d122c0 0f91fd73 9071c922 c0e9d2ff
+          66a384d6 6dbbd15e 7c0c8a42 75770471
+          5e441ed3 74fe6e37 2a3e5d0e 5f5b3b13
+          793adbe8 2c718b1e 918f4496 c80966ca
+          dd791179 2c968f64 31fdf98a 3b6e8d23
+          81c73165 b75c1fe3 d877f871 e610f924
+          eef85900 2c0971e8 a78a3bbd 4b152a92
+          084f5d03 2abf5a03 d9eda339 b304713e
+          451e4bae dccd6da8 fc7c1514 75e75d2f
+          db72b991 316f2662 06f70765 66fcacf1
+          a2809901 c570a7a7 fc24d7f5 78dc862b
+          4f658591 a5843f63 8eb088c4 1d3f0fbe
+          146143c6 e2b9b0a4 a5eab744 1104ada8
+          fbc43bcb e077ba48 dc11c479 5778d06e
+          afb71e38 8ce62d3b f52f5ca8 c57ba288
+          ac8b1669 891d7547 e044d858 606d5b5b
+          78d3b14b 2eb880c5 7a12783c e1ada9c6
+          b10b16ce 695fbff9 27cc116c f438f021
+          ee04a388 f8d1c311 37713453 f8fa93c9
+          032ce3af fa6c15ba aa6a8235 3cb40140
+          10e75fe3 a9fdf17c 8ad668dc 5953a72f
+          f26419a6 a404642c 9a035324 d5e37193
+          ccdb90dc b5fff0ef 58accff2 565592c0
+          e342dcd5 d6e0e8c2 39f1dd45 479f620e
+          90460f3b 2f51c100 7bbf0c64 5d381f70
+          f7a0258a 20c071f8 285af615 21a00448
+          dc11442f 8b5cb2d7 8b136f7f 0cd9e5d1
+          3f7ef578 59623746 4bf00493 44228f1f
+          913794c5 fa574aae b83092d3 c7842f8e
+          2d9d67ef 3e54f292 487577fc 3ce70a60
+          8e8b41f6 254b6030 99f5e7cc 0a0678db
+          da50fade 2750d439 b324ee08 a2570670
+          774313aa 57ac638f 740f1673 b7075997
+          2cd2123d 7aa8f9f1 11f6514f 31982cf7
+          fa9a1ab9 1b2ac995 c0f3b7b6 8882c576
+          17fbc0d5 7e77f484 f392c5d9 2d5a4b14
+          6b4e3ffd 96280cb5 254af9b2 e5909d6e
+          dab92388 de8a568f a7a071cb 0eb41f3e
+          d683b540 add31091 75c10298 e363b4c4
+          8fe040e4 5861736c da71d391 d9539732
+          0d40022f 1c913bda 7178dac4 298e6d7b
+          6e661fb8 9ddc9e93 ec4d1410 933f1029
+          8be7002e 574fde82 fa759bd0 71a444ff
+          12064110 e75fe4b1 87b6f2d3 e57035f7
+          2078b304 cfde3f17 c9d327c3 18612591
+          c7491c10 6d48ea3e 74fc51a6 0152e4f6
+          76127861 25ee3a3b 7168d218 abb3f8c4
+          df442bd2 e9689697 e7da007b 462ab22f
+          5daa1dcf f4245874 9594a196 093caabb
+          2388bef2 a007e06d 77a0fab3 55ecb9ed
+          81626389 5ecabc99 881e980b 4134503d
+          1e2f22cf 8ac14c03 bc7b78ea 783309bc
+          30e2d084 9146f7b1 93afb30f 7810793a
+          27cfb30c 58e2a291 b97401c4 087b8f76
+          e3e46e17 4e7eba3c d85f8b20 883ea4f1
+          02683b5a 82fa8ddb d0a35e28 6e37b2af
+          b808f64c 96ef0728 93e30583 800982c5
+          f21bc5e5 e2629278 d80bbc80 d76b10cc
+          e67ba1f6 bb63229e 5c9c8b84 1ea2c588
+          d811f988 1c3e18f0 f97af4a6 f28fbf84
+          a7b119d4 2c8b20fa e2830fd4 ac5c8fee
+          ea5afdef 65099f18 6947fac2 d9b0c447
+          6b0921c1 81e031c3 debdefd0 cd87c60d
+          bf806903 12787dfa 79670f71 d1c8c183
+          9d878fde 62302192 dc9b8f45 5e257a40
+          0eb22e5f dab35164 4cd0b5ec 3f84d603
+          c5745a43 107d1875 f7fdc4bb cba0f4a0
+          cfa53ac9 267ad450 244e180d c96ea27a
+          3c4e3098 91e42a39 f104d306 f18130af
+          b30e6b81 77203f4f 749f38f9 9ec1880c
+          726b4ef4 1dcbc423 b2d2917d e585805f
+          d6df8d63 5f773536 e1e4fb9f 317147f2
+          8e20fa3a eea61654 7cb15abb 61ab8bcb
+          8db4f933 5942d83f 38a98696 003e449e
+          11039836 f8906904 12787d91 fd83b2e1
+          29af78c7 20219fdc 991f7167 8c8d40f2
+          d4093026 c46b1dec 75dfe3f7 e1e4079f
+          53dd1d41 84118ddb f7a2adf8 a8fea58b
+          407070a9 da00dd9a 92c4d601 b21d2f1a
+          8fbdc6b3 cfff2112 787d32d8 fbae661f
+          de3cf647 23f93207 e24e51eb 2b44248c
+          198ef8a9 137b766b 96094035 d3efaaa8
+          21031244 5845ef00 2a962d87 a7ad5d7f
+          17df2fc3 949a8c94 590530c7 45b0a48f
+          ecc7858f 4888f054 54de7660 58ff2524
+          f0fa1007 86e60df0 d6d4fdc9 202296dc
+          98077517 14789139 59c85c34 0706affe
+          a50af5d6 9da3bc0a 8d5bf782 ce650822
+          fcf039ba 50fec1e7 903d3d28 a6f77890
+          30653ce2 860f81c1 64a07a3c 5e449e10
+          48709f28 7b866986 6812787d 43dc099e
+          13651f18 0c813450 27332e50 d8fa6d4f
+          4b40d6d2 f930582d 3d3a9af5 393a51fa
+          e6fba095 9c20c235 7a038ed2 0a346edb
+          8980ba26 841a4fa6 b6bdf4cb c8bc603e
+          a2f27282 47b594f7 71e1254c 2be431cd
+          b0ec407e 2e09bcde 4cd1d03c 78caca5e
+          60bfd510 12779c88 3bb6109b e26c4899
+          3609b6bc 6ca6dcfc 7a8fb356 7cadb644
+          f175bac8 4b0822ac 09a066d5 467455d5
+          e8f7c264 2250b0d9 90b97016 ece9f15a
+          e2487081 aa8326f9 db5a1f24 81d78bf1
+          35d47f8f fde36af6 3293cf72 b07407b4
+          c695881d 3618f1d3 26027a47 31a7c45d
+          c3e6ed68 3f5c1abc 35471044 f8625077
+          f87d38f9 de27f075 77b388a7 93d1f9fd
+          b00dcc43 72c10498 62ac74e9 821f8967
+          931d9d3f 3f386af0 421278bd 9083a386
+          f4975dee 3fb3073a 9abc9593 85db0d44
+          e565216b c95ce6c8 86d0c5d4 eaf10ccb
+          e0bbcaca 51f95521 fb0b3a9a 25084e82
+          375c8d6d a85eb14e 137ba18f 6a0330f8
+          7c489c39 85258e83 fe9d4812 3cec1828
+          f1ae23c7 5e605a22 8a045eaf 1277f946
+          e791a31f b20f288d bc940f64 1760cf4a
+          40c6a239 106362b4 cc3bb420 34c0dfed
+          44f9a72b 10f079e8 68962078 ca0759a4
+          6bda7e10 adfb0fe9 d7e32901 08ec0dea
+          948ba8fe 995a2249 eb052788 c8655a62
+          19d31424 f07a0bae a347fe68 103182bc
+          930f14b5 df5d8411 89e34622 62c840fd
+          51648200 c5e345d5 97abe1ac 69848106
+          d6110467 0a4fdd89 f3a3fce3 d57037b7
+          8416782a 6c4d3125 25207556 01aca931
+          41914770 e1292c3e 4c679ae2 011278bd
+          80439346 5d0ed170 133898ab 4b9c1278
+          5e2076e8 4024cd9b 0e83dfa7 7f342b2b
+          683f7c04 8d3b0f53 dd1d41f0 1ab9d9b3
+          ef773ab5 a935feae 2e401475 455ef4a8
+          11489a38 1aa255a4 fe78fc60 649ae26e
+          a62d1690 c03bafe2 6efca0ae 9d07fec2
+          023ccd99 e5240b57 8f66a307 66207de1
+          2c884693 26de428a 3b467755 8d96b96b
+          2b341db5 1004b708 46c071bc 1af59bb6
+          05e7d50a 2142a07a 541b5090 3c633262
+          470cd626 e5503d1e 27040231 4c5bbcc4
+          34469fee a5db2705 9ec2b2b0 4353261b
+          ba76ec7e 4eb02085 bc910f64 37604b8b
+          43c6bce9 b0a4a769 cd494322 0a2c53ef
+          46f9672b e0ede8a4 a3598220 d40906a8
+          5dbb0b1d c74e045b a7843aae f5fb2146
+          d8913167 3aa2fa67 686b1025 899c2403
+          16e4328d f12fa635 34cd4102 ef5cfdd0
+          361b1cdb b6ff51b0 622eb921 2709953a
+          8a8c2dcc f1638622 6ad430fd 4b15a208
+          d9e942ed ba8de82c ad866022 1b120411
+          3caa953d 6e547fb5 1ace86a6 53022f84
+          6af3f961 c9ce4452 c13898e3 6d503c64
+          436e449e 15f399d6 f8b5aa39 48e09d23
+          8a67cdbe 4834e336 358c930b f281cc16
+          d5f8d1f9 489e330d 06f59c44 d1399a65
+          dfd375b2 02751bf7 d2ce1d41 10ff33ff
+          63095f77 4d0b6ad7 6cd01241 18432c12
+          ea7ae397 113b6e24 92278cd2 76006900
+          0e47be62 c6dd4c73 f4c9fe78 7d4ee015
+          cf9dd7bf 63c3fa67 59161641 aec743ba
+          cdd65697 3a673619 694cdc19 a3a3f46f
+          cd8a22dc 2c332fff 6825025e 2f5dac20
+          08e27f2f 2d4cd3b5 ec398ae6 ddfbb45b
+          f66a49c7 e9334c99 89423392 67162066
+          d800f869 178f1f3f 1110cb34 c70b4c7b
+          2491c03b 4bf85b5b 7064d122 73fbbab5
+          cf895664 93dbf181 7a1c628e b52175e6
+          24d8b2b3 00b7ceca 2a49f0b7 77a04a3d
+          7ea96da1 a3598220 4e17b811 08c8a85e
+          b119dd15 55d02dae 63c9a231 210e6933
+          a722322b 414b3ca9 1e8f0f98 e6c863da
+          e375a641 44558b90 c03bd306 8e8941cb
+          ca950f49 362c2677 e345dd05 17e1e482
+          b1881d3f 3a782c1b ea1a9b28 6a3b764d
+          bbf7a265 ef318816 32214110 2102a0c4
+          745b4717 2abf580d 6f6b1b60 32a9e36b
+          438abc88 81794899 3611a648 33cdabe5
+          08a63d16 310d728f d6589f04 de99e5d8
+          e5572e31 9af033d0 35756e50 074ec40e
+          eb8fe4a9 13209acd da31c9e9 d3f160dd
+          9de36829 6ad6ec08 7a3665d7 0441e86d
+          1eb044d0 515283fa c22d90bb ba99c893
+          42249d01 6d59899b 38168913 860767d5
+          524ce203 f639330d 720fd322 8bfb4a74
+          e91302ef e86597e5 b57cb2ec 4983043b
+          791907fc dfbabb7e 49489d3b 0dc6f8f8
+          d047b3ea ae9ed108 4f730baa 57ad87b7
+          bd4beb77 451004d1 a340c8d6 8bfa4dfb
+          d17ee848 30911442 dfaa95ec 56a4ce9a
+          8ab81139 f053eb14 7e429384 38a6459e
+          629a2483 04de77c4 575f8763 dfbb32a2
+          65d9b2a7 8d360ca1 4c890fd4 630fc966
+          44cab4f1 881cd85f bf258ad9 047f8703
+          b56b0be1 28ab8364 a5ac9a20 886f10b8
+          45b6ccb0 24b266f5 46ad31ba 5acb1b2a
+          0155134e 536a3252 674e8135 295a6bc0
+          4e228f03 d4bd04a6 45982679 8169138b
+          aa5148e0 7d5b44c1 58f7c187 7733835e
+          44019b9f 07486d41 905c301a 71e34605
+          77e742b5 44110404 98006c3d 70080d5b
+          8bb49a1a 8220886f 8a6406ba aa9a51b3
+          a610bee6 56fd7a3c 9f1f9183 072065e6
+          04082681 4699f125 f22e64da e44ea651
+          7a7513ae 5e2bf002 7e9fa1fc 9e7b675a
+          8cb893c4 1d2f6934 b4e38e98 21fd903a
+          630aa4c8 08c01ba2 258a5a77 270ae82c
+          2943edea ad08c80a f5bc2308 e2dbef29
+          3091d7ba f7189a77 ee85e276 87ee8f27
+          cb4cd899 9038612c 12c60f83 4c172eb8
+          12794c9b dcc334ca 3c55ab90 c0fb26b6
+          f3ba517a c38da9f5 6fbcf1a8 60441479
+          131fa8c7 1cb6a458 a4cf9906 537222e0
+          f69e7e8c 90bab367 36c1dbd8 829ad51b
+          e06c68a5 5bb30441 7cb71cf3 5444ac2b
+          dc0dc7b1 d2e0fa13 6a9499c7 0b29260a
+          69330b10 3d208d5a a77004d3 26b14ca3
+          3cc6b44a aeaa5948 e0f5006f 75154a7f
+          f29388fa 37df7cd0 6cc378da bde32421
+          92d5eef2 2252674f d48e3d20 2b08793e
+          6234c2df d9a50d0d 6f2fae80 6401d5dd
+          1104f1dd 83a299e5 96ad9da8 5d5d0877
+          4d5df0a8 3664d0f2 c19a958e b439d361
+          898da051 66dc042d 806994d1 4cab3cc6
+          344b94aa 5d48e0e9 2077b409 d5afbd71
+          0533dc2d 14b0f9c1 ef059226 8f40fcc4
+          31301825 fd9628ec bf1d078b d1b0655f
+          b0ee8eb2 668220ce 50e0562f 6ab5abad
+          53366ed3 12499842 5ccb574f 13d87fa3
+          f3072265 fab8e05f d128339e 44de954c
+          b35ccfb4 4bafebdd d0ab049e d2dd85da
+          bfbe3cde 22e15724 ee38e154 4b94e8bc
+          34aded80 a48e22f3 e814b398 4de83e59
+          89da75db e0ebf230 41486624 08e2cc22
+          b275a571 e741b4ee de87805f d62e749d
+          3e43f543 b05a9038 791ce2c7 0cd26667
+          13fc883c a659ee61 da659aaa 6148e0fd
+          173c5595 3879d75d c9352fbe f45bc984
+          c1e4357c a0b64431 47db91b9 6816cc29
+          c9c17e77 a16a5e4c 26789b9a 51bba610
+          8ef27a6a 894210c4 d9c93d99 c0f33b7d
+          a8fd7a3b 3a8f97f5 e0a8d60b 637c1cd2
+          e6ce4044 467cb03f 1ec1054c b3a433ed
+          f2d0c95f de952b77 76f49e24 e5a1871e
+          ea153f88 ebf001eb 91db7e76 87dd869b
+          0314b0f9 487cd463 0cf6ca58 380df1e3
+          46c1a00e fb0ed512 4514a1b0 4cb961fd
+          66d46ddc abb535a0 a3598220 ce166a03
+          64779b0b b2d381a8 7e991063 22019f7c
+          fa758705 2f635424 248b091d c74e2220
+          cbffbeb8 41843746 13fab56e df6355da
+          5a37da47 8cf048d1 e77fa459 af703db9
+          a3ddd0fc f1a773cd 12ee2271 c70fea31
+          46e2f821 482a98a0 1d6fe8d6 dd0906b4
+          1f388c86 ed07829b 7c24ee08 8238ab59
+          280bdc6c 696a3d50 86864d5b 21bb745a
+          a7b004d5 2089881d 391429d3 46d3ac5a
+          9e5c85f9 8ad5869b 2bfff6f7 1fb94b4b
+          ccbd2241 39df3f80 b7ba1215 bf79a07f
+          e59f9fb9 9729e048 72133e50 8f2fd463
+          8cf48573 b53603f0 e814ad98 4d7057d7
+          a26ec316 b89a1cda 4d3782f8 df89008b
+          b1ae605d e7b779d1 4402e2bf f994dace
+          b6714711 daf61405 7ba9841a 65e6f543
+          8cb06b53 2ea2f333 a9750a67 22cf2ce2
+          aed6e52b c7c9edad e7fde739 ef7dff9d
+          45fbed15 2fbc7473 840d05b4 7bc707ea
+          806ea3dd 868cc5b3 614e8a0f 36330e59
+          77670c8e 222bdc0a 4769ad96 5153dd1d
+          df015776 6a7f6a67 af06f652 e705a973
+          045c324b 15620bc6 f92d7903 be958778
+          2acbc5d6 0ddb6c2c 9eab0557 6a856722
+          7b25a82f d1467ec7 2b5aeb94 36a776ab
+          d69a9c04 7b5ebfd3 37615797 324fb01e
+          afdf050b e0aeff00 5e8783e6 637382d1
+          8ceccaa7 ff7c77c0 e32acdb8 f7be0653
+          46169f02 4f6e6b15 3b366d59 cc14efad
+          24ee7849 7182b577 69b3c721 66d86018
+          44750864 88a35941 8022cb68 dcb2034d
+          2c833650 4b14de45 5d13739f 8371f367
+          1c37c627 1c61ff7e 88bdf6ab e24ecd17
+          7d1e20fb d13fc03a 38ff5bfd bff1949f
+          84e1ae7b d4455a15 7871eca5 5ef81ae4
+          6f6b1bda b2f2eba1 02308009 bd4c127a
+          fcad5b6a 62d959d6 80ba0d9b 911d1703
+          89bd42de f89715d8 32d391b1 641a4ebe
+          bf8aad7b 7eaac7e3 c15598af d86cb8b8
+          e285bfee 88993deb b9b88c2c 177702cf
+          5b538d9a a7fe34a8 f2d9bfdc c98c6127
+          81c707ea 389fd861 b94856eb ee2c666d
+          9e63a8a0 ae363476 ec2d42c3 e63d503c
+          0a4dabe0 14bf1327 e216ccda 2f4444ac
+          f67bf0c6 c0f7def1 881167be a2c39c9d
+          83211f7f a42d51ec 557feab5 411d5b75
+          f48a2b6d 82af6b69 ebaaf597 32d71c25
+          5a31883e 19be920c f5666deb 81a3b0a5
+          a52265c1 4c967f86 b818a6d6 e3b1f52b
+          7ecc2874 57d6a27e e33e5abf 38127966
+          117774ee dc73286a c6ec2fa5 b8f8f3e3
+          b281f3a4 ac5ade7d 2ba2e89a 1f3d1261
+          c31d24ee f840f131 a78f8fc6 c01bbe0f
+          7b5646e8 4b155ab4 35c15bdf 88b2f73e
+          45ebe172 487444c6 9fcfb8d0 c6c2e7ee
+          a869d39f 1e56b861 55c8a3fc 73b47217
+          2f5a0c21 e09ad6ba baf07e16 de270a56
+          6da78fe0 25497503 96a46864 5fba08b1
+          63476aed 51102a88 4912bccd cd2879e5
+          5d745534 4230910d b9c807d8 52e574e2
+          ebac9fff f4d6f47b ef2b31a5 a59ff39f
+          e1bc6c18 fb5b5b8d ddc5472e 350b7434
+          cb4d46a3 a8b5c912 b296ce81 2d3d35f4
+          82a8228a 6c21f5a0 6efd16b4 1f2b0f66
+          bee42b7c 0552174a a2e74c7d 2c7ad6ec
+          25c33616 9e7f7177 6ad5ce5f b9028357
+          6dd8c47e aec5d173 a73dca7e ce43f469
+          f1836805 5c0d1da8 2fdc024f 5dbd7e7f
+          3c96c89a e2629175 f1124876 9b369691
+          e020e605 8f6a6797 3fffc2ed edab96db
+          cfc7cf70 ce059eaf be0e558f fc7ef8c9
+          471fff85 d102ca65 787176b6 a8254d19
+          8ed8a1a7 eaee42f5 bb5303b9 24a279eb
+          2e34ed2e faf75f11 5c89bb3d 111327fd
+          68c8da4d 4f0dfd7a 9daf37fe 8cece7c2
+          90351bff 1c3579f2 358a4bab 0324b858
+          cca0259c ed47ab50 bb6e1364 af474b48
+          43467a18 10d12f1d 697327b1 7f172859
+          e548e499 04dce02a 2dbbd8df da72ce4b
+          e2ceb9c0 6bfdf4a3 c88a679e bf9e29db
+          d1b47bc7 07ead16c 44762a32 97cc83c0
+          32d89e1c cd76959c 40e396dd f07578e8
+          4883377f 716157c4 b871170d dfbe6d47
+          5ff87987 6edd5a14 3171c202 f673af82
+          d6ba9b08 77b471d8 4cd3b5ee 2b46cbe6
+          1d5a421a 320b6509 ad6036b3 24773c62
+          87e5699d 04083e30 59602f7f ec8f7754
+          3dfcd008 5f437df8 0a3cb9bd 4df2d4d6
+          5f6514f0 5312779c 64304ab0 254acee5
+          eaf1845d 5fdc4912 fc1d9da8 59b5019d
+          158da0d6 149c893b 37f6dbc7 8c5e387c
+          d7ae9abe f4730fdb bea3d13e 61f4258a
+          0785f429 f281daf6 c4dbe146 c3d6dde8
+          d64699e9 f441616b 9fba0666 5db400d6
+          c4680448 e4f11103 8347b5e3 2b9e7de1
+          fa967fbd 7d4e8f6a cf99c0f3 3737a3ea
+          0f0f0d3b f9f063d7 9be82611 47115b40
+          d685b361 4b4ffb8f b79fd61b 056dfba3
+          76c55ab4 1f3d01d1 4ce28eab 85d08b43
+          b651c32e 1ebe676f 6b5ffcf9 87efd8eb
+          8a183be2 96800f47 e8d3e4c1 6183f578
+          dd954da8 5953087f 5b8796a0 ea457b4b
+          4202b22e 5e048364 026d74f0 23f28c02
+          aef536b5 5c22b7b7 896127f0 9a5e7dd9
+          5af1ccf3 d731253b 899c9a13 a796832d
+          51e2c78c d4da0584 5ccdb4ba 3b09ed7b
+          0ea0f5e0 512dbba5 9e511cf9 8a1f8dd6
+          e143ae1f b1f76045 5ffe3d86 ed3c709c
+          89d4cb98 ef97d3a7 ca076a6f cef623a5
+          a85b5bc8 12d440e8 a35a750d 140d881e
+          3c00a933 c721a050 71312f98 2c882c7f
+          f48f3fa9 7ae8c121 fed696f0 1178b2c3
+          017f67f7 12c9801f 90b8e304 05302744
+          21f7aa4b 82fdeef4 8e669900 544791d5
+          aedd0867 3d8d22e3 2b421a3a acf983ef
+          18b1af78 7738fc3a c3771d3c 621b3ef4
+          5e96a1b4 d287cb81 fbaa77c6 7c01b4ec
+          2f46c7de 22fd5bb5 4a0082c9 88b439d3
+          1199934e f578bc24 b1c159b5 0595cfbd
+          706dfd4b 7f392711 eeac0b3c d9d181aa
+          dfff36a5 fc91c7af 365a114f 1f330f9e
+          0c6dc72e ef075704 ebee149d ba735164
+          0ba40f55 cbd7a2ab a2019215 7434cb8f
+          afb80493 e9a511fb 8ffc2b9c 7eade1bb
+          0f7d20da ac2faabf 1f7dc8e1 8f5a8fe7
+          6a74a076 fd5678ea ebf58f6a d99a285a
+          2dc8bbfa 52b6de99 69bde348 e4a91b5d
+          8adbbd54 eeec3ceb faebacff 3fa87dfa
+          0963d59f 9fbfd16c c325e4c4 dc2c7748
+          99350976 b5ee4eaf bf09fb7a c028a16e
+          d57a741c 2dd5b261 821b64b6 26ac31a6
+          a6fd3a1c 7f39f67b 3dc8feb1 4efb3d89
+          b047ad19 ee3a5983 aaafd630 fd266b35
+          c57a6b9f 39360659 17cd678f 012d7cbc
+          20599152 f1e813b7 54fdf6d7 fd98c8eb
+          bb024f71 7623e057 e608065c 4fe28e9f
+          0c256a60 3f64cc9b c5b25a49 bfa1b1c9
+          888ebd07 d1b4731f fc2e3f09 3c8e5c85
+          fda7d29c 9773cbc8 e2b2b0fc 05471495
+          c03270e0 35507002 b44713fe 1882af8e
+          a365685c bf0501f5 56ad21f4 62699044
+          248e1f8d c4b1f95a c701828b 950f661b
+          e6553df7 c28f6b1f fb83b1cf 0abc9a47
+          7e1f53f1 d8133f36 5a904b9f 2a27d989
+          dd8aeccb 2f8081a9 7a5d7127 49f0b5b4
+          697577ee c60eed98 83e06591 33b49973
+          727f3ef2 70597d38 ff9a230e 1c735806
+          0dbc2310 109ae943 e740e3b1 04d5e708
+          b64ee92a 3ea6d516 eb65c406 51d06ed5
+          5a1269e2 1d4f228f 85c8ab02 81c05cc5
+          7df6aa38 ce9ac00b 78bdeaff edabd9ff
+          5c4c9f26 2f08c8bd f22298e3 627b347a
+          22c03cbc 7ac53a74 55d66837 d1084ed6
+          36196e63 72e24723 8b4f7cc9 c3efcb44
+          de4a7346 da5becf7 a67a3c1e 564113e0
+          aa6b656b dbd70838 d9472eea 8759d166
+          d3eaf104 89b25c5e 305a9057 f9c4d357
+          553ff86b 5b9f1278 01bf1f95 bfbe6768
+          c5e34faa 172bc863 f9c85d91 327d0262
+          f207326d d783abff 160b1ad6 6e446bd1
+          1116f003 d412852b 57311c34 98ccb770
+          f52b1b4d f7b0dffb ffb4771f 70725575
+          ffc77f77 fa6c2fd9 924d4f36 bd6f1282
+          40e84511 1444f0b1 3c585eea a3d81015
+          fdcbf3c7 02162ca8 204d50a4 08080a22
+          01911688 94505248 4202a467 379bed7d
+          a7cfdcff 397736fc 43c9cc10 52f6cefd
+          bc5faf31 6d37c173 cf3de77b ceb9e7dc
+          9785a55a 875c6f91 81ed8db2 fdde07c5
+          f466ef02 758b5938 66948c3a ed38c9bc
+          ae8bfcea 35e51cd5 f9fdb7ca 4c07e5a2
+          1f946e75 e7772f32 9a7e7dd5 f9bea01c
+          cd257486 82513532 fafd27aa ba9a4395
+          520ddee0 e6add2fe dc4b12ef 0ff3dc9d
+          83984969 0f8c1bf3 9d79afef 74d4ffef
+          b91bb6a4 82f513bf 6326a485 5ae08431
+          8c482a61 4acfc64d d2f9cc8b 6a409bfd
+          540cfd58 4bed7147 4be9f449 14a04378
+          8352d0f8 8b2b3fbe e3e26f4e cd7adac4
+          700978e2 721daffe f7035c3e 6770e923
+          513e798e b87c39bc 34d66af9 92b2f3fe
+          7f4b6877 a7b8589a 754eb84b 48283071
+          fc6d735f ddeec8d7 79cd59b7 e9e9e0b4
+          c9379a71 19a03638 20e4a981 6bb47b40
+          763fbedc 3ae333eb d1294321 6ffcd91f
+          146f4931 05e89c90 775cf36f 7effd9ed
+          dffceaf0 0f783b2e fa9aecba f2eaff52
+          ffd133b8 744e18a9 1a32e1bc 0f49b0ba
+          2ab76ff0 fb64c7df 96ca40e3 2ee16400
+          a7253c79 c54c242e cee5f9cc 3cbd59f4
+          e32b3f52 e5f01295 c121835f b73e1faf
+          431a973e 2aa9582c fbd129ba 89ac2897
+          711f393d b7475d90 2f3eacea c6a9c33e
+          e0299f96 54ea1ce1 41024784 bbf23933
+          a462deac 9cc35dcf eaf5d2fd ca6b928a
+          2684f6cb 41d92e26 dbfc13c6 7c6acefa
+          4d8e3e13 6ef6ea0d a9c0d489 5f31a3d2
+          48ad7042 2329d6f1 277d9bb6 49cbe3ff
+          c969a956 2b9f394d aa8f3e82 90e7109e
+          a04cd9fd fb1bcedf fe8d2f1f d03d0b07
+          34e0edbc f8c28ae6 dfdf708e fa8f65bf
+          b7132a65 51914cfc af0fe7d6 08b9dd12
+          6fef92a6 871e9568 571fcfdd 3929dc45
+          a42f503f eeaa39ab 376e72f9 038e2e0b
+          fdff7ff6 8b6b3716 34ccbe2a a5ca85da
+          e1808ca7 7ad94428 2a6d2fac 92de975f
+          b1cefecc 65f03cf6 f4932558 5b238c84
+          9d514dcc 44e20c31 cdcf0ecb 80a792a7
+          34ffeeba cfbaddf1 5384d9bb fcefa8bc
+          1ea93fff a3e2f2e5 3022d50d 94fa6cb9
+          f35e196c 6e570d1e 1b099d22 15954441
+          c3ac7fce 7a61edb5 aec2422e bcbe770a
+          0a4d331a beda34e5 096157ad 337a6ffd
+          3c9e1ad8 363df4b8 24070673 5aaa3554
+          10acffcc b9e2f6f2 a0b21378 0252daf2
+          873f7f50 65a903f6 00e6010b 78662c36
+          35158b9d a5a25d80 4b95c7f4 7b66dd2e
+          19f3c153 a468e2f8 ec87196b 054169fe
+          f71332a8 cfbba33f 7350af66 2d4fbd9a
+          8a842e76 9794c428 90ff6fc6 53cf470b
+          66d67f29 19965719 0e3be576 3025b46b
+          b7ecb8f7 c19c976a 03555532 f6c3ef57
+          83623743 0107b497 a968f454 95a53e3f
+          ac02de8e 0bbf226d b7def571 95401773
+          95f2bf12 168f1b23 55c72c16 239770a7
+          46a17d6b 3748db8a 95920847 99db7550
+          3d4986a4 a560da84 efcc58f6 dc6e0ae4
+          2da3f5f2 0a99f9e4 b3ad05b3 a65ca6ca
+          a99dfbc2 211d7832 253d1b36 59af32d3
+          67816695 4ac988c5 0ba462d6 3411b741
+          c8cbf776 212081d6 5beffaf0 ceef7e73
+          c2b00978 c981fe19 8950e834 558139d4
+          389fa9c6 c55b5a22 f5e79f27 ae5c9e0b
+          71b92439 1892a6a5 8fa49fbb e33063c7
+          48852552 fcbe0577 ce78f2d9 c7bd55d5
+          14c83b35 e623aa64 c6634fdd 5bb264f1
+          52555ebc 89d40919 4f359bf1 0135f259
+          f6b4449a 76e57674 8afaa671 1ffb9004
+          ac370451 86f93e08 3092a1a3 765f7dc3
+          67b77ffd 4b873fe0 e9b35b3a eefec757
+          54f25cc0 d5c9e36c 97d2a7b3 bb65fcd9
+          a78b4785 bc9c9666 837ed9f1 f7a532b8
+          ab85a559 873552aa 7aac34a3 e11f7b6b
+          6ae314c8 bea9f289 9ab1c8c5 295356d3
+          793be4f6 50bd6ea4 b35b76dc f72f9164
+          22fb260a 7533790a 0b65c2b9 1f12b75e
+          da652890 eff5c39b 0c874f49 f4744f3e
+          ec012fd1 d9be20d1 dfbfc460 f62e8fd3
+          9daa281e 97d41cb5 48cae64c 578d520e
+          275de857 913dbc4c bad7bf2a 6632c5c8
+          d341e12e 19924d05 d3275d30 e5de077b
+          2890ec26 df7d7f47 f1dce93f 4e84a499
+          fbc42154 a2efdfba 439a1e78 54249703
+          e2130929 9a3c5146 1e779435 d036192f
+          e7354f40 1abaeeff d717b75f 78c1e10b
+          783bbef5 75e97ee0 df5f76fb 651a9724
+          7fc39df5 dcdd8471 5277c6a9 eaa7b92d
+          cd467636 caeea79e 9324cfdd 392adca9
+          90d25f38 6bca75d3 1e7a64ad 7fdc78ca
+          2407feb1 e364cafd 0f3d5c76 e292bfa4
+          2212a544 9c71afa4 6271695f b946fad6
+          6db05edf 98f55b54 aaab39f9 5829993c
+          217d3415 212f9feb 872fd1d7 7f72bca5
+          79ee610b 78b1a61d 0df19ede 23f59422
+          57244ff3 5d4a8d26 8a0b65dc 59a789c7
+          ef939cde 97e776cb b6bbff29 b1ee5ec2
+          9d932625 222265c7 1f73dfd4 fb1ffca3
+          7ffc440a e4dd84bc 71e36386 a4ae48a6
+          e429ee19 e784bc78 dfa0342e 7d4c12fd
+          fd392dd5 ba5510d4 bb6a0355 e5e4bb3c
+          e7f6cb8c 9e479efc 8c7e3bd8 210f783b
+          2ffea6f4 3cfe9f2f a8ff88c9 5c8a3c0d
+          774955c9 025e1977 e669121c 334ab546
+          891c7a2a bf34def7 a00cecdc 45013aac
+          b352e164 8de1322f f54face7 00dffd30
+          feda9b3a 4be6cfba 241e922d 843ce718
+          dcdd22db fff68048 2ee7dd25 1212acab
+          95d1a79d 286e9f57 bfdf19f9 daa4bac4
+          17efee3d 2eba75f3 7eaf90ee 77c00bbf
+          b67166bc b3fb68fd 1fc1a5c8 c370a71f
+          9bf31852 3167ba54 2c9a6ff5 de59a9d1
+          65d7ca35 d2f6fc2a f5fd3c77 e7a89988
+          903415cf 9d71e9f8 abaedf41 81ec9fe0
+          d46932f9 ae7b5eaa 38e5841b 9211e9a7
+          449c71ef e899b99e 575e97b6 e5cf5903
+          e4ec212f 29650d73 a46ae15c 71f9dc56
+          5b8dfca4 67f17a9f 7efef37a 42ed9005
+          bcc6ef7f 47fa9f7b e9abea1f 9fc225c8
+          c77497fe a1684c9d 8cfef007 d22b07d9
+          9eea555f 14efee91 a6a58f4a 321ca10c
+          1d448591 78f949c7 dd39f9ce bb1f0ece
+          9c4581bc 078129d3 c415f4dd 9448c9a3
+          0c909c23 158fcbae c7964b68 9b1a1fb9
+          b374cbaa 2dd6c754 d59d718a 148caa7d
+          539b8d3c cbff2ef1 c73bbb4f 08ad5bb3
+          5fe7e2ed 57c01b5c f5e29458 7be731fa
+          1fe712e4 61639310 f19514ca 98334f15
+          5f799935 62ccc5d6 3bef9370 5b1705e8
+          b0190815 4696bb8b 825706a7 cfe44894
+          0360cc8f 7fd65dba 70eea5b1 90ac23e4
+          3947acab 4fb6dff3 40fa9482 6ccfe3a9
+          aff19614 cb8473cf 145f6991 d566233f
+          b97d3263 70e5bacf eb89b583 1ef09a2e
+          fd3f32b8 fa954fa8 7f741245 9f7ff473
+          779ea057 461e7fb4 144fad17 89e6b0a9
+          cfe79396 27fe233d af6ee630 6387853b
+          15423696 ce9ffdfd d1fff7b2 160ae4c0
+          28983b5f ea6fbef5 95cad34e fa4d322a
+          ed948843 6e27d576 f6efd895 3e1fcfed
+          cefe0dd1 98144c1c 2fa34e5a 229e021f
+          cfe3e56b bd704b20 d6de796a df938fd7
+          1ef480d7 fbe8c335 b1b68ed3 0c8f0429
+          fa3c0b77 43ef992d 9f3555aa 8e3b2ab7
+          e7ee5443 d4ffda26 697ef259 31986d70
+          5ab8eb2f 5d30f7fa fa9b6f79 a170c142
+          cae44086 bc5973c4 5755f1b7 4452ee63
+          16cf5921 4f3fc3dc bd7a9d6a 5b73d874
+          118bc988 258ba562 ee0c7179 5d9c8f97
+          bfcded64 4f79c5c7 0e7ac0f3 54549ead
+          feb169ac f9e79f54 5c752c35 9532faac
+          0f885b1f 8992ed40 63fddc5d 6fafec54
+          23ce787f 88027490 6454a4f2 d413ef9e
+          f4c73fdd ae679c70 e0d57ced a2feb223
+          1a7eae82 f4330c9e 9cd40e27 64e7038f
+          48b4ad5d f5d0592e 7c2aa582 9d57469d
+          798a0447 55338b97 af01cf27 a503abd6
+          7ea8e9b2 4b0f5ec0 dbf5b3cb 6470f5fa
+          b3f43f46 91e71753 85bb4045 918cfee0
+          c9e2afa9 cabe346b b53ba675 86d3c0ce
+          1696669d d4d818d6 63992ff8 c78dfe59
+          c1dc061e ba3c488a 8e385226 fde1c66d
+          a58b17fe 321a9226 429e73ee af706bb7
+          343ef888 2423b1ec df108d89 bfaa4ac6
+          9e7e8af8 2b8aad81 3af2ac4e 7844622d
+          ed53bb97 de7fe241 0b785df7 dd734274
+          77eb6cf5 8fd1d4e4 d388d13a efce23d5
+          47364859 c36c9150 38f343be fa8f0cb7
+          b43efd82 74bcf4b2 55f9e09c ce47858d
+          9d658b1a 7e5af599 cf6fa144 0e2e15a0
+          65d2f537 3c5c71da 29372762 42d7ed10
+          2eafea6f d76e94d6 ffac483f 8f67644b
+          84612999 33536a8e 5a28eea0 974d17f9
+          c6b4ea44 75acb1e5 d3cd575c 7ee003de
+          ee5f5f21 b15d6dff a3ff1196 67f3a8de
+          98e9ca53 3265a254 9f746cfa b9bb1c1e
+          e408ed6a 965d0f3f 25668aca e0242a64
+          24ca4f39 f19689d7 5dff60f1 514b2890
+          4311f2e6 35440b67 4dbb3e96 907f338b
+          e7a491b7 ea77973d 2b7d9bb6 4ad64345
+          759b9d48 48f50947 4bd98c7a 2bf3f13c
+          5e9e0dae bde28ded 6e5bdcfe e73fe5bc
+          c135e780 d77ae3f5 6362cdad f3d53fc2
+          7c4d3e05 bcb83eef ae5a469f 798a78cb
+          4a446259 260954cb 91523dcd 8ebf3f28
+          b1be4196 669dd4c0 e88d152a 6414cd9d
+          f587c205 8b982338 842acefa 6873c5fb
+          165d190d c936429e 43a8b635 de1f969d
+          ff7858fd 98c3abcc e271f194 94c8a853
+          8e93c2d1 d5d68908 c8a7ceda 8af9a33d
+          d5559f38 e001cf5b 5dfd31f5 97d7327b
+          974703c4 98baae25 011979c2 3152503f
+          5e642094 6569363d 2c6c7e7c b9746fdc
+          692d23c0 29e9ce5a 9add5071 c482cbcb
+          cff87013 05726815 1f73ac4c bae6dae5
+          15a79c7c 5d2226ec 68724ac6 536decc0
+          d6ddd2f4 f01343af 17cad23e 87425230
+          619cd49d 7cacf84a 83d66628 e45133ec
+          95c2e896 1d1f68b9 fab7ee9c ea4f2e5f
+          d47afd35 12ddbef3 0cf59797 50c47932
+          1850a33b 97df23d5 8be74bc5 c279aa15
+          09677fce 4385bbce 952fab80 f7b4b839
+          24c751e1 2e1692be 922316fe 6ee235d7
+          ac2839ee 44cae430 2898bf30 597aecd1
+          b7c612f2 0f9e8276 50c80b88 b43db3ca
+          6a7bdf68 8b3389c5 a562c13c a95a34df
+          7ab69a99 bcbc0a78 12dbd53a 6ef795bf
+          9877c002 5ef3cf2f 9f136b6c a93798b1
+          c9937497 1e0c964d 9b203527 1dab2a8d
+          d7da6e9f 31dce919 9c8e4ed9 f9c01362
+          c653f42f 0ea277e5 959f78c2 1d93aebd
+          e6dec285 8b2990c3 a8f89863 5b2b8e3c
+          e20afd96 0b966a9d 65e7d265 126ade9d
+          3de4e9b6 dce5929a 1397586d bc15f058
+          79cb9bbe 5b5dfa72 6f4d4d4e cbb43905
+          3c6f6ded 27f45f4a 25c90fc9 9848415d
+          85d49db4 447c5595 22912cef 8e55173f
+          190aab06 e63109b7 f7b26bd6 492346c3
+          9a105851 71fa69bf 2a5c7044 07257278
+          951c7fa2 4cbce69a b565271c fff3449c
+          b75c38e6 3e74ab66 baad579a fef5b8c4
+          0707b3cf e245a356 db5e77b2 7e1e6f84
+          d5e6234f 7824186b 6e39a9fd e69b7cef
+          39e075dc 7e8bc477 b79ea0fe d2024ad6
+          fef46c8c af2428b5 c71d2545 d3a7880c
+          66391245 8d04f588 b06dc54a 697b71a3
+          78589a75 50af623d 77d75876 c4c29f15
+          cc5fc091 28c34461 c3421971 ce594ba3
+          71b98ba9 74e7d08f c574aeda 246d4f3f
+          2fa6de0c e7caf23c 9e6adb8b 664c919a
+          638f146f 7180f3f1 f2a559d6 cbb48d2d
+          23775e72 f1b1ef39 e0edf8ce 85b3a23b
+          778f6679 d6fef4b2 ac6e142a 1b664ad5
+          918b44e2 7a23a499 b991304d e97d7d8b
+          343ff2b4 78fc94a1 9324e392 285d72cc
+          f593aebb f6a1d213 4fa64086 91e08c99
+          7d158b16 5c1b0fc9 0a429e73 b87c22cd
+          8f3eadda e4cd43ef 96cc74f1 4d6b26af
+          7271838c 506dbea1 da7e3345 19dabf23
+          b766744b 7db523cf 79cf01cf 37b2ee53
+          fa2f6379 d6fef4ae d99289a3 a4e6b8f7
+          895110b0 b6d5671e 2a18126e 6993c607
+          1e93587f 9823511c 354cb496 661faafd
+          f4276f2b 6ce04894 e1a6f484 93a5fe86
+          eb5f2d5a b8f0272a e4b510f2 1c725baa
+          36381949 4ad3c34f 49a4ad33 87a35312
+          e22e2c94 9a63df27 c513eaac 3e00f990
+          f4c51fef e85ad879 cf5f5dfb 1df0bafe
+          71af243a bb4e525f 554889da 3fdcf9ca
+          0aa466c9 62094e18 97fd6d15 6e972443
+          21d9bdec 19e9dbba 5bdc01ca d049e14e
+          8586d7cb 1ae65fe6 1f3fb191 02199e0a
+          e62f94c9 37ddf078 f1d147dd 904a087b
+          259dd2b7 abb6b86f 4bb3ec7e e23f561b
+          6dbde922 c3205db7 f5c1f163 a5faa823
+          c45f1664 a9361f9a e8f432ed e86d5ffa
+          dca4fd0e 785b3ff7 dfe5d19d 2db52ccf
+          da9b757c 92cb2d23 8e982315 f3675b67
+          25656e41 5c622612 d2b96aad b43db726
+          1dee98c1 7552b8eb 2f9c3fef f2c937fd
+          e1a59293 4ea54c86 73c89bdb 101efdcd
+          affd311a 937f338b e794065d ac36b965
+          f91ae95c bd4eb5d5 c9f4b3d2 9944a352
+          b1608e54 2e9cad32 9f8bb75c e4411d30
+          3c12f08d ac3b79bf 039eb7ae ee74f597
+          04e9dced 4d1f7659 3c659454 1f7d8418
+          01d53224 b21c89a2 0c6c6b94 c6879edc
+          fbb7e000 fa1d9685 8b16dd36 e5cf7f7a
+          48cf1061 f8f35456 3596ce9f f7934448
+          1a09790e 198719e9 599ca687 97cbc0d6
+          ede90178 a6865a0d d85d4585 52fdbe45
+          523ca156 5211cad0 f65c5298 e8eb3fb9
+          e7df0fbd fb80d7fb f8a392ea 1f385d7d
+          05871bdb 39e8ab0e db5b1490 ea450dd6
+          34bd8423 990f3456 23c15877 8fec7ee2
+          69897487 ac877ae1 1cd1983c 3feed2ef
+          ffb260ce fc4e4ac3 1e4a8e3f 49a6defa
+          e7e70b16 2cf8850a e8614ac4 21fdbb0a
+          78918e7e ebcd42b1 aeeeccb3 7843bb6a
+          8313c64a c5bcd9e2 f67bd970 61f790ef
+          116faca9 75dea673 3f5cf0ae 03deeb67
+          9fee8d36 b634e8bf 84a2b46b ba4bcfc8
+          54364c93 b2393354 b8cbd2f6 eba5d978
+          4cbad6ac 93f6d59b c41b1496 661d2411
+          96dd25b3 66fec85d 54bc9dd2 b097e0ac
+          b9c9893f bbec6e15 d0efa534 9cd3beeb
+          63abba5e de2a1dab 5e16331a cdfc3c9e
+          fe867852 cae7cd94 f2d91378 162f0fae
+          bfca6725 deda91f3 df75c053 df34597d
+          73051dbc 7de91b38 505d2895 f3678967
+          44c5d0b1 28927194 37b06397 343ff6ac
+          7888f5ce aa2b2989 0767cffe f5b4bbee
+          784ccf08 c186237a 7fb0ad64 e68c9f27
+          c3b295d2 70d07557 6d75f313 cf4adfb6
+          1d629a66 e6159a58 54fca3eb 6444c35c
+          f195f8ad 0900d89a 3ebcec7d ef3ae029
+          470e7d33 6c9aee4d d390cab9 b3a4b87e
+          82c86028 cbae59b7 24fafaa4 7dc54a09
+          770d0a1b 6b9c45b5 fb0fd75f fd9b3b83
+          33e730ae b7a9e263 8f97e9f7 fc75bd7f
+          e6cc8bcc 94f45122 cea0976a a33d1169
+          79ea3989 eba5da6c bb6ac361 299e3249
+          ca674d13 93bbddee 82ea9a2e 79f701cf
+          3016a9ff e5700c9b d2b37785 234bd44d
+          3c5d5cc5 c559df35 6baa3fef dfb25dda
+          5f7859bc 7e6169d6 41921169 2a9c32f9
+          1231dccd 9486bd05 a6cf9229 375dbf3c
+          1a953f53 1ace19cc 7b037aa9 76b37500
+          b2194f66 1eccc713 e2a9aa90 b21953c4
+          5bea6316 cfce0cf1 a422d1d9 83ab57ba
+          730e78a1 f56bf57a fe02f5cd 3c626fd3
+          1b5ecfd4 97cd9c26 05e3468b 44a299bf
+          dee39668 7b87ecfe cf8be983 3039d0d8
+          39e12e2a 83de0993 2e9a79ff 7debf40c
+          10f2e0f6 4f99dd85 93265da5 aeed6a4a
+          c3311dbd f56959fe 82849b5b ac363dd3
+          805eefa6 2a9a345e ca674e65 16cfce97
+          5d5de6d8 aed6a257 8e5e3426 e780b77e
+          f1fc8268 53eb58f5 cd6cbab7 213d7b57
+          505b6acd deb94b4a 542f9ecc d830e8d9
+          bb816d3b a56bc376 71f3c661 07050149
+          79c74fbc 6dd6bf96 3e1e9836 9302c913
+          c5472d91 990f2ddd e2193bfe 223560eb
+          a1449cc1 ed17e9db d62a3daf 6d123312
+          cbbcab36 96105f6d 95944f9f acda7c0f
+          3b6aed1d f2bcde9a 9a869c03 9eb7bafa
+          38f54dcc ded935e0 a93c5736 4bcfde8d
+          b20eb8cc c8e39168 6bbbb4af 5c2b6e1d
+          e7599a75 0c553556 cfb8ef6f 9707a64c
+          eba234f2 8bbaa632 fbf1475e 708f1ef7
+          2bd31416 e11c3162 5321cf23 d2f1c21a
+          09edda9d e5593cdd 00c4a570 fc18299f
+          314992bc c2cccef4 435547e4 1cf09439
+          3ae7516e 36bcc755 b8f3957a a4b47ea2
+          b84b4b33 cfde49fa e5d383db 9ba47bdd
+          36eb1538 70c82020 2ebd8151 75df4b45
+          a3bb288d 3c0d7913 278766fd ebc19b62
+          11594e69 38833eb7 b4bfb153 fa366f15
+          3316cd3c 8b178f5b 3b6acba6 4d16437f
+          1d837bbb d293710b df4dc05b 24eca0b5
+          67c7ad46 62252adc 0547568b c4b20ccb
+          bc1e89b5 b64afbaa b57b1ee1 80130601
+          a6c48d11 b53f98f3 d493cb8b 161d4981
+          e4737b10 0eb7fa6b 6bfed74c 4807a5e1
+          849b5b75 ea6e918e 55eb25b4 ab25cbb9
+          78626dbe 0bd6d54a c9f8119c 8b675f6e
+          33999c18 dbd5e4ca 1af0e2ad 2d622653
+          fa811c66 f06c7873 8bdb2515 7366887f
+          cca8ece7 deb90c09 b5b449cf ab5b98bd
+          73907844 1e9bfbd4 b2bb0293 26b33093
+          e70a1b16 cabce79e 5d659456 5da6823d
+          d7db09bd bd2ffd2c 5eff961d d63bc533
+          eea88dc5 a570f448 29993ac5 7ab407f6
+          a3375a24 5ada4bd7 4cd33b2a b304bc35
+          53c6ba13 2d6d9586 9b82b3dd 685dcfde
+          4dac49cf de25b284 3b8f5b12 ddbdd2ab
+          c25d2aca fb661d33 064849bf b7bcfcc2
+          5428d44a 6938836f fcc4e8dc 15cfdcae
+          82fd7d94 8633e815 d7ee751b 25b25bdd
+          e6de0c73 35a99418 25c55234 61ac788b
+          ddd6233e b0e30517 b7a7ac7c 6ad680e7
+          2e2999a1 7ed74389 d94f3225 523a7992
+          14d4e880 97e54e75 bb25d2da 26bdaf6d
+          b10eca84 23c25ddc f4957c7a deca1737
+          17cc6ba0 409c34f8 0b87bb3c 252597a8
+          3ad04d69 38a0bff7 89f4bcb6 53c22d6d
+          d64a4d46 f1b804ab 2ba5b49e d797d998
+          ce6cd903 9ea47763 d0e5dbaf f3165f91
+          db1a8919 c545598e 4631ac19 bec1c6dd
+          32d0dc2d 2ee2bc33 aa88bbe0 d6f9eb5e
+          5ee69f30 8943111c 26386b8e cc5ffbf2
+          7655073e a77ec9ae da3c6718 e93ea16f
+          cb764974 f5647e16 2f919440 f508299a
+          384e6818 6c1df0e6 e412f0a6 0c7d31ec
+          3442b736 574c9040 55654eb3 77b18e2e
+          ebdd85ac cc3a4322 2ceb1b36 bc72897f
+          dc78ce45 732875ed 93aa0e2c 5375e116
+          61cf64de d32b335d eb37caa0 3e32c59b
+          a14bd76f 39f2f9a5 68549d04 cb032cd3
+          da37e04d 21e0e56b c03375c0 1b9f0e78
+          c92c77a8 c72d91b6 0e1950a3 3b96679d
+          513d0cb7 fb42331e 6fa7289c 4dd5815e
+          55172e57 3f6da434 f23ce0a9 5e3cd236
+          2851d5d6 5baf36ca f4a0b5ea 337c232a
+          ac152096 696d494f d18e3593 89ac016f
+          0e01cf66 8db61a80 790b0c09 d6568b04
+          0259df3b ab67f8f4 eed95067 98e55907
+          4884e5db 0d9b373d 17a89fcc 0a8cc3a9
+          3a20aa2e ec5075e2 e3948633 f46fdb29
+          311df232 2dd3aa80 17a82c97 e2f163ac
+          c902d82d cdab76be a3b37cd5 a811a3f7
+          19f0568d 1ee1565f 54c6bb48 ed458fb8
+          8ad5c8cb 5f519ed3 ec5db4a3 4bfa7734
+          b13ceb8c 70b76cc1 f66db7f9 c74f0853
+          1ad0545d 30559d58 1b0fcb2f 288d3cef
+          f7bd22bd 9bb6a637 5b645ba6 0d04ac49
+          026fd0e0 d565b68d 7952bdcf 80672612
+          e3243dd5 071bd137 63415dad f84a8b33
+          cfde697a f76c7b87 0cee6c64 79d601f9
+          4ed5862f 181e3787 dce2cd21 6fecb881
+          85dbb7ff 321996e7 288d3cee f1f5326d
+          6748c2ad 6de967b3 332dd3a6 92aa0f29
+          91825135 2a0b5076 360d7835 fb0c78ca
+          5811e6ef eca8684c 9db84b8a b3cfe0b9
+          5c12ebec 9650cb00 cbb3792e 1e968f2f
+          dcbc69a7 af6e3485 8137531d bd2be0ef
+          5403806f a95f4529 907c9e01 1009ed6a
+          95444f6f 9665da94 f8ca4ba5 68ec680e
+          3db6277d 71a7660a 78fa0fe9 f66d442f
+          cf168e2c 137f6585 647dd998 c72389f6
+          4ee9dfde 987ee816 f91cee6e 5fb079d3
+          bf039326 c539c51a efc45b55 6daa3ab2
+          5ad5954b 288dfca5 07f2838d bb24dcda
+          6e3da2b3 efce2425 1e3d8357 57cb7129
+          f60d7853 32053c66 f0ec3638 53232d7f
+          5585780a 0b24eb83 136e97c4 d4284e2f
+          d11a5ce5 fc0dfd51 7975e196 2ddf0a4c
+          aaefe70d c3d877cf ef125547 22aaaefc
+          51d59947 2990fc64 a880176a ee946877
+          af35c8df 776762a6 eb4475a5 14540499
+          c5b367c0 1b9f29e0 4d21e0d9 ac3357f7
+          64a11a71 794b8ad2 afb2c878 f9dd12e9
+          ec96c1a6 669667f3 5822255f f55455f1
+          dc1d72a2 ea4a8faa 33ffa37e 3a4869e4
+          61c0d307 27c4cdf4 0cdee060 fa3d6619
+          3a144f41 81044654 f01c9e0d 876cea62
+          d7ee3be0 19c674e1 2d16b653 386aa4b8
+          f5db2bb2 1d8fa2c4 7a7a2436 9812de35
+          9c9fe261 f9de828d 1b9f7617 16b2ca82
+          dc86fd85 85a2eacc 0e55773e 4769e469
+          cfaf9a7f fd6aca68 674f9680 97126f51
+          a1f53e73 76d2da2d c98b91ec eb1bb776
+          fef4b707 bc758b66 8bfac33a 5674ec23
+          7dfe9d4b 3cc58592 f5392b75 5327ba7b
+          d428ae43 0c1ebfcb 4f2979a6 61c386eb
+          82d3a645 3336e2c0 5bda0655 6752aaee
+          3ca0ead0 1d14481e f6fdaa39 88b4774a
+          ac3bcb46 0b1df00a f50c5e25 af3ab165
+          1f90f245 b76d1df9 b6801779 fdb502f5
+          872cdcd9 e95a2644 8ac68d12 afde3d9b
+          6db8a51a f1786f9f bac17b78 e63e3fc5
+          e351f9b4 eaa8fb28 0aec0f55 77c2aa0e
+          7d57fdb4 99d2c8b3 80e7d6c7 a57449ac
+          af3ff346 0bfd1c9e 751e5e95 78bcecc5
+          b3e3a536 3c9eb707 3cc3eb65 8385cde8
+          0d16beb2 327107fc 22c92c77 a2db23b1
+          ae1eeb3c 2417cbb3 795611ac a5d9cfcc
+          5fb37a0b 8581f742 d5a1266b a9968e3d
+          ef025e7c 30a506f9 bdd95f5b 2686b569
+          cf5b56c8 32adfde8 0c57fbb6 80a7940b
+          5beeecd6 af4b6044 997882c1 ec432db7
+          21513582 0bb70f5a bbaa9047 e12e2277
+          cc5bf5d2 df0ae6ce 13a667f1 1ec6fea2
+          eb90aa4b 8fa93af5 3b425efe b515918e
+          6e49f6f6 a71fcacb 3073e0f6 fb255056
+          664d22c0 7682ef14 f0c612f0 eca7a0b6
+          5a5c0539 bc7f361a b5a6e753 7aa68fab
+          9c370db6 2b18dc30 f78515df 2e9cbf20
+          4681e040 507529a9 ead465aa 6ead26e4
+          e5517e57 9f68778f 24f42c9e 2bf332ad
+          9ec10b8e ac22e0d9 f332d7bd 53c02b17
+          9668edd3 b7ab86d7 e337c453 5494f9a1
+          59eb2abb 24391092 58ff00d9 2e9fa424
+          1eeb0e7f a368d1e2 dd14060e 2455a73a
+          55ddfaba aa63fd94 469ef4fc aa9bd09b
+          2c627a06 cf9df9a8 14773020 7e7d540a
+          c56637ae 7d05bcd1 c2dc8e7d 029e3ee0
+          b8bc28fd fc5db6e5 59bdc162 6040e2ea
+          c6e602e7 0975fd93 31f9d1ac a7963d41
+          61e06050 75eb6955 c77e2acc e2e44dc0
+          8b7677ab 80d797f5 a81497df 6fbd9796
+          47f06c19 f06ade29 e08d1266 f0ec13f0
+          527a8345 a9b87cbe f469c759 025e62cf
+          0c1e1b2c f2e0e2ab 017859c9 ea994f3c
+          7a75c9b1 c7d306e3 a050754b 541dbb56
+          d5b52755 9da39ee5 41c08b0f 24243118
+          cefc460b cde3165f 49b178fd 063b696d
+          7699651f 9b2c4a29 1b1bd167 e09516ab
+          8097c35e 76b75ba2 5ddd1269 ef22e0e5
+          43be4bc8 40a2abef 7325279c cc912838
+          b8214fd5 3155d7be a7ea5c88 d2c8836e
+          43f51bb1 3ed56c44 22593664 99e20af8
+          c5571460 27adbde8 4c37f29d 02de2449
+          bfcb0c76 e8e455a6 d353e86e 3d83972d
+          e0a91b39 de372089 fe3801cf eed73d2e
+          09d5d95e 39f51fff 5c4369e0 505075ed
+          7955e7ae d1758fd2 b0371de9 f46a4e4a
+          7d322fd3 ea7d181e f1140485 07f16c76
+          890da3f0 ed01cf30 2a8567f0 6cc5130c
+          a89bd09d 35dc495c b5cce170 d6955c0c
+          f3709714 d3535db9 7eea3fef fb5dd907
+          cfa44070 48e8baa6 eadcaf55 dddbccae
+          4afb073c fda84e7c 20cb3b69 4d535c7e
+          9ff51810 d7dc5e17 d88c462b b77efed3
+          ff3fe06d bbe0f3fa 378b8877 36eaecd5
+          47bfc1c2 f06759a2 5537b1ba b692d053
+          f2b0f78d 1b97706a b0fffaf2 33cfeaa2
+          407028a9 3ad7aeea deaf551d 0cd14fd8
+          b81971e9 19bcc1a1 8067640c 786e15f0
+          fce5652c d1da2d1b c413c1d6 3fdeea7d
+          23e0b55c f747affa 4d1f4563 2fbee262
+          fd0692cc 01cf30ac 1b5a7fd8 4163e39b
+          36a1ae77 5d55e3c4 3fdc7c17 a581c341
+          d5bd3b54 1ddc6cb2 506b5faa 13488523
+          92549fac 33781e8f 755c0a6c 3719607a
+          4a7d956f 043cf58b 62fd9b94 8c9daea1
+          a4375864 7ba9bcde 411b0aa7 774ec1b6
+          17db8cc9 a099885c 5779de27 7a29101c
+          0eaaee85 541dbc59 d74566f1 6cda94e8
+          2776c2e1 74c03332 cfe0192a e0798a0a
+          0806b68c f1e94db3 7bd20187 1cdb889e
+          3277070c eb06cc25 1c242311 49a81bda
+          e00adbf3 7aebd9bb d1d53bc6 fee2b737
+          531a389c 541dbc55 d74566f1 ecdbf5eb
+          be201109 679d1c70 f93ce2d5 07e9c38e
+          0af70e78 6594879d 7a7cbdc1 c22b2e8f
+          3ba7438e d3337821 22bc5d25 24e2ad19
+          f148d5f9 9fe35814 1c56aa0e 76a9bab8
+          4cd5c928 a5613f7a d22e1535 25158b67
+          9dc1132f 337876bd cc929eb4 7ba3cb2f
+          1476d0da 27dfe983 6efd0131 f40eda6c
+          77df9e80 170a3183 67d35bd5 4c4977bc
+          a5e5160a 03c381aa 8b7f5175 b2971ec3
+          9eed492a 29e98097 f56b5de2 f67a2933
+          7b2ad83b e015501e f6a2b7b0 677dfe6e
+          48321295 64384e84 b723bd3c 3baaba71
+          d4f77fc0 b9771816 545d7c41 d5c94daa
+          6e32b963 c70902dd ace85315 b2cde2e9
+          8ce7768b c7677016 9efdbc69 06af9af2
+          b0d71dea 0d0687ce c0cbbe44 6b261292
+          8a0b01cf 8e973a2e 03beb175 0fd55cf0
+          750a03c3 82aa8b49 5d2755dd eca734ec
+          daaec4ad f351b32d d3babc1e f1167a39
+          2ac55e5c 6f0d786c b2b0d3cd a9375904
+          735ca255 52f184b5 ac4bbeb3 19c36a63
+          bbe26d6d f7501818 4e549dbc 5fd5cd1e
+          1a155b36 2be93e21 9ec3aa8e cb252ebf
+          9f42b31f efde018f 255adb65 74d7d0e8
+          2bf31978 7a94a677 d19a7bee 6cd827c8
+          ebe5d991 55bb6abe f8958d94 06861355
+          2737a8ba d9cc6e5a 7bb21edb 519f8c0f
+          669be925 5ab70a78 264bb476 f3a619bc
+          62cac366 f9cee755 192fdb19 782ad145
+          62928cc6 28303b06 bc98c4fd 5326aca9
+          bbf8fb34 af185674 9d5475f3 25554769
+          5cecd8b6 2493d627 cb57a9fc 67a4cf5b
+          a505b213 3d95f3a6 73f0fcc2 fc8e8dee
+          4e11b775 c871f687 5f53f1b8 351dcfc5
+          b5e56dda 6dc6e22b 28080ccf 01487cb9
+          aaa3bc36 cf865289 a4fa2432 f7fa6fcc
+          e0f90878 36b527e0 f13e129b 053c97d7
+          ab465759 66f00c63 28e0c529 331b8ec1
+          4c53ba93 fdfd4f50 18188e54 dd5cce73
+          78366c5a 8ca1817f d65db4a6 d5c7e823
+          b958a2b5 9de2bd03 5e21e561 ab7c671d
+          9362b85d 590e3a26 e0d9f61a ebe7efaa
+          2a3a2a3e 747613a5 81e148d5 cd565547
+          5b790ecf 8683477d b2823583 97e59814
+          f5e78641 82b7df15 7ef33978 25c212ad
+          ade81741 4b0ecfe0 a5a23135 528b09f7
+          a8cd025e 4c9285f3 66be3ee6 27bfa030
+          302ce9ba a9eae846 55578978 36632dd1
+          c61399bb fd3d4bb4 413f4bb4 f6e3d93b
+          e0b10fda 66f47311 86cb9d25 c7eb19bc
+          58fa4626 e0d94dbf 0ae7af50 0c18d641
+          211a5bab 7ee8a124 ecc35aa2 8da507fe
+          5947fe7a 89d6c733 7876bbc4 ea53b477
+          c00b0811 c05e5750 8dacd2f7 66e63bcf
+          4c99eac3 2995760c 78eab381 62c030f7
+          ca505d85 1d935ee6 de23bd44 abfa1af2
+          9dedb8f7 0e78cce0 d9886905 bca173f0
+          cccc3770 72e89814 96686d77 8dfb5291
+          08337818 d6541ddd a9ea6a98 92b053b0
+          dbb38b36 997d17ad 4b053c8f 9b32b39f
+          376db2e0 a0e3bc1b 7da5ef64 53bff682
+          2d5036eb 35453ca5 c5bd850b 16eda430
+          309ca93a ba5dd5d5 416191c0 5601cf3a
+          072f874d 16d69b2c bc5ecacc 7ede3483
+          572c2cd1 da8ade64 a1475759 e97047c0
+          b357be8b 8a941cbd a87fc2b5 37521818
+          d6741d55 75b54fd7 59d8488e fd8261a8
+          80e7f350 5eb68bf0 6f9ec183 edf2b93b
+          f36b66f6 8cd4627b ce3ba2c8 ecd4fca6
+          a2b1368a 01f61890 c4b68a30 87679bde
+          3fe773f0 f6ce0bb0 233659d8 f5c25901
+          cfc87a27 2753a9f4 260baeae 9de8679a
+          1a2906d8 44a7fa44 28061b8d 2093ba5f
+          c8ed5565 8687193c bb65f8a1 4cf746c0
+          630dcf8e 973087a1 9a194f1f 68c9260b
+          5b09a94f 33c5009b d0b3cd2c d2da29e0
+          59037f33 eb260bdd 71b8dc2e 02820d2f
+          f1de018f eedf66ac 193c9791 fd390a9e
+          c1b323fd 02f70e8a 0136a197 68072806
+          bb4500fa 853c66ec 1df07817 6d9e5ee2
+          379eb580 9de8d990 4e8a0136 d1353428
+          815dfa85 442afdaa b22c733b 7a89965d
+          b4b6c412 ad6d075e 32f4aab2 6c9b2cf4
+          31297a2a de3499a3 b517fdc0 3acf34c1
+          2e74b84b 520cf918 065dd95f 8989e11a
+          1358a2cd fbab6c3d 6b4139d8 8c5eeeda
+          4631c026 7ad58765 02bb6436 eb1cbcf4
+          59783c9c 9dbf9779 ef80c79b 2cec26c7
+          5db4d681 96dcc776 bc39b962 b08b5dc2
+          db2cf236 0dbad845 6b47febd 031e80e1
+          43efa26d a2186013 7ac63941 31d8477a
+          068fa59d 7cc70c1e 30fcd0f2 c2567981
+          3a9b8f57 d5b44e6a 70f12e5a 3b6206cf
+          1923b594 355a83ad 70c50000 ef09010f
+          00c0a0c4 29868e4f cd699385 7e17adc7
+          c3311b04 3c0cbf1b 79689385 293cb20f
+          0000010f c0e1a2f2 782a15e5 183cd883
+          aeab26cf e001043c 00fb9654 b9aeeca4
+          6393d3fe b58cc280 2de8baaa eb6c9231
+          0940c003 b00ffaa5 235e8fb8 8b8a280b
+          d882aeab bacef2a0 96ad9a19 eb10fcac
+          f4337a6e 76d112f0 303c6f64 fd260b8a
+          01000002 1e000000 08780000 0020e001
+          00008080 07000000 021e0000 00010f00
+          0000043c 00000010 f0000000 40c00300
+          0000010f 00008080 07000000 021e0000
+          00087800 000020e0 01000080 80070000
+          40c00300 0000010f 00000004 3c000000
+          10f00000 0040c003 000020e0 01000080
+          80070000 00021e00 00000878 00000020
+          e0010000 10f00000 0040c003 00000001
+          0f000000 043c0000 0010f000 00000878
+          00000020 e0010000 80800700 0000021e
+          00000008 78000000 20e00100 0010f000
+          000040c0 03000000 010f0000 00043c00
+          000010f0 00000008 78000000 20e00100
+          00808007 00000002 1e000000 08780000
+          00043c00 00000c7f 1e8ac0a6 c9dce713
+          0906d44f 326474f5 e72e0f97 1800f096
+          3e44f70d ba0f89c5 f7fd457e 5fbaaf01
+          010f8786 57dd93af df789b18 567833f7
+          fd85864b 529188f5 f50000ec e943763f
+          f98cb4ae 58a9ba90 5486af34 c44c24e8
+          43087838 540c4324 311816d3 cced6bc5
+          a0cc0000 6fe43649 46629208 c772ea43
+          0cfa1002 1e0eed0d ca4d0700 a00fc13b
+          61930500 0000010f 00000004 3c000000
+          10f00000 0040c003 00000001 0f000080
+          80070000 00021e00 00000878 00000020
+          e0010000 80800700 0040c003 00000001
+          0f000000 043c0000 0010f000 000040c0
+          03000020 e0010000 80800700 0000021e
+          00000008 78000000 20e00100 0010f000
+          000040c0 03000000 010f0000 00043c00
+          000010f0 00000008 78000000 20e00100
+          00808007 00000002 1e000000 08780000
+          00043c00 000010f0 00000040 c0030000
+          00010f00 0000043c 00000002 1e000000
+          08780000 0020e001 00008080 07000000
+          021e0000 00010f00 0000043c 00000010
+          f0000000 40c00300 0000010f 00008080
+          07000000 021e0000 00087800 000020e0
+          01000080 80070000 e0c88067 5014c0b0
+          c1fd08ea 2c80f774 3fee0978 51ca0318
+          36621401 a8b300f6 5374ef80 0760f830
+          29025067 01bc177b 025e9ca2 00868d04
+          45009b49 5204c0b0 11db3be0 f5320203
+          86d7cd09 d8483f45 000c1b6f 5aa20d51
+          1ec0b031 4011c066 22140130 2ce8c9ba
+          bebd03de a0308307 0c070902 1e6c282c
+          2cd302c3 25e0f5ee 1df01a09 78c0b0a0
+          9767bb28 06d84cb3 b012040c 9780d7b1
+          77c0e319 3c607808 0d759680 9de85967
+          360701c3 23e0edd8 3be07550 26c0b0a0
+          67f0ba29 06d88cae b39ca70a 0c0f6f9a
+          c1dba43e 29ca0438 ecf4b34c ad14036c
+          66bbf0ec 28301ce8 2cb7eb8d 8017ef8d
+          ad159325 5ae0b032 ac3bb33b d1d9f132
+          85013b51 7576634a efdce385 65c0e1a5
+          b29cca74 2fbe11f0 265e75e5 7ac3e74d
+          11f180c3 38ec8a89 14cd98da 53f7dd4b
+          d864015b 5175b64d d7dd1427 38028733
+          dc89ca72 4995e95e 7923e0d5 7eed9b83
+          86cfd724 6cb4000e dfbd9910 299839a5
+          ade29cf3 280cd88a aeb3aaee 36aa3a4c
+          1f021cc6 6e4465b9 3695e9de f22e5ad3
+          d4537a9c 63041c3e 7da9686c 35c5003b
+          52757783 f0460be0 704aaa2c f7f49e5f
+          b8f6fa83 47846dee c0e1d4a2 3e2b2806
+          d894ee58 38e20738 9c014fe4 c9b705bc
+          4467e851 31794416 382cd21b 2cda92bd
+          3deb290c d8b267e9 ed59a9ea 7027bd08
+          70989892 5459ee89 b705bcba ef7dabd9
+          f0fb760a cfe10187 febe8c4b aaa07ec2
+          ab233ef9 e91e4a03 76a4ea6e 54d5e12d
+          ba2e531a c0a1ef46 5486dbae b2dcb6b7
+          05bcb13f fb95b80a 0bef54f1 2e4e3901
+          87562a2e 2dc5472e 7cacfa0b 5fa63060
+          4bbaeeaa 3afca0aa cb9ce308 1cf27827
+          3195e1fe aeb3dcdb 029e96e8 ecbe4d7d
+          51989202 0e21c39a 36df126f 6f7f94c2
+          809da93a fc98aacb afb14c0b 1cf28097
+          5019eed6 bd7fcbf5 e611d817 371b01ff
+          7af5854c b10387ea be4c482c 307eecca
+          f2d3cfe4 fc3bd89a aec3aa2e bfa4eb34
+          a5011cb2 709752d9 ed159de1 f619f026
+          fcfe063d 9b709524 25428901 87802192
+          88c996e2 231beea8 fdfa4594 076c4dd7
+          615597ff aaeaf466 66f18043 24295175
+          bffdd6ca 70fb0a78 5ae5473f 76b7ab20
+          f02ab378 c0211878 2524161c 3b7a59d1
+          a2f7bd48 69201fa8 bafc92aa d34faaba
+          cdb15bc0 41ef4424 a532db7a 95ddfef6
+          d63f7a5b c09b78e3 2d6204fc bf553727
+          b378c041 168fc9e6 b21397dc 3cf2a28b
+          290ce405 5d97559d be5dd5ed 2d940670
+          d0270922 2ab3fd5a 65b778d6 80a7959e
+          fafedbd4 0f2bcc24 47a60007 4b2a2a61
+          ff888a7f 0667cc7e 89d2403e 5175fa39
+          55b7efd5 759cd200 0e52b84b 67b4152a
+          b3fdf59d fedcfdc3 1ffef06d bf5971f6
+          4725baf5 f50d91cd 5bde2fc9 4429cf52
+          0007f8c6 4c8978aa 473e36f6 07fffb83
+          da6f7cbb 8f12413e 293e7a89 788a83af
+          0fac7a79 6e2a3450 6fd08700 07b81311
+          71f9833b 2acf3bf7 8bf5b7dc f98e6f90
+          71edeb7b 27fdf98e 97bc3555 7f346332
+          40490207 56222a8d 23ce3deb f7355ffe
+          7a23a581 7ca4ebb6 aae3d7ab babe8bd2
+          000e70be 53d94c65 b4eb7556 dbd7d7b8
+          32fd0585 f31b7e2c 1ee33633 c9c3b2c0
+          81928a49 8fb7b2e2 57bebad1 0f511ac8
+          67aa8eff 43d5f5cb 749da734 800314ee
+          921257d9 ec6f2aa3 5d91e9eb de718976
+          8fca73ff 4b22afae 7f30b2e1 b545629a
+          93551c64 a21d782f e12e2e03 ae92f25f
+          8ebbec87 bf1c79e1 b72910e4 356ba9b6
+          28b8b2ef 99e77da9 70e408c3 2d3e4a05
+          780f9292 30c4fd40 e5473ff2 a9c977fe
+          dddcef80 a7559c73 9e845f5d f76878c3
+          eb334d33 35d67089 871206f6 23dc25a4
+          c7282ebf 7ac24f7e f4d39a0b be9ea444
+          e004458b 168bb7a4 e0b99ea7 57149b91
+          c84cd587 042815e0 dd339312 31c4f340
+          c547cffe 72fd5fee 19ccf6f5 5903de50
+          c80b855f 5b77970a 79632491 9aac6e50
+          3f737940 8e37654a 7de2d261 1495fd70
+          c21597ff bcfa4b5f 25dcc151 0a172d4e
+          f9ca8b1e eb79ead9 90198e2c 52fd4701
+          1b2f805c 3b11ab0f e91797e7 b6ca73cf
+          fa4c2ee1 2ee78067 85bc8f9c 2791d7d6
+          2f4df686 0d33169a 68269225 ea067551
+          f240c611 57cc5351 b6c55539 f2c2f197
+          5d7a4bf5 172ea050 e0cc90b7 e008f18d
+          287da1ff e50d5bdc 5e99910a 454a5911
+          02b24e10 245d7edf 2e6fedd8 df97bdff
+          a48beb6f bf27e7ef 354cf3dd 1f75b7e5
+          739f9ade fe977bae 52496f91 e1961235
+          1a632c06 ec2d2529 7563769b c19207ea
+          7fffdb4b 46fcf767 9b291420 ade3b69b
+          cb377ff5 c29f1be1 be8faa90 5722043d
+          e02dc9ce 9a20e835 0ddf8aaa 4f9efb83
+          497fbafd f977fb57 ec57c0d3 b67efe7c
+          77d7d247 3f6646ba be910ac7 a6a99bb4
+          4898d183 c36f48f5 49a85baa df5356bc
+          266114fe 6ffd9557 3c5bf989 f3291be0
+          2d3aefb8 55365ff4 dd133ce6 e0b7123d
+          fd470ff5 21043d38 bd0f49aa 3e24e40a
+          fab61981 8a2b2ace 38e58e89 37ddba5f
+          7fdd7e07 bc3db65d f005e9b8 fbbeb325
+          d17f612a 129b6b18 d603b43e 66f5e0a0
+          9b52df46 514f4951 afe1f33f 178bc84f
+          27df70ed 8b95e79c 47d900d9 82dedfef
+          964dff73 c162af11 fd4ea27f e0c4a13e
+          c4cf8401 9c14ec74 1fe20af8 06dd45c5
+          6b9311b9 76c47967 df33e1da 1bdfd35f
+          fb9e03de 1e3bbef9 5569f9d3 ed933c41
+          f94c2a12 fe88198d 8d55bfed 561faffa
+          78887bc8 9b11965e 804d7fe2 ea970977
+          61c19678 dcfbd719 f7dc756d e9a9efe7
+          ad14c07e e859fa4f d9f8c9f3 2b7c41f9
+          9499887f 3c39189a 698835a3 b7e7c3b4
+          01f26652 40f48127 629d311c 37fcbef6
+          9419bc6f e4973e7b fdd82b7e b3f940fd
+          33072ce0 edadf1d2 ef4bd3af 7f37c61b
+          9425ea97 a7a8cf09 663c5e65 c6e2aea1
+          5199f10e 1fd9eb47 e070c7b8 b77f4c49
+          b9828190 b85c1bd4 aff54df8 cf58581e
+          99f5afa5 8325c79e 40a90107 48dff265
+          b2fe0367 94a8b077 a2fae531 ea73baa4
+          526352e1 88873e04 76ee430c 9f376e78
+          bd5bd4af 37aacf93 f1b0dc37 fa5bdf68
+          1ff3e39f 1ef0ff88 8312f0de 49eb7557
+          7b365ff8 ed99ea86 9da37e39 417daad5
+          a7467d46 0ffd5cdf 98c5433f ea297a2f
+          75048748 547d2243 1fab7fd1 55567d5a
+          d4a7578f 59d4e775 7523be3a ffe5351b
+          82d3a69b 14197068 855fdd28 abe7ceab
+          f70665ae fae5e4bd fa905af5 193bf465
+          a5433fa6 97798143 23be571f a2fb8790
+          fab4a9cf 6ef5e919 fa71534c f521f5bf
+          fdd5869a 2f7f2d74 28fea30e 59c00300
+          00c0a1f1 ff00ae6e 48046a1f 55290000
+          00004945 4e44ae42 6082 
+        </hexdata>
+      </glyph>
+      <glyph graphicType="png " name="Y" originOffsetX="0" originOffsetY="0">
+        <hexdata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000276 000002e1 08060000 00d237e9
+          0200008e 73494441 5478daec dd078014
+          e5ddc7f1 ffccced6 6b5c3fe0 8e767415
+          7bc102f6 de7b624b 8c2db6d8 137da3c1
+          148d31a6 98c41235 269aa2d1 d863af41
+          50419026 483be08e e37a2fdb 77df67e6
+          4e05396e 1785e366 f7fb79df 8d9e1ced
+          9999e7f9 3d75b478 3c2e0000 00b03f63
+          a07ea3ba 07fe98b1 ea07d7ef edf2ca2e
+          eacbd1ea 93a73e85 ea53a63e d9bd1fa7
+          fa68bdff 04768490 fa047a3f e6bdd8a0
+          3eebd4a7 467dead5 a74a7d96 84fcb278
+          cadb6f75 651d7020 25060cd4 c3b97ebd
+          36afbc7c 0fc32b53 d497e3d4 a7487d86
+          a8cf48f5 c9e9fd77 4fefb7bb 29310c80
+          b0fa04d5 c7dffb75 5b6f9b51 dddb6698
+          ff5c1af6 cbc23177 dfd95272 f575dbfd
+          0fa46d8f 11bbf5b7 dda255dd f3fb32a7
+          578e505f 1ea73ebb c5c3e192 7828acab
+          a652ef6d 3037fec8 46ff0406 13f30189
+          f5fe33fe c5d77189 e91e77a5 381cb3d5
+          d7ff5341 efbf939e f84763ee 09275362
+          c036d035 ff635978 d0f46297 57f6525f
+          9eae3e7b 482c3e3a e6f73b7b db117da3
+          7643a71d c1206a33 366d2fcc 7faa3643
+          73396b35 a7738efa fa5d15f4 5e2dbbfe
+          076b4b7f 7ac7e00d 76ebaebd 4a6afff2
+          f8eeaa27 757e2ce0 3f311e0c 993d2973
+          44d01c7d 73f0b821 051f5ff3 e189aa4f
+          44fd4bd8 e1f35668 4ed7d32a e43d32ee
+          be3fd414 9c731e65 046c858e f7de9525
+          279e3c4c 85b90be2 91c829d1 aeae89da
+          c6ed8819 dc684b60 ef762362 b619e647
+          73bb36e8 1eef8b11 bf3c5c72 e1799f8d
+          fced1f06 47b05b73 f9c57ae3 bf9f3d4b
+          221dd7c4 02a1899a 660d83bb 78f890a6
+          412fa81e a9802333 634e443c bf2effd5
+          9d6f145e 78316503 f4a3f5b5 57e4b373
+          ce3bc0a9 876e88b4 771cbc51 3ba2533a
+          48e13643 3e6f3374 8f6bb918 59bf2b38
+          f3942747 dff7506c 8704bb8a 8bced79a
+          5f7ae382 78a0f9ba 983f344a d3258387
+          10f8e281 8dc663d2 e9c8ce5c 1c7364fe
+          7ac44f6e 7ebee4fb 57532ec0 c681ee85
+          6765f925 974f37c4 7f73a4a5 6d5fd58e
+          64aa76c4 a0649086 6d464cb5 195dbad7
+          55a179f2 eecf3bee 8807c73c f2d8c005
+          bbd5179e bb4fc33f 9efa9516 0fedae39
+          249bd139 600b62d6 c3da6e0c c97c23e6
+          1e724bd9 7557ad2a b9f626ca 0569aff9
+          d9ff14ae b8f0a2bb b4aed693 54a0cb11
+          dd9a6a05 d23de049 3c2a1dba cf35df91
+          557473ce 21fb7f50 fed893db 2fd8ad3e
+          ef4c697f ffe31991 e6ea0b63 c1d070f5
+          30324207 2417f0c2 b1a8ac13 6fd6cf47
+          fee496bf 0dbdfe47 9409d252 d313ff90
+          8a9b6f3d c9116abb 33dcd03c 4633d8bd
+          0af411f0 62f18854 8be6fc4b c15927cf
+          287ffcdf db3ed8ad 3af70c69 7aeab947
+          251a395d 735ac3e5 00b6f659 8d48b378
+          b2ee29bb ed47770c bbe1160a 04e915ea
+          fef998ac baeadaef c7db9a7f aa39a440
+          638c0ee8 bfcd884a 8788f144 fe19275f
+          35f6ef4f 05b759b0 5b75ce19 05cd4f3f
+          f7685c22 47a80791 de15f0cd c25dbb78
+          b31f2ffb bf9bae1f 76d3ff05 2911a483
+          c6bfffd5 a8b8e686 5b636d4d 57e94ec9
+          a54480a4 c35d5013 e3cdbcd3 4fbe60ec
+          3f9e6a4a f4fd8e19 3366240a 75d2fcf4
+          b34f8a44 8f55a18e 8383816f 48d355e7
+          2818dca5 fdc38f73 753df25a d681d329
+          14a4b486 c7fe226b aebbe946 15ea6e52
+          a12e8712 01b6aacd 30241e2b f72f5d3e
+          c5ffd9e2 67f24e3b 33f2b583 9d35fdfa
+          f47ffe29 123b5985 3a06cd81 6df5a0aa
+          4e525c85 bb8e0fe7 853409cf ce3a8870
+          87140d75 7f7d48d6 dc78cbf7 632d8db7
+          e92e421d f0b5e8a2 c7e3f1d1 fea5cb86
+          06962f79 31efd433 b73ed8ad 3cfb5469
+          fecf33b7 a97fbd48 35422e4a 15d8e6e1
+          ce150f04 a774ce9d 5717ef6a 5b947de8
+          e1140a52 4aedbdbf 91cadb7e 7a5caca9
+          e197ba5b 8a2911e0 1bb419e6 86d5b88c
+          f72f5dea f27fbae8 bdfcd3cf da4206dc
+          82ce0f67 ef25512b d479294e 603b3da8
+          2e2909b7 755c1258 f9d9684a 03a9265c
+          bd7e58a0 bee11215 eaca280d 609b0c08
+          64a96c76 a195d1b6 a0cf60b7 eafcb325
+          d2d2f64b cd29c329 4660fb72 b8659fd6
+          7767dfb0 7ec68f29 0ca48cda 3ffd4eea
+          9f7cfa42 975b0ea3 34806d18 ee9c52aa
+          32da5d2a ab39920e 766dafbf 7a5eac3b
+          30953749 0003f090 eae20ed5 351cdbb5
+          60dea194 06524560 e5f23dfd ebaaceb4
+          de4a0460 1b361aa2 ab8cb69f 99d5920a
+          7615175f 20f140f0 06cdb0de d5076000
+          385c52da 39e793ef 56ff7c06 8501dbab
+          bbff0fd2 fce2abdf 76ba6422 a5016c87
+          6ca7329a ca6a57aa cce64c18 ec9a9e7e
+          f21c9504 27305a07 0ce8436a 046beaf6
+          6f7fffdd 83280dd8 5df7c24f f6eaae58
+          7ba4baaf 39220bd8 2e8d8635 6a374965
+          b633fa0d 766baebc d47c8dc5 b5c27975
+          c0c08a5b a37665fe 25cbcfdb 70d72f28
+          0fd856fd 43f74beb dbff3bcd e9920912
+          a73c80ed c6211ef5 8c7ddfca 6e5b0a76
+          f50ffdb9 3c1e0832 5a07ec88 0e9821ce
+          6075ed1e cdcf3e5d 4069c0ae dade7875
+          48d7cad5 bb325a07 6cef4643 7495d976
+          51d96ddc 16839d91 9f7bb1fa 461e4660
+          47889bcf a98c7216 149e4c61 c0ae8cc2
+          a2c354c3 3285d13a 6040c29d 5765b70b
+          fb0c7695 37df20b1 aeae53d4 37711831
+          b0a39e51 a7e4752d 5a7a6ced 1f7e4b61
+          c0761a1e 7b54dade 99395d77 ca304a03
+          189060e7 54d9ed54 33c37df1 9fe2f19e
+          6ed5879a 365a77cb 62616b3a b0231f52
+          8976cb82 8c5d274c 9fb2e0b3 760a0476
+          b2fc8423 b31b5f7a e3df4e9f 1cc5881d
+          304062d2 150bca4e fbc5e3eb cc2fbf18
+          b133f27d 87aa46c5 a084801d a8673a36
+          cf1892bb 0f8501bb 7164e74c 528dca70
+          421d30a0 030286ca 705fbc93 f2cb3576
+          9a76b0f9 5c5242c0 0e676e9e 38986280
+          dde81e8f f99aa352 4a0218d8 3e95fa1c
+          b949b0ab b9e72e89 87420712 ec8041c1
+          a7b99c53 2806d849 f3b34f49 e7bc05e6
+          6ed82194 0630c0c1 4ed3f6d9 24d8adb9
+          e14779f1 50b85834 731608c0 8ea41a46
+          0956ac1b daf8cfc7 791e611b 75f7ddab
+          772c5c32 5c67fb1d 30e0cd46 3c142aaa
+          fdc36f33 be0876ce 1c1767d7 0183e509
+          550da37f 756566f5 ed3f1e4e 69c02e8c
+          dcbc3cd5 88e4b0be 0e18e846 43241e0a
+          3b2aaebe 6e972f82 9d32a6e7 8700ec70
+          e6060a4d 7c464121 efd9849d 8c509f2c
+          8a01d821 e14e73e6 b8266f1c ecc68b30
+          62070c22 6e317717 02f60a76 391403b0
+          43e8bdcf e017618e 5d4cc0e0 e2519fa1
+          14036ca4 587d7c14 03b0c394 6e1cecf2
+          85a95860 307113ec 603339bd 1d120003
+          cfcc70c3 370e7645 043b60d0 05bb128a
+          0136924b b003064f b01b46b0 0306dd43
+          caebfd60 2785eae3 a418801d d666646e
+          1cec9c04 3b60d0e1 157fb013 421db063
+          839df1d5 60076070 e14d30a0 230260ab
+          9e419d07 1218b4b2 2902d011 0190a49c
+          8d831d87 4a0283b4 f705d804 479d003b
+          9673e360 17a33c80 41879733 81fb15c0
+          563d83bc 6d020000 204510ec 00000008
+          76000000 20d80100 00806007 00000082
+          1d000000 c10e0000 00043b00 000010ec
+          00000040 b0030000 20d80100 00806007
+          00000082 1d000000 08760000 0020d801
+          000010ec 00000040 b0030000 00c10e00
+          0000043b 00000082 1d000000 08760000
+          0020d801 00008060 07000040 b0030000
+          00c10e00 0000043b 00000010 ec000000
+          40b00300 0020d801 00008060 07000000
+          821d0000 00087600 0000043b 00000010
+          ec000000 40b00300 0000c10e 00008060
+          47110000 0010ec00 000040b0 03000000
+          c10e0000 00043b00 0000821d 00000008
+          76000000 20d80100 00806007 000040b0
+          03000000 c10e0000 00043b00 000010ec
+          00000040 b0030000 20d80100 00806007
+          000080ed c4a0086c 2caefe3f 9ef8db34
+          cdfc1f8a 0b00907c db61b61b 1a6d07c1
+          0e03f45c aa87d299 e115cd30 7a9ed22d
+          3e98bac4 02018906 42843b00 20d489ee
+          7689c3eb 51ff1eeb 37d5c523 51097775
+          13ee0876 1808e180 c8e42bcf 96acdda7
+          8804825b fec6ec4c a979f279 a978fa0d
+          717a2937 0048f7b6 63e49153 a5f4fc33
+          45da3ab6 fc8d6ea7 742d5d29 9fdcf180
+          b8683b08 761818b1 5058c4af 9ed2603f
+          c1ce6948 2c12a1b0 000096b8 d926986d
+          4720d04f 0313556d 4c88c2b2 21364f00
+          000010ec 00000040 b0030000 00c10e00
+          0000043b 00000082 1d000000 08760000
+          0020d801 00008060 07000040 b0030000
+          00c10e00 0000043b 00000010 ec000000
+          40b00300 0020d801 00008060 07000000
+          821d0000 00087600 0000043b 00000010
+          ec000000 40b00300 0000c10e 00008060
+          07000000 821d0000 00087600 000020d8
+          01000080 60070000 40b00300 0000c10e
+          00000004 3b000000 10ec0000 00087600
+          000020d8 01000080 60070000 00821d00
+          0000c18e 22000000 20d80100 00806007
+          00000082 1d000000 08760000 00043b00
+          000010ec 00000040 b0030000 00c10e00
+          00806007 00000082 1d000000 08760000
+          0020d801 00008060 07000040 b0030000
+          00c10e00 0000043b 00000010 ec000000
+          08760000 0020d801 00008060 07000000
+          821d0000 00c10e00 0000043b 00000010
+          ec000000 40b00300 0000c10e 00008060
+          07000000 821d0000 00087600 000020d8
+          01000010 ec000000 40b00300 0000c10e
+          00000004 3b000000 821d0000 00087600
+          000020d8 01000080 60070000 00821d00
+          0000c10e 00000004 3b000000 10ec0000
+          0040b003 000020d8 01000080 60070000
+          00821d00 00000876 00000020 d8010000
+          10ec0000 0040b003 000000c1 0e000000
+          043b0000 00821d00 00000876 00000020
+          d8010000 80600700 0040b003 000000c1
+          0e000000 043b0000 0010ec00 000040b0
+          03000020 d8010000 80600700 0000821d
+          00000008 76000000 043b0000 0010ec00
+          000040b0 03000000 c10e0000 80600700
+          0000821d 00000008 76000000 20d80100
+          00806007 00d82e8d 89db4d21 00043b00
+          7d8a8b68 4e27e500 5b5873e5 a5d2f4fc
+          2b0e8787 b2000876 00367f30 5503d939
+          6fa1b6ea dba75318 18f4826b d748b4a3
+          53f54628 0b806007 6073aa81 8c75fa75
+          ffd22594 0506ffed 6a181402 40b00390
+          e0e9d435 978b7200 ed09001e 44200538
+          d487a110 0000c10e 00407b02 f0200218
+          4c18b183 5d382802 806007a0 ff86328b
+          62800d98 8b41399f 0720d8e1 9b884763
+          ea7fe209 bf4fd334 4e21b027 b3a1cca1
+          186003d9 bde10e83 9c66b509 34fd043b
+          0cd26467 86bae482 1d6cc941 b0834d64
+          11ecec94 ee681308 760076d4 f3e9a518
+          600399c2 542c40b0 03d02fb3 a1cca518
+          600379ea c3cb6201 821d8004 cf6726c5
+          001bc810 a6620182 1dbe9978 2cb9cd13
+          5faca788 53663663 1e75924d 31c026c1
+          8ea9d841 df68f434 079aae25 d7768060
+          87017e46 937930d5 f7688643 749d5c67
+          43e6d4d6 308a0136 50268c2e db079b27
+          08761884 cfa56c34 6297e821 b5ba6894
+          994d83dd 088a0136 304a3873 1120d8e1
+          1b8a257f 8e1d57da 86e2e2d4 9cc6480a
+          0283be21 71bbcdcd 13bc7902 20d8e11b
+          b5fb66a8 4b66899d c321ba43 988bb519
+          cd1009ad af296c78 f421c65b 3168353d
+          f584742f 5996a3f1 f23b9b54 2c92fc54
+          ac397800 821d0630 d8253362 f7f9542d
+          d1c07ef5 af532458 59ebadba f5477994
+          0606ab9a dfdc6574 2e5d9ead b327d61e
+          f58aa3a7 b39fc4c8 414f1b03 821d0650
+          2cb92138 4dd7d583 cca5b65f 72b72a61
+          c35958c4 060a0c5a 465e7ebe 6e1ea4cd
+          8cc0e0af 52d43532 676f7443 4ffe2780
+          6087017c 48931ab1 132bd459 3d349e51
+          3bf2a8cf 188a0183 d82861e3 847d5853
+          b17a52b3 3d661bc3 640fc10e 03ddfd4a
+          66f38439 62a773a9 6dca3c1f 6c378a01
+          83d82ed2 f3e609d8 21d7d11e 10ec3078
+          c5c2e19e 51bb7ebb 54e63976 86f56154
+          dd963c9a c3318162 c0a00d0a 4e638af0
+          ea3b9b0c 06f4cce0 e8d60c4e e2d99e38
+          8d06c10e 03fc8c7e f1d069fd 7d937a88
+          7b1f64d8 f1093522 cdad135b 5f7f85b2
+          c0a0d3fe bf772554 b961b4e6 e0ad1336
+          8ae2c99d 7daada8e 58384271 11ec30a0
+          c12ed973 ec7447cf d03b9d2f fb55c1aa
+          b90caca9 ce5df5ed 33bc9406 069bb557
+          5feeeaf8 64719eee a62c6c53 a7a84e7e
+          72bb6263 128b12ec 087618c8 3e97c482
+          e1c4dbd1 cda177c3 614e9750 68b64cef
+          d679766e 6749c9ee 1406061b 6761e178
+          dd9c86a5 d3689ffa c4d17b4a 42324765
+          71dc09c1 0e03fd90 c6935803 d1b3c64e
+          571f2a5f dbca145d 9f463160 d075301d
+          8efdd53f 0a2809db e4ba2f4f 4948b28d
+          01c10e03 281a0a4a 3c12eb7f bd84d943
+          d3cd578a e93ca3b6 6d3dc517 ebea3ab0
+          73ee4794 05068dee 859f48b8 b1795fd5
+          8ab071c2 46c92ed9 a539f198 b9c62e4a
+          9911ec30 a0cf6834 2a8987e1 e2d6689d
+          c3602ad6 b6b9ce29 8ee09a0d bb2c3b62
+          1a2b9930 68ac3ae7 0c67c7bc 8593758f
+          50b9d8a9 d13737d3 1949bc63 5205bb78
+          34c23976 360e768c e5d8adb1 379fbb50
+          a437dcf5 ff70ea6e 97f5e12a dbb8976d
+          88d7595c b2378581 c1c22828 1cc7fa3a
+          3bd6254e d55974f6 3fcdaa69 d6329f38
+          237636bc c25f06bb 20e561bf 64170d87
+          9238c7ae 771794c1 9b276c2e 4355b627
+          500c183c c302da74 f5bff914 84cd38b4
+          e45e3119 8f493412 a6bcec25 b871b063
+          b4d58ed1 5cf5a67a 76c5f67f 8e9dc3e5
+          541f17b9 cede41de 1b0f850e 0eac5ac9
+          7961d8f1 adc7da35 12ebec3e 46dd976c
+          9cb0539b 61bd2bd6 61adb34b bcc62e26
+          f120c1ce 762d8530 156bebcb 170b0625
+          1e4d3062 a77a5d0e 9f578c0c 1f6566e7
+          cbadeae2 6065cdd8 c57bedb4 33a5811d
+          edb3630f 2bec98bb 70bccee9 8a364b76
+          624dc3ea 2e67523b 5ee34403 1b5ee12f
+          835d80f2 b059436f 06bb6834 f17127d6
+          9a0ac33a c78e47d4 f6e1ce6d e4171c43
+          49604733 f2f24fd7 ccd13a2a 15dbd1f5
+          cfdf4414 ef77e020 1e894ac4 1f643acf
+          5e021b07 3bae9ded 5a7915ec 02c1decd
+          13fd4fc5 aaa7581c bcf43915 f8d4f53e
+          255c5bc3 fbe1b0c3 841bea1d f160e838
+          55edb0be ce66ace6 c0e31687 d76d6dac
+          ebaf8131 a762cdf7 91930eec 960c988a
+          b53573c4 ce7a38b5 044fb2db 694dc56a
+          3ca0f67e 621da285 37d48f5b 3071d46e
+          94067694 a5d3f79d d4f1f1a2 9d750f65
+          61470e73 2ad6dc15 9b4cb3cf e1a7b6cb
+          ee1b073b 76c5daad 9157572e d2d52d31
+          6bd75282 c4d67be4 89c3652d b9839de9
+          e2736466 df14696d 25a663c0 45dbdb44
+          f7fabeab ea9f4286 036cda76 388d9e60
+          17ef7f2a 36664ec5 7676596d 0d6c6393
+          5db1b0dd d3a92ad9 60a8672a 3699265e
+          375f2343 b1a540a0 77461a1a 8ff96454
+          f1284a03 036dc93e bb66767f b2f464dd
+          2decc6b2 2133ca99 27246849 4cc59ac1
+          2f16e51c 3b7bf6ff 7b0484e9 581b06bb
+          98ea5525 38eec47a 9ae3e270 bbc5f07a
+          d5c34cd1 a5c0b577 e95eef25 b1007b9e
+          307062c1 80e86ecf 39aad5c8 a1346c5c
+          7d9867d8 e98e84ed 4b2c12b1 46ec5863
+          67df6007 bb3d9cea 6153cf9d c423492c
+          6eed0d76 e6872513 29f1d4ba a3ad6de7
+          cd1b9a9b 476160a0 2cda75a2 d1fde9f2
+          6b34179b 266c3c1e a0da018f 88d348b0
+          7e4eb38e d28a8642 accdb60f f3826eb2
+          2b96aebf 4d1fd248 975f241c 917e9fbe
+          584c0c9f 478c4c1f 6bec5247 a166183f
+          26a96360 9a8cb879 6cd20f55 a5339cc2
+          b06fb3af ab3ca7bb 8ce4f64d c4a2120d
+          0419b1b3 974d821d 6c2a1a52 0f5e12ef
+          8b357c5e 71666430 159b3aa9 de15ebec
+          fccebcc2 cc091406 b6b7853b 95e70656
+          acbe5873 4a16a561 d35ca7ea 7ec3e754
+          ed80cfea ecf75fbf 683de7d8 75064909
+          36f4f925 6b15d6d8 d9923962 d773d650
+          ff67d9e9 2eb7383c 6e2e726a c98e47a3
+          3fa718b0 fd4341ec 0ef58f62 4ac2c6d7
+          5055fe66 1be0f078 121f63a2 7edc1cad
+          4bd4b460 705d62f5 69db38d8 71e96cc8
+          9a8aed36 835de2a9 5867964f 5c395904
+          bbd4ba01 1cb160f0 f8792543 8ea730b0
+          bd2c9c32 6ea7e0da ca933443 38b9cece
+          cc113baf 577d3c89 47ece231 6b2a9660
+          603b9b4c c5326267 c7765d3d 75e18e0e
+          89854209 839d6464 88339b60 9782dcd1
+          b6b63be7 95e47ae3 615ed88d 6d1cea76
+          1e278195 ab1fd3f4 7809a561 6fd6546c
+          46ef7bc3 fb3beac4 9c860d47 24dcd641
+          a1d9ec12 ab8f7fe3 60c7e609 3b063b75
+          f5c2ed9d d6797609 c7cbcd61 7833dcb9
+          39a438d5 6e03f57f 93a22dad 3fd1acd3
+          e4816d27 dadef673 89c72709 b33a29d1
+          eceb2ea7 f5e9ff70 62737d9d 0a76dddd
+          5c743bb6 07043bfb 5f42ebed 1389a662
+          7bbb6be6 10bc33cb 4bb04b3d 0ef57f57
+          cf1f316c 2f1a606c 2b8bf698 5c1e696c
+          fa9eea40 7a298d94 c8756264 66589f7e
+          a762555b 620e1604 9b5a585f 673f6d5f
+          0d76ccd2 d92dd739 d4856b69 95a87950
+          6da227d0 5c6797e1 13575696 c4394c3c
+          1579c3b5 350fcf1f 39c28876 75511af8
+          66a16ef7 c91258ba ec57aa17 584869a4
+          4eb07365 668a233b 3341b013 eb8d46d1
+          40886ea2 fd2e31c7 9dd83ed8 9923769d
+          116b5bba f9cab044 c1cedc3c e12ec863
+          c42e55ef 0743a604 2babbeef c8c8a03a
+          c637125c b7f61cd5 a81f616e d0a13452
+          a0c55775 bec3a982 dd902c11 a72be154
+          6c2c1496 50470723 76f6d3ba 71b06ba7
+          3cecd892 f774bc82 2dea5a06 83fd8fda
+          4563e2cc c9164f7e 2e43b329 7c47383c
+          3263c1f8 09e3280a 7c5d4bf6 ddb5341e
+          0cfe54d5 2f9c5997 2ac14e55 face4cb7
+          18999989 8f3ab182 5d48422d 2dd63a6e
+          d84a6ce3 6067eea4 a0bdb767 b6934043
+          a344bbba fb1fb553 0950f379 558f2dbb
+          f749a7ec 52f486c8 0dac5e71 ff829d26
+          7b22cd4d 9407b6aa f55fb2df 6ed2357f
+          d19d128f 8da24052 e8d246cd d1ba9c9e
+          fa3fd181 f62ad899 6bb7fdf5 9de62c00
+          6c7499d5 27b271b0 0b512636 6dc7cd23
+          4fda3a24 9a68c4ae f79b9d39 39e2ce76
+          253cc608 f6a5bb65 7ad7d265 671979f9
+          4ca361ab 2a93eef9 0b8fd39c 72b2b04c
+          27b5586f 9df05ae7 d8253aea c47c4565
+          a8bd4362 517aff36 0c76751b 07bb7a61
+          0cc79e75 b1ba82e6 eea54877 121b2854
+          8fdc9995 29eedc1c 3650a436 87e1965f
+          2eda6bcf a1e1c606 4a03895b 84484496
+          4edbcf21 4eedb7ea cb4c4a24 c5729d6a
+          ddbd8579 e22ec8ed 7fc4ce9c 860d04ad
+          e53d2caf b3a5ee8d 831ddbe8 ec1aec1c
+          ea4a3634 5a6fa010 47824e76 b4670385
+          c7dc4041 b04bf5c0 5fd2316f fe9d465e
+          2e932948 7cbf1886 b4cffce8 2ed5f963
+          7d668a72 e50e113d 273b61b0 8b040212
+          686822d8 d9b07fa6 3e8d1b07 bb0e61c4
+          ceb6c12e d81e9270 47fb170f e696835d
+          545c05f9 e22b1d2a ccc4a63e c32d672c
+          39e860f3 75634cc9 a25fcb8e 38746fdd
+          2b975112 29d8da9b ebebb2dd e2cecf93
+          84e797e8 baf58e58 eb0c3b6a 0d3b06bb
+          e68d835d 0bc1cec6 57d3dc19 dbd42a71
+          7f20e1ab c5348fdb 1a8e7718 893747c1
+          e6a15f17 77c7ec59 bf5a326d 5a716843
+          350582cd ab04bf5f 961d79a4 a7f5cd77
+          1e505f66 50222978 8d55b0f3 e46559b3
+          3509374e a86067ce fe74adaf 63e3843d
+          6d724071 1be5615f e6453487 ce231d9d
+          ea8bc407 15bb7372 c45b942d f1086597
+          ea0caf8c 6b9939f3 5a4766a6 8bd2c066
+          7587d72b cd6fbc71 a3c32b7b 501a29da
+          f15759ce 9d97ab82 5d8eb51c a75f9188
+          049b9a25 d419e6a8 131b5e6a f9cae609
+          46ecec5c 393b44ba aad64ba8 55e57347
+          82f17373 3a76488e f8860e65 9d5d9a70
+          b9e4e2cf ce386bff 7824ccb2 196c4c5b
+          7ed2c913 0c975c4b 51a4ea15 ee69d87d
+          434bc455 5c6805b7 2d3724ba c4fc3deb
+          eb60cf58 176a0b75 7c11ecd4 177e629d
+          8d9f5d43 a47b438b ea65758b 1809c6cf
+          cd0d1405 7992513a 54d8cd9e 36f7474e
+          ebebaffd 6ad90927 0c0956ae a34020d1
+          f636597e daa95ad3 0bcfdfa5 ee8f5c4a
+          2445db7a 95e3dcd9 6ef10d2f 11cd95e0
+          8d132ad8 853b3bc5 5f5397f0 4546187c
+          a14ef3b8 c3939e7e d2ff45b0 9bf0cfc7
+          5b35b78b 313bbb36 dcea2a46 c2310934
+          36493c10 4c6a9d9d 57f5de0c b7c6354f
+          9387dee9 93bd1b5e 7ded32dd 303c1408
+          1cd93952 ffccb317 1a3e3989 3a207559
+          ebebf293 3c98d8e1 90486797 74acab12
+          9df57536 ecc01ba1 bcd3ce8c 7e11ec0a
+          be756e48 733ad928 69e78baa 3efeea1a
+          8924331d abc29da7 2057324b 0b2516a6
+          ecd225dc b99d72ed ea2baf9e 1cf77753
+          1e696ee5 39e78e75 396506a1 2ef5839d
+          790a82a7 a82071b0 53ed42a0 a9457dba
+          d9116bc7 1a3e1e6f fefc8b2f 075ce3f1
+          3acac6be ac7576eb 37f4bc37 3651b08b
+          4454b0cb 97acf2d1 099f75a4 d03de294
+          c2c6679f b9fdb373 ce2908ae a9a040d2
+          90f99ab9 95e79fe7 a9ffe73f 7eaa3965
+          382592da 9d397336 2763c470 31f286f4
+          1fecccd1 bad63669 af582b9c 85655b8d
+          9b073b91 2ae192da 96aaa4a5 7b43b384
+          9acc6097 7867ac9e 95255923 cbc4e1d6
+          998e4da3 8adee593 e36b9f7d ee9478d0
+          efa440d2 30dcbbdd fa86c7ff 7ea4d327
+          67f3dca7 b65844c4 579223de e222e9d9
+          4511ef37 d8055bda a4b3629d 354800fb
+          5d6ef569 ea2bd8d5 12ec6c1c ecd4731b
+          09c5a4bb b65662e6 268afe56 bfc67b9e
+          73b7b989 62681ed3 b16916ee 3c4eb9a1
+          f217778e 0bae594d 79a49148 53a3acb9
+          fe8662b7 537ea6ee 037648a7 b8a80a76
+          39e3c648 c6b06291 70824ade 7058c765
+          75d5b458 8304b065 b0abea2b d8755036
+          36ef8dab abd9a17a 5cc1fac6 c4bb63cd
+          e9d8c27c c91e57ce 746c9a31 9c32befa
+          effff861 b4ad359b d248abde 9f63fd83
+          0f5ea33b 650a8591 0697db9c 862d1b2e
+          8edc21fd 9f5f671e 73d2d925 9d95d512
+          094613be 721cf60a 764dc288 9dbd839d
+          ea6975ac a995eeda 7aab07d6 7f772e26
+          8eac2cc9 1e3b5a0c af61bdbd 02e9c19c
+          91f11872 5ef5bd7f 3a2edada c2c44b7a
+          5c75bdf2 f69f1eee 36e44aa6 60d3a095
+          0f8bf88a b27a364d 6809a661 9d4e09d6
+          3548ebb2 e5095f37 8e41ada5 af605729
+          acb6b27d 0f2dec0f 4b77d586 9ee9d844
+          4fa97ad8 bd254592 533e9ce9 d83463b8
+          44ab79f4 d19b565f 7df5e440 0553b2a9
+          2c5c5f2f 6baefe41 c9fa7bef fd99ee12
+          1f2592ea 0d41cf34 6cde949d c457364c
+          2414eae7 7b35abd9 efaead93 ceca3a6b
+          7000f6cc f2d2fbd6 89af06bb 7a61c4ce
+          f6ace9d8 356b2550 5367f5c4 fa158988
+          3b3f5772 779a68ee a7401a31 3bf05e9f
+          ec56fdf8 dfbf17aa 5c4b639f ca357e57
+          a76bdd1f fe70a1ba de7bd375 4f83673b
+          aaaafe0c 97e44c28 17c39c86 8d44fb6d
+          30a26ded d2b17a9d 4482315e 2366e3cb
+          ae3e357d 05bb5a61 c4cefec1 ce9c8e55
+          3dafcef5 d51bf5c8 b6dcba6b 5e8f648c
+          2c154f8e 8f578ca5 61b833a7 641b9e7c
+          eae84853 2353b2a9 788dc321 a97df0a1
+          5ddd865c 13a7764f 0bd1a0a8 cefa186b
+          364682a1 fedb00a7 612ddd69 5bb13ae1
+          295918d4 b12e1e69 eaaedf2c d8459abb
+          ebd40f32 6e6373d6 5b28fc51 6b134534
+          99c38ac3 11f11415 4ae13e3b 4b2444f9
+          a51bc325 79d50f3c f8a3b537 de38865d
+          b2a9255c 5727eb6e b9257fdd 5dbffca1
+          bacef994 487a74d6 cc574ce6 4e9e28ae
+          c2048712 abc0170f 85a46bdd 7ae9ae69
+          651ad6ce edbedb15 197add55 d59b05bb
+          922b2fad 513fc87e 9814e050 0f76fbaa
+          75d2a91e d884d3b1 eac13786 64c99029
+          93c5e973 b389220d 1b02738a aeead1bf
+          9ed7bd70 41062592 42c1aea6 da55f1eb
+          7bce51d7 f73446eb d283355a 3771a464
+          8d1991c4 a6094342 0d4dd2fa d9ca9e7a
+          9fd6dfa6 95b8c4f5 8c8ce691 f7dc1bda
+          2cd88dba f7814ef5 83ade637 5152f666
+          f6bcfcb5 6dd6ba09 b3479670 ff7a3466
+          bd3bb660 8f0956c5 80f40b77 6e875cda
+          face7b07 451aea29 901410f3 774be373
+          2fece171 c88d84ba f4798ecd 199bc27d
+          f714b739 0d9be8ec 3a5db78e 3869afa8
+          16878bf2 b3f3e32e b1d8da4d 2eeda63f
+          1c5b296c a0480966 966b5bb1 4afce6a8
+          9dc7ddff 3747a2e2 ccc991fc 3d7611dd
+          e560a565 1a72baa5 a8f2de3f 5cb1ee96
+          9b8705d7 aea1406c 2c5c5f27 eb7ffeb3
+          fc35b7df 7e95baae a594487a 88064486
+          4c2895cc 11a53d09 2fc1685d b4a5555a
+          97ad9070 67c89abe 858d839d c8aa2d07
+          3b910682 5d6a70a8 2cd75e51 d333cc6e
+          1e4e99c4 a993bee1 43a560b7 71120950
+          7ee9d8db f7f9e4f8 ca87ff72 5efbbb6f
+          792811fb f22ffdd4 5875c72f 8f51d7f3
+          db8cd6a5 c9f3abaa 7887c725 4507ec27
+          eee2c224 de3461a8 8e7f85b4 7cbaca3c
+          b09ccebc bd45d4a7 a2bf60b7 81609722
+          b49e6def 2d4b3f93 c0ba2a11 7782b1f6
+          48449c43 7254c5b0 8fe8aa82 a04148cf
+          70e776c8 455d8b97 ec1baaaa a4406c28
+          dc502fed b33f98e8 71c8b53c c3e9c35c
+          4253b4cf 64c9195f 9e786d9d 0a7591d6
+          366959a2 da86563f af104b81 aa5b7a06
+          e5b618ec 2a0876a9 c3708b74 566cb086
+          dbad6b9f 70d44e13 dfb01229 da7b92c4
+          18b54b4b 4eb78c5d f39bdf5f d2f2dfe7
+          87501af6 d3356f6e c6cafffb f1c5ea3a
+          ee4169a4 49ab1e11 710dc990 a2fdf7ee
+          39b72ed1 689dd390 5615ea5a 16af10a7
+          4b18adb3 3f33b3ad ef2fd899 f3b49c66
+          962ad4d5 8daa67bc 79f132e9 ae58a79e
+          fec4a376 46769614 4d551544 86971db2
+          e9d848a8 4adea5cb b7fdabd7 9e13696c
+          60e58d9d 6af7ce0e a36bd1e2 e3d4f5bb
+          9ad1ba74 7960cd99 194d861f 3e55bc43
+          8b7b425d bfe7d639 25d2dc22 ad9f7e26
+          818e206b eb52e31e 88c4fcdd 9f6d31d8
+          c502815a ceb24b2d 86c73cfa 648334cd
+          5ba42a80 68e2513b d522f854 0531ecb0
+          bd25c6b9 7669c9a5 ee9975bf fecd772b
+          6fbd65f7 70cd060a c406224d 8d52fd9b
+          7bc6adfa e1cdd7b8 5821993e 615ed5d1
+          3913cba4 60cf5d45 f7f9a4df 57089975
+          bf4397a6 f98ba579 09a375a9 427319b1
+          ec6907d7 6c31d865 ed37759d e63418b1
+          4ba9abde f38f96a5 aba4e3b3 9589d7da
+          4563aa82 f04ae13e 7ba88097 c33b64d3
+          b103d8b3 9162cfca 071efe5e e3e37fe5
+          6c3b1b68 7beb75ef aa9fdc7e 96ba6e53
+          19ad4b93 e73422e2 ccc990d2 630eb3d6
+          47f7fb4e 58abc7e6 924055b5 342ff854
+          225d51d1 78d3440a 247b95d5 73735b26
+          bcf87a68 8bc16ee2 abef3439 f2725b19
+          b34b2de6 0ed9ceca 06a9ff70 9ec4bafd
+          89df4611 358f3fc9 96d2630f 174d37e8
+          d5a569b8 73ea7256 a8b1e9d4 4873134d
+          c060aedb 3b3b2458 557d884b 971f10ea
+          d2e7f98c c71d5276 fc219239 6a44e29f
+          a0eb1253 f57a836a 035a97ad 1307a3ba
+          a92222b1 d8c79b5d eecd6b89 d832e9d9
+          3e8b1462 1e5adcbe 628d34cd 5d206238
+          12bf43d6 69c890c9 13a468ea ce8cdaa5
+          29974786 54defd9b 4b2affef e60991c6
+          060a6410 8ab6b6ca 86dfde33 7ce50d37
+          5d605e2f 4a244d82 9d6aa10b f79c2079
+          537632a7 e2124cc1 4acf8689 f98ba469
+          c1526ba4 4ee32d13 a9c21ca9 4b22d889
+          2c25d8a5 60b053cf 7ea0a94b ea66cf91
+          eeb595d6 83de7f8b 111387cf 2b65c71c
+          2ede9221 aab74719 a6e3a880 db2707ae
+          7fe0a1f3 6b7e7f0f 7dfc41a8 e93f4fb8
+          57dd76fb 053e9f9c c9685d7a 88a9d639
+          a3b45046 9c74b418 3959d601 f3fd723a
+          2554532f 75ef7f24 5db56da2 f3968954
+          6266b595 c9043b33 fd314693 82cce1f7
+          aeb5b552 fbde07e6 4699c453 b2aa1768
+          6466cae8 b34e12c3 5c694bc3 9186e94e
+          c4d0e4bc 983f785c b4bd8df2 184c0d7c
+          779744da da0f716a ec824da7 ce96c3e9
+          9691271f 6b2d9749 18ea1cba c4c211a9
+          99f981b4 ad5c676d a6a31e4f ad60178f
+          4697270c 76f158b4 4a18b14b 49d6b995
+          ead3b268 99d4bf3f 47e2e6a8 5d822959
+          d135c91c 3942861d 7980ba37 740a310d
+          195e1956 79cfefbe 57f9c3eb 8711ee06
+          49a8ebea 940dbfb9 bb60d5f5 3ffc96d3
+          2bc59448 9a04bba8 434a8f9f 2699a347
+          58ebe6fa 3d88d8ac db5587bc 71d61c69
+          9cb330b9 a34c61af 36dde108 b98697ae
+          4e18ec9c 45252bd4 0dc3885d 8a32a764
+          43ed21a9 ff68be74 7db64ac4 6524ec22
+          ea2a009a 67dbe58c 2de56cbb b46c4dac
+          29d963d6 3ff0c815 eb67fc98 93af0681
+          fa871e70 54dc7afb 291e9f9c cf084c9a
+          3c861191 fc5dc749 d13e7b8a ee72f6bf
+          aeceea91 19d2bd7a ad34ccf9 4442ad01
+          abee472a 857c7589 4b8a1b76 99bb389c
+          30d84df9 6459a3b3 a4b831ce 9aaa94d5
+          3325db20 d5afbf97 dc2e5915 ee8c0c9f
+          8c3ce304 7166f978 37499a86 3b5d9353
+          d43fa7c7 fcdd94c7 0e140b06 cde9b53d
+          d5f5b884 50972e17 5d54ddeb 9191a71e
+          238ecc8c a4425da4 bd43aa5e 7e53da57
+          6e108797 224c41aa 2288cde9 eb07f42d
+          34e4e69c 2da37629 4c779b07 175748f5
+          cb6f4bdc 48e2340b 4d136f7e 9e8c38fe
+          48757b30 259b8e9c 1e9954f5 bb3f5eba
+          f6daabb2 ac359a18 f87c1d0a 49cddd77
+          66adbee9 e68b5c1e d98b1249 970adb21
+          63ce3d5d 9cd9d989 439daaab e30e5d6a
+          de9a296d cb575975 3d5292b9 237671f2
+          c14ee4a3 de9f8414 a599af1b 0bc5a469
+          c11269f9 68be8827 c1a64773 2d87ea05
+          e6efbeb3 144ddd95 29d934e5 f2cae935
+          0ffee59a 75d75d49 badf0136 dc7397b6
+          e6d6db4f 777be57c 4a233dc4 229a941d
+          7fa8e494 8f52f576 82757526 b74b9a67
+          cf95c679 0bad1db4 1a4f6aaa 3207df16
+          6d4db033 77c60629 b714ef04 9a47a034
+          b65bdbe0 43b57556 704b14ee 34f53d23
+          4e38523c 05b9ecae 4ad33e81 2672b2e8
+          8ebdac57 d461c0f4 be1270b2 2a7f33d4
+          310e930e d75c75a0 b3c7974a d1be7b8a
+          e674260e 75ea7bfc 6baba4f6 7f1f4aa0
+          a9937575 a95d13fb d53df17e f2c14ed3
+          cce13d46 ecd224dc 75a88a60 fdcb6ff5
+          0cf127b1 6dcae1f1 48f939a7 aa9e205b
+          acd291e1 953d6afe f4e7abd6 5e713185
+          318036dc f933597b f3addf76 7ae5604a
+          231d529d 59d7baa4 fcec53c5 e1f5240e
+          7556dd1d 97f5afbc 231d1535 e2e0bcba
+          d4bd3522 12738f28 5dbedb8a ca70d2c1
+          6eb75555 f5ee9165 cd710e3d 4983d4df
+          b3bba6f5 b31552fb de6c116f 12e7d0aa
+          0a24a374 b80c3be6 5046edd2 d7f1a2eb
+          97520c03 d90bd34f 53ff7b09 05913ec6
+          5e7086b8 ccf7c026 43d5ddd5 2fbe2e6d
+          cb578ae6 a4ec529c 39a3ba68 8b55459f
+          edb66e2d a6673a36 5db29dc3 3c022520
+          0db33f96 ce652b45 5cce247e 8e2e430f
+          da4fb2c6 8ea400d3 90e19521 b57ffeeb
+          f96b2ebf 6822a5b1 fd6db8f3 a7c3d7dd
+          f6d3f39c 5e29a034 529fd9d9 2e99b6af
+          648fe95d 579744a8 6b7a7f8e 34cc5d20
+          d1608475 75a9cfaf 3eb3b62a d8f57aab
+          f727231d 06030c91 eeda26a9 7ee52d89
+          87823d87 5f26fa39 4ea78c3c f5387165
+          67518069 c8e18cee 57fbd0df aeacb8f8
+          020a633b 5a3fe3ff 64ed4f7e 7ea9e18c
+          1e4f69a4 878cb212 197ed4a1 561d9bb8
+          976548a8 ae516a67 7d64ad99 d61c945f
+          ea277ff1 c70281ff 7d9d60b7 5018b14b
+          2b9a1e97 8eb59552 f5dc6bc9 4dc92abe
+          c202293d fe08a664 d3f28611 3d1e899c
+          188f464f a730b667 251e3f36 1e0e9fa7
+          ca9b263b 1d3ad92e a78cffce 59e2f024
+          b13fc65c 57e77048 e50baf48 d7da6a15
+          0429bf94 af0ec222 9eb1a35a 76fd7455
+          c35607bb 6847c712 894913c5 98560db5
+          44435169 5abc549a 66cd492e dce9bae4
+          efba9314 eebb07e5 97860c8f 9436fcf3
+          e94bd65c 7e115384 db41f51d b77babee
+          feddb79c 1e61cd43 9a1873f6 29e2ca1d
+          92dc37fb 3cb25e85 bad6652b 54fea777
+          9d2602aa ddfd9f23 2b3bbed5 c16ecaa2
+          e55177f9 c84f543a e4a0e274 ca76ea8e
+          08b5744a cddb3325 54a73a04 491c5eac
+          a91ea379 048ab788 b63d0d3b 03ea9609
+          1e5cff97 c7afafb8 e83cca63 1baafaf1
+          8fa4ea17 777d578f f9cf30cb 9912496d
+          e6d12666 073977f2 84e45eea ea714bfb
+          824fa579 fe628976 87595797 3edad5e7
+          bffd7dc3 166f0567 61916886 c35c67d7
+          4139a659 5badc5c5 5f5b2f55 2fbe96f8
+          94f35e0e 9f57cabf 758a15f2 90667471
+          c682a1d3 a31d1d47 5118dbb0 a10ffaa7
+          46bbfd17 a9069b33 eb52fd5a ab4f6669
+          89ea201f 65b6bb49 54b80e89 b6b54bf5
+          ab6f8bbf be957575 e974af44 a53dd2da
+          f2f6d70a 76a64873 f36bf188 74d3574c
+          b76467de 3c71eb75 3435efcc 56354e46
+          12775b5c 7c234a65 e86107f5 fe021463
+          3a717864 4cf34b6f 5cbaf60a 4ee3d816
+          36dc3143 aff9f35f 2f313c32 85d2487d
+          86473d40 dfee3daf 2e990ada e99435ff
+          7e413ad6 6db0d646 234d425d 58e2de09
+          6356eff2 e182c0d7 0e763bcf 9e57e39d
+          387a7d9c a38ad332 dc853b03 523f7b8e
+          747cba3c a923504c 66b0cb1e 3f46e80c
+          a4ddfda2 4ba4fb88 fabf3c76 4bc545bc
+          edea9ba8 fabf9ba4 ea97f75c 2481f653
+          35364ca4 7e631d13 1971c211 e21d5a9c
+          f81062f3 877d1ed9 f0df37a4 edb355bd
+          87ca5386 e973b348 bbe676bd e12a1b21
+          5f3bd8b9 478f896b 6ef75bea 17eba444
+          d3b0add6 cc578eb5 cafa175f 97582098
+          78dd87aa 941c2e97 8c3ef5d8 e40fd544
+          eadc2f0e c98c0682 67863754 ef4c697c
+          7d91e6c6 f24847e7 b7557966 531a29df
+          504bd1d4 dd257faf dd12873a 93d7259d
+          cb5648e3 9cf912f1 0713b4e0 48b1ce73
+          cf346c43 fdbf137d 6bc2db22 5c5ff794
+          fac53ae8 15a4e78d a499a9be aa5a2a9f
+          7d59f514 bd897f4e 342aeee2 221975f2
+          31d6c19a f118c598 4e1c6e99 dc3e6bce
+          2d15975d 48617c0d 55b7de2c 8d4f3d7f
+          a5e196fd 288dd497 51364cca 4e385a74
+          67122f75 b5d6d575 4ae50baf 4ba0a135
+          a9fd1548 a13e80ca f1dec9e5 ab27bd35
+          73fd370e 76935e7b f753efa4 f2154cc7
+          a6712f21 1295e685 4ba5e5c3 79d64eac
+          846231c9 9932490a f7db5374 4367bd5d
+          3ad1c519 69ef3c26 b06a05af 1bfb1a42
+          d5552787 9a9acf60 c344aab7 d2e67975
+          2e19a93a c086b986 39964425 6918b2f6
+          9997a4b3 52b5eb5a 9c29d8f4 bb67daf4
+          0cdf2bde 899313de 2c09839d 6f972911
+          3d33e32d 89b13b36 9dc35da4 ab5bd6bf
+          f2b6046b ebad9e63 ff3760dc 3a9d61f8
+          b1878be7 f3235008 7769c3e1 96215d1f
+          2fbe64dd b55770f6 da56d870 d7cf4b5b
+          5e7aed0a c32dc328 8d146e9f cd590c5d
+          b3d6d565 8e1ea12a d7245eca aec25ffd
+          bbb3a4d5 5cef1c23 d4a5631b acee9bf6
+          706ddd13 c9f5af93 10aead79 4afda26d
+          dc4ce9cd 5fdf2055 cfbf9a5c 4a8bc5c4
+          999521a3 cf3e590c af8729d9 74a26a95
+          5857fb2e 750f3c34 630dbb64 93b2fe27
+          b748f59d bfba36d2 dc78a0a6 53d3a66e
+          aa538f87 439382dd 779182fd f64eeee7
+          b87ad6d5 d5a86017 35d7d571 77a4df6d
+          1394a877 f2d879e3 9f79b172 9b05bb71
+          4f3ebbc2 3ba97c91 fac51977 49e31e83
+          a975d94a d9f0da3b 223e5f12 3d828864
+          968f92a1 07ef2fba 93f57669 75bb38c5
+          190d848f edfce883 13288dc4 ba3f5d74
+          78b8ade3 34dd251e 4a237543 9dc95394
+          2fa5271e 25ba795e 5da20d13 ba2ed1f6
+          0e6b8d73 a0b18532 4cd75b27 264d466e
+          ce53997b ed936cdf 3ab1aca9 07c48cbc
+          dc97d42f de4a11a7 b7980a6b 75b3e648
+          c7d2e5d6 9a8f64c2 5dd1a107 4aeee4f1
+          a2e99af0 d69bf4a1 bba52058 51f9a3ca
+          9bae616a b11f3577 dfe1689f 35f77287
+          4bca288d 946e9cc5 e1f3c8c8 538e1357
+          de106ba3 59426e97 ac7ff94d e9acacde
+          a4838d74 ea255bb3 ef4da19a 9a9792ae
+          7b93fdc6 50cd86a7 d42fbe81 1b8b9b2c
+          dcde2555 cfbf26b1 60208924 181387c7
+          2365271d 2ddec23c f5354598 36b78a2e
+          7ab4a37d dfda7bff 70e3baeb aea240fa
+          50fdb3db 64fdcfef fc41a4ae fe08cde0
+          f08a940e 752e8794 1e3e5db2 278d5795
+          68126fea f47aa4e1 ddd9d238 7f51cff3
+          44db9b9e f74e50ba 32268f7b a5fc91c7
+          dbb779b0 1bf3e05f 1a332695 bf6afe26
+          1435e1ce dc9955f9 ec2b568f 3271af20
+          24eea125 32fce843 c599e991 7884224c
+          9b5bc529 8e582876 66cb8bcf 1f4e696c
+          aefdbdb7 f78bb477 5eaa7b25 93852e29
+          da30f7ee 75c89950 2e85d3a7 f68cd425
+          bad68621 5dabd748 f51bef49 b43b4821
+          a6715bab 6e977a4d 8fdd9f7d f0a149ff
+          b4a4835d cee1e63b ecb447d5 6fd2c8a8
+          1dcc7ba0 71ee27d2 327781f5 7a9b8482
+          41c9db73 5729d873 4acf7abb 2845982e
+          74970c8d b4b4dd5a f5e31f1a 94c6976a
+          7ef72bc3 bf74d58d aa7cca09 75a99cec
+          447cc38b a5ece463 d4b3e04c fcfe6d4d
+          9398aa2f 2b9f7b55 422d6d4c bfa6f3ad
+          139458e6 e4f1f347 fefade55 5b55e76e
+          cd378fb8 f39e4f33 268dfb24 1ea21a82
+          aa9f2231 a97ce90d 09d6d426 f5560ab3
+          822a39f2 60c91c39 fcf3fa0e 6991ec44
+          8bb6b74f ddf0cb5f fdb0ea96 1b290f31
+          8f36f985 54dd3ae3 c2504d9d 3905cb6b
+          c352b561 8ea87e6f 8647ca8e 3b5c3ca5
+          c3540737 8903610d 87b5aeae 63f5ba9e
+          5047b04b 4f3da375 8dbacff9 ef9ca38e
+          ddca2a77 2b0c39ee 44718f2c 7d5cddac
+          0d943acc 1bcf5fdf 22eb54cf 321e8d24
+          0e77a1b0 b8f2f3a5 ec8423c5 9d9f6dbe
+          d018e972 ab18e254 d9fef2fa 471edc9d
+          d210697a ea5fe5d1 4eff65ba 4fb2e8e1
+          a468a88b 9a1b881c 527cc03e 923365b2
+          48772071 1de9744a d387f3a4 7ed65c02
+          5d9a8ba9 3e40c6e4 f1cb86df f6f357b7
+          be2fbd95 c2b535af c5443ee5 a6837503
+          19222d9f ae949a77 6625aeb4 cc1ff7fb
+          2573d278 29993655 0caf5362 acb74b9f
+          7bc52925 f168fca7 eb6fbf35 adcba1e6
+          b777ab7a b4f12655 1e3b11ea 5234d499
+          1314ba26 43268e95 e2c3a7f7 4ebf263e
+          da245857 2f55afbc ad1a752a c6b4bf87
+          22d2ee19 3beab9dc 134e6edf da9fbbd5
+          c16ed8cd b775654c 28ff8f4a 936d143d
+          acbca6ee a29ab767 49c7ca0a eb44f584
+          8221293c 703fc9dd 65a2680e ceb74b9f
+          64277aa4 b5f3c89a dfdc95d6 af1bab7f
+          e4c1d383 d5756769 4e717153 a468a31c
+          36cfabcb 93d2138f ee796558 38f1a2e2
+          a8eaf4ae b3ceab6b 13f647a7 7ba36a1d
+          20f169b8 b6f65f5f afaadd4a f9677e4b
+          9c45b94f 4623b288 513b7c7e 1346ba82
+          52f9d2eb 126eef94 847308d1 a8387c5e
+          1976dce1 92595ac4 2ed974ca 762acc68
+          86ebe60d 77df393a 2d43dd9f ef2b8bb6
+          76dea2ca 2187d1ba d40d75ce 6c8f0c3b
+          62ba7847 9759b314 09db4a87 4336bc35
+          539a17ae b4664190 de6221e9 ca9838ee
+          e5a1d7de 54f7757e be63c68c 195bdf8e
+          3b5ddd81 4f97e446 9a5af6e7 65d5b0ee
+          09d54508 3674482c 12b4a61f 343d419f
+          211c561d 8402711a 8674ae59 ab826158
+          349690a7 4527201e 0ce7b4bc fa569e91
+          e9792e6b ff03d3e6 af5ef7c0 1f65dd35
+          57df1969 e93c46f7 b0612225 439deaa4
+          3a3c2e29 99beaf94 1c76900a 75491c55
+          e2d0a575 d132a97c e175556f 327d411d
+          29a29ad1 4559bbed f4e311bf faedd79a
+          19fd5a03 be05e77e 47dc2387 ff2b1296
+          c58cdae1 8b9bc925 52fffe3c 695eb0a8
+          7737573f 3787f963 9d5d92bb d76e5238
+          750f5519 3a390225 5d385486 77c9d9eb
+          6ebcf9c4 74fa6b57 ffe2f683 6281d8b7
+          759f3819 ad4bc554 d7f38fec 09a3a4c4
+          5c57677e 9dc4ab76 02b50db2 ce3cf09d
+          7575106b c4b73363 c2b8170b cefbeeba
+          afdd167f dd9f5870 f6b93519 e3ca5f32
+          ff105c0a 7cded330 3f952fbd 23ddeb37
+          7c19e0b6 78075b35 9f144fdf 5f725465
+          184f627d 3152a517 202e6388 e76775f7
+          dd5b920e 7fdd86c7 1ef5485c fb85e662
+          0a3655c5 c222bea1 f932eca8 83c5c8c9
+          b20e66ef bfbed424 1e8948e5 0baf4977
+          4d333316 e819ad0b cb32eff8 d18f159e
+          ffdd6f50 bd7e4d85 175e2cde 4963ffa1
+          fe10acb5 c397f7a5 aa9c02f5 6d52f5c2
+          9b12e9ea 4ebcb624 1812677e 9e0c3bf4
+          40f10dcf 67976c1a 5560317f 60caea2b
+          7ef0c3ba 07ff94ea a14e2a2e bcf09a50
+          6dc3be9a 416d99aa a1ce95e3 93a1874d
+          b376fd8b 55f72598 b150ea66 cd95c6f9
+          2bc4c182 2688355a e7cf185f fe7aeec9
+          a7adfd66 fde66f60 c8712756 fac68efe
+          8ffac374 7049f039 8757a469 f16aa99f
+          3d47753f a2892b38 7f403277 9a282507
+          ed274686 dbaa2491 0654ed63 78e4e255
+          975d99d2 0bedd65e 7dd96ef1 98dcacbb
+          c4c5685d 0a36c6e6 79754e43 0af7dd5d
+          0a0ed85b a423c15b 377bebc3 9625cba4
+          eabf6f8a c3431942 3e1fad5b e0db75a7
+          bf155d78 c937ad5a bfbee24b 2e97cc3d
+          a6fc5dfd 613ea01f 8a2f6b3a b12aabf5
+          afbe27ad 9fadec3d d429c194 6c282c05
+          fbee2505 7beccc11 28e95599 65b8321d
+          f7343cf6 a83715ff 7acd4f3f e9d6bd19
+          776b2ec9 e462a766 5d67d655 43268e92
+          a2830ff8 b23e4b20 d8d02495 cfbf21d1
+          6034e1f1 9f480fb1 88b4fbc6 8e7922e7
+          b023576e 833ef337 9335ed90 7a4f71e1
+          bfa3ddd2 46b8c3c6 9dd25828 a67aa46f
+          4b405562 09ef8d70 58f40caf 941c7a90
+          64950fb7 4edd469a b48d91e8 3ecb2fb8
+          f0070d7f 7face705 e9a912ea 9e794a56
+          9c71f605 91c69643 349d93c9 52b23136
+          df0e303c 5f861e31 5ddcc38a 45020976
+          c1eabac4 d4f754bf f99e7457 3799c7ff
+          003da375 21f9207b ffbdff55 7ce915df
+          f897fbc6 954dc915 3f9011bf fcc5d3ee
+          51a3dee1 3c326c72 73b9453a d7d649ed
+          bbb325da e9b72ab5 7e9360b7 5f3ca3ca
+          64e8b4fd c55d9029 51c25dda 546a2eaf
+          fcfcb3f3 2e38d93c cf2b55ac 3cf3ccbd
+          34436668 4e8e3649 c90ec9e7 ebea0e51
+          9d51f395 616d1d09 979d989b 25eae7cc
+          979a990b ad252b80 f54ed86e 69f39414
+          3d93b9cf d46df2ba d66dd28b 2cfccec5
+          6db9474c ff673824 8d8cdae1 cb9a4fdd
+          60aaf2aa 99394f9a 172e3147 661287bb
+          8e4ec9dd 7b572939 701f7138 3902258d
+          385c6eb9 b7f9a9a7 26a6c25f a6f595ff
+          0e33f232 ef57c16e 28973605 ab3673e9
+          b0e190c2 a9bb49c1 7e7bf6ac abebafed
+          b3025f5c da57aeb6 ceab33d8 2c81cfef
+          a588ea20 8c1af5da c83b7ff1 44f1153f
+          d826bfe6 369b1ef0 ee3ce53f de91652f
+          a83f24ab a3b07167 c43abcd8 5c6fd755
+          b9be67ed 78a2f576 b1b814ee bf97e4ed
+          36be27d8 b1e03c3d ee155dca 3e3df3cc
+          df373ffd 74819dff 1e6d6fbc 9ab9f4d8
+          e3ef88b6 75ee4547 37353bac 66bd9433
+          69b4141e b0af88d3 e87d176c ffc1ce5c
+          92b2fe95 f7241688 59752260 0a87a43e
+          ff84a39e 28fcce45 eddbead7 dc66b757
+          c9d5d7c5 f24f3aee 31f587ac e0526193
+          9b4cd57b fec60e15 eede9570 736bffa3
+          7626f308 94a22229 993e5532 4716b14b
+          368db87d 72e4a767 9c7153f3 33ffc9b7
+          e39fbffd 9d377d9f 1e79cc35 865b2e60
+          02363559 e7d50dcf 9761871d 249e11c3
+          ad5dfdfd 5780ba44 bbbaa5f6 9dd9d2b2
+          72bde8ec 82c5e77d 8498c4bd 23ca5ef6
+          8c9bf0f2 366d73b7 69a53cba fc3d6fe9
+          b0a7d51f 36c825c3 c60c5fcf 11288d1f
+          7f22f160 30f1946c 5797648e 2f57e1ee
+          00ebbd8b 84bb74a9 e9ac7077 e392d34e
+          bfa9f9d9 676d3572 d73ef3dd 8cc5871e
+          71a5e191 9fb15522 456f4f55 0f3932dc
+          32f49003 246be749 eaa27726 5e57178e
+          48e3bc85 b2fedd79 e65a5266 20f08570
+          50d6149e 75cae325 575dbb4d 33d336ad
+          7e865e73 83147efb 8cc72241 f9884b86
+          af36d8e6 ba92aa97 de91d665 2b249ecc
+          1128aa42 ccdf6317 29dc7b57 31df43c5
+          1128e973 af787c72 d392534f bdb1f9b9
+          e76c11ee dadf9fe9 5b34ed90 2b9d1eb9
+          8be9d714 bd2dcdfa 477348d1 7ebb4afe
+          5ebb25de 016bd66f eaffdb57 55a87aef
+          4d71b2ae 0e9bde4f 21cff0a1 4f388797
+          bdbdad7f ed6ddeaf 74160f5d e62ac8ff
+          7b2c245d 5c3a6c52 cfa9bb2d 161559ff
+          faffc4bf be26f194 6c342a9a cf2b8507
+          ec234326 95316a97 86e16ef1 29a7dcd8
+          fcfc7363 06f31fb5 73d6fb25 8b0e9a76
+          a9cb2bbf 24d4a5ee fd68aeab cb9d3246
+          8aa71f20 bacf97f8 681e5d13 7f5d8354
+          bff9be84 3ac3acab c326c241 995bf2dd
+          f39e1cfa 831bb6f9 afed9831 63c636fd
+          05b3a61e 28cedccc f56d1f7e 5c1e0f74
+          eda43a2d 5475f8b2 ae738af8 eb3b55a6
+          8b4856d9 7055417a fa3fd033 1c519d85
+          4271ba5c d25eb15a 225d61de a998469c
+          2e3960c3 e34f4ece de73b725 317fa0d6
+          595428da 206921bb 162c90d0 da8a310b
+          0e38f00e 15ea6ee4 6aa52eb3 53e92d19
+          22234e38 4a32c697 5b4b45fa 9d7170e8
+          12e9e892 9ab7df97 ba394bc5 e9a30cb1
+          c9fdd465 e4e6dd95 7bd491af 644e3d60
+          dbb7b3db e30f5d74 f1e5f5c3 afbcf461
+          9548d771 09f1d59e afa1b25c ddfb0ba4
+          71c16289 87c249ac b7eb969c 5d7792e1
+          874d53bd 5e2753b2 6976bf78 7c72c4b2
+          134f796a eeeebb1f d5fcec33 b9e65960
+          3b5ac7ff 6666cfdb 7df7e90b a71ef04f
+          974fcee5 42a5f02d 689ed2e4 71599bb9
+          b2cc50d7 9920d4a9 facc3cda c93ce269
+          c35b73c5 c9ba3a6c 7c3fa9f6 4bcf2d7c
+          66f45dbf 78bae4da edd31fdc 6e5d5f3d
+          33f34d57 7ededfe3 7161020d 9b653573
+          1c77c39b ef4bc79a 7589a764 cd113df5
+          c9db7d8a 14ee3349 559a1a15 65ba7506
+          bc52eef3 c9ab4b4e 3ff3ce96 e79fddd9
+          bf62852f 1e1ee0aa 45dd83ea f7f576cc
+          9a3976c1 f469ffa7 02e7bbaa d1de977b
+          31856f3d f3f4a5a8 2ec507ec 2185fbed
+          d553eff4 37c3606e 96509fce 8ab552fd
+          da7bd689 004c5961 63e1a054 97de70f5
+          3f0bbf77 59cbf6fa 3d8cedf5 0b0fbdf6
+          26898743 cf54fcf0 d6435d5e d99fcb89
+          4d82bf53 24d0d825 35ef7c20 eedc21e2
+          2e2aec7f cd4a282c cec27c29 9e3655ba
+          6b6aa573 6dbde82e ca31dd02 9ed72797
+          7e76fa99 678744ee d8e55f7f 7f3673bf
+          fd1b543b dbe61a36 2caebbb7 fdeaf4b8
+          ba278395 95667b9d 155cbba6 70c12187
+          9da82acd 9bdc3e19 4aa04b7d e6146cde
+          e49152bc ffdea267 f8126f98 70e812a8
+          ae95f5af bf27fe86 4eb34302 6cdc5188
+          387373ff a6bbbdaf 6ecfdfc7 d89ebfb8
+          e6747f62 64653d14 0f76ecae 39845b1c
+          9bde7ce6 11280b56 4ac688a1 3dd3ac2e
+          57ff077d fafd9231 7aa40c3b 749a543c
+          f9bc4402 61d1596f 9776e14e 35963986
+          2677adf8 d6b93f53 5ffe5b85 bcfb777a
+          f8818a9c a38f330f 14335f44 67ced546
+          55251a75 e6e58aee cb48dc80 0783126e
+          68d05480 33878fb5 debad1d3 b5708167
+          d171278c 52fd9033 d57f3cdf e393fc9e
+          511b2e45 ca873a75 17b9f37c 5272f0fe
+          e219592a d2e5ef7f f8cde190 6847a734
+          cc9e2bcd 4bd6f6ac abe33ec1 46c20199
+          537ee78f 9e2af9c1 f5dbb76d dd9ebff8
+          d06b6f14 4766c653 15d7fd70 5f2dd279
+          89689cee 844d1b69 874764fd 2bff136f
+          5181e4ed bdbb6871 adffa98e 68547277
+          9928c535 aa57fcca 6c113dc6 5c47bade
+          3b5e31c7 6ccf5521 efdcd517 5dd6aaee
+          8499eaeb 39ea53a9 3ed56191 faf2db6e
+          692dbee2 ea846f1d 6e79fe19 f7b24b2e
+          2f51012e 5b7d99a5 3ec3d5e7 60756bed
+          e7f5a97f 8f7ff9fb 221d529d 79b28921
+          a5471c2c 393b4fe8 3984b8bf 7ac65c57
+          178f49cb d2e55233 738eb58e 987b059b
+          54593109 1899190f 8b6e2cd8 debf9716
+          8f6fffbb afee0fbf 9dbaeaea eb1e707a
+          650a9717 9b65b5a0 6a494715 cbe8334e
+          948cf251 aa5b9360 ed94cb25 810d35b2
+          ee3fff55 3de30a71 703e14fa acdd54fb
+          ecefbf9f b0d1b78a ce080b7a 45029a0c
+          3f742f29 3df63071 6466aaff 9060c38e
+          d3291dcb 56c8aa7f 3c23c1e6 0e6ba909
+          b0514734 16d57d0f 8cfdfd3d 37155e74
+          d9763f0a 6e4046d0 54c5fa81 ee71ff5e
+          d848813e 98c1acad a24eea66 cd917073
+          8b35a5d1 af50483c c387c9d0 430f1477
+          aed77a89 32d04765 6abdbec9 e14dfcd1
+          d9b9885e e6babaec d1c552b8 ef1ee218
+          929d38d4 1986ea68 d6c98677 674ba09e
+          50873e9a ac80ac28 bfe7ae7f 0d44a81b
+          b0605772 f5b5527e ef6f5f0c c75cffa2
+          fa449f75 a36a80eb 3f5868bd 7ac75cb0
+          deef7102 bde12e7b d258293b ee086bd7
+          1a007ce3 be80aa7a 9c596e19 71fc11e2
+          1b334a24 9060165f d7251608 a87a6b81
+          347db2d2 ea24005f e9604635 97ebde78
+          2cf6fe40 fd9603d6 22165dfc fd86717f
+          fcdd4321 bface24a e3abac1c a7eec69a
+          776749db f29522ee 045b5ead f9354d72
+          268f97e2 03a74824 401902f8 6662614d
+          861f314d b2c68f49 bc24c4ac b4744d9a
+          172c91ea 3767b224 047d0a87 1d7f1f77
+          dfbdff29 befcea01 fb3d0774 a8c34aac
+          9af69030 6a87be6e 464324d8 d02d7533
+          3f924055 8db56ea5 5f91a8b8 f273a5e4
+          a0a99235 ba98578e 01f8da22 7e91a203
+          a658e765 6a4ea3ff 1dfa2697 4b3a96af
+          960d6fbc 27f1709c 57866133 21bf348d
+          fbf3fdff 2afadea5 f503da96 0ee46f56
+          7cc9e532 eec1fbfe a5feb24f 71c9d117
+          732aa3f9 9355d230 77bec443 c1c48717
+          87c2e21b 314cca8e 3ec45adb c25b2900
+          6c2db353 9835aa50 861eb49f b80af2ac
+          4e63bf0c 43c24dcd d23067be 74ae6f61
+          5d1db6e4 1ed54178 63a07fd3 81ed6368
+          9a147def 92ea097f 79e88f2a dc5570cd
+          d1e74de9 16a9797b 96347dbc 40c493c4
+          fc462c2e d9e3c648 e951074b 2c48f901
+          485eef8b 6da4ecd8 c3c537ba ccea2cf6
+          5f41e962 bef5a4ee fd8fa47e d6620e21
+          469f827e 7962c2c3 0f3e5e78 e145033e
+          dc30e083 c79aaec7 55c09ba5 9ea3bbb9
+          f4e8fb1e 51592d14 97daf7e7 48e767ab
+          1287bb58 4c749fd7 9a42c99d 32da3a3e
+          05009211 0d888c3c e9d09ef7 c0469368
+          83dd2e69 59b8546a fff78168 4e561561
+          7321bfb4 4dfcdb23 8f165c70 61b5b603
+          4ed1df21 ab020ace 392f36e1 cff73d13
+          f0cba3dc 02e8f3c6 7489b4af ae53bde2
+          0f25dcd4 22e24c70 9676242a 9ee20219
+          7ef83471 e5b8addd 6d00d06f a8f38b14
+          efbf8b14 ecb59b38 cc0e64a2 75755e8f
+          74ada890 9a77df97 48675834 de7c83be
+          c61a446e d30cc75b 9a61ec90 e4bf4382
+          9de67449 e105df6b 98f4d747 1e0805e4
+          536e03f4 c570c5a5 61ee5269 f8689e58
+          47c8265a 6f178b4b e6e89132 e2a4a325
+          16a2fc00 f4535d98 af0c2bca 9412735d
+          5d7e6eff efaa36a9 ce654475 32cdce66
+          fbea5ad6 d5a18f70 2312f0cb 3f263ef4
+          c053f9a7 9dbdc386 1776d83e 1ecde58a
+          eb5eefbc 685ceee0 9550d8d2 dda9c5a3
+          d65a9696 f98b124f c9c6e3a2 7a48923b
+          69bc944c dfdddae5 06009bd7 15621d6c
+          3eea9463 c5377278 1253b0aa 91329cd2
+          f0e17ca9 fff05371 a84e27ed 16be2aec
+          9796498f 3dfa48e1 f9dfa9d5 dc3beefc
+          9b1dba41 3bf78493 a2131fbc ef257fb7
+          fc9a8704 7d56a786 ea013574 aaca74ae
+          04aaaa13 9f6f178b 89919d25 c5d3a64a
+          d6a8228e 4001b059 4633d7d5 8d38f910
+          193271ac 685a82f7 539b3f96 e19596b9
+          f3a576e6 6cf51fa2 843af479 5f45e272
+          93232beb 7dcde5de a18b2f77 68b0d3bd
+          3e29bce0 bbed93fe fac85f55 d29dc59d
+          81be3854 966bf974 9dd4cf9a 2b7173c7
+          5aa22959 55117b4b 8aa5ecb8 c324519d
+          0d20bd98 23f9b953 caa560f7 5d551be4
+          b19670f4 2bc3275d cb5749cd db3325d8
+          dcc5142c fa0c75fe 6ef9ebc4 3fdfffc2
+          90238fd9 e1c3093b fc4845dd ed116761
+          e167e1b8 dca51ae1 2eee10f4 f5d098bd
+          e4fa0fe6 49c3ec39 d602e644 c14ed335
+          c91e3b5a ca4e38d4 ea9dd3c3 06606eaa
+          72e778ac 732fdd85 7989374b 381c12ef
+          e892860f 3e96b695 b5d621ea c05785fd
+          5231f9af 8fde5f78 fe771a74 9f6f87ff
+          7906c559 d9d9d30e 8e4ebaef 4faf7777
+          cb6d1a0d 30faba51 55851ae9 0aa97037
+          573a96ae b07ad1fd bebfc43c 02c5e391
+          823d7795 fc5dcaad dd6f00d2 9bb93463
+          f45927f4 acabb30e b04bf013 3c6ea99b
+          f9a1d4cf 59aceaa0 181d446c 3eeea0ee
+          89505c6e 760f1f36 5f777b06 c5fcd0a0
+          08768ecc 2c29baf0 c2e0c407 eefb6757
+          b73cc6c3 833e6f56 a748e7ba 06a97d6f
+          96841b9b 445c09ba cfaae276 e5e6c8f0
+          a30f16f7 102f47a0 0069ccec dc951d7b
+          900c9930 4eacb3c5 12adabcb cc90960f
+          e759ebea 62218e36 415fa94e 4465967b
+          263f70df 2b19fb4e 8d0c9ab6 72d0948f
+          db2345df bdb06ef2 230fff36 1290c5dc
+          31e8eb21 d2f4b8b4 2c5e29f5 ef7fd4f3
+          2ed9fe86 78ad8a5b 938c11c3 a5eca423
+          25ca1128 407a86ba a048cec4 1152b8df
+          9ebdebea 129f5717 58b7deda 916f6ede
+          620a169b 35473deb eae64f7c e0be4754
+          76e97064 650d9a3f dba07a6d b1b993c4
+          b7f34e8b 4331f909 53b2e8f3 1e7198d3
+          291169f8 609e347f f871ef94 6cff3d6f
+          cd7048ee ce9364d8 f43d9892 05d28cf9
+          fe6857b6 5b46a8ce 9ddb3caf 2ed16e2a
+          87c37aad 58edbbef 4bdbf22a 6b673eb3
+          48f8aa70 40ba273d f2f0cdc5 177e6f85
+          ca2e83ea cfa60fb6 c2f24e9c 1c9df8c7
+          7b5feeee 965b0877 e8f3a635 8f4069ea
+          b07ad37e d5ab4eb8 99221617 c3e79592
+          430f948c 1105d6f9 5500d224 d899e7d5
+          9d769c64 8e28fda2 b3d77f23 e4b176c0
+          36cc3127 8ea2d62b 0e814d06 18543609
+          c6e41799 7beef18e e6740dba 453e83ee
+          96756467 4bf1a597 05c7ffe1 de4755b8
+          7b9e7087 3e1f2cd5 a9ee585d 2df5efce
+          927830d4 d3cb4ec0 53982fa3 4e3da6b7
+          72a70c81 54671e6d 32f4e0bd ac117b49
+          eabc3a9f b4cef944 751ae748 d41fea19
+          ad03be12 ea543679 6aa2ca28 eed1e583
+          f2a4d441 d917d10c a70cfdfe e57513ee
+          bfefe7e1 a0ace356 c266f788 6e1e5d10
+          95c6f98b a5c15c6f 97e14dd0 6defa9d0
+          b3c68e91 b2130feb 39020540 ca325f2b
+          38644299 0c3de440 d1cd83cd 138dd479
+          3c12dc50 27b5333f 9040431b e7d5a14f
+          2a93d4a8 6cf24b95 516acc81 2882ddd6
+          7038e2b9 471f3d3f 18951b59 df80be3b
+          00ea21eb 0c4addac 39d2b178 5972ebed
+          744d8aa6 ee2579bb 95f3560a 2055a96a
+          c095e395 91a71edd f31ed8c4 ed8d4824
+          2c356fbc 2b6d2baa 7a76c0d2 ee60b346
+          47446592 6b553659 90cc2c11 c1ae0f46
+          5e7e6cc2 3dbf7e36 d02d3fe1 21439f37
+          b00a77dd 358d52f3 d64c89b4 b4aadadc
+          99b0c237 7c3e1971 c251e2ce cb52dd7a
+          ca104835 e60ef8d2 630e950c 735d5d5c
+          9218ad73 4bed3bb3 a471de62 894722ac
+          ab439fa1 4e65913b 552679c6 cc2683ba
+          5d1ccc7f 38739873 d875d747 caefbefb
+          8faa40ff 4bb8435f 0f9ba975 5985ea6d
+          bfd7f32e d9241666 7a879548 e97187f1
+          ba3120d5 425d40a4 64fa1e92 bfd7ae3d
+          af0b4b78 5e5da635 e25f37eb 2389f843
+          4cc1624b a1ee0d33 8ba84c12 1eac53b0
+          5f64a719 33660cfa 32cdde7f 7fbf333b
+          fbd3d657 5f3f4673 c810ee32 6cf2cce9
+          e6112831 0936368a 5b55d2de b1a34582
+          c18467dc 798b0b25 eaef968e 551b5824
+          0da400f3 10f2ac51 4365d419 27883323
+          23f14ff0 b825d8d0 2855cfbf 229deb6a
+          44670a16 7d8885a4 66f4afef f9def0eb
+          af5f6187 3faf6d06 9c8b2fbb 7421ebed
+          b0c51b59 f5b2c3ed 7ed9f0f6 4cf1afab
+          4a7c048a f9730c43 861d3e5d 3247165b
+          675d01b0 73aa1371 789c32f2 94a3c535
+          6448e2e9 575d357f e1886c78 f92d695b
+          b1b6b797 4831e2ab 2307d6ba baeb5506
+          99679bf6 d04e0f6d f9cf7ef6 54b05bee
+          e4e1c396 eee6aeea 7aa97ee5 2d899b23
+          768916b7 f6be726c e429c788 ee727004
+          0a6063e6 cb244a8f 394432c7 8d49fc66
+          09538657 1a3e982b 4d0b165b 23febc32
+          0c7d86ba 6ef9b3ca 1e4fd8a9 7db0c554
+          acd566bb 5c923d6d 9ae89af6 71f31bef
+          eeeb70c9 68ee3a6c f20c9ac7 5445e212
+          6a6e1687 db299913 c65a3df2 44e1ce5d
+          54200ed5 7b37d7e9 b1681ab0 1ff310e2
+          a27d7795 d2630f57 6db196f0 9937df03
+          dbf1e972 a97ae975 09b676f1 ca30f419
+          ea42ddf2 cea81933 ae28bbed b62e3383
+          10ecb693 9c830f0e ea7159d8 fede7bc7
+          a84698f5 76d8f459 ec5d6f17 6868948c
+          9242710f 1f9a38dc a9de7de6 a832e9ae
+          ae117f5d 13e10eb0 53a88b89 788bf3a4
+          fc9cd3c4 e1f3259e 82753925 d4d22695
+          4fbf289d 9535d6f3 ce41f8e8 a3b3d039
+          e2c73f39 bb6cc68c d576fbb3 dbb2092b
+          bd7dc682 50586e10 26cfb085 bb3ad8d4
+          26eb5f7d 47c2cdad 224612dd 71dd2165
+          271e259e a25cee2a c0468c0c 8f8c3cf9
+          6871e6e7 259e8235 139cfad4 bcfab6b4
+          afaefce2 3f015fa5 32c6cd66 d6b06913
+          683fb150 4846de74 e3d361bf fc8adb0f
+          7dd5dda6 8e8a4a55 81bfd3b3 d62e51ed
+          ad1a046f e950293b f63071b8 5d142230
+          d8c57b3e c30e3950 7276df45 249cc489
+          e35eb734 7df0b134 7ebc40e2 e6ba3a46
+          e7d10795 2d1e5619 e38f66d6 b023db4d
+          c55a0db7 6aa8738e 3842e25d 1d335bff
+          f7c1810e a78ce256 c457c35d 3c1a177f
+          5d9db887 648b6fcc 48d5058b f4bfeb2d
+          1a156fd9 3009b7b5 4be7daea 9e6fa537
+          0f0cca50 67ceb8e6 ef325146 9c7c8c68
+          c96c96f0 b8a56bd5 1a59f39f 9725d4de
+          d573b409 b079a85b 587ac375 e78cfcd5
+          af039ac3 9e3789ad fb2b23ef be27587a
+          fdd55789 aeafe576 c4e61d00 95d58261
+          a97af94d 09546d10 712771f2 683426c3
+          8e9c2e59 23877f39 2a006050 853a93af
+          a4504a4f 3a5a3497 91785d9d 6aa0a3dd
+          7ea97af1 0d0936b5 30fd8a2d 2422bd55
+          658aeb54 b668b5f5 5fc3eed7 61e4af7f
+          bf24da15 bb513dec 11ee4a6c 9eee4442
+          cd6db2fe 85d72416 08f49c5d d51fd5f3
+          77e60eb1 d6db39b3 32283f60 b0e53af3
+          bc3ab74b ca4e3842 3cc34a44 22d10475
+          8066a5c1 9ad7de91 8e8a7556 3024d8a1
+          8f0e434c 65899fa8 4cf1b6ed f3692a5c
+          8f92cb2f 7e3a1a90 3f31ba82 beeaf478
+          2c2ecd4b 3f93baf7 3e502d42 12b77c30
+          24593b4d 90a1871c 289ac1f9 76c0206a
+          7c457768 5238754f 19b2cfee 2266672d
+          11b75b9a e72f969a 77674b2c c2ba3af4
+          7d5fa90c f16f9525 ee4d85bf 4e4adce2
+          a3fff467 29b9ec7b d7c602f2 362f75c7
+          66e14eef 79d5d086 b7df97f6 e5ab4592
+          398fa83b 2025471d 2c43268f ff629400
+          c00e6c7b cd675075 d472274f 90b2e38f
+          14e9ec4e fc939c86 f8ab3748 d57fdf94
+          78244aa8 c3e65466 50d96191 ca103799
+          59826037 98c2ddfd 0fc78b2f f9ee359a
+          dbb58611 166c16ee 548310f5 0764fd7f
+          dfb03647 249c9235 6f22d510 949d7094
+          f8861553 80c00e4f 7622dea2 7c293dd9
+          5c57e74a dcdb520f bdf9cc57 3efbb2b5
+          ae0ee8eb 9e5299a1 4e6587eb 5586a84a
+          95bf564a f55f463f f897c59a 16ff8944
+          a58b3b16 9b56f23d 0f71e7da f53daf1c
+          4b26fc47 22e2291b 66bd4fd6 99e5634a
+          16d851ed 6f4cc4e9 f3c8d0c3 a759cf64
+          52479b18 86d4be35 535a97ae fab20e00
+          361695b0 ca0cf7aa ecf0662a fdb5526e
+          603af7a4 d31e8f47 e56faa22 6052167d
+          86bbfad9 73a579de 426b9a26 a1ae6ec9
+          db777729 dc7337d1 0cdd6a60 000c60a8
+          33d7d519 0e29d87b 372998be 7f7253b0
+          eafb3b96 af920d6f bcc7b145 d8626741
+          65859754 66f865aa fddd522e d895ffed
+          5f5270ee b7ae9688 bc43238c beee7873
+          e06dddb3 ff95606d 43729b29 c211293d
+          f630c91a 35a27737 06c5080c 4ceb2bd6
+          bb5f73c6 8f91b293 8eb63a5a 899f715d
+          82750d52 f1c4733d 8f2aebea febfbdfb
+          808faa4a fb38fe4c 9f4926bd 17028484
+          d07b93a2 aebdac6b 5bbbbbea eadaddd5
+          575dcbba 6b5744ec 0d2b62ef acbd820a
+          28a08228 2020bd43 2a69d3e7 ce7bee9d
+          a0684226 f494dff7 f3ce4ba2 a0eb937b
+          cff99f73 cf3d074d 5c576687 63beca0a
+          97a9ccd0 ee9242bb bce4bb4d 7a299c7e
+          c6a95798 1dce9fe9 84f17bfa e03de4f5
+          c9aab7de 97b0d71b 7bef8370 584cf171
+          927be4c1 e2ca48e1 450a606f f5bffaba
+          baac34c9 3de20f62 723844b4 d8379f7e
+          5ac0aab7 3e105f39 ebeab09d eb2a249b
+          ad490937 a8acb0be 9dce5fb4 4f2a85cf
+          b765a48e 8d04a592 cb184da9 5ab84c36
+          4f9b1dfb 7c499d0a 82897d7a 48e6a8a1
+          6275d98d b76c01ec 39fa6d69 73bb246b
+          fffdc4dd aba4655b 9ba8415a e9ccefa4
+          eaa765bc 018ba643 9d267e95 7c26b9f7
+          1bfd4e7b fd6f6cd7 977efca0 2113c56a
+          7a4dfd20 d9bc188d 2f7eabc8 8629d3a4
+          46dfb434 d65bb2fa ac5ebd47 b20ffd83
+          24f7ecae 3a0d338f 64813d96 ea442c36
+          8ba40dea 2799871f 2052531b 7b665dfd
+          7d7d03e2 d56f7fc4 7161d85e a8d32462
+          fa38ed84 13ae297e 7572fbed dbdaf30f
+          b1f8b5b7 25edf8e3 2e3545cc d324cccb
+          14682c1c 08c9aad7 df9590de 71c46c15
+          22c663d9 fc3f1e26 f19d7354 23c18a6c
+          60f7f7be eaff5448 4b2a2e6c f97e75ea
+          f707aab6 c8f217df e43d096c f7ba32db
+          9c8bd34e 38ee8aa2 97df6cdf 9316edfd
+          6759f4f2 5be1d413 8ebdce64 33d6db31
+          c782dff7 07e2dd5c 216b3f9a 225aa005
+          5b280483 e2c8cf91 ecfd478a 3dd5cd23
+          596077f7 bf9a7e0e 6cbae41c 3246ccfa
+          b17ee1d8 6372cdef 97b51f4e 157f4535
+          0544d3d7 955f363b 8bbbdcaf 32c18af6
+          fedfda21 5621a81f e437ae3e 25f7683e
+          29e7f246 a3706711 299d3157 aa162c6a
+          d9111375 f592367a a8a40fec 2b168745
+          980b0676 53e7ab06 4ab64497 648d1a26
+          09fdfb88 78bcb1ff 90a649c5 9cf952f6
+          f50fc6bd 0c343158 f099ec96 e71c9dbb
+          3ed911fe 7b3bccf2 527b4ede 53268775
+          b2ea8483 5ce66814 eeac222b 5f7f478d
+          f85bf8ae 8dc72779 471d2249 25c51211
+          b6400176 9926c65e 916903fa 4ae62163
+          44f41362 4cb11fac d6ad592f 2b5e7b57
+          cc0e4a88 26af2b4d 22d61929 7f3afadf
+          dd277fd0 21fe933b 4cb0eb3e f97d49fd
+          d3d19744 c2d62f84 cd8bd1d4 fdef0fc9
+          8a37de95 90c713bb 43d13431 c7bb24e7
+          0fa3243e 2f43341e c9023b2f a2df5226
+          49ecd659 720f3bc0 d83bb225 f475752b
+          5e9ecc1b b0d8de75 1589887d 51eab147
+          ffabf8b5 b73bcca4 4e87ba1d 8a5ffb5f
+          28f584a3 ff1b89d8 97b0de0e 4ddd0dd5
+          3fad92b2 99738cbd b062f2f8 c5ddbb44
+          32860d12 5ba293f5 76c0ce0e aad4bd13
+          97976e84 3a5b7a7a ec60a79f 03ebf3cb
+          8629d3c5 b3a99293 25d0f475 e593f2c4
+          d1431f52 7dfff71d ac2beb58 8a5ffddf
+          aca48347 3da67ee0 ec6f87c6 37845d64
+          cdfb538c c73b31f7 b7d33b13 af4fb20e
+          1a2d297d 7a8a316d c05c30b0 438c7360
+          139c923d 72a8240e e8abeea9 d8ebeab4
+          5048aa7e fc49367e 3e47cc36 6a88a62e
+          2c0999ac d60fccf1 098f77c0 398a8ec7
+          e4743d64 b25ade52 3f78e658 d064605b
+          f9f264f1 6fa96ed1 2359fd0f e41f7aa0
+          2416e58b 1666ea00 d881ced7 e88652f4
+          cdbf470f 17a9ad6b d11ff3ac db282b5e
+          7b472c4e 4a8826af 2b2d12b4 7c9972dc
+          91ff2879 fbc38e37 41d1117f e6256fbd
+          2f29c71f 7d9916b4 4ce1912c 1ae53a7d
+          0b94b26a d9f0e9b4 96adb70b 06c59e9f
+          2359aa63 726626e8 c7d50068 8170d024
+          c9259d8d b398c56a 89fd56ba d924c19a
+          5a59fdbf 0f24128e f008164d 8fb78396
+          9f524e38 fabaeeaf bf53d311 fffb3bec
+          92d3eeaf bded4f3b f1985bc3 01f3626e
+          0334ba31 6c229b67 7e2fd50b 97484405
+          b798e1ae de23a9fb 0d95f441 fd452c26
+          ce930562 76be22f1 b9a9927d c048b167
+          66b46c5d 5dbd5736 7c364d6a 976f606b
+          13343d58 f04a7dea b147beaa faf86f3b
+          6cffd591 2f80e257 27cf483f e9d809ea
+          42e0b468 34be3954 c7b1eaad f7c55756
+          d1b23fe0 f34bee61 074ada80 5e120950
+          3f607bf4 757516a7 59d2870e 90a4817d
+          8db5aab1 425d445f 57b76091 6c98f2ad
+          b1161668 ead252d7 8a7eacc4 6d1dbaef
+          62d8a83d a4feffeb dc0f68dc 9988843c
+          4159f5bf 0f8dc73f 31cf930d 87c51c17
+          27d96386 8bbb4bb6 68843ba0 a9aed778
+          833c7d50 3fc93d68 b488bf65 378ab7ac
+          5c56bff7 29fbd561 fb4db057 a6a49f78
+          cc15dddf 78a7634f 4a74f40b a1e8a537
+          22e9271f 777dc82b 9f725ba0 51b6b38a
+          6c59b45a cabe9923 619f2ff6 2359bf5f
+          dcbd4a24 63f840b1 ba6d6c81 02fcbef3
+          f58b24f5 e82c3907 8f11b1db 63bf7d6e
+          314bb0ba 5656e97b 4c567bd8 b30e4d52
+          7df86ad5 973f5af4 f29b952d d9d89a60
+          d7ae3b6e ab143dff 6a45c6a9 27dc1ef4
+          ca226e0f 34ea57ec 1159fbee 9752bf76
+          43c34513 a3d1f0f9 2573f430 491bdcdf
+          78e4c4eb 39409416 1271a6c5 4be6b041
+          e2ec9467 bc78d47c 0f6536d6 d56d9cf6
+          b554ffbc de186801 4d84ba40 e65f4e79
+          4af5e5ef e87d7a47 c7d847ef a7d5a8b1
+          db332fcc c839fbcc c7433ea9 a522687c
+          9168b2e2 95c9e22f 2b376610 9aefbd34
+          31d9ec92 b3ff0849 ea5e2061 1fe503f4
+          418ed96a 92ecfd87 1b2f1ae9 03a0e6ef
+          3993b173 71cdb215 b2f1b3d9 62b13142
+          c276ae2d 919723e1 f038d597 f38c8460
+          b74d215c ae70440b 4f8844e4 255ea147
+          a33ec6a2 faa1cdd5 b27eea74 09d5d4a9
+          7017e395 bc60d098 91c81a33 425cd989
+          c61b8040 47a63f82 4d1bd8db d8afcea4
+          1f351173 6b13b378 376e365e 60125398
+          ad4dd044 c3ac9a5a af7c9b79 f2f17715
+          3e3e9155 cd04bbc6 ba3c34c1 9f75ea9f
+          6f0a7ae4 131a1134 ba59f42d 50be9a2f
+          d53f2d31 ded08bbd de2e2029 43fa4bc6
+          f041d151 25130ee8 a8a1ce2b 92d43dcf
+          d8dac492 e01609c5 9858b15a 8d7360d7
+          bcf389f8 2bead8da 044d873a 8faccd3c
+          f5cf7715 3ef5ec12 8bdb4d4d 08768d59
+          1213a5eb 84273765 9d71f2bd 7e8f2c23
+          dce1f70d 89c91496 d5ef7e2c f5fa9163
+          fa5bb2cd 853b95e4 4c614d72 0e1c2999
+          c3fb461f c9724da1 83d1d7d5 39525d92
+          3d7ab8c4 75eb127b bf3a755f 457c7ea9
+          9cfba354 2e5c2566 964ca109 21af8472
+          fe7ecee3 85139e7c d79290c8 618e04bb
+          66c25d52 b23e7337 35efc2f3 1e52174e
+          3d15c16f 6e18d5c9 04b67865 c3279f4b
+          b06a8b31 b3d0fc54 4558cc09 6ec91c39
+          4c120bb3 25eca186 e838f449 6a7d1942
+          e6c82192 327ca098 f497259a 9bbad607
+          4aeaef6f 59f4b3ac 79779aba df340643
+          6872901d 8ec8bb26 8bf921d5 67f30896
+          60179b35 252568b6 d99e0eb1 de0edb09
+          7795f357 4ae9ac39 12aef7c4 5e6f1708
+          4a7cf742 c91c3d54 ac6eab31 83017488
+          ced72b92 31ac9771 dc9e59df a7448bb1
+          1ec16e13 efa65259 f3de272a 10fad9da
+          044d5e57 7e8fccce 3eede41b f36fb9a3
+          868210ec 5a2cefdf ffadcf3d f394dbd4
+          05f431e1 0ebf6f58 4c564dd6 7f3253ea
+          96af8c3d a3a03f92 d5346343 d6cc9183
+          556fc77a 3bb47ffa ec745271 9ee41e34
+          526ca929 b1b73651 a12e5851 25eb3ffd
+          42bc1bab 385d024d 0af96463 fe85e73d
+          d0f5c187 17da3232 2908c1ae e56c59d9
+          d2e5de07 d7e4fcf5 f4fb586f 8746d94e
+          9f7c08fa 65d5e40f c4bb6e83 8823c676
+          f8a1e8a9 14592387 4972efae 7ae3c423
+          26b45bfa 4bafd678 8b648d1a 2271855d
+          d5f51f7b 5d9de6f5 4bc577f3 a4fcbb25
+          62b25143 343da80e 69f2ac25 3eee7fd6
+          f40cd6d5 6d87e5a6 9b6ea20a db6b6be2
+          e32561d4 e875e2ab 939a99df 1d62b612
+          84b14d1b 63110954 f924120e 484241be
+          ba5ee29a df453f12 116b72a2 581d76a9
+          5fbd4682 357e1686 a3ddd127 a3f5ad4d
+          f20e1d29 5907ecd7 f0083646 1f6cb74b
+          cd92a5b2 f2b58fd5 efe5112c 9a686fa3
+          8f6027e7 9e71f275 79ffb9b9 467fd911
+          dbc92e94 a079b6cc 2cbf2d23 fd99a026
+          4f9b9861 c1ef6f20 bb48d9b7 8ba4f287
+          856a2819 6c7ef362 fdf96b28 2c897d7a
+          48cefec3 a32fd432 e644bbea 7da3ebea
+          32f57575 6346a8fb c36ebc40 d4ec3de1
+          748877ed 7a59f7de 2712f278 d8da044d
+          863a9f47 becbfecb 6977741e 7f7f853d
+          2f9fa210 ec764dc6 d9e755e7 fdf58c71
+          aacd9942 b8c36f1a 1c7d3242 05ba0d9f
+          4d939a65 2bd51d15 a357d234 f55bac92
+          31629064 eed74f02 7ee1912c da0d3dd4
+          b9f3d324 7bcc30b1 a7a5c6de dac4e190
+          6059856c f8f473a9 5db9592c acab4313
+          823ef1e4 5d7cc184 2ee3ee99 6bcbcea1
+          2004bb5d e728e82c 9def1ab7 3cefecbf
+          e8e16e19 e10edbd2 3b236f59 ad11eefc
+          a5e5c60c 44b3e7c3 0683c69e 8959a347
+          485261ae 843c843b b47d9170 7413ef9c
+          83468abb a8b0f999 3aa3f731 1b6bef2a
+          e72d9032 7d5d9d95 fb004d0c 9ed53511
+          d0e42167 41feeb2a d4f18c83 60b7fbd8
+          b273a5f3 9d777d59 70e9c50f 06fdc279
+          74f8ed8d a43ab42d 0b574be9 cc6f245c
+          57a72e98 183377aa 438bebd2 4972ff30
+          42acf136 8e1c439b 170ae8eb ea4649ea
+          90016232 b7605d9d 435f57b7 4cd67df2
+          95b18b31 ebead054 a8f37ae4 fdfcbf9c
+          767fdac9 a7b3b509 c16e4f84 bb1c7f7c
+          ef9ecffb c3f224b3 76f84d03 a4df4966
+          4d367d39 57aae72f 6ed9a914 eaefa7f4
+          ed253907 0c8b063b b640419b bcf88d53
+          00247350 89648e1c 2e16a7b3 f9b760f5
+          75752ea7 7857ad91 b51f7c26 81aa5a63
+          60043411 ea16e6fc f58cbb3a 8fbd7b93
+          a36b2145 21d8ed19 49871db1 25ffac33
+          ef5117dc 54c21d7e 7333a9ce 2954ef95
+          759f7c21 9e956b44 e29ccd6f 58a73a3f
+          b3eae0b2 460d95f4 81451260 0b14b441
+          fa517971 d9c9927d e07e624f 6fc1ba3a
+          bb5d425b aa65f3b4 5952b374 83581cd4
+          108d05fd e2c9bbe8 82fbba8c 1d37d396
+          9b474108 767b8ea3 b048d485 b62ce7ac
+          bf8c53e1 6e0de10e bfce4488 585496ab
+          5f53211b 3f9f21c1 d28ad8fb db85c3aa
+          334c579d e22889cf 4a32b689 00dacc25
+          1f8e9ec4 9277d818 892fead2 f0f8b599
+          c18c39da e5547e37 4f4abff9 29badd0f
+          6d287ec7 d8da242c 8f270e1b f2ba2d27
+          97b37a08 767b9efe 564e97bb c67da146
+          13e3596f 87463795 5da47cee 1229ff66
+          aeeaf842 cd1f39a6 cfe8a9ce d0ddad8b
+          e41d3c32 faf49647 b26823c2 01919c3f
+          ec276983 fbab9066 8dbdaece e990ea05
+          8b64fda7 5f4bd8e7 63236234 19eabc1e
+          99927fe6 e90f248c da9f7575 04bbbd18
+          eeb2b2fd 29871d3c 498d2a26 326b87df
+          344c16fd 548a906c 9afe8d54 2f5ca22e
+          9618bb10 eb5ba0d8 6caa731c 2059a307
+          e9aff633 8b81567e 918b04bd 2229bd3b
+          4bd698e1 62d6d7d5 055bb2ae 6e6df4c8
+          b0b2eae8 23580631 681cea16 669f79fa
+          cd5dee1e bfda59dc 9da210ec f6aeb83e
+          fd6bd4a8 427f24fb 05e10edb d23b2ddf
+          e61ad9f8 d934f1ad dda02e96 b898ebed
+          2c09f192 7dc04849 eaca1628 68ddf425
+          03aecc64 c93bfc40 71e8fbd5 c5dadac4
+          6e97706d 9d6c9e3e 536a7e5e 27562735
+          4413cd60 4024f7bc 731fec7a cf3d5fb3
+          5f1dc16e 9f701675 97aef7de b734fbcc
+          336e63bd 1d9a0a77 d53faf95 cd3366a9
+          4eadd638 e4bcf9de 322caeac 4cc93f72
+          7fb1b91d 12610b14 b4469abe aece2c9d
+          8e3a4012 ba35bca9 1889b1ae 4eb58d15
+          b3e648d9 b73f4507 2cb495f8 1de37489
+          903c9571 e271afdb 32b359e2 44b0db77
+          ac199952 f8c0fd33 72fe76ce 3835da60
+          91277e7b 77a9c6aa ec9b05c6 6271634f
+          94e6d2bf a63a47b3 49127b14 4beec123
+          24cc1628 6885827e 91ac9103 257d405f
+          31592d2d 5a575733 7fb16cf8 7ca6846a
+          bdc61a54 e0f7a14e df6922ef b4536e71
+          742baea2 2204bb7d 1fee52d3 fd39e79d
+          3b498d36 26316b87 dfdc60aa 130bd678
+          65e3175f 4bddd2e5 22f1311e c986c362
+          7638246b d430491f 5c2c21d6 dba115d1
+          8f0c4be9 5920397f 18252687 bd05fbd5
+          b9c4bf7e 936c9832 4d3c9bb6 88c5c560
+          058d439d cf231bb3 4e39f996 c2871e5a
+          eb2c2ea1 2804bb56 12ee32b3 ea724f3e
+          e94e7581 4e23dce1 d7ce4d8c ceacae61
+          0b944059 c39163cd 519da535 2141f20e
+          3f48e2b2 52440b50 46ec7b9a ca708ed4
+          44e974f4 c1e2c848 6f7e80a2 b3db245c
+          5f2f9bbe 9821d54b d7b05f1d 9a6eee82
+          2259679c 7177d163 8f7d634d cba02004
+          bbd6c3d9 ad488a9e 786279e6 e9a7dfa0
+          2ed4b554 04dbd2cf 93ad9cbf 4c4abffc
+          5a22a170 f35ba034 88cbcd96 bc2346b3
+          050a5ac5 0045ffe4 1f395adc 5d3a37fc
+          b5e6d6d5 4517d295 cf982d65 b3e74b24
+          1ce1c830 3462ccd6 05e5e9bc 2b2f7fc2
+          9292eaa5 2204bbd6 d7792725 4bc10dd7
+          cd5217ea ddaa4d63 9e05bf36 60fa165f
+          feb094cd fa41b6cc 9b6f9c93 d92c4d33
+          d62fa50d e82b19a3 061abbfb 03fb4ac8
+          2f9231a2 9fa40f1d a8aecb16 9c03eb72
+          4add92a5 b271fa6c 09d4fac4 cc6c1d1a
+          358ac623 d879d9c7 1f779325 21a99e82
+          10ec5aef b5ea7005 b38f3bf6 d980479e
+          676d147e d1702a85 afac4636 7c3e437c
+          abd71a9d 5ff3ebed 3431bb5c 927fd881
+          e2ee9ac5 a914d827 f4eb2eb1 5b8e743a
+          ea6063fd a77e5d36 779d8bd3 29fe8da5
+          b27eca34 f1956e11 2bebead0 d4d83528
+          9b324e3c f1baee93 26ad7716 15531082
+          5debe52c ec263d5e 7cb136ed 98636e53
+          e16e16e1 0ebfb9e1 54bf58bb 7c836cf8
+          f44b09d5 a941aa2d c616289a 26f6a424
+          2938e650 b1ba5d12 d1a821f6 e278242c
+          eafa8b97 cec71e21 f6e4e4d8 ebea6c16
+          1504fdb2 495ddf5b 7e5a616c d60d3462
+          52974950 c6771d37 f6334b42 22b19f60
+          d706aed9 b878e9f1 fa6babd2 8ef9d335
+          5a48d653 11fc726d 34dc7155 0b964ac5
+          d7df8ae8 db4534f7 b68dde91 9acd9254
+          52247907 8f90081b ea606f85 ba8818cb
+          01a2fbd5 75f9f52f 6ef7e256 d7b1d56a
+          5cd7e573 171aa190 75756822 d449d023
+          6f661c79 c413ea02 a14523d8 b5a16bd7
+          e194e2a7 9ffcca1f 903bd585 cc4334fc
+          7ad3d945 02353ed9 ac3ac0ba c54b451c
+          311620e9 eb9954b8 cb1e3342 92fb14ea
+          8f30803d 1fec5497 9b3d7aa8 648ed92f
+          1ada62cd d6391d52 bf74856c 9af6b504
+          aabdacab c3f642dd c2e4c30e bbb1e75b
+          6fd53aba 16521382 5ddba2f9 fde1f443
+          0e9918f2 c84b3c92 c5af3da6 baf19c22
+          9e7515b2 ee932f24 b8658b71 e452ac70
+          a79fc7d9 f9b8a3c4 9e9260cc 86007bac
+          ed0a8824 f5ec2a9d 8e39343a b088f5b2
+          84ba7e03 1595b2f6 fd29e2d9 50190d75
+          3c6043e3 c14255f2 a1875dd1 ebbd7717
+          9b9c2e0a 42b06b7b ecf99da4 f7c71f79
+          120ffcc3 7f54b8fb 968a609b 81abf1a6
+          6cedb235 c679b25a 38143d7a 29066746
+          bae41f7e 80982c66 3a4eec99 50171471
+          77ce94c2 538e1593 d9127ba6 4e5d8b9a
+          0a7e1b3f fe426a96 ad8a79c0 0a3aae40
+          401eecf1 faab9f9b 6c1c3f42 b06bd315
+          b6489fcf a7ae4fdc ff802b22 61d94441
+          f04bb853 7d66d817 92b2d9df 4be5ac39
+          d1172962 acb73359 4c92397c 90a40de9
+          4301b1db e92fe738 d392a4f0 b4e3c591
+          9eda828b d8642ca4 ab9cf59d 547cbf40
+          85428d17 26d0a490 573e4a19 33faae70
+          5d3debea 0876ed43 efa99f7d a5462bb7
+          ea8316aa 815f6e40 87ba20b6 7865c367
+          d3a57ec5 2a63b7fe 66e9e7c9 5accd2f9
+          b82325a1 5b67de92 c5ee0b75 ead2b2ba
+          1cd2e584 2324ae53 5ef3db9a 6ca5aed7
+          da25cb64 dd8753d5 75ece11c 583429ec
+          95150923 f6bba4cf b4e95e7b 5e1e0521
+          d8b593d1 4a659524 0d1bfa94 bac0dfa4
+          1af8b537 15e3a825 cfc64a59 f3f68712
+          aca98dbd 058ad101 c719e1ce 919c40b8
+          c3eeb90e 6d56c939 6894240f ecd7b250
+          a77e7fa0 ac42d6be ff99f82a 6aa2a18e
+          e501f8fd a5a5497d fcb06197 f49df9f5
+          0aaa41b0 6b576c19 19d26ff6 3781f881
+          832ed7bc 32878a60 5bfae3ab ba55eb64
+          83ea24b5 86b7609b 6f2d2312 979f2bf9
+          471dac82 a18d0e15 bb14ea4c 368b648e
+          182cb97f 3c4c24d8 82d7ae2d 1609fbfc
+          b2f69d8f a56ecd7a b635c1f6 2735fcf2
+          64bfd9b3 3ea11204 bb76abdf dc39a571
+          fdfa5f2a 9a54520d 6c1bec34 bf26e573
+          7f34ced7 8c985bb0 025d05c0 f4fd8648
+          d6a8a162 8ef50817 d86ea8b3 4ada803e
+          5270cab1 22de169c 5da7ae4b 7d1cb169
+          ca74a95a b0847360 b1fd26ca 2733dc7d
+          fb5e1daa a8e4b902 c1ae9d87 bb1fe6cd
+          0afb596f 87dff597 2a9b056b fdc6a914
+          b54b961a 9bbdc614 0c49a7e3 8f92f4a1
+          fda3b37c ccdc6147 ae39fd3c e2febda4
+          f02f2789 f85bd01c e9830d8b 59aad400
+          64d3b459 12f60679 59024d87 3abf94ba
+          7af43cbb ff8f3f86 ac696914 8460d7be
+          85ebeac4 d5bdf881 484026eb d73f15c1
+          2f37a4ca 72feaa5a 59fbdea7 e22f2d37
+          d631b528 dc1d7da8 24157589 6e8302b4
+          24d4592c 92dcbb87 149e7ea2 48a8851b
+          23aa3f53 b762b5ac fedf876a 10e23306
+          23402311 f1398bba 5fd2ffa7 9f96530c
+          825d8760 71bb65c0 929f238e ae5d2f89
+          84642e15 c1afbd6d f418a6ba 551b64c5
+          cb6f4948 9f4569c1 fe7616a7 538aff76
+          ba24742d 601331c4 beccac16 49ead14d
+          8acf3ea5 a1236ec1 54af1a34 f8cbca65
+          d90b6fa8 c1479d98 0975d80e cd279306
+          2c59f206 9520d875 380396ae a8701474
+          ba447d59 4335b06d b8d3efcc da956b64
+          f52b9345 33f60a8b 11d654c7 6cb6dba5
+          eb9f8f11 675a2a35 44b3a12e b9578974
+          3ffb5431 9eddb724 d4a9c145 d8e39365
+          cfbf2efe 8a2dfaf6 9c40d34d 51587e72
+          74e974a1 e6f5520c 825d070d 77cbd67c
+          a37eb959 7d7c5403 bf74befa d19ce188
+          b138bd74 ca748998 4d2d7a99 c2919d29
+          c5e79c2a ae0cd6b4 a0a95067 95943e3d
+          8c6b442c d6e8be88 2db818b5 605056be
+          3a593c6b 37444f47 6452184d b641526a
+          cfc9fef3 40352835 bb38328c 60d7616f
+          044dac19 99f7aa81 f3bbea3b 76e4c6af
+          fda959df d83320eb 3ffb52b6 7c3f5f22
+          2d79f530 1c16575e 8e149d7d aa380977
+          d84a3f9f d86e33de 7e2d3af7 8c68a0d3
+          5ab0bc57 0f75ea97 356f7f14 7d0356ff
+          73843a34 cd67cdca bc6ae0aa 8d8b28c5
+          3eee3b22 115ea36b 0de676ce 4c0d9595
+          4d575ff6 643c8cdf 8d82c596 182f457f
+          3d49dc45 85626a49 876cb188 6fe326f9
+          79e22be2 2bab145e 97edd8a1 cee27248
+          c6d08152 70da7122 9e963f1c d0678ad7
+          bff7a96c fc72a644 f4172c68 99b09d21
+          a5c966fb dfd0eac0 9f29c5be c78c5d2b
+          31687569 a53523f3 42d572b2 de0e8dee
+          d2604dbd 2c9df49a d42d5fd9 a29729f4
+          993b674e b6b1383e be731e2f 5474d44c
+          a70f0a12 e225efb0 03a5e0f4 13762cd4
+          99cc52aa 02dda669 843ac41a 7acacfe6
+          78f7a994 82608746 e16ef374 4b52e26d
+          6a84eda1 1af8fd9d 1aaaf3c8 f2e7de90
+          606d6dcb 829afe58 5685bb9e 179f2389
+          ddbbb62c 10a21d85 3a9338d2 92a4eb49
+          7f926c15 eca47e07 9a15757d 55cfff49
+          d67e3045 b420a10e cd5d6852 6149493d
+          63f0864a 961211ec d0647b6a b18c57bf
+          7c28acb7 43a38b43 24505727 4b9e785e
+          85bbba96 853b4d13 b3d52add cf395d92
+          8abba83b dec453d9 8ed0d7aa 9fb1bb4b
+          ae949c7b ba24f7ef 2de2f3ef 50a8ab59
+          be5296bd f8a66881 20a10ecd 853aaf35
+          2df59ec1 eb2bbea7 18ada8ab 608d5deb
+          6b91e776 ca480b95 574c53b1 bb170541
+          531d6f5c 4e96945c 70a6d8e2 dd2dff73
+          769bac79 fd1d299d 3557c2aa c3e6e96c
+          bbec688d 4daa937b 97185bdf 5813d5f5
+          11deb13d d06b56ac 9265135f 91909717
+          f5d1dca0 5142d68c f4f706ad 293d4135
+          26040982 1d9a6d9b 8341f9be 6bce1f82
+          a5156f99 ac924c45 d074b8cb 9492f3ff
+          2a36777c cbaf2d93 592abe9d 2b6bdefb
+          44825b3c 62b252ca 76d36e84 45acf10e
+          c9fdc328 c93e647f 31e9536d 3bd2beab
+          6baa76f9 4a593af1 5515ead8 830c3183
+          dd624b4a f2b0c11b ab6a2906 c10e2db9
+          677c3ef3 8f7d8baf f6af5ef7 1fd5f9c6
+          53113462 364b5c76 4634dc25 b85bdc89
+          47d49ff3 aedf24cb 9f7f5dea d7977182
+          407b682f 822271b9 695270ec 9192d4b7
+          8798823b be92a376 c52ae32d ea303375
+          88d58684 a4c69a96 7ce2c095 1b3e333b
+          d9af8e60 87169bdb 29dd1edc 5831d964
+          97c3d5b7 ecf58e26 eee08699 bb0bf499
+          bb96873b fdcf856a eb64f53b 1f4bc5f7
+          3fea0d35 87b9b7c5 0e563fe2 d52492da
+          bf9774fe d3e1624b 4d51df46 767ca66e
+          d94a59fa 2c337568 51a8f338 0af2eee8
+          3b6ff158 8bdb1da6 22043bec 8070f516
+          f9a14f71 567043f9 1493437a 53116c3f
+          dc6549c9 f97f115b 6242cb36 9e6da069
+          11a99cf3 83acfb70 8af8ca6b c56ca79c
+          6da67df0 8938d3e3 a5e0d823 2465707f
+          316bda8e ff43cc26 a9fa7191 2c7ff12d
+          d182018a 8a98975d 2420ef5a 33534e1d
+          bca1d24f 390876d8 09a18a0a f97150af
+          43026b4a 5f31bb84 a304d04c b8cb34b6
+          b6882fe8 14ddc0ac 857f4e6f 037c65e5
+          b2eefd29 52f1c3a2 e82905ac bd6bb534
+          fd29abfa 19a50fe9 2df9471d 2c8eb454
+          f563dcc1 f5740d3f fbcd5f7f 2b6bdff9
+          54b4809f b75f1153 c42fabac 592907f6
+          9ffff36a 6b5a3a05 21d86167 054b375b
+          171d76c0 0d9e054b ae36db25 8e8aa0e9
+          bb59c416 1727ddce 3a45128b bab67ce6
+          ae211484 8341a95e b454d6be f7a978d6
+          5789c529 74f6ada9 53553fce 7040c45d
+          90219d8e 3a48127b 9788453f 666e4743
+          9dbe9fa1 fafdeb3f 9e2a1ba7 ce8cced4
+          f173468c b645f348 a53d2fed 8cbe7317
+          7e64cbcc a226043b ecaa79c5 9ddcde65
+          eb5eb6c4 c9d16ab4 4e338ced f4fe2256
+          b70a7767 9c2049bd 4a8c4d8a 5bbc6f9d
+          eaf023ea f7076b6a a5f4ab6f 65d3b4af
+          25501b16 2b016fdf fe4823d1 c7ae8e24
+          9be41ebc bfa40f1f 24d6f838 7dcfcb1d
+          7aec6ed0 ff4c2020 abdefe48 ca66ff20
+          5a28c0b6 3788490b 882fae77 f7877b7c
+          38f53a7b 6e1e7bac 12ecb03b f8d7ac96
+          8507ec97 eb5fb571 aa0a7725 6c328bed
+          0601fd18 a9b878c9 3964a4e4 a8206024
+          831d99bd 3389b1cf 9dafb44c 364fff46
+          0580ef45 f30b3378 fb20d085 f4409760
+          958c9143 2463c800 71666588 d9668bfe
+          cd1d6dbb 2d66f157 6c9155af bd2d35cb
+          578b160c 89892dea 11b34d50 ed8147a6
+          3abbe59e 3070d9fa 6a0a42b0 c3ee0c77
+          ab56caa2 83c71ce9 5db17e92 0a771984
+          3b6c3714 84f520e6 90943ec5 5278eaf1
+          62723854 4ad88181 76c3e3ba 903f20de
+          75eb65f3 8c6fa562 ee420907 d53f57fd
+          a308047b 36988754 90b6bb2d 92b1df10
+          c91c3a40 1c99e962 b1dba3c1 7b4767e9
+          f49fa50a 759e55eb 64d9f3af 8bafbcd2
+          5857c94c 1d5a18ea d63a3a65 1dd4fb8b
+          af96390a bb511382 1d767bb8 5bb1ccba
+          f4b493ff 53f7ddf7 57991dac b743f301
+          413f85c0 dd255fba 9d71a238 32d25562
+          08eed891 62faa33b fdf1acd7 2bbe8da5
+          52fedd3c 299ff3a3 84eac2c6 1bb46c91
+          b21b7f5e 2a7707f5 fde8529c 923e62b0
+          a4f5eb25 8eac74b1 ba1aa64a 77e6ad57
+          abc5f879 577c3357 d6bef7b9 f8abb744
+          031da10e 2d0b7515 ce2e3917 f6fceccb
+          379cdd8a a909c10e 7bcafcc1 bd536ae7
+          fef4ac35 4efec4ac 1d9a0d0b faf5a13e
+          ae2c7df3 dac325b9 6fef6840 08efe0f6
+          530debb9 421e15f0 4acb65cb a29fa56c
+          f65ca92f add32783 8c4d8e99 c5dbf18e
+          530f73e1 867717e2 f292256b f408492a
+          e926f6e4 44b1b81a 367edd99 40a7b35b
+          45abf7ca 864fbf94 cd5f7d2f a1fa7a4e
+          1a418b69 7e09b907 0f78aae8 8597ffe9
+          ecde83bd 700876d8 93bc0b17 c892e3fe
+          d8d9b36c f57b2adc f521dca1 f974177d
+          346b4b4a 948c617d 24ff8883 c5141727
+          e2df896d a8f480a7 bf41ebf5 49a06a8b
+          d4afdb28 553f2d91 eac54bc5 5f1d349e
+          fae95ba5 3093d7cc 8f231cdd b224a4f2
+          5a7c9a4b 927b7597 b4febdc5 9e91268e
+          e424313b 9dd130b7 b3814eff 21d86de2
+          5db95a56 bef68e78 3695ab9f 57805087
+          1d1a7484 3cf245c2 a05ea7f6 9db37033
+          0521d861 6f85bb13 8f3ddeb3 64c50415
+          ee320977 883902d7 d7c7b96c 92d0255f
+          0afe74b8 c4752d50 ad7778c7 67efb686
+          07fd130c 4aa0b64e 02d535e2 59ab42de
+          a22552b7 728df8aa fcc62cd4 d690d791
+          67f38cf7 1c42d130 a7473557 924d124b
+          8a24a9a4 58e2f373 8cd93963 63697dea
+          53ff79ec 4a9bacaf a5543f93 cdd367c9
+          e62f668b b7ac42fd 10984dc5 0e87bad5
+          71859d8e ebfed63b f3e2fa0f a026043b
+          ec2d9e05 3fda575e 74c1f5b5 5fcfd2d7
+          db719e2c 62870c3d 59e88f66 33d3256b
+          cc10c91a 355c7de3 14f10776 2e50e80b
+          b62cd1e4 10f1fb8d 8017aaab 177fe516
+          a95db15a 6a96af14 cf863209 374c0e1a
+          dbae3504 bdf61836 8cfaea93 6de1e8d7
+          7a452d36 7defb92c 492c2a54 a1ba5343
+          904b147b 528231b3 265aa4e1 0fecc2bf
+          589f4975 dac5b766 9dacff60 aa542d5e
+          29a15a0f 27896067 425d655c 7197abbb
+          bf31f999 b87e843a 821df6ba 4587ee9f
+          5ef9d9f4 676c7172 0cb37668 294de538
+          5ba24b12 bb1548ce c163c4dd bd9b4858
+          8baededf e9d6a421 e4e93379 ea9f15aa
+          ad5541af 56053d8f f8abaac4 bba94c7d
+          4ac55b56 2efe8a6a 23ec45a2 7d49742d
+          7fc35ebb 5bffc2b6 8bfc7f79 83736f2f
+          fa6fb8a7 b6ae55dc f66be357 ad6146ae
+          e16fdb1d 7a5de3c5 959d699c 04e2cac9
+          12474ab2 d812dc46 90b3a85f 8dfa18eb
+          1cb55d9b 9ddb4a05 f3486dbd 6c9a314b
+          cabffd51 d578b391 13393d04 3bd12e44
+          dcc3873f 55f8c8a3 97c70d18 e4a12204
+          3bec0375 dfcc92e5 7f3bab7b ddc29f5f
+          53e1ae3f e10e2dce 2ca16850 72666448
+          4abf62c9 5501cfaa bf39ebf5 eddce3d9
+          df87bcad 8f6bf5af 55600c7b bc12acf7
+          48d8eb95 90fa77e8 ebf47c95 5512aa51
+          01b0a6ce 7831433f 845ef3f9 55e80b18
+          dfeb1b26 6ba15f8f d1d2f672 8dcc0d61
+          d2a23f52 7698d597 26b1c5c7 8925ce25
+          16875dec 8909c663 547b7292 3852938d
+          3758cd76 bbb181b0 f171c7ab a467fd75
+          562ebc1b ff0bf4c7 ae2a48d7 2d5e2a1b
+          3e9d2635 cb56ab10 ed8fced2 f1d62b76
+          f89e55b7 a94766a6 1e32e6d4 9e9f4e5b
+          43410876 d887eae7 7e2bcbcf faeb09b5
+          0b163fac c25d0ee1 0e2d4f77 d1b7322d
+          2eb32476 2d90b421 fd2563f0 00914415
+          483cbe9d 5fc0df64 d06b087b db1c8315
+          f1f944f3 eb412e28 5a30fa89 86b9b0f1
+          b5f17bd4 ff868816 dd6439a2 eddd6867
+          6a08a7fa affad631 3a7d3f39 93d52a66
+          abc50877 6615b0f4 3d034d5b b72531a6
+          f0b48630 b79b66e5 b6a5ef67 a7feddbe
+          556b64f3 d7dfc996 c52b8cd9 50638293
+          593aec64 a80b7964 657ccfa2 b30a9f9a
+          383d61e4 686a42b0 c33e0f77 73beb1af
+          baeacaff d44c9f71 95c52e4e 2a821dca
+          777afe50 39ca9e12 2f09859d 24a57f6f
+          49571f49 7437cce0 eda140b5 7546afe1
+          c48b5f9e bffef275 43afb3cd 2ffb22fc
+          fe6601dc d6531f22 db7e1dd9 7d21787b
+          c1585f8f a7025d60 c36663ab 99ca1f7f
+          16cfc60d c663757d d3686067 435dd023
+          95ee5edd ffdd6de2 a409ee61 23a8491b
+          c7f8ae9d 881f3c2c e0c8cd7a 2414963e
+          16931cc7 ac1d76a8 6dd7f395 0a07819a
+          7aa998bb 586a57ad 932d0b97 484adf9e
+          92aa3ea6 e4c4e80c 5e68371f 13a96914
+          3f56a073 3a8c5f7d 6bd74bc5 773f48f5
+          925552bf 7e9d04eb a2e7f812 eab04bb7
+          a05f2479 ccc8773b df3dfe45 421dc10e
+          ad4ce605 976caa5f b8f886ba 1f1676b6
+          c5c940c2 1d76d4d6 05f781aa 3a29fb66
+          81d42e5f 2d553f2c 9484a2ae 92dabf97
+          d872b2a3 fbdf0543 84b23dda 32eb6fb9
+          3a8d5ad7 2efa59aa e62f96ba 95eba46e
+          ed5a0979 224698b3 ba281376 75e0a06e
+          e5b0cc74 14e4dee9 1ebe5f2d 0569273f
+          561ec5b6 2f75b3bf 96e5175e 787cddbc
+          f90fdbe3 24971f2f 7669341f 6cd8dc38
+          d12e095d 3a4b5249 5749ecde 4de2f272
+          44e25cd1 6d52f459 3c2eb45d a76f59a2
+          af9fb398 24545619 dd2ee6e7 e552b374
+          95d46f28 351e959b 39a717bb 33d47964
+          85bb6fcf f3bbdeff e094c483 0ea12604
+          3bb4e270 675b7ded 75ffae99 31ed1a8b
+          8df576d8 757ab8d3 5fb2b0c5 9924ae20
+          5fe27332 25be204f 12bb7511 871ef2f4
+          d7468381 e806bbcc e4b5b0f5 3545c39c
+          cd6684b9 484dad11 e26a57ac 12cfa632
+          f1acdf28 bed26a63 fda3f1b8 95375db1
+          1b2fbd80 47eaddfd 7adf54f8 d863e313
+          468ea128 043bb476 6baebc2c 67d5bd0f
+          3fe88c93 3ff34816 bb2de069 d13df0f4
+          66c39e6c 17772715 f23ae54a 5c7e8ec4
+          e7668b33 2b5d443f ae4c9fc5 0b376cef
+          41d0db26 c899a361 ce6a358a 182c2d37
+          8e658b86 b872a95b bd567d5d 69944c7f
+          2c6e3c1a 27d06137 0b074512 f61bf55c
+          e7b1775e ad425d29 15695f58 63d74e25
+          1df9c78d 495f4cbf a56eee0f c5ec6f87
+          dd964df4 5cd23007 1cf604a4 72fe0ae3
+          e34c8f93 b89c6c71 a960e7ca ca50014f
+          7dd252c5 9e9a24a2 efe5166e 388e61eb
+          c6bced3d ec6d7ddb 570f715b c35c2020
+          e19a5af1 95578aaf acc20872 9e8da5ea
+          b349bc9b aa8cbdfa 8c30678f ee0c03ec
+          999bd858 223b3371 d8a03b08 75edf447
+          cc8c5dfb 5537eb2b 597ef1a5 27d57d3f
+          ef4115ee b20977d8 23b69e88 158cee00
+          a22fea37 c25d7a9a 3854b0d3 37efd543
+          9e2d457d ad429e2d 2951243e aee1cf46
+          7edd4a25 d2b0e79b 16d966cd 5ee4d79d
+          46f6555b 653235fe fa972d5a 4cbfeed1
+          b7f56b3d c4d5d7ab e0eb9340 d516e398
+          357f55b5 71fa4660 4badf855 b0f39696
+          a9efa3e7 ac313387 bd1aea3c b2deddbf
+          ef059def 1afb7ef2 e1475113 821dda5e
+          b8fbdab1 f25fd75c 5b3f6bc6 b5aaf360
+          bd1df67c ced31a0e bd6f38fe d46c9c6e
+          e1364e66 304e6a50 41cf9ee0 168bcb29
+          d67897d8 12128c0d 802d4ebb b1f9afd5
+          e51293cd aa3eb686 fdecccbf 1e55a687
+          a616745e e253a129 10fc6d28 6bf43f34
+          12dd1b4e df4e647b cd60b861 96716bd8
+          0c87a29b 3a1b1b2b 07440b86 8c5332c2
+          fa6c9cbe d1b21ee6 6a6b2554 53a73a50
+          8f0a717a 98db22be f26a638d a234fce7
+          e841ce64 e15ac1de a585a42e 7ee87eb7
+          771937f6 ee8451fb 87a948fb c4a3d876
+          ce3d62a4 3fed9823 1faf9a3e a3a7d326
+          a7306b87 3d3e5ad4 9f42da1b 8ee33202
+          9488bfaa 4e859bba 68e89386 993dd5fa
+          d812ec62 4f4e3682 9d7e1c97 7e8283cd
+          1d2f6615 b8f4a3b9 a2ff3cb3 98ac1695
+          eb1a1e6b 9a4cb13a 2f49e959 28ae4eb9
+          d16d59b6 470547ef ba8d52f5 d3f2a6cf
+          556d984d d454428d 349cebaa 35bc01ac
+          1f771656 e15153e1 31a4429e 1ef4f423
+          d2f423d3 fcb5e15f cebf3526 f12cd10f
+          db9360df de98c658 e7cd4e47 1efa34a1
+          8e608736 2eaedfc0 4d4903fb df5ef7fd
+          0f25b638 1940b8c3 deee508c e0646d9c
+          9b42f501 09d69446 2fc96d4e df6aea12
+          6de965ab 4f8cf53e f5087115 756d3ed8
+          39ecc61b a88bdff8 48eccdff cfdfee5f
+          337e356f 736086fa da4e8043 2bbc0783
+          1e9993d8 afcfddf1 83869451 10821dda
+          b8e4c38f 146b72d2 fc65175e 7c73fdbc
+          1f1e63bd 1d5a455f a38721cb 1e7824e9
+          1563c62f e69a3cf5 f7cd369b 11ea0863
+          68e7a16e 7d7cbf3e 77747bf4 918509a3
+          f6a726ed 1c5b5d76 10eee123 a5f88909
+          1fba870f 7b540be9 5d1f00a0 bd53edbd
+          cf3d74c8 2345131e 9d4ca823 d8a19d89
+          1f3ac29f 77f1f94f fa03f20e 6fe00140
+          3b67320e 877937e7 efe73ce7 de6f0ccf
+          69087668 8facd9b9 9b12fbf6 b933e491
+          45843b00 68bfa14e 6fe7137b f7badd9e
+          d7693d05 21d8a19d 4a3eec48 e93ef1e9
+          1f9cbd7b 5fad6e7a 36a70480 7648b5ef
+          9b9d3d7b fea7f8a9 277e483e ea180a42
+          b0437b16 3f7898f4 787ed214 57bf7e0f
+          6a61f153 1100683f 54bb1e72 f5edfb50
+          f749cfbc e31e318a 8210ecd0 11c40d1c
+          ec2bbceb f6670201 798f6a00 40fba1da
+          f54fbade 7ae30bee a1238254 8360870e
+          c464776c 8cefdefd c6b05796 530d0068
+          fb547bbe 3ebeb8e8 bf2667dc 6aaa41b0
+          43079378 d0a1d2f3 8d5717da ba15fd33
+          ec930a2a 02006d38 d4f9a4ca 5658784d
+          8f975f9a 9374f891 14846087 8e28aeef
+          00e9fdf6 9b536c85 45f74434 09501100
+          687b54fb add9ba76 7bacd79b af4f8e1f
+          3c948210 ecd091b9 7af7f3f5 7cf1b967
+          fc7ef980 6a0040db a3daefd9 25cf3ef5
+          54dc8041 1eaa41b0 03440b06 37bbf2f3
+          afd382b2 866a0040 5b6abfa5 dc959777
+          45241c5e 493540b0 832161f4 fed2f7d3
+          8f179b33 722ed042 524b4500 a00d84ba
+          9078cde9 39ffecf3 c17bb313 0f3c9882
+          8060875f 397bf492 7e5f4c9d 6e4acdba
+          2712115e 93078056 4cb5d311 534ad673
+          7d3ffbf8 2357bf01 1404043b 3411eeba
+          f7a8ef37 f5b34783 3ef9906a 0040eb15
+          f0c9fcbe 9f7c78a7 ab77df4a aa01821d
+          b64bf3fa ca6cc9c9 574734a1 b1008056
+          48b5cff5 aa9dfe9b e6f3b15f 1d087668
+          5efce021 d2ffdbd9 3f47ec09 a744221c
+          390600ad 2ad44524 18b1255c d9ffabe9
+          0bdcc3f7 a32020d8 21366751 7719306f
+          ee2ccd1c 37567dab 51110068 1db94e33
+          c5bdd2ff bbd96fb8 7af561e0 0d821d76
+          20dc1516 d50d5cf0 e32341af 7c413500
+          60df53ed f19201df 7f77bdab a427a705
+          8160879d 181a0683 65268be5 02f5651d
+          d500807d 4a53edf1 c5915068 3da500c1
+          0e3bc5d5 bd870c5a b26845c8 2b27500d
+          00d87782 5eb972e0 821f67c5 f5ee1ba1
+          1a20d861 a7390b8b b4c1cb96 ce548dca
+          6d540300 f63e35b8 7e4b0db2 5f7095f4
+          f4520d10 ecb06b4c 26711476 ab1bb262
+          f90361af 7c494100 60af5aa3 895c6bb2
+          58caf5f6 1820d861 b7843bb3 d359ae1a
+          9773d577 1c320d00 7b49c02b 170e5e30
+          7f85b36b 378a0182 1d761f5b 56b60cfe
+          79c96ad5 c89c4635 0060af84 ba7f0f5e
+          b8e04b57 afde6131 d3658360 87dd7ab5
+          98c559dc 3d3464c9 922941af dc474100
+          60cf09fb e48bc18b 7e7ad2d5 ab978747
+          b020d861 8f7176ef 5e3f68c9 92db2301
+          99413500 600f3049 69282217 d83232ca
+          f46f0082 1df6287b 7e7e4528 2c670beb
+          ed006077 873a0978 e4e2c1f3 e62db3a6
+          a4500f10 ecb0e759 9c4e19b8 60fef280
+          57cea01a 00b0fb04 3d72c7c0 efbfff20
+          ae7f7f4d cc160a02 821df6c6 956396b8
+          de7d64d0 821f3f08 7ae5010a 0200bb2e
+          12965903 e67d7f4f fc8001ec 5707821d
+          f6beb8de 7d03037f 9c77b3c9 6265bd1d
+          00ec1a6f 302067c7 f5eb5749 2940b0c3
+          be0b777d fb5705eb 42e74b44 6aa80600
+          ec9ca057 2e1af0cd ac25bc2a 01821df6
+          b97eb367 2e0af9e4 1cd1a805 00ec9088
+          11eaeeef 3febeb57 dc43878b 98e89a41
+          b0c33ee6 1e3642fa 7e35fded b05f1e90
+          30f50080 96863ab3 c3b1b0df 8c69b7b9
+          87efe7a7 2020d8a1 d5481839 3adc67da
+          e7b75b92 1266eb8d 15002006 4dfc816a
+          ff5909a3 c654500c 10ecd0fa c2dd9803
+          cb4215b5 7f8f84a4 9c6a0040 33c21209
+          05e492de 9f7c3487 62806087 56abe483
+          f7e7ab60 77452428 21aa0100 8da93652
+          c20179a1 d787ef4f 4a3af470 0a02821d
+          5aafe4c3 8f921e1f bcfb7224 2c8f6801
+          1eca02c0 6f429d26 624d495a d4f3bdb7
+          6f483ee2 2806c020 d8a10d84 bb23ff18
+          2e79e77f 77da7332 be8ef032 0500fc1a
+          ec42521d aeae3e37 f9e83fad a11a20d8
+          a1cd48f9 e3b19b35 6ffd6591 806ce40c
+          6b0050a1 2e20fada ba9b8b9e 7f7926d5
+          00c10e6d 4ed7479f fade6497 eb34af78
+          0977003a 2cd5fe69 d1cd4c5e 2b7ef5a5
+          09a9279d 4a4d40b0 43db9376 f26952f4
+          fc4b93cc 0ef344cd a3c6a984 3b001d90
+          feb2843d 27e387a2 179ebf41 b58b9c03
+          0b821dda 76b8eb36 f1b93b1d 9d73a747
+          82d40340 0763321e c1564b24 fcafb453
+          cf5c4a41 40b043db 0f77a79e b1ceecb4
+          5dab0565 35b37600 3a52a8d3 bc12323b
+          4de3f36f b9f3130a 02821dda 8d9cabaf
+          9f6d4d70 dca279a4 967007a0 23880425
+          e2c8cb7e bbeba38f df9779ee f9140404
+          3bb41f7a a3d6e5a1 479eb124 c44d52e1
+          2e48b803 d0aee9b3 7541996f 4e70de94
+          71cedfeb 29080876 687732ce 3a5785bb
+          07ee7674 e9f425eb ed00b4eb 50e7910a
+          8bdb7e53 d6c5ff5c 404140b0 433b0e77
+          e7adb117 e4dc140e ca7266ed 00b4d350
+          e733bb5d 8f761e7f cfe4ec4b 2fa72620
+          d8a17d4b 3be9b4af ec298963 c31ed942
+          b803d09e e8ebeaec f9b99f74 befbaef1
+          59175c4a 4140b043 fba78f60 0bee1afb
+          94353579 920a777e c21d8076 21baae6e
+          81b3a8e0 8eac0b2f aba12020 d8a1c3c8
+          fcfb45d2 f9ae3bee 75742e98 16e1186c
+          00ed21d4 79a4ca96 9c3036f9 e8636753
+          1010ecd0 f1c2dd79 17ad4918 36606c28
+          20cb98b5 03d0c643 9dd7929c f854a75b
+          6e7e29f7 aa6ba909 0876e898 dc23464d
+          7564a48e 67bd1d80 b62a1292 882d2ff7
+          d3825b6f 1a9b75d9 15140404 3b745c39
+          fff72fe9 74ebcd4f 58d3d39e 0d7bc54f
+          4500b435 e1802c4e 1c3dec9e ac4bafa8
+          a41a20d8 a1c3cbba e0d248c1 ed37df6f
+          cfcb9f1a 09530f00 6d87e697 1a5b6af2
+          fdf1fd07 4da31a20 d8015bc3 ddf997ac
+          4e3beec8 7b8201e1 906c006d 25d479cd
+          c9294f77 baf18627 72affb0f 0501c10e
+          d896a34b e1144746 da78d558 56530d00
+          ad594413 b166664d 2db8f186 71d9ffb8
+          92828060 07fc5ece 95d74ae7 db6e7ad2
+          9c92f68c 16900015 01d05a85 fcb222e3
+          a4631fce fec7ff6d a21a20d8 01db9179
+          fea5912e 77def298 393deb73 7d440c00
+          ad8d1694 1a5b4af2 7db6ecdc 8fa80608
+          7640ac70 f7f78b97 e65e74de b8a05f96
+          530d00ad 2cd405cc eee48905 37dff070
+          eef53752 1010ec80 96b02424 4e5523e2
+          7b2221a9 a51a005a 834844c4 94983aa3
+          e0d6ff4e c8be8c75 7520d801 2da6ef6f
+          5738ee8e 27c59dfa 846a4c59 6f07609f
+          0bfa645d de3f2eba 2ffbd22b 16530d10
+          ec801d94 71de45a1 827f5ffd b86a4c3f
+          d707cb54 04c0be12 098bd79a 9030d6ec
+          74bd4735 40b00376 92c96a5b 6a4d70df
+          1bd16415 d500b04f 429d26a1 88cdfd42
+          d73b6f99 987bcdbf 29080876 c0cecafe
+          e7955278 efdd9f68 d6f871aa 71f55111
+          007b37d5 a95c678d 9fd975dc ed0f655d
+          72b98782 806007ec a2ccf32e 946e0fdd
+          37316c76 3da41a59 0e1d03b0 d7047d52
+          d6f5b69b c6665dfc 8ff95403 043b6077
+          85bb73fe ee2f7ae0 dea7033e 992aacb7
+          03b03744 246476d8 1f520dce 07140304
+          3b60770b 8797986c b6f1eaab f51403c0
+          9e8e75c1 90edf9c2 fbc63f92 73f95554
+          03043b60 77cbbcf0 12e9fef8 239f04fc
+          e63bd4b7 9c4b0160 8f097865 4ef183f7
+          3e9275d1 65555403 043b600f c938e7ef
+          52f2cc13 2fa946f7 51aa0160 0ff1abcf
+          f888a6cd a11420d8 017b3adc fdf59cea
+          92e7263e a2c2dd54 aa016077 536dcbed
+          c58f3ef8 7ad64597 520c10ec 803dcea4
+          2e5d9369 4944649c fa6e1305 01b0bbf8
+          bdf262f1 84879fce 3cff624d cc749320
+          d8017b45 faa9a747 7a4c7cea 33d508df
+          423500ec 0e01afac 2e79fa89 2732cfbd
+          60a3c962 a12020d8 017b8bc9 6a93f433
+          fe1aeef1 ec33aff9 bcf23015 01b08bc2
+          9ac83526 abf52bf5 614b2510 ec80bd1e
+          ee6c2adc 9d7e4665 af975f78 34e8932f
+          a808809d 6b4c44d4 00f1ae92 c71e7e27
+          ede4d3d8 041d043b 60df853b 7bc4e28e
+          5f128ac8 38d538d7 5011003b 1aeabc1e
+          79b764c2 a3cf649e 739ed7ec 74521310
+          ec807d29 e9e0c3b5 9e139ffe 4c35ced7
+          e98d3400 b454d02b eb7a3dfb f4dd9967
+          9fb3d2e4 70501010 ec807d7e 21bb5c92
+          71fa19c1 9ecf3ef3 9a0a778f 98087700
+          5a406f2b 8211b9de 9a963ad3 e470b2e9
+          39087640 ab69a0ed 0ec938ed 8cf25e2f
+          bdf868c0 2bb3a908 8058a1ce e3917b7b
+          3cf6c83b 89071e12 a22220d8 01ad2edc
+          d9c5d9b9 60492022 3789496a a9088066
+          42dd7b25 131e7934 f3ecbf55 5bdc6e8a
+          02821dd0 1ac5f51f 10eefdf4 9353f4f5
+          763c9205 d094804f fc3d9e79 fadeacb3
+          cf5dc1cb 1220d801 ad9825de 2d197f39
+          2bd8f3e9 a75e5123 f2c70977 00b6a5b7
+          09014dfe 19d7b3e4 2b93c3c1 7e7520d8
+          01adbee1 b6d924e3 acb32a7a 4d9a746f
+          d027df52 11005b43 9d1af03d d0f3e107
+          5f8eebd5 37404540 b003da4a 036eb14a
+          c27e2396 fa35f937 ebed0034 84ba7925
+          0f3ff468 d6f917d6 58121329 0a087640
+          5b62cbc9 8df49af0 d8e73e8f 5ccffe76
+          40c716f4 8baf64c2 846bb22f bc70b93e
+          ab0f10ec 8036467f d32deb82 0b43dd1f
+          7df445af 479e25dc 011d94ba f7fd61b9
+          3ce59083 3f138b95 23c340b0 03dab2ec
+          8b2eaaea 3161c21d 61bf7c47 35808e17
+          ea7c1e79 a5e4de7b 5eb26664 b2093108
+          76407b90 7eeac94b d5885d3f 72cc4735
+          800e15ea 7e281a3f fe96dc2b feaf8e75
+          7520d801 eda57db7 39a4e4be fba6f838
+          4f16e830 c27ea92b baf7de2b 73afbc72
+          91fa96ad 4d40b003 dacdc51e 17273997
+          5f1e5123 f727fc1e 9944b803 dafb68ce
+          58573736 ebbc73a7 500c10ec 80764a8d
+          dc3d4577 8fbf3912 90f95403 68c7a1ce
+          236f14dd 7edb6362 a6ab03c1 0e68d772
+          aeba72a5 3f2457aa c6bf9e6a 00ed8f1a
+          b82dea7a eb6dd7e7 5fffef4a fd341a80
+          6007b4e7 46dfef97 6eb7def2 69c023ff
+          a51a403b 63925020 24ffcabf e1df4b29
+          06087640 4768f71d 0ec9bbe1 3fd2e5e6
+          9b1e0c7a e5392a02 b41f6ac0 36b6cbf5
+          d7bfa779 bd140304 3ba023c9 ffef8da1
+          ce37dd70 bd984cac b703da01 35507ba7
+          e09a6bc6 76bafd76 31bb5c14 04043ba0
+          c385bb1b 6f5d1ff2 44ae9088 6ca11a40
+          9bb6a1d3 bfaebeaa 60ec58d6 ce826007
+          74589188 74baf6ca 29219fdc aec25d80
+          82006d92 37e895ab 0bee1ac7 ba3a10ec
+          800ecd64 924e778e 97bcab2f 1f6fb2d9
+          82140468 6b833309 87bcf260 fe3f2f79
+          2912e618 5810ec00 289dc7dd 2726a783
+          bde981b6 c66a09e7 5e76e12d 9def7f58
+          4c160bf5 00c10ec0 d6913fa9 0e685bf7
+          ac7e5ca0 2dd2e5c1 c73c1403 20d80100
+          18900104 3b000000 10ec0000 0040b003
+          000000c1 0e000080 60070000 00821d00
+          00000876 00000020 d8010000 10ec0000
+          0040b003 000000c1 0e000000 043b0000
+          0010ec00 00000876 00000020 d8010000
+          80600700 0000821d 000000c1 0e000000
+          043b0000 0010ec00 000040b0 03000020
+          d8010000 80600700 0000821d 00000008
+          76000000 20d80100 0010ec00 000040b0
+          03000000 c10e0000 00043b00 0000821d
+          00000008 76000000 20d80100 00806007
+          00000082 1d000000 c10e0000 00043b00
+          000010ec 00000040 b0030000 20d80100
+          00806007 00000082 1d000000 08760000
+          00043b00 000010ec 00000040 b0030000
+          00c10e00 0000043b 00000082 1d000000
+          08760000 0020d801 00008060 07000040
+          b0030000 00c10e00 0000043b 00000010
+          ec000000 08760000 0020d801 00008060
+          07000000 821d0000 00087600 0000043b
+          00000010 ec000000 40b00300 0000c10e
+          00008060 07000000 821d0000 00087600
+          000020d8 01000080 60070000 40b00300
+          00406b62 a50400da dd88d566 13713945
+          c2daf67f 93fafbc6 ef038076 c4148944
+          a802d0e0 bb0c779d 56571f2f 266ad156
+          e94d9a55 0f6d0e5b f49bedb6 7e26d102
+          4109797c fa9768b3 3f70f5a3 743afc43
+          ab7c4e8a 01306307 a0bd8d56 55480b7b
+          7d46606b c9ef25d4 0120d801 40ab4e77
+          0436001d 132f4f00 000010ec 00000040
+          b0030000 00c10e00 0000043b 00000082
+          1d000000 08760000 0020d801 00008060
+          07000040 b0030000 00c10e00 0000043b
+          00000010 ec000000 40b00300 0020d801
+          00008060 07000000 821d0000 00087600
+          0000043b 00000010 ec000000 40b00300
+          0000c10e 00008060 07000000 821d0000
+          00087600 000020d8 01000080 60070000
+          40b00300 0000c10e 00000004 3b000000
+          10ec0000 00087600 000020d8 01000080
+          60070000 00821d00 0000c18e 12000000
+          10ec0000 0040b003 000000c1 0e000000
+          043b0000 00821d00 00000876 00000020
+          d8010000 80600700 0040b003 000000c1
+          0e000000 043b0000 0010ec00 000040b0
+          03000020 d8010000 80600700 0000821d
+          b0579828 01c07d0b 10ec80f6 c14f0900
+          ee5b8060 07000000 821dd08a 042901d0
+          e6042801 40b0039a 52ad3e11 ca00b419
+          fafdcaa3 58806007 34c943b0 03da9c1a
+          4a0010ec 80a6d413 ec803645 bf5fab29
+          0340b003 9ab28e60 07b4b960 574e1900
+          821dd014 d6d8016d 2fd8ada6 0c00c10e
+          684a39c1 0e68732a 280140b0 039af2b3
+          fa842903 d06668ea b39e3200 043ba029
+          8b1a3a0a 006d4144 b4e016ff b7140220
+          d8018d04 cbeb97aa 58c77d01 b48d5027
+          26a73d5c f8e0bd0b 290640b0 031ae972
+          dfb82a73 9cb34c75 18acb303 da40b433
+          d9eda5d9 975dc106 c500c10e 682ce7f2
+          ab23a638 e70215eb 586707b4 7e618944
+          bea20c00 c10ed83e 2d325d38 3316680b
+          42eaf305 65000876 c0f67b8a f2eacf44
+          e3050aa0 d58b8816 2aaf9f4a 2100821d
+          b05db9d7 fceb2773 bcab8a55 7640ab0e
+          751193d3 be32f7ba ab56520c e057a648
+          84de0bf8 bdb99db3 9e0f6d2e 3d450d7d
+          6c540368 8534f15b d252c60d 5e5ff95f
+          8a01fc8a 193ba009 a1ca8a97 54c711a0
+          12402b15 9160a8b2 ea390a01 10ec8098
+          32ff76fe 7473bc6b 338f6381 5619ea34
+          93cbb130 f3bcf397 510ce0b7 78140b6c
+          c79caca4 3b839535 ff67b689 9d6a00ad
+          48483ca6 78e7b943 abbcaf50 0ce0b798
+          b103b623 f5f83f3f 6271c76d 60d60e68
+          4522a299 e39c0bd2 fe7cca9b 1403688c
+          193ba019 730bb2c6 073694fe d36c172b
+          d5005a41 ae0b4abd 3523f96f 833754bd
+          463580c6 98b1039a 9174c861 f75bdcae
+          c51c3106 b4825017 36eec359 49871e41
+          a803b683 193b2086 79dd0bce f62f5ffb
+          80c92189 5403d857 a94e7558 76d7cad4
+          e38f3fa9 dbb32fce a12040d3 98b10362
+          700f1bf1 ac584d1f 45c29c46 01ecb35c
+          17903a5b 76c6e384 3a806007 ec92a217
+          5e93f4d3 4ebddc24 96d92adc 01d8dba1
+          2e2c41b1 9a5f8f1f 38e82eaa 0110ec80
+          5dd6edd9 9736a69d 76f2b522 d679843b
+          60af86ba 901a54bd 9d76d289 e716bf32
+          99820004 3b60b785 bb6919a7 9ff40f11
+          1be10ed8 3ba1cea7 06536fa7 9e74c245
+          452fbcc6 82708060 07ec5e85 135f9a1e
+          0d77f66f f599042a 02ec8944 67aca9ab
+          5583a889 e9271f7f 920a75e5 14056819
+          de8a0576 c28a73cf 2c287be1 f5fb440b
+          1c6cb24a 9298a809 b05b329d 2661b3d3
+          bec19a92 f764e2e8 a1b7767b fe558a02
+          10ec80bd 10eecefb 8b547dfc c5855a6d
+          d9e59ac7 dfd96411 27010fd8 d944673c
+          7aad8e98 ecb332ce 3cf9c66e 4f3f3f9b
+          a200043b 60af5b79 f179d915 6fbd7f5d
+          c45f75a2 0a786926 33010f68 6998539f
+          b0ea86ea cd2efb2a 9333756c ea3187bd
+          5cf8e424 6a0310ec 807d6bf5 1597e46c
+          7efed54b 4da1ba13 349f3fcf 641297fa
+          cb56421e d038d0a9 aec76f76 3aea2d6e
+          f78f619f 3c9271ca 096f7479 e4096a03
+          10ec80d6 65cdb557 cac6c79e dedfea92
+          8b359ff7 90883fe0 34025e34 e459a810
+          3a6090d3 3b1afd5d 72fd85a3 a0c9612f
+          d322aec9 39179d3b a160ec3d cb281040
+          b003da84 f5b7df2c 6bee1837 d0e69283
+          d4b70746 82c12191 40304105 3c3de8e9
+          6fa56f9d cf336df3 916d7e05 da46746b
+          ea1311cd 64b7054d 36db72f5 fd22f5f9
+          3ce895ff 75baeaf2 b2fc9b6f a76a00c1
+          0e68db36 3ffe882c fbc7ffa5 d85dd257
+          7ddb5f7d 0ad42741 7d52d4a7 93fa6435
+          84ba8486 3fa23fce b55139b4 3241f5f1
+          357cf44e c4a33ea5 eab3517d b634fcba
+          34e095c5 450fdebb 30eb824b bc940c20
+          d8010000 6007fc3f 3f9a076d 8a153e83
+          00000000 49454e44 ae426082 
+        </hexdata>
+      </glyph>
+    </strike>
+  </sbix>
+
+</ttFont>
diff --git a/Tests/subset/data/test_cntrmask_CFF.desub.ttx b/Tests/subset/data/test_cntrmask_CFF.desub.ttx
new file mode 100644
index 0000000..ff1c8c5
--- /dev/null
+++ b/Tests/subset/data/test_cntrmask_CFF.desub.ttx
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="LibertinusSerif-Regular">
+      <version value="6.8"/>
+      <Notice value="Copyright © 2012-2019 The Libertinus Project Authors."/>
+      <FullName value="Libertinus Serif Regular"/>
+      <FamilyName value="Libertinus Serif"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-98"/>
+      <UnderlineThickness value="40"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-6275 -256 6171 1125"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-12 0 429 442 460 472 600 610 645 658 688 698"/>
+        <OtherBlues value="-238 -227"/>
+        <FamilyBlues value="-12 0 429 442 460 472 600 610 645 658 688 698"/>
+        <FamilyOtherBlues value="-238 -227"/>
+        <BlueScale value="0.0396"/>
+        <BlueShift value="6"/>
+        <BlueFuzz value="1"/>
+        <StdHW value="36"/>
+        <StdVW value="79"/>
+        <StemSnapH value="36"/>
+        <StemSnapV value="79 86"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="0"/>
+        <nominalWidthX value="595"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -95 endchar
+        </CharString>
+        <CharString name="Idieresis">
+          -298 -2 33 583 33 62 96 hstem
+          6 96 4 85 4 96 vstem
+          cntrmask 00011100
+          191 122 rmoveto
+          401 vlineto
+          0 83 17 5 70 3 6 6 0 21 -6 6 -44 -1 -47.5 -1 -38.5 0 -33.5 0 -48.5 1 -47 1 -6 -6 0 -21 6 -6 70 -3 17 -5 0 -83 rrcurveto
+          -401 vlineto
+          0 -83 -17 -5 -70 -3 -6 -6 0 -21 6 -6 45 1 48.7002 1 36.2998 0 35.59961 0 48.40039 -1 45 -1 6 6 0 21 -6 6 -70 3 -17 5 0 83 rrcurveto
+          4 635 rmoveto
+          -26 22 -22 26 26 22 22 26 26 -22 22 -26 -26 -22 -22 -26 vhcurveto
+          -189 hmoveto
+          -26 22 -22 26 26 22 22 26 26 -22 22 -26 -26 -22 -22 -26 vhcurveto
+          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/test_cntrmask_CFF.ttx b/Tests/subset/data/test_cntrmask_CFF.ttx
new file mode 100644
index 0000000..9e7d205
--- /dev/null
+++ b/Tests/subset/data/test_cntrmask_CFF.ttx
@@ -0,0 +1,417 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="Idieresis"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="6.79999"/>
+    <checkSumAdjustment value="0xd2a3b881"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Aug 24 21:44:22 2006"/>
+    <modified value="Thu Jan  1 00:00:00 1970"/>
+    <xMin value="-6275"/>
+    <yMin value="-256"/>
+    <xMax value="6171"/>
+    <yMax value="1125"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="8"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="894"/>
+    <descent value="-246"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="6272"/>
+    <minLeftSideBearing value="-6275"/>
+    <minRightSideBearing value="-953"/>
+    <xMaxExtent value="6171"/>
+    <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="4"/>
+    <xAvgCharWidth value="559"/>
+    <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="261"/>
+    <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 00000010"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ALIF"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="207"/>
+    <usLastCharIndex value="207"/>
+    <sTypoAscender value="894"/>
+    <sTypoDescender value="-246"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="894"/>
+    <usWinDescent value="246"/>
+    <ulCodePageRange1 value="01100000 00000000 00000001 10111111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="429"/>
+    <sCapHeight value="658"/>
+    <usDefaultChar value="32"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="12"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2012-2019 The Libertinus Project Authors.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Libertinus Serif
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      6.8;ALIF;LibertinusSerif-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Libertinus Serif Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 6.8 
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      LibertinusSerif-Regular
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0xcf" name="Idieresis"/><!-- LATIN CAPITAL LETTER I WITH DIAERESIS -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="28" language="0" nGroups="1">
+      <map code="0xcf" name="Idieresis"/><!-- LATIN CAPITAL LETTER I WITH DIAERESIS -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0xcf" name="Idieresis"/><!-- LATIN CAPITAL LETTER I WITH DIAERESIS -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="28" language="0" nGroups="1">
+      <map code="0xcf" name="Idieresis"/><!-- LATIN CAPITAL LETTER I WITH DIAERESIS -->
+    </cmap_format_12>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-78"/>
+    <underlineThickness value="40"/>
+    <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="LibertinusSerif-Regular">
+      <version value="6.8"/>
+      <Notice value="Copyright © 2012-2019 The Libertinus Project Authors."/>
+      <FullName value="Libertinus Serif Regular"/>
+      <FamilyName value="Libertinus Serif"/>
+      <Weight value="Regular"/>
+      <isFixedPitch value="0"/>
+      <ItalicAngle value="0"/>
+      <UnderlinePosition value="-98"/>
+      <UnderlineThickness value="40"/>
+      <PaintType value="0"/>
+      <CharstringType value="2"/>
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FontBBox value="-6275 -256 6171 1125"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-12 0 429 442 460 472 600 610 645 658 688 698"/>
+        <OtherBlues value="-238 -227"/>
+        <FamilyBlues value="-12 0 429 442 460 472 600 610 645 658 688 698"/>
+        <FamilyOtherBlues value="-238 -227"/>
+        <BlueScale value="0.0396"/>
+        <BlueShift value="6"/>
+        <BlueFuzz value="1"/>
+        <StdHW value="36"/>
+        <StdVW value="79"/>
+        <StemSnapH value="36"/>
+        <StemSnapV value="79 86"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="0"/>
+        <nominalWidthX value="595"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            -26 22 -22 26 26 22 22 26 26 -22 22 -26 -26 -22 -22 -26 vhcurveto
+            return
+          </CharString>
+          <CharString index="1">
+            401 vlineto
+            0 83 17 5 70 3 6 6 0 21 -6 6 -44 -1 -47.5 -1 -38.5 0 -33.5 0 -48.5 1 -47 1 -6 -6 0 -21 6 -6 70 -3 17 -5 0 -83 rrcurveto
+            -401 vlineto
+            0 -83 -17 -5 -70 -3 -6 -6 0 -21 6 -6 45 1 48.7002 1 36.2998 0 35.59961 0 48.40039 -1 45 -1 6 6 0 21 -6 6 -70 3 -17 5 0 83 rrcurveto
+            return
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -95 endchar
+        </CharString>
+        <CharString name="Idieresis">
+          -298 -2 33 583 33 62 96 hstem
+          6 96 4 85 4 96 vstem
+          cntrmask 00011100
+          191 122 rmoveto
+          -106 callsubr
+          4 635 rmoveto
+          -107 callsubr
+          -189 hmoveto
+          -107 callsubr
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GDEF>
+    <Version value="0x00010002"/>
+    <GlyphClassDef>
+      <ClassDef glyph="Idieresis" class="1"/>
+    </GlyphClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=1 -->
+      <Coverage index="0">
+      </Coverage>
+    </MarkGlyphSetsDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <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="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="1"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="2"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="3"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=4 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="3">
+        <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="Idieresis"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+            <ClassDef glyph="Idieresis" class="1"/>
+          </ClassDef1>
+          <ClassDef2>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=1 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=5 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <ScriptTag value="hebr"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="4">
+        <ScriptTag value="latn"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="0"/>
+    <mtx name="Idieresis" width="297" lsb="6"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/subset/data/test_hinted_subrs_CFF.desub.ttx b/Tests/subset/data/test_hinted_subrs_CFF.desub.ttx
new file mode 100644
index 0000000..50374b3
--- /dev/null
+++ b/Tests/subset/data/test_hinted_subrs_CFF.desub.ttx
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.37">
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="SourceSerifPro-Regular">
+      <version value="1.0"/>
+      <Notice value="Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries."/>
+      <Copyright value="Copyright 2014 Adobe Systems Incorporated. All Rights Reserved."/>
+      <FamilyName value="Source Serif Pro"/>
+      <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 -249 560 758"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-15 0 475 488 527 540 549 563 646 659 669 684 729 749"/>
+        <OtherBlues value="-249 -239"/>
+        <FamilyBlues value="-15 0 475 488 527 540 549 563 646 659 669 684 729 749"/>
+        <FamilyOtherBlues value="-249 -239"/>
+        <BlueScale value="0.0375"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="0"/>
+        <StdHW value="56"/>
+        <StdVW value="85"/>
+        <StemSnapH value="41 56"/>
+        <StemSnapV value="85 95"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="0"/>
+        <nominalWidthX value="604"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          36 0 50 569 50 hstem
+          80 60 360 60 vstem
+          80 hmoveto
+          480 669 -480 hlineto
+          240 -286 rmoveto
+          -148 236 rlineto
+          296 hlineto
+          32 -523 rmoveto
+          -149 239 149 238 rlineto
+          -360 -477 rmoveto
+          477 vlineto
+          150 -238 rlineto
+          -118 -285 rmoveto
+          148 236 148 -236 rlineto
+          endchar
+        </CharString>
+        <CharString name="y">
+          -92 -249 110 572 42 -40 40 -36 36 0 1 hstemhm
+          0 512 -195 195 vstemhm
+          92 -249 rmoveto
+          82 56 75 177 67 hvcurveto
+          161 425 54 11 rlineto
+          36 -195 vlineto
+          -36 vlineto
+          87 -12 -123 -340 rlineto
+          -125 341 88 10 rlineto
+          37 -240 vlineto
+          -36 vlineto
+          55 -8 181 -457 -4 -12 -19 -54 -29 -54 -42 -36 rlinecurve
+          -5 5 rlineto
+          28 -27 -21 10 -26 hhcurveto
+          -31 -29 -15 -29 -7 hvcurveto
+          -39 42 -27 50 vhcurveto
+          hintmask 11001000
+          endchar
+        </CharString>
+        <CharString name="yacute">
+          -92 -249 110 572 42 -40 40 -36 36 10 5 5 4 3 2 2 1 1 0 hstemhm
+          0 512 -195 195 vstemhm
+          92 -249 rmoveto
+          82 56 75 177 67 hvcurveto
+          161 425 54 11 rlineto
+          36 -195 vlineto
+          -36 vlineto
+          87 -12 -123 -340 rlineto
+          -125 341 88 10 rlineto
+          37 -240 vlineto
+          -36 vlineto
+          55 -8 181 -457 -4 -12 -19 -54 -29 -54 -42 -36 rlinecurve
+          -5 5 rlineto
+          28 -27 -21 10 -26 hhcurveto
+          -31 -29 -15 -29 -7 hvcurveto
+          -39 42 -27 50 vhcurveto
+          hintmask 1100100010000000
+          155 825 rmoveto
+          26 -19 41 36 39 35 41 37 rlinecurve
+          28 26 6 15 14 vvcurveto
+          26 -19 12 -19 -18 -17 -10 -31 -19 vhcurveto
+          -32 -48 -29 -46 -28 -47 rrcurveto
+          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/test_hinted_subrs_CFF.ttx b/Tests/subset/data/test_hinted_subrs_CFF.ttx
new file mode 100644
index 0000000..daf9f0e
--- /dev/null
+++ b/Tests/subset/data/test_hinted_subrs_CFF.ttx
@@ -0,0 +1,351 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.37">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="y"/>
+    <GlyphID id="2" name="yacute"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.0"/>
+    <checkSumAdjustment value="0x30fffb39"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jan  4 11:55:59 2017"/>
+    <modified value="Sat Feb  9 07:43:13 2019"/>
+    <xMin value="0"/>
+    <yMin value="-249"/>
+    <xMax value="560"/>
+    <yMax value="758"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1036"/>
+    <descent value="-335"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="640"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="560"/>
+    <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="3"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="554"/>
+    <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="285"/>
+    <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="121"/>
+    <usLastCharIndex value="253"/>
+    <sTypoAscender value="730"/>
+    <sTypoDescender value="-270"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1036"/>
+    <usWinDescent value="335"/>
+    <ulCodePageRange1 value="00100000 00000000 00000001 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="475"/>
+    <sCapHeight value="670"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="3"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Copyright 2014, 2015, 2016 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'.
+    </namerecord>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Serif Pro
+    </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">
+      2.000;ADBO;SourceSerifPro-Regular;ADOBE
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Serif Pro
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 2.000;PS 1.0;hotconv 16.6.51;makeotf.lib2.5.65220
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceSerifPro-Regular
+    </namerecord>
+    <namerecord nameID="7" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
+    </namerecord>
+    <namerecord nameID="8" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Adobe Systems Incorporated
+    </namerecord>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Frank Grießhammer
+    </namerecord>
+    <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      http://www.adobe.com/type
+    </namerecord>
+    <namerecord nameID="13" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      This Font Software is licensed under the SIL Open Font License, Version 1.1.&#13;
+&#13;
+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="1" platEncID="0" langID="0x0" unicode="True">
+      http://scripts.sil.org/OFL
+    </namerecord>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2014, 2015, 2016 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Serif Pro
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      2.000;ADBO;SourceSerifPro-Regular;ADOBE
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Serif Pro
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.000;PS 1.0;hotconv 16.6.51;makeotf.lib2.5.65220
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceSerifPro-Regular
+    </namerecord>
+    <namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
+      Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Adobe Systems Incorporated
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Frank Grießhammer
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      http://www.adobe.com/type
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      This Font Software is licensed under the SIL Open Font License, Version 1.1.&#13;
+&#13;
+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>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x79" name="y"/><!-- LATIN SMALL LETTER Y -->
+      <map code="0xfd" name="yacute"/><!-- LATIN SMALL LETTER Y WITH ACUTE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x79" name="y"/><!-- LATIN SMALL LETTER Y -->
+      <map code="0xfd" name="yacute"/><!-- LATIN SMALL LETTER Y WITH ACUTE -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x79" name="y"/><!-- LATIN SMALL LETTER Y -->
+      <map code="0xfd" name="yacute"/><!-- LATIN SMALL LETTER Y WITH ACUTE -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="SourceSerifPro-Regular">
+      <version value="1.0"/>
+      <Notice value="Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries."/>
+      <Copyright value="Copyright 2014 Adobe Systems Incorporated. All Rights Reserved."/>
+      <FamilyName value="Source Serif Pro"/>
+      <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 -249 560 758"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-15 0 475 488 527 540 549 563 646 659 669 684 729 749"/>
+        <OtherBlues value="-249 -239"/>
+        <FamilyBlues value="-15 0 475 488 527 540 549 563 646 659 669 684 729 749"/>
+        <FamilyOtherBlues value="-249 -239"/>
+        <BlueScale value="0.0375"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="0"/>
+        <StdHW value="56"/>
+        <StdVW value="85"/>
+        <StemSnapH value="41 56"/>
+        <StemSnapV value="85 95"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="0"/>
+        <nominalWidthX value="604"/>
+        <Subrs>
+          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+          <CharString index="0">
+            rmoveto
+            26 -19 41 36 39 35 41 37 rlinecurve
+            28 26 6 15 14 vvcurveto
+            26 -19 12 -19 -18 -17 -10 -31 -19 vhcurveto
+            -32 -48 -29 -46 -28 -47 rrcurveto
+            endchar
+          </CharString>
+          <CharString index="1">
+            195 vstemhm
+            -107 callgsubr
+            161 425 54 11 rlineto
+            36 -195 vlineto
+            -36 vlineto
+            87 -12 -123 -340 rlineto
+            -125 341 88 10 rlineto
+            37 -240 vlineto
+            -105 callsubr
+            -39 42 -27 50 vhcurveto
+            return
+          </CharString>
+          <CharString index="2">
+            -36 vlineto
+            55 -8 181 -457 -4 -12 -19 -54 -29 -54 -42 -36 rlinecurve
+            -5 5 rlineto
+            28 -27 -21 10 -26 hhcurveto
+            -31 -29 -15 -29 -7 hvcurveto
+            return
+          </CharString>
+          <CharString index="3">
+            -92 -249 110 572 42 -40 40 -36 36 return
+          </CharString>
+          <CharString index="4">
+            hstemhm
+            return
+          </CharString>
+        </Subrs>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          36 0 50 569 50 hstem
+          80 60 360 60 vstem
+          80 hmoveto
+          480 669 -480 hlineto
+          240 -286 rmoveto
+          -148 236 rlineto
+          296 hlineto
+          32 -523 rmoveto
+          -149 239 149 238 rlineto
+          -360 -477 rmoveto
+          477 vlineto
+          150 -238 rlineto
+          -118 -285 rmoveto
+          148 236 148 -236 rlineto
+          endchar
+        </CharString>
+        <CharString name="y">
+          -104 callsubr
+          0 1 -103 callsubr
+          0 512 -195 -106 callsubr
+          hintmask 11001000
+          endchar
+        </CharString>
+        <CharString name="yacute">
+          -104 callsubr
+          10 5 5 4 3 2 2 1 1 0 -103 callsubr
+          0 512 -195 -106 callsubr
+          hintmask 1100100010000000
+          155 825 -107 callsubr
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+      <CharString index="0">
+        92 -249 rmoveto
+        82 56 75 177 67 hvcurveto
+        return
+      </CharString>
+    </GlobalSubrs>
+  </CFF>
+
+  <hmtx>
+    <mtx name=".notdef" width="640" lsb="80"/>
+    <mtx name="y" width="512" lsb="0"/>
+    <mtx name="yacute" width="512" lsb="0"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/subset/data/test_math_partial.ttx b/Tests/subset/data/test_math_partial.ttx
new file mode 100644
index 0000000..c0a70da
--- /dev/null
+++ b/Tests/subset/data/test_math_partial.ttx
@@ -0,0 +1,391 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.10799"/>
+    <checkSumAdjustment value="0x266835f6"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Sun Jan 10 17:35:12 2016"/>
+    <modified value="Sat Jun  8 23:59:34 2019"/>
+    <xMin value="-761"/>
+    <yMin value="-509"/>
+    <xMax value="3000"/>
+    <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="3000"/>
+    <minLeftSideBearing value="-761"/>
+    <minRightSideBearing value="-365"/>
+    <xMaxExtent value="3000"/>
+    <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="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="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="STIX"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="65"/>
+    <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="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="28" language="0" nGroups="1">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="28" language="0" nGroups="1">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    </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>
+    <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="-761 -509 3000 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>
+        </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>
+      </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>
+  </MATH>
+
+  <hmtx>
+    <mtx name=".notdef" width="250" lsb="0"/>
+    <mtx name="A" width="722" lsb="15"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/subset/subset_test.py b/Tests/subset/subset_test.py
index e9b3cba..12682ab 100644
--- a/Tests/subset/subset_test.py
+++ b/Tests/subset/subset_test.py
@@ -1,7 +1,11 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
+import io
+from fontTools.misc.py23 import tobytes, tostr
+from fontTools.misc.testTools import getXML
 from fontTools import subset
+from fontTools.fontBuilder import FontBuilder
+from fontTools.pens.ttGlyphPen import TTGlyphPen
 from fontTools.ttLib import TTFont, newTable
+from fontTools.ttLib.tables import otTables as ot
 from fontTools.misc.loggingTools import CapturingLogHandler
 import difflib
 import logging
@@ -10,6 +14,8 @@
 import sys
 import tempfile
 import unittest
+import pathlib
+import pytest
 
 
 class SubsetTest(unittest.TestCase):
@@ -44,15 +50,14 @@
         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.
+                # Elide ttFont attributes because ttLibVersion may change.
                 if line.startswith("<ttFont "):
-                    lines.append("<ttFont>" + os.linesep)
+                    lines.append("<ttFont>\n")
                 else:
-                    lines.append(line.rstrip() + os.linesep)
+                    lines.append(line.rstrip() + "\n")
         return lines
 
-    def expect_ttx(self, font, expected_ttx, tables):
+    def expect_ttx(self, font, expected_ttx, tables=None):
         path = self.temp_path(suffix=".ttx")
         font.saveXML(path, tables=tables)
         actual = self.read_ttx(path)
@@ -74,6 +79,16 @@
 # Tests
 # -----
 
+    def test_layout_scripts(self):
+        _, fontpath = self.compile_font(self.getpath("layout_scripts.ttx"), ".otf")
+        subsetpath = self.temp_path(".otf")
+        subset.main([fontpath, "--glyphs=*", "--layout-features=*",
+                     "--layout-scripts=latn,arab.URD,arab.dflt",
+                     "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_layout_scripts.ttx"),
+                        ["GPOS", "GSUB"])
+
     def test_no_notdef_outline_otf(self):
         _, fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf")
         subsetpath = self.temp_path(".otf")
@@ -237,6 +252,13 @@
         subsetfont = TTFont(subsetpath)
         self.expect_ttx(subsetfont, self.getpath("expect_keep_math.ttx"), ["GlyphOrder", "CFF ", "MATH", "hmtx"])
 
+    def test_subset_math_partial(self):
+        _, fontpath = self.compile_font(self.getpath("test_math_partial.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--text=A", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_math_partial.ttx"), ["MATH"])
+
     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
@@ -298,7 +320,7 @@
         self.expect_ttx(subsetfont, self.getpath("expect_prop_1.ttx"), ["prop"])
 
     def test_options(self):
-        # https://github.com/behdad/fonttools/issues/413
+        # https://github.com/fonttools/fonttools/issues/413
         opt1 = subset.Options()
         self.assertTrue('Xyz-' not in opt1.layout_features)
         opt2 = subset.Options()
@@ -324,6 +346,14 @@
         self.assertTrue("x" in subsetfont['CBDT'].strikeData[0])
         self.assertTrue("y" in subsetfont['CBDT'].strikeData[0])
 
+    def test_sbix(self):
+        _, fontpath = self.compile_font(self.getpath("sbix.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--gids=0,1", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath(
+            "expect_sbix.ttx"), ["sbix"])
+
     def test_timing_publishes_parts(self):
         _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
 
@@ -333,10 +363,8 @@
         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
+        logs = captor.records
 
         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]))
@@ -418,8 +446,28 @@
         self.expect_ttx(subsetfont, self.getpath(
             "expect_desubroutinize_CFF.ttx"), ["CFF "])
 
+    def test_desubroutinize_hinted_subrs_CFF(self):
+        ttxpath = self.getpath("test_hinted_subrs_CFF.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(
+            "test_hinted_subrs_CFF.desub.ttx"), ["CFF "])
+
+    def test_desubroutinize_cntrmask_CFF(self):
+        ttxpath = self.getpath("test_cntrmask_CFF.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(
+            "test_cntrmask_CFF.desub.ttx"), ["CFF "])
+
     def test_no_hinting_desubroutinize_CFF(self):
-        ttxpath = self.getpath("Lobster.subset.ttx")
+        ttxpath = self.getpath("test_hinted_subrs_CFF.ttx")
         _, fontpath = self.compile_font(ttxpath, ".otf")
         subsetpath = self.temp_path(".otf")
         subset.main([fontpath, "--no-hinting", "--desubroutinize", "--notdef-outline",
@@ -447,6 +495,46 @@
         subsetfont = TTFont(subsetpath)
         self.expect_ttx(subsetfont, self.getpath("expect_notdef_width_cid.ttx"), ["CFF "])
 
+    def test_recalc_bounds_ttf(self):
+        ttxpath = self.getpath("TestTTF-Regular.ttx")
+        font = TTFont()
+        font.importXML(ttxpath)
+        head = font['head']
+        bounds = [head.xMin, head.yMin, head.xMax, head.yMax]
+
+        _, fontpath = self.compile_font(ttxpath, ".ttf")
+        subsetpath = self.temp_path(".ttf")
+
+        # by default, the subsetter does not recalculate the bounding box
+        subset.main([fontpath, "--output-file=%s" % subsetpath, "*"])
+        head = TTFont(subsetpath)['head']
+        self.assertEqual(bounds, [head.xMin, head.yMin, head.xMax, head.yMax])
+
+        subset.main([fontpath, "--recalc-bounds", "--output-file=%s" % subsetpath, "*"])
+        head = TTFont(subsetpath)['head']
+        bounds = [132, 304, 365, 567]
+        self.assertEqual(bounds, [head.xMin, head.yMin, head.xMax, head.yMax])
+
+    def test_recalc_bounds_otf(self):
+        ttxpath = self.getpath("TestOTF-Regular.ttx")
+        font = TTFont()
+        font.importXML(ttxpath)
+        head = font['head']
+        bounds = [head.xMin, head.yMin, head.xMax, head.yMax]
+
+        _, fontpath = self.compile_font(ttxpath, ".otf")
+        subsetpath = self.temp_path(".otf")
+
+        # by default, the subsetter does not recalculate the bounding box
+        subset.main([fontpath, "--output-file=%s" % subsetpath, "*"])
+        head = TTFont(subsetpath)['head']
+        self.assertEqual(bounds, [head.xMin, head.yMin, head.xMax, head.yMax])
+
+        subset.main([fontpath, "--recalc-bounds", "--output-file=%s" % subsetpath, "*"])
+        head = TTFont(subsetpath)['head']
+        bounds = [132, 304, 365, 567]
+        self.assertEqual(bounds, [head.xMin, head.yMin, head.xMax, head.yMax])
+
     def test_recalc_timestamp_ttf(self):
         ttxpath = self.getpath("TestTTF-Regular.ttx")
         font = TTFont()
@@ -477,6 +565,753 @@
         subset.main([fontpath, "--recalc-timestamp", "--output-file=%s" % subsetpath, "*"])
         self.assertLess(modified, TTFont(subsetpath)['head'].modified)
 
+    def test_recalc_max_context(self):
+        ttxpath = self.getpath("Lobster.subset.ttx")
+        font = TTFont()
+        font.importXML(ttxpath)
+        max_context = font['OS/2'].usMaxContext
+        _, fontpath = self.compile_font(ttxpath, ".otf")
+        subsetpath = self.temp_path(".otf")
+
+        # by default, the subsetter does not recalculate the usMaxContext
+        subset.main([fontpath, "--drop-tables+=GSUB,GPOS",
+                               "--output-file=%s" % subsetpath])
+        self.assertEqual(max_context, TTFont(subsetpath)['OS/2'].usMaxContext)
+
+        subset.main([fontpath, "--recalc-max-context",
+                               "--drop-tables+=GSUB,GPOS",
+                               "--output-file=%s" % subsetpath])
+        self.assertEqual(0, TTFont(subsetpath)['OS/2'].usMaxContext)
+
+    def test_retain_gids_ttf(self):
+        _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
+        font = TTFont(fontpath)
+
+        self.assertEqual(font["hmtx"]["A"], (500, 132))
+        self.assertEqual(font["hmtx"]["B"], (400, 132))
+
+        self.assertGreater(font["glyf"]["A"].numberOfContours, 0)
+        self.assertGreater(font["glyf"]["B"].numberOfContours, 0)
+
+        subsetpath = self.temp_path(".ttf")
+        subset.main(
+            [
+                fontpath,
+                "--retain-gids",
+                "--output-file=%s" % subsetpath,
+                "--glyph-names",
+                "B",
+            ]
+        )
+        subsetfont = TTFont(subsetpath)
+
+        self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder()[0:3])
+
+        hmtx = subsetfont["hmtx"]
+        self.assertEqual(hmtx["A"], (  0,   0))
+        self.assertEqual(hmtx["B"], (400, 132))
+
+        glyf = subsetfont["glyf"]
+        self.assertEqual(glyf["A"].numberOfContours, 0)
+        self.assertGreater(glyf["B"].numberOfContours, 0)
+
+    def test_retain_gids_cff(self):
+        _, fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf")
+        font = TTFont(fontpath)
+
+        self.assertEqual(font["hmtx"]["A"], (500, 132))
+        self.assertEqual(font["hmtx"]["B"], (400, 132))
+        self.assertEqual(font["hmtx"]["C"], (500,   0))
+
+        font["CFF "].cff[0].decompileAllCharStrings()
+        cs = font["CFF "].cff[0].CharStrings
+        self.assertGreater(len(cs["A"].program), 0)
+        self.assertGreater(len(cs["B"].program), 0)
+        self.assertGreater(len(cs["C"].program), 0)
+
+        subsetpath = self.temp_path(".otf")
+        subset.main(
+            [
+                fontpath,
+                "--retain-gids",
+                "--output-file=%s" % subsetpath,
+                "--glyph-names",
+                "B",
+            ]
+        )
+        subsetfont = TTFont(subsetpath)
+
+        self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder()[0:3])
+
+        hmtx = subsetfont["hmtx"]
+        self.assertEqual(hmtx["A"], (0,     0))
+        self.assertEqual(hmtx["B"], (400, 132))
+
+        subsetfont["CFF "].cff[0].decompileAllCharStrings()
+        cs = subsetfont["CFF "].cff[0].CharStrings
+
+        self.assertEqual(cs["A"].program, ["endchar"])
+        self.assertGreater(len(cs["B"].program), 0)
+
+    def test_retain_gids_cff2(self):
+        ttx_path = self.getpath("../../varLib/data/master_ttx_varfont_otf/TestCFF2VF.ttx")
+        font, fontpath = self.compile_font(ttx_path, ".otf")
+
+        self.assertEqual(font["hmtx"]["A"], (600, 31))
+        self.assertEqual(font["hmtx"]["T"], (600, 41))
+
+        font["CFF2"].cff[0].decompileAllCharStrings()
+        cs = font["CFF2"].cff[0].CharStrings
+        self.assertGreater(len(cs["A"].program), 0)
+        self.assertGreater(len(cs["T"].program), 0)
+
+        subsetpath = self.temp_path(".otf")
+        subset.main(
+            [
+                fontpath,
+                "--retain-gids",
+                "--output-file=%s" % subsetpath,
+                "T",
+            ]
+        )
+        subsetfont = TTFont(subsetpath)
+
+        self.assertEqual(len(subsetfont.getGlyphOrder()), len(font.getGlyphOrder()[0:3]))
+
+        hmtx = subsetfont["hmtx"]
+        self.assertEqual(hmtx["glyph00001"], (  0,  0))
+        self.assertEqual(hmtx["T"], (600, 41))
+
+        subsetfont["CFF2"].cff[0].decompileAllCharStrings()
+        cs = subsetfont["CFF2"].cff[0].CharStrings
+        self.assertEqual(cs["glyph00001"].program, [])
+        self.assertGreater(len(cs["T"].program), 0)
+
+    def test_HVAR_VVAR(self):
+        _, fontpath = self.compile_font(self.getpath("TestHVVAR.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--text=BD", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_HVVAR.ttx"), ["GlyphOrder", "HVAR", "VVAR", "avar", "fvar"])
+
+    def test_HVAR_VVAR_retain_gids(self):
+        _, fontpath = self.compile_font(self.getpath("TestHVVAR.ttx"), ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--text=BD", "--retain-gids", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(subsetfont, self.getpath("expect_HVVAR_retain_gids.ttx"), ["GlyphOrder", "HVAR", "VVAR", "avar", "fvar"])
+
+    def test_subset_flavor(self):
+        _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
+        font = TTFont(fontpath)
+
+        woff_path = self.temp_path(".woff")
+        subset.main(
+            [
+                fontpath,
+                "*",
+                "--flavor=woff",
+                "--output-file=%s" % woff_path,
+            ]
+        )
+        woff = TTFont(woff_path)
+
+        self.assertEqual(woff.flavor, "woff")
+
+        woff2_path = self.temp_path(".woff2")
+        subset.main(
+            [
+                woff_path,
+                "*",
+                "--flavor=woff2",
+                "--output-file=%s" % woff2_path,
+            ]
+        )
+        woff2 = TTFont(woff2_path)
+
+        self.assertEqual(woff2.flavor, "woff2")
+
+        ttf_path = self.temp_path(".ttf")
+        subset.main(
+            [
+                woff2_path,
+                "*",
+                "--output-file=%s" % ttf_path,
+            ]
+        )
+        ttf = TTFont(ttf_path)
+
+        self.assertEqual(ttf.flavor, None)
+
+    def test_subset_context_subst_format_3(self):
+        # https://github.com/fonttools/fonttools/issues/1879
+        # Test font contains 'calt' feature with Format 3 ContextSubst lookup subtables
+        ttx = self.getpath("TestContextSubstFormat3.ttx")
+        font, fontpath = self.compile_font(ttx, ".ttf")
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "--unicodes=*", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        # check all glyphs are kept via GSUB closure, no changes expected
+        self.expect_ttx(subsetfont, ttx)
+
+    def test_cmap_prune_format12(self):
+        _, fontpath = self.compile_font(self.getpath("CmapSubsetTest.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("CmapSubsetTest.subset.ttx"), ["cmap"])
+
+    def test_GPOS_PairPos_Format2_useClass0(self):
+        # Check two things related to class 0 ('every other glyph'):
+        # 1) that it's reused for ClassDef1 when it becomes empty as the subset glyphset
+        #    is intersected with the table's Coverage
+        # 2) that it is never reused for ClassDef2 even when it happens to become empty
+        #    because of the subset glyphset. In this case, we don't keep a PairPosClass2
+        #    subtable if only ClassDef2's class0 survived subsetting.
+        # The test font (from Harfbuzz test suite) is constructed to trigger these two
+        # situations depending on the input subset --text.
+        # https://github.com/fonttools/fonttools/pull/2221
+        _, fontpath = self.compile_font(
+            self.getpath("GPOS_PairPos_Format2_PR_2221.ttx"), ".ttf"
+        )
+        subsetpath = self.temp_path(".ttf")
+
+        for n, text in enumerate("!#", start=1):
+            expected_ttx = self.getpath(
+                f"GPOS_PairPos_Format2_ClassDef{n}_useClass0.subset.ttx"
+            )
+            with self.subTest(text=text, expected_ttx=expected_ttx):
+                subset.main(
+                    [
+                        fontpath,
+                        f"--text='{text}'",
+                        "--layout-features+=test",
+                        "--output-file=%s" % subsetpath,
+                    ]
+                )
+                subsetfont = TTFont(subsetpath)
+                self.expect_ttx(subsetfont, expected_ttx, ["GPOS"])
+
+    def test_GPOS_SinglePos_prune_post_subset_no_value(self):
+        _, fontpath = self.compile_font(
+            self.getpath("GPOS_SinglePos_no_value_issue_2312.ttx"), ".ttf"
+        )
+        subsetpath = self.temp_path(".ttf")
+        subset.main([fontpath, "*", "--glyph-names", "--output-file=%s" % subsetpath])
+        subsetfont = TTFont(subsetpath)
+        self.expect_ttx(
+            subsetfont,
+            self.getpath("GPOS_SinglePos_no_value_issue_2312.subset.ttx"),
+            ["GlyphOrder", "GPOS"],
+        )
+
+
+@pytest.fixture
+def featureVarsTestFont():
+    fb = FontBuilder(unitsPerEm=100)
+    fb.setupGlyphOrder([".notdef", "f", "f_f", "dollar", "dollar.rvrn"])
+    fb.setupCharacterMap({ord("f"): "f", ord("$"): "dollar"})
+    fb.setupNameTable({"familyName": "TestFeatureVars", "styleName": "Regular"})
+    fb.setupPost()
+    fb.setupFvar(axes=[("wght", 100, 400, 900, "Weight")], instances=[])
+    fb.addOpenTypeFeatures("""\
+        feature dlig {
+            sub f f by f_f;
+        } dlig;
+    """)
+    fb.addFeatureVariations(
+        [([{"wght": (0.20886, 1.0)}], {"dollar": "dollar.rvrn"})],
+        featureTag="rvrn"
+    )
+    buf = io.BytesIO()
+    fb.save(buf)
+    buf.seek(0)
+
+    return TTFont(buf)
+
+
+def test_subset_feature_variations_keep_all(featureVarsTestFont):
+    font = featureVarsTestFont
+
+    options = subset.Options()
+    subsetter = subset.Subsetter(options)
+    subsetter.populate(unicodes=[ord("f"), ord("$")])
+    subsetter.subset(font)
+
+    featureTags = {
+        r.FeatureTag for r in font["GSUB"].table.FeatureList.FeatureRecord
+    }
+    # 'dlig' is discretionary so it is dropped by default
+    assert "dlig" not in featureTags
+    assert "f_f" not in font.getGlyphOrder()
+    # 'rvrn' is required so it is kept by default
+    assert "rvrn" in featureTags
+    assert "dollar.rvrn" in font.getGlyphOrder()
+
+
+def test_subset_feature_variations_drop_all(featureVarsTestFont):
+    font = featureVarsTestFont
+
+    options = subset.Options()
+    options.layout_features.remove("rvrn")  # drop 'rvrn'
+    subsetter = subset.Subsetter(options)
+    subsetter.populate(unicodes=[ord("f"), ord("$")])
+    subsetter.subset(font)
+
+    featureTags = {
+        r.FeatureTag for r in font["GSUB"].table.FeatureList.FeatureRecord
+    }
+    glyphs = set(font.getGlyphOrder())
+
+    assert "rvrn" not in featureTags
+    assert glyphs == {".notdef", "f", "dollar"}
+    # all FeatureVariationRecords were dropped
+    assert font["GSUB"].table.FeatureVariations is None
+    assert font["GSUB"].table.Version == 0x00010000
+
+
+# TODO test_subset_feature_variations_drop_from_end_empty_records
+# https://github.com/fonttools/fonttools/issues/1881#issuecomment-619415044
+
+
+def test_subset_single_pos_format():
+    fb = FontBuilder(unitsPerEm=1000)
+    fb.setupGlyphOrder([".notdef", "a", "b", "c"])
+    fb.setupCharacterMap({ord("a"): "a", ord("b"): "b", ord("c"): "c"})
+    fb.setupNameTable({"familyName": "TestSingePosFormat", "styleName": "Regular"})
+    fb.setupPost()
+    fb.addOpenTypeFeatures("""
+        feature kern {
+            pos a -50;
+            pos b -40;
+            pos c -50;
+        } kern;
+    """)
+
+    buf = io.BytesIO()
+    fb.save(buf)
+    buf.seek(0)
+
+    font = TTFont(buf)
+
+    # The input font has a SinglePos Format 2 subtable where each glyph has
+    # different ValueRecords
+    assert getXML(font["GPOS"].table.LookupList.Lookup[0].toXML, font) == [
+        '<Lookup>',
+        '  <LookupType value="1"/>',
+        '  <LookupFlag value="0"/>',
+        '  <!-- SubTableCount=1 -->',
+        '  <SinglePos index="0" Format="2">',
+        '    <Coverage>',
+        '      <Glyph value="a"/>',
+        '      <Glyph value="b"/>',
+        '      <Glyph value="c"/>',
+        '    </Coverage>',
+        '    <ValueFormat value="4"/>',
+        '    <!-- ValueCount=3 -->',
+        '    <Value index="0" XAdvance="-50"/>',
+        '    <Value index="1" XAdvance="-40"/>',
+        '    <Value index="2" XAdvance="-50"/>',
+        '  </SinglePos>',
+        '</Lookup>',
+    ]
+
+    options = subset.Options()
+    subsetter = subset.Subsetter(options)
+    subsetter.populate(unicodes=[ord("a"), ord("c")])
+    subsetter.subset(font)
+
+    # All the subsetted glyphs from the original SinglePos Format2 subtable
+    # now have the same ValueRecord, so we use a more compact Format 1 subtable.
+    assert getXML(font["GPOS"].table.LookupList.Lookup[0].toXML, font) == [
+        '<Lookup>',
+        '  <LookupType value="1"/>',
+        '  <LookupFlag value="0"/>',
+        '  <!-- SubTableCount=1 -->',
+        '  <SinglePos index="0" Format="1">',
+        '    <Coverage>',
+        '      <Glyph value="a"/>',
+        '      <Glyph value="c"/>',
+        '    </Coverage>',
+        '    <ValueFormat value="4"/>',
+        '    <Value XAdvance="-50"/>',
+        '  </SinglePos>',
+        '</Lookup>',
+    ]
+
+
+@pytest.fixture
+def ttf_path(tmp_path):
+    # $(dirname $0)/../ttLib/data
+    ttLib_data = pathlib.Path(__file__).parent.parent / "ttLib" / "data"
+    font = TTFont()
+    font.importXML(ttLib_data / "TestTTF-Regular.ttx")
+    font_path = tmp_path / "TestTTF-Regular.ttf"
+    font.save(font_path)
+    return font_path
+
+
+def test_subset_empty_glyf(tmp_path, ttf_path):
+    subset_path = tmp_path / (ttf_path.name + ".subset")
+    # only keep empty .notdef and space glyph, resulting in an empty glyf table
+    subset.main(
+        [
+            str(ttf_path),
+            "--no-notdef-outline",
+            "--glyph-names",
+            f"--output-file={subset_path}",
+            "--glyphs=.notdef space",
+        ]
+    )
+    subset_font = TTFont(subset_path)
+
+    assert subset_font.getGlyphOrder() == [".notdef", "space"]
+    assert subset_font.reader['glyf'] == b"\x00"
+
+    glyf = subset_font["glyf"]
+    assert all(glyf[g].numberOfContours == 0 for g in subset_font.getGlyphOrder())
+
+    loca = subset_font["loca"]
+    assert all(loc == 0 for loc in loca)
+
+
+@pytest.fixture
+def colrv1_path(tmp_path):
+    base_glyph_names = ["uni%04X" % i for i in range(0xE000, 0xE000 + 10)]
+    layer_glyph_names = ["glyph%05d" % i for i in range(10, 20)]
+    glyph_order = [".notdef"] + base_glyph_names + layer_glyph_names
+
+    pen = TTGlyphPen(glyphSet=None)
+    pen.moveTo((0, 0))
+    pen.lineTo((0, 500))
+    pen.lineTo((500, 500))
+    pen.lineTo((500, 0))
+    pen.closePath()
+    glyph = pen.glyph()
+    glyphs = {g: glyph for g in glyph_order}
+
+    fb = FontBuilder(unitsPerEm=1024, isTTF=True)
+    fb.setupGlyphOrder(glyph_order)
+    fb.setupCharacterMap({int(name[3:], 16): name for name in base_glyph_names})
+    fb.setupGlyf(glyphs)
+    fb.setupHorizontalMetrics({g: (500, 0) for g in glyph_order})
+    fb.setupHorizontalHeader()
+    fb.setupOS2()
+    fb.setupPost()
+    fb.setupNameTable({"familyName": "TestCOLRv1", "styleName": "Regular"})
+
+    fb.setupCOLR(
+        {
+            "uniE000": (
+                ot.PaintFormat.PaintColrLayers,
+                [
+                    {
+                        "Format": ot.PaintFormat.PaintGlyph,
+                        "Paint": (ot.PaintFormat.PaintSolid, 0),
+                        "Glyph": "glyph00010",
+                    },
+                    {
+                        "Format": ot.PaintFormat.PaintGlyph,
+                        "Paint": (ot.PaintFormat.PaintSolid, 2, 0.3),
+                        "Glyph": "glyph00011",
+                    },
+                ],
+            ),
+            "uniE001": (
+                ot.PaintFormat.PaintColrLayers,
+                [
+                    {
+                        "Format": ot.PaintFormat.PaintTransform,
+                        "Paint": {
+                            "Format": ot.PaintFormat.PaintGlyph,
+                            "Paint": {
+                                "Format": ot.PaintFormat.PaintRadialGradient,
+                                "x0": 250,
+                                "y0": 250,
+                                "r0": 250,
+                                "x1": 200,
+                                "y1": 200,
+                                "r1": 0,
+                                "ColorLine": {
+                                    "ColorStop": [(0.0, 1), (1.0, 2)],
+                                    "Extend": "repeat",
+                                },
+                            },
+                            "Glyph": "glyph00012",
+                        },
+                        "Transform": (0.7071, 0.7071, -0.7071, 0.7071, 0, 0),
+                    },
+                    {
+                        "Format": ot.PaintFormat.PaintGlyph,
+                        "Paint": (ot.PaintFormat.PaintSolid, 1, 0.5),
+                        "Glyph": "glyph00013",
+                    },
+                ],
+            ),
+            "uniE002": (
+                ot.PaintFormat.PaintColrLayers,
+                [
+                    {
+                        "Format": ot.PaintFormat.PaintGlyph,
+                        "Paint": {
+                            "Format": ot.PaintFormat.PaintLinearGradient,
+                            "x0": 0,
+                            "y0": 0,
+                            "x1": 500,
+                            "y1": 500,
+                            "x2": -500,
+                            "y2": 500,
+                            "ColorLine": {"ColorStop": [(0.0, 1), (1.0, 2)]},
+                        },
+                        "Glyph": "glyph00014",
+                    },
+                    {
+                        "Format": ot.PaintFormat.PaintTransform,
+                        "Paint": {
+                            "Format": ot.PaintFormat.PaintGlyph,
+                            "Paint": (ot.PaintFormat.PaintSolid, 1),
+                            "Glyph": "glyph00015",
+                        },
+                        "Transform": (1, 0, 0, 1, 400, 400),
+                    },
+                ],
+            ),
+            "uniE003": {
+                "Format": ot.PaintFormat.PaintRotateAroundCenter,
+                "Paint": {
+                    "Format": ot.PaintFormat.PaintColrGlyph,
+                    "Glyph": "uniE001",
+                },
+                "angle": 45,
+                "centerX": 250,
+                "centerY": 250,
+            },
+            "uniE004": [
+                ("glyph00016", 1),
+                ("glyph00017", 0xFFFF),  # special palette index for foreground text
+                ("glyph00018", 2),
+            ],
+        },
+    )
+    fb.setupCPAL(
+        [
+            [
+                (1.0, 0.0, 0.0, 1.0),  # red
+                (0.0, 1.0, 0.0, 1.0),  # green
+                (0.0, 0.0, 1.0, 1.0),  # blue
+            ],
+        ],
+    )
+
+    output_path = tmp_path / "TestCOLRv1.ttf"
+    fb.save(output_path)
+
+    return output_path
+
+
+def test_subset_COLRv1_and_CPAL(colrv1_path):
+    subset_path = colrv1_path.parent / (colrv1_path.name + ".subset")
+
+    subset.main(
+        [
+            str(colrv1_path),
+            "--glyph-names",
+            f"--output-file={subset_path}",
+            "--unicodes=E002,E003,E004",
+        ]
+    )
+    subset_font = TTFont(subset_path)
+
+    glyph_set = set(subset_font.getGlyphOrder())
+
+    # uniE000 and its children are excluded from subset
+    assert "uniE000" not in glyph_set
+    assert "glyph00010" not in glyph_set
+    assert "glyph00011" not in glyph_set
+
+    # uniE001 and children are pulled in indirectly as PaintColrGlyph by uniE003
+    assert "uniE001" in glyph_set
+    assert "glyph00012" in glyph_set
+    assert "glyph00013" in glyph_set
+
+    assert "uniE002" in glyph_set
+    assert "glyph00014" in glyph_set
+    assert "glyph00015" in glyph_set
+
+    assert "uniE003" in glyph_set
+
+    assert "uniE004" in glyph_set
+    assert "glyph00016" in glyph_set
+    assert "glyph00017" in glyph_set
+    assert "glyph00018" in glyph_set
+
+    assert "COLR" in subset_font
+    colr = subset_font["COLR"].table
+    assert colr.Version == 1
+    assert len(colr.BaseGlyphRecordArray.BaseGlyphRecord) == 1
+    assert len(colr.BaseGlyphList.BaseGlyphPaintRecord) == 3  # was 4
+
+    base = colr.BaseGlyphList.BaseGlyphPaintRecord[0]
+    assert base.BaseGlyph == "uniE001"
+    layers = colr.LayerList.Paint[
+        base.Paint.FirstLayerIndex: base.Paint.FirstLayerIndex + base.Paint.NumLayers
+    ]
+    assert len(layers) == 2
+    # check v1 palette indices were remapped
+    assert layers[0].Paint.Paint.ColorLine.ColorStop[0].PaletteIndex == 0
+    assert layers[0].Paint.Paint.ColorLine.ColorStop[1].PaletteIndex == 1
+    assert layers[1].Paint.PaletteIndex == 0
+
+    baseRecV0 = colr.BaseGlyphRecordArray.BaseGlyphRecord[0]
+    assert baseRecV0.BaseGlyph == "uniE004"
+    layersV0 = colr.LayerRecordArray.LayerRecord
+    assert len(layersV0) == 3
+    # check v0 palette indices were remapped (except for 0xFFFF)
+    assert layersV0[0].PaletteIndex == 0
+    assert layersV0[1].PaletteIndex == 0xFFFF
+    assert layersV0[2].PaletteIndex == 1
+
+    assert "CPAL" in subset_font
+    cpal = subset_font["CPAL"]
+    assert [
+        tuple(v / 255 for v in (c.red, c.green, c.blue, c.alpha))
+        for c in cpal.palettes[0]
+    ] == [
+        # the first color 'red' was pruned
+        (0.0, 1.0, 0.0, 1.0),  # green
+        (0.0, 0.0, 1.0, 1.0),  # blue
+    ]
+
+
+def test_subset_COLRv1_and_CPAL_drop_empty(colrv1_path):
+    subset_path = colrv1_path.parent / (colrv1_path.name + ".subset")
+
+    subset.main(
+        [
+            str(colrv1_path),
+            "--glyph-names",
+            f"--output-file={subset_path}",
+            "--glyphs=glyph00010",
+        ]
+    )
+    subset_font = TTFont(subset_path)
+
+    glyph_set = set(subset_font.getGlyphOrder())
+
+    assert "glyph00010" in glyph_set
+    assert "uniE000" not in glyph_set
+
+    assert "COLR" not in subset_font
+    assert "CPAL" not in subset_font
+
+
+def test_subset_COLRv1_downgrade_version(colrv1_path):
+    subset_path = colrv1_path.parent / (colrv1_path.name + ".subset")
+
+    subset.main(
+        [
+            str(colrv1_path),
+            "--glyph-names",
+            f"--output-file={subset_path}",
+            "--unicodes=E004",
+        ]
+    )
+    subset_font = TTFont(subset_path)
+
+    assert set(subset_font.getGlyphOrder()) == {
+        ".notdef",
+        "uniE004",
+        "glyph00016",
+        "glyph00017",
+        "glyph00018",
+    }
+
+    assert "COLR" in subset_font
+    assert subset_font["COLR"].version == 0
+
+
+def test_subset_COLRv1_drop_all_v0_glyphs(colrv1_path):
+    subset_path = colrv1_path.parent / (colrv1_path.name + ".subset")
+
+    subset.main(
+        [
+            str(colrv1_path),
+            "--glyph-names",
+            f"--output-file={subset_path}",
+            "--unicodes=E003",
+        ]
+    )
+    subset_font = TTFont(subset_path)
+
+    assert set(subset_font.getGlyphOrder()) == {
+        ".notdef",
+        "uniE001",
+        "uniE003",
+        "glyph00012",
+        "glyph00013",
+    }
+
+    assert "COLR" in subset_font
+    colr = subset_font["COLR"]
+    assert colr.version == 1
+    assert colr.table.BaseGlyphRecordCount == 0
+    assert colr.table.BaseGlyphRecordArray is None
+    assert colr.table.LayerRecordArray is None
+    assert colr.table.LayerRecordCount is 0
+
+
+def test_subset_keep_size_drop_empty_stylistic_set():
+    fb = FontBuilder(unitsPerEm=1000, isTTF=True)
+    glyph_order = [".notdef", "a", "b", "b.ss01"]
+    fb.setupGlyphOrder(glyph_order)
+    fb.setupGlyf({g: TTGlyphPen(None).glyph() for g in glyph_order})
+    fb.setupCharacterMap({ord("a"): "a", ord("b"): "b"})
+    fb.setupHorizontalMetrics({g: (500, 0) for g in glyph_order})
+    fb.setupHorizontalHeader()
+    fb.setupOS2()
+    fb.setupPost()
+    fb.setupNameTable({"familyName": "TestKeepSizeFeature", "styleName": "Regular"})
+    fb.addOpenTypeFeatures("""
+        feature size {
+          parameters 10.0 0;
+        } size;
+        feature ss01 {
+          featureNames {
+            name "Alternate b";
+          };
+          sub b by b.ss01;
+        } ss01;
+    """)
+
+    buf = io.BytesIO()
+    fb.save(buf)
+    buf.seek(0)
+
+    font = TTFont(buf)
+
+    gpos_features = font["GPOS"].table.FeatureList.FeatureRecord
+    assert gpos_features[0].FeatureTag == "size"
+    assert isinstance(gpos_features[0].Feature.FeatureParams, ot.FeatureParamsSize)
+    assert gpos_features[0].Feature.LookupCount == 0
+    gsub_features = font["GSUB"].table.FeatureList.FeatureRecord
+    assert gsub_features[0].FeatureTag == "ss01"
+    assert isinstance(
+        gsub_features[0].Feature.FeatureParams, ot.FeatureParamsStylisticSet
+    )
+
+    options = subset.Options(layout_features=["*"])
+    subsetter = subset.Subsetter(options)
+    subsetter.populate(unicodes=[ord("a")])
+    subsetter.subset(font)
+
+    # empty size feature was kept
+    gpos_features = font["GPOS"].table.FeatureList.FeatureRecord
+    assert gpos_features[0].FeatureTag == "size"
+    assert isinstance(gpos_features[0].Feature.FeatureParams, ot.FeatureParamsSize)
+    assert gpos_features[0].Feature.LookupCount == 0
+    # empty ss01 feature was dropped
+    assert font["GSUB"].table.FeatureList.FeatureCount == 0
+
 
 if __name__ == "__main__":
     sys.exit(unittest.main())
diff --git a/Tests/svgLib/path/parser_test.py b/Tests/svgLib/path/parser_test.py
index 7865870..b533dd8 100644
--- a/Tests/svgLib/path/parser_test.py
+++ b/Tests/svgLib/path/parser_test.py
@@ -1,6 +1,3 @@
-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
 
@@ -290,8 +287,150 @@
     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")
+def test_arc_to_cubic_bezier():
+    pen = RecordingPen()
+    parse_path("M300,200 h-150 a150,150 0 1,0 150,-150 z", pen)
+    expected = [
+        ('moveTo', ((300.0, 200.0),)),
+        ('lineTo', ((150.0, 200.0),)),
+        (
+            'curveTo',
+            (
+                (150.0, 282.842),
+                (217.157, 350.0),
+                (300.0, 350.0)
+            )
+        ),
+        (
+            'curveTo',
+            (
+                (382.842, 350.0),
+                (450.0, 282.842),
+                (450.0, 200.0)
+            )
+        ),
+        (
+            'curveTo',
+            (
+                (450.0, 117.157),
+                (382.842, 50.0),
+                (300.0, 50.0)
+            )
+        ),
+        ('lineTo', ((300.0, 200.0),)),
+        ('closePath', ())
+    ]
+
+    result = list(pen.value)
+    assert len(result) == len(expected)
+    for (cmd1, points1), (cmd2, points2) in zip(result, expected):
+        assert cmd1 == cmd2
+        assert len(points1) == len(points2)
+        for pt1, pt2 in zip(points1, points2):
+            assert pt1 == pytest.approx(pt2, rel=1e-5)
+
+
+
+class ArcRecordingPen(RecordingPen):
+
+    def arcTo(self, rx, ry, rotation, arc_large, arc_sweep, end_point):
+        self.value.append(
+            ("arcTo", (rx, ry, rotation, arc_large, arc_sweep, end_point))
+        )
+
+
+def test_arc_pen_with_arcTo():
+    pen = ArcRecordingPen()
+    parse_path("M300,200 h-150 a150,150 0 1,0 150,-150 z", pen)
+    expected = [
+        ('moveTo', ((300.0, 200.0),)),
+        ('lineTo', ((150.0, 200.0),)),
+        ('arcTo', (150.0, 150.0, 0.0, True, False, (300.0, 50.0))),
+        ('lineTo', ((300.0, 200.0),)),
+        ('closePath', ())
+    ]
+
+    assert pen.value == expected
+
+
+@pytest.mark.parametrize(
+    "path, expected",
+    [
+        (
+            "M1-2A3-4-1.0 01.5.7",
+            [
+                ("moveTo", ((1.0, -2.0),)),
+                ("arcTo", (3.0, -4.0, -1.0, False, True, (0.5, 0.7))),
+                ("endPath", ()),
+            ],
+        ),
+        (
+            "M21.58 7.19a2.51 2.51 0 10-1.77-1.77",
+            [
+                ("moveTo", ((21.58, 7.19),)),
+                ("arcTo", (2.51, 2.51, 0.0, True, False, (19.81, 5.42))),
+                ("endPath", ()),
+            ],
+        ),
+        (
+            "M22 12a25.87 25.87 0 00-.42-4.81",
+            [
+                ("moveTo", ((22.0, 12.0),)),
+                ("arcTo", (25.87, 25.87, 0.0, False, False, (21.58, 7.19))),
+                ("endPath", ()),
+            ],
+        ),
+        (
+            "M0,0 A1.2 1.2 0 012 15.8",
+            [
+                ("moveTo", ((0.0, 0.0),)),
+                ("arcTo", (1.2, 1.2, 0.0, False, True, (2.0, 15.8))),
+                ("endPath", ()),
+            ],
+        ),
+        (
+            "M12 7a5 5 0 105 5 5 5 0 00-5-5",
+            [
+
+                ("moveTo", ((12.0, 7.0),)),
+                ("arcTo", (5.0, 5.0, 0.0, True, False, (17.0, 12.0))),
+                ("arcTo", (5.0, 5.0, 0.0, False, False, (12.0, 7.0))),
+                ("endPath", ()),
+            ],
+        )
+    ],
+)
+def test_arc_flags_without_spaces(path, expected):
+    pen = ArcRecordingPen()
+    parse_path(path, pen)
+    assert pen.value == expected
+
+
+@pytest.mark.parametrize(
+    "path", ["A", "A0,0,0,0,0,0", "A 0 0 0 0 0 0 0 0 0 0 0 0 0"]
+)
+def test_invalid_arc_not_enough_args(path):
+    pen = ArcRecordingPen()
+    with pytest.raises(ValueError, match="Invalid arc command") as e:
+        parse_path(path, pen)
+
+    assert isinstance(e.value.__cause__, ValueError)
+    assert "Not enough arguments" in str(e.value.__cause__)
+
+
+def test_invalid_arc_argument_value():
+    pen = ArcRecordingPen()
+    with pytest.raises(ValueError, match="Invalid arc command") as e:
+        parse_path("M0,0 A0,0,0,2,0,0,0", pen)
+
+    cause = e.value.__cause__
+    assert isinstance(cause, ValueError)
+    assert "Invalid argument for 'large-arc-flag' parameter: '2'" in str(cause)
+
+    pen = ArcRecordingPen()
+    with pytest.raises(ValueError, match="Invalid arc command") as e:
+        parse_path("M0,0 A0,0,0,0,-2.0,0,0", pen)
+
+    cause = e.value.__cause__
+    assert isinstance(cause, ValueError)
+    assert "Invalid argument for 'sweep-flag' parameter: '-2.0'" in str(cause)
diff --git a/Tests/svgLib/path/path_test.py b/Tests/svgLib/path/path_test.py
index 09b9447..8ee334a 100644
--- a/Tests/svgLib/path/path_test.py
+++ b/Tests/svgLib/path/path_test.py
@@ -1,6 +1,4 @@
-from __future__ import print_function, absolute_import, division
-
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import tobytes
 from fontTools.pens.recordingPen import RecordingPen
 from fontTools.svgLib import SVGPath
 
diff --git a/Tests/svgLib/path/shapes_test.py b/Tests/svgLib/path/shapes_test.py
new file mode 100644
index 0000000..24e3dd2
--- /dev/null
+++ b/Tests/svgLib/path/shapes_test.py
@@ -0,0 +1,130 @@
+from fontTools.svgLib.path import shapes
+from fontTools.misc import etree
+import pytest
+
+
+@pytest.mark.parametrize(
+    "svg_xml, expected_path, expected_transform",
+    [
+        # path: direct passthrough
+        (
+            "<path d='I love kittens'/>",
+            "I love kittens",
+            None
+        ),
+        # path no @d
+        (
+            "<path duck='Mallard'/>",
+            None,
+            None
+        ),
+        # line
+        (
+            '<line x1="10" x2="50" y1="110" y2="150"/>',
+            'M10,110 L50,150',
+            None
+        ),
+        # line, decimal positioning
+        (
+            '<line x1="10.0" x2="50.5" y1="110.2" y2="150.7"/>',
+            'M10,110.2 L50.5,150.7',
+            None
+        ),
+        # rect: minimal valid example
+        (
+            "<rect width='1' height='1'/>",
+            "M0,0 H1 V1 H0 V0 z",
+            None
+        ),
+        # rect: sharp corners
+        (
+            "<rect x='10' y='11' width='17' height='11'/>",
+            "M10,11 H27 V22 H10 V11 z",
+            None
+        ),
+        # rect: round corners
+        (
+            "<rect x='9' y='9' width='11' height='7' rx='2'/>",
+            "M11,9 H18 A2,2 0 0 1 20,11 V14 A2,2 0 0 1 18,16 H11"
+            " A2,2 0 0 1 9,14 V11 A2,2 0 0 1 11,9 z",
+            None
+        ),
+        # rect: simple
+        (
+            "<rect x='11.5' y='16' width='11' height='2'/>",
+            "M11.5,16 H22.5 V18 H11.5 V16 z",
+            None
+        ),
+        # rect: the one above plus a rotation
+        (
+            "<rect x='11.5' y='16' transform='matrix(0.7071 -0.7071 0.7071 0.7071 -7.0416 16.9999)' width='11' height='2'/>",
+            "M11.5,16 H22.5 V18 H11.5 V16 z",
+            (0.7071, -0.7071, 0.7071, 0.7071, -7.0416, 16.9999)
+        ),
+        # polygon
+        (
+            "<polygon points='30,10 50,30 10,30'/>",
+            "M30,10 50,30 10,30 z",
+            None
+        ),
+        # polyline
+        (
+            "<polyline points='30,10 50,30 10,30'/>",
+            "M30,10 50,30 10,30",
+            None
+        ),
+        # circle, minimal valid example
+        (
+            "<circle r='1'/>",
+            "M-1,0 A1,1 0 1 1 1,0 A1,1 0 1 1 -1,0",
+            None
+        ),
+        # circle
+        (
+            "<circle cx='600' cy='200' r='100'/>",
+            "M500,200 A100,100 0 1 1 700,200 A100,100 0 1 1 500,200",
+            None
+        ),
+        # circle, decimal positioning
+        (
+            "<circle cx='12' cy='6.5' r='1.5'></circle>",
+            "M10.5,6.5 A1.5,1.5 0 1 1 13.5,6.5 A1.5,1.5 0 1 1 10.5,6.5",
+            None
+        ),
+        # circle, with transform
+        (
+            '<circle transform="matrix(0.9871 -0.1602 0.1602 0.9871 -7.525 8.6516)" cx="49.9" cy="51" r="14.3"/>',
+            'M35.6,51 A14.3,14.3 0 1 1 64.2,51 A14.3,14.3 0 1 1 35.6,51',
+            (0.9871, -0.1602, 0.1602, 0.9871, -7.525, 8.6516)
+        ),
+        # ellipse
+        (
+            '<ellipse cx="100" cy="50" rx="100" ry="50"/>',
+            'M0,50 A100,50 0 1 1 200,50 A100,50 0 1 1 0,50',
+            None
+        ),
+        # ellipse, decimal positioning
+        (
+            '<ellipse cx="100.5" cy="50" rx="10" ry="50.5"/>',
+            'M90.5,50 A10,50.5 0 1 1 110.5,50 A10,50.5 0 1 1 90.5,50',
+            None
+        ),
+        # ellipse, with transform
+        (
+            '<ellipse transform="matrix(0.9557 -0.2945 0.2945 0.9557 -14.7694 20.1454)" cx="59.5" cy="59.1" rx="30.9" ry="11.9"/>',
+            'M28.6,59.1 A30.9,11.9 0 1 1 90.4,59.1 A30.9,11.9 0 1 1 28.6,59.1',
+            (0.9557, -0.2945, 0.2945, 0.9557, -14.7694, 20.1454)
+        ),
+    ]
+)
+def test_el_to_path(svg_xml, expected_path, expected_transform):
+    pb = shapes.PathBuilder()
+    pb.add_path_from_element(etree.fromstring(svg_xml))
+    if expected_path:
+        expected_paths = [expected_path]
+        expected_transforms = [expected_transform]
+    else:
+        expected_paths = []
+        expected_transforms = []
+    assert pb.paths == expected_paths
+    assert pb.transforms == expected_transforms
diff --git a/Tests/t1Lib/data/TestT1-weird-zeros.pfa b/Tests/t1Lib/data/TestT1-weird-zeros.pfa
new file mode 100644
index 0000000..666b36b
--- /dev/null
+++ b/Tests/t1Lib/data/TestT1-weird-zeros.pfa
@@ -0,0 +1,63 @@
+%!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

+00000000000 00000000000000000000000000000000000000000000000000000

+0000000000000000000000000000000	000000000000000000000000000000000

+00000000000000000000000
+00000000000000000000000000000000000000000

+0000000000000000000000000000000000000000 000000000000000000000000

+00
+00000000000000000000000000000000000000000000000000000000000000

+00000000000000000000000000000000000000000000000000000	00000000000

+00000000000000000000 00000000000000000000000000000000000000000000

+00000000000000000000
+00000000000000000000000000000000000000000000

+cleartomark

+%%EndResource

+%%EOF

diff --git a/Tests/t1Lib/t1Lib_test.py b/Tests/t1Lib/t1Lib_test.py
index 0e0fcbe..92b3e9e 100644
--- a/Tests/t1Lib/t1Lib_test.py
+++ b/Tests/t1Lib/t1Lib_test.py
@@ -1,7 +1,6 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 import unittest
 import os
+import sys
 from fontTools import t1Lib
 from fontTools.pens.basePen import NullPen
 import random
@@ -13,6 +12,7 @@
 LWFN = os.path.join(DATADIR, 'TestT1-Regular.lwfn')
 PFA = os.path.join(DATADIR, 'TestT1-Regular.pfa')
 PFB = os.path.join(DATADIR, 'TestT1-Regular.pfb')
+WEIRD_ZEROS = os.path.join(DATADIR, 'TestT1-weird-zeros.pfa')
 
 
 class FindEncryptedChunksTest(unittest.TestCase):
@@ -27,6 +27,14 @@
 		self.assertTrue(chunks[1][0])
 		self.assertFalse(chunks[2][0])
 
+	def test_findEncryptedChunks_weird_zeros(self):
+		with open(WEIRD_ZEROS, 'rb') as f:
+			data = f.read()
+
+		# Just assert that this doesn't raise any exception for not finding the
+		# end of eexec
+		t1Lib.findEncryptedChunks(data)
+
 
 class DecryptType1Test(unittest.TestCase):
 
@@ -50,6 +58,11 @@
 		data = self.write(font, 'OTHER', dohex=True)
 		self.assertEqual(font.getData(), data)
 
+	@unittest.skipIf(sys.version_info[:2] < (3, 6), "pathlib is only tested on 3.6 and up")
+	def test_read_with_path(self):
+		import pathlib
+		font = t1Lib.T1Font(pathlib.Path(PFB))
+
 	@staticmethod
 	def write(font, outtype, dohex=False):
 		temp = os.path.join(DATADIR, 'temp.' + outtype.lower())
@@ -67,8 +80,7 @@
 
 	def test_parse_lwfn(self):
 		# the extended attrs are lost on git so we can't auto-detect 'LWFN'
-		font = t1Lib.T1Font()
-		font.data = t1Lib.readLWFN(LWFN)
+		font = t1Lib.T1Font(LWFN, kind="LWFN")
 		font.parse()
 		self.assertEqual(font['FontName'], 'TestT1-Regular')
 		self.assertTrue('Subrs' in font['Private'])
diff --git a/Tests/tfmLib/data/cmex10.tfm b/Tests/tfmLib/data/cmex10.tfm
new file mode 100644
index 0000000..d5427ad
--- /dev/null
+++ b/Tests/tfmLib/data/cmex10.tfm
Binary files differ
diff --git a/Tests/tfmLib/data/cmr10.tfm b/Tests/tfmLib/data/cmr10.tfm
new file mode 100644
index 0000000..6594c9c
--- /dev/null
+++ b/Tests/tfmLib/data/cmr10.tfm
Binary files differ
diff --git a/Tests/tfmLib/data/cmsy10.tfm b/Tests/tfmLib/data/cmsy10.tfm
new file mode 100644
index 0000000..3e4488b
--- /dev/null
+++ b/Tests/tfmLib/data/cmsy10.tfm
Binary files differ
diff --git a/Tests/tfmLib/data/dummy-space.tfm b/Tests/tfmLib/data/dummy-space.tfm
new file mode 100644
index 0000000..fc0719e
--- /dev/null
+++ b/Tests/tfmLib/data/dummy-space.tfm
Binary files differ
diff --git a/Tests/tfmLib/tfmLib_test.py b/Tests/tfmLib/tfmLib_test.py
new file mode 100644
index 0000000..ad74ed7
--- /dev/null
+++ b/Tests/tfmLib/tfmLib_test.py
@@ -0,0 +1,90 @@
+import glob
+import os
+
+import pytest
+
+from fontTools import tfmLib
+
+DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
+
+
+@pytest.mark.parametrize("path", glob.glob(f"{DATA_DIR}/cm*.tfm"))
+def test_read(path):
+    tfm = tfmLib.TFM(path)
+    assert tfm.designsize == 10.0
+    assert tfm.fontdimens
+    assert len(tfm.fontdimens) >= 7
+    assert tfm.extraheader == {}
+    assert tfm.right_boundary_char is None
+    assert tfm.left_boundary_char is None
+    assert len(tfm.chars) == 128
+
+
+def test_read_boundary_char():
+    path = os.path.join(DATA_DIR, "dummy-space.tfm")
+    tfm = tfmLib.TFM(path)
+    assert tfm.right_boundary_char == 1
+    assert tfm.left_boundary_char == 256
+
+
+def test_read_fontdimens_vanilla():
+    path = os.path.join(DATA_DIR, "cmr10.tfm")
+    tfm = tfmLib.TFM(path)
+    assert tfm.fontdimens == {
+        "SLANT": 0.0,
+        "SPACE": 0.33333396911621094,
+        "STRETCH": 0.16666698455810547,
+        "SHRINK": 0.11111164093017578,
+        "XHEIGHT": 0.4305553436279297,
+        "QUAD": 1.0000028610229492,
+        "EXTRASPACE": 0.11111164093017578,
+    }
+
+
+def test_read_fontdimens_mathex():
+    path = os.path.join(DATA_DIR, "cmex10.tfm")
+    tfm = tfmLib.TFM(path)
+    assert tfm.fontdimens == {
+        "SLANT": 0.0,
+        "SPACE": 0.0,
+        "STRETCH": 0.0,
+        "SHRINK": 0.0,
+        "XHEIGHT": 0.4305553436279297,
+        "QUAD": 1.0000028610229492,
+        "EXTRASPACE": 0.0,
+        "DEFAULTRULETHICKNESS": 0.03999900817871094,
+        "BIGOPSPACING1": 0.11111164093017578,
+        "BIGOPSPACING2": 0.16666698455810547,
+        "BIGOPSPACING3": 0.19999980926513672,
+        "BIGOPSPACING4": 0.6000003814697266,
+        "BIGOPSPACING5": 0.10000038146972656,
+    }
+
+
+def test_read_fontdimens_mathsy():
+    path = os.path.join(DATA_DIR, "cmsy10.tfm")
+    tfm = tfmLib.TFM(path)
+    assert tfm.fontdimens == {
+        "SLANT": 0.25,
+        "SPACE": 0.0,
+        "STRETCH": 0.0,
+        "SHRINK": 0.0,
+        "XHEIGHT": 0.4305553436279297,
+        "QUAD": 1.0000028610229492,
+        "EXTRASPACE": 0.0,
+        "NUM1": 0.6765079498291016,
+        "NUM2": 0.39373207092285156,
+        "NUM3": 0.44373130798339844,
+        "DENOM1": 0.6859512329101562,
+        "DENOM2": 0.34484100341796875,
+        "SUP1": 0.41289234161376953,
+        "SUP2": 0.36289215087890625,
+        "SUP3": 0.28888893127441406,
+        "SUB1": 0.14999961853027344,
+        "SUB2": 0.24721717834472656,
+        "SUBDROP": 0.05000019073486328,
+        "SUPDROP": 0.3861083984375,
+        "DELIM1": 2.3899993896484375,
+        "DELIM2": 1.010000228881836,
+        "AXISHEIGHT": 0.25,
+    }
diff --git a/Tests/ttLib/data/TestOTF-Regular.otx b/Tests/ttLib/data/TestOTF-Regular.otx
index 573fe24..92e0b2f 100644
--- a/Tests/ttLib/data/TestOTF-Regular.otx
+++ b/Tests/ttLib/data/TestOTF-Regular.otx
@@ -142,13 +142,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
     <namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
       Test TTF
@@ -184,13 +184,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
   </name>
 
diff --git a/Tests/ttLib/data/TestTTF-Regular.ttx b/Tests/ttLib/data/TestTTF-Regular.ttx
index 8ffcefe..1f1dd2b 100644
--- a/Tests/ttLib/data/TestTTF-Regular.ttx
+++ b/Tests/ttLib/data/TestTTF-Regular.ttx
@@ -462,13 +462,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
     <namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
       Test TTF
@@ -504,13 +504,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
   </name>
 
diff --git a/Tests/ttLib/data/woff2_overlap_offcurve_in.ttx b/Tests/ttLib/data/woff2_overlap_offcurve_in.ttx
new file mode 100644
index 0000000..a36dbf5
--- /dev/null
+++ b/Tests/ttLib/data/woff2_overlap_offcurve_in.ttx
@@ -0,0 +1,306 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.18">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x9aec19bb"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000010"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Jan 28 15:17:57 2021"/>
+    <modified value="Thu Jan 28 15:52:10 2021"/>
+    <xMin value="178"/>
+    <yMin value="72"/>
+    <xMax value="586"/>
+    <yMax value="480"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="750"/>
+    <descent value="-250"/>
+    <lineGap value="100"/>
+    <advanceWidthMax value="639"/>
+    <minLeftSideBearing value="178"/>
+    <minRightSideBearing value="53"/>
+    <xMaxExtent value="586"/>
+    <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>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="2"/>
+    <maxPoints value="20"/>
+    <maxContours value="1"/>
+    <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="445"/>
+    <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="300"/>
+    <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="NONE"/>
+    <fsSelection value="00000000 11000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="65"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="100"/>
+    <usWinAscent value="750"/>
+    <usWinDescent value="250"/>
+    <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="250" lsb="0"/>
+    <mtx name="A" width="639" lsb="178"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER 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"/><!-- contains no outline data -->
+
+    <TTGlyph name="A" xMin="178" yMin="72" xMax="586" yMax="480">
+      <contour>
+        <pt x="382" y="72" on="0" overlap="1"/>
+        <pt x="336" y="72" on="1"/>
+        <pt x="261" y="101" on="0"/>
+        <pt x="207" y="155" on="0"/>
+        <pt x="178" y="230" on="0"/>
+        <pt x="178" y="276" on="1"/>
+        <pt x="178" y="322" on="0"/>
+        <pt x="207" y="397" on="0"/>
+        <pt x="261" y="451" on="0"/>
+        <pt x="336" y="480" on="0"/>
+        <pt x="382" y="480" on="1"/>
+        <pt x="428" y="480" on="0"/>
+        <pt x="503" y="451" on="0"/>
+        <pt x="557" y="397" on="0"/>
+        <pt x="586" y="322" on="0"/>
+        <pt x="586" y="276" on="1"/>
+        <pt x="586" y="230" on="0"/>
+        <pt x="557" y="155" on="0"/>
+        <pt x="503" y="101" on="0"/>
+        <pt x="428" y="72" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Weight
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Unnamed
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;Unnamed-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Unnamed Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      Unnamed-Regular
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </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>
+
+  <gasp>
+    <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+  </gasp>
+
+  <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=2 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=0 -->
+        <Item index="0" value="[]"/>
+        <Item index="1" value="[]"/>
+      </VarData>
+    </VarStore>
+  </HVAR>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=1 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=0 -->
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>400.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>700.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph="A">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-64" y="-44"/>
+        <delta pt="5" x="-138" y="30"/>
+        <delta pt="7" x="-127" y="73"/>
+        <delta pt="8" x="-108" y="92"/>
+        <delta pt="10" x="-64" y="103"/>
+        <delta pt="12" x="-21" y="92"/>
+        <delta pt="13" x="-2" y="73"/>
+        <delta pt="16" x="9" y="13"/>
+        <delta pt="17" x="-2" y="-14"/>
+        <delta pt="18" x="-21" y="-33"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/ttLib/removeOverlaps_test.py b/Tests/ttLib/removeOverlaps_test.py
new file mode 100644
index 0000000..1320c9b
--- /dev/null
+++ b/Tests/ttLib/removeOverlaps_test.py
@@ -0,0 +1,51 @@
+import logging
+import pytest
+
+pathops = pytest.importorskip("pathops")
+
+from fontTools.ttLib.removeOverlaps import _simplify, _round_path
+
+
+def test_pathops_simplify_bug_workaround(caplog):
+    # Paths extracted from Noto Sans Ethiopic instance that fails skia-pathops
+    # https://github.com/google/fonts/issues/3365
+    # https://bugs.chromium.org/p/skia/issues/detail?id=11958
+    path = pathops.Path()
+    path.moveTo(550.461, 0)
+    path.lineTo(550.461, 366.308)
+    path.lineTo(713.229, 366.308)
+    path.lineTo(713.229, 0)
+    path.close()
+    path.moveTo(574.46, 0)
+    path.lineTo(574.46, 276.231)
+    path.lineTo(737.768, 276.231)
+    path.quadTo(820.075, 276.231, 859.806, 242.654)
+    path.quadTo(899.537, 209.077, 899.537, 144.154)
+    path.quadTo(899.537, 79, 853.46, 39.5)
+    path.quadTo(807.383, 0, 712.383, 0)
+    path.close()
+
+    # check that it fails without workaround
+    with pytest.raises(pathops.PathOpsError):
+        pathops.simplify(path)
+
+    # check our workaround works (but with a warning)
+    with caplog.at_level(logging.DEBUG, logger="fontTools.ttLib.removeOverlaps"):
+        result = _simplify(path, debugGlyphName="a")
+
+    assert "skia-pathops failed to simplify 'a' with float coordinates" in caplog.text
+
+    expected = pathops.Path()
+    expected.moveTo(550, 0)
+    expected.lineTo(550, 366)
+    expected.lineTo(713, 366)
+    expected.lineTo(713, 276)
+    expected.lineTo(738, 276)
+    expected.quadTo(820, 276, 860, 243)
+    expected.quadTo(900, 209, 900, 144)
+    expected.quadTo(900, 79, 853, 40)
+    expected.quadTo(807.242, 0.211, 713, 0.001)
+    expected.lineTo(713, 0)
+    expected.close()
+
+    assert expected == _round_path(result, round=lambda v: round(v, 3))
diff --git a/Tests/ttLib/sfnt_test.py b/Tests/ttLib/sfnt_test.py
index 2119351..9f81744 100644
--- a/Tests/ttLib/sfnt_test.py
+++ b/Tests/ttLib/sfnt_test.py
@@ -1,8 +1,59 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.ttLib.sfnt import calcChecksum
+import io
+import copy
+import pickle
+from fontTools.ttLib.sfnt import calcChecksum, SFNTReader
+import pytest
 
 
 def test_calcChecksum():
     assert calcChecksum(b"abcd") == 1633837924
     assert calcChecksum(b"abcdxyz") == 3655064932
+
+
+EMPTY_SFNT = b"\x00\x01\x00\x00" + b"\x00" * 8
+
+
+def pickle_unpickle(obj):
+    return pickle.loads(pickle.dumps(obj))
+
+
+class SFNTReaderTest:
+    @pytest.mark.parametrize("deepcopy", [copy.deepcopy, pickle_unpickle])
+    def test_pickle_protocol_FileIO(self, deepcopy, tmp_path):
+        fontfile = tmp_path / "test.ttf"
+        fontfile.write_bytes(EMPTY_SFNT)
+        reader = SFNTReader(fontfile.open("rb"))
+
+        reader2 = deepcopy(reader)
+
+        assert reader2 is not reader
+        assert reader2.file is not reader.file
+
+        assert isinstance(reader2.file, io.BufferedReader)
+        assert isinstance(reader2.file.raw, io.FileIO)
+        assert reader2.file.name == reader.file.name
+        assert reader2.file.tell() == reader.file.tell()
+
+        for k, v in reader.__dict__.items():
+            if k == "file":
+                continue
+            assert getattr(reader2, k) == v
+
+    @pytest.mark.parametrize("deepcopy", [copy.deepcopy, pickle_unpickle])
+    def test_pickle_protocol_BytesIO(self, deepcopy, tmp_path):
+        buf = io.BytesIO(EMPTY_SFNT)
+        reader = SFNTReader(buf)
+
+        reader2 = deepcopy(reader)
+
+        assert reader2 is not reader
+        assert reader2.file is not reader.file
+
+        assert isinstance(reader2.file, io.BytesIO)
+        assert reader2.file.tell() == reader.file.tell()
+        assert reader2.file.getvalue() == reader.file.getvalue()
+
+        for k, v in reader.__dict__.items():
+            if k == "file":
+                continue
+            assert getattr(reader2, k) == v
diff --git a/Tests/ttLib/tables/C_B_L_C_test.py b/Tests/ttLib/tables/C_B_L_C_test.py
new file mode 100644
index 0000000..fed25f9
--- /dev/null
+++ b/Tests/ttLib/tables/C_B_L_C_test.py
@@ -0,0 +1,80 @@
+import base64
+import io
+import os
+
+from fontTools.misc.testTools import getXML
+from fontTools.ttLib import TTFont
+
+
+DATA_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), "data")
+
+# This is a subset from NotoColorEmoji.ttf which contains an IndexTable format=3
+INDEX_FORMAT_3_TTX = os.path.join(DATA_DIR, "NotoColorEmoji.subset.index_format_3.ttx")
+# The CLBC table was compiled with Harfbuzz' hb-subset and contains the correct padding
+CBLC_INDEX_FORMAT_3 = base64.b64decode(
+    "AAMAAAAAAAEAAAA4AAAALAAAAAIAAAAAZeWIAAAAAAAAAAAAZeWIAAAAAAAAAAAAAAEAA"
+    "21tIAEAAQACAAAAEAADAAMAAAAgAAMAEQAAAAQAAAOmEQ0AAAADABEAABERAAAIUg=="
+)
+
+
+def test_compile_decompile_index_table_format_3():
+    font = TTFont()
+    font.importXML(INDEX_FORMAT_3_TTX)
+    buf = io.BytesIO()
+    font.save(buf)
+    buf.seek(0)
+    font = TTFont(buf)
+
+    assert font.reader["CBLC"] == CBLC_INDEX_FORMAT_3
+
+    assert getXML(font["CBLC"].toXML, font) == [
+        '<header version="3.0"/>',
+        '<strike index="0">',
+        "  <bitmapSizeTable>",
+        '    <sbitLineMetrics direction="hori">',
+        '      <ascender value="101"/>',
+        '      <descender value="-27"/>',
+        '      <widthMax value="136"/>',
+        '      <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="101"/>',
+        '      <descender value="-27"/>',
+        '      <widthMax value="136"/>',
+        '      <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="1"/>',
+        '    <endGlyphIndex value="3"/>',
+        '    <ppemX value="109"/>',
+        '    <ppemY value="109"/>',
+        '    <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_3 imageFormat="17" firstGlyphIndex="1" lastGlyphIndex="2">',
+        '    <glyphLoc id="1" name="eight"/>',
+        '    <glyphLoc id="2" name="registered"/>',
+        "  </eblc_index_sub_table_3>",
+        '  <eblc_index_sub_table_3 imageFormat="17" firstGlyphIndex="3" lastGlyphIndex="3">',
+        '    <glyphLoc id="3" name="uni2049"/>',
+        "  </eblc_index_sub_table_3>",
+        "</strike>",
+    ]
diff --git a/Tests/ttLib/tables/C_F_F__2_test.py b/Tests/ttLib/tables/C_F_F__2_test.py
index 191f60b..10f9b2f 100644
--- a/Tests/ttLib/tables/C_F_F__2_test.py
+++ b/Tests/ttLib/tables/C_F_F__2_test.py
@@ -1,9 +1,7 @@
 """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
+from fontTools.ttLib import TTFont
+from io import StringIO
 import re
 import os
 import unittest
@@ -41,7 +39,7 @@
         font = TTFont(file=CFF_BIN)
         cffTable = font['CFF2']
         cffData = cffTable.compile(font)
-        out = UnicodeIO()
+        out = StringIO()
         font.saveXML(out)
         cff2XML = out.getvalue()
         cff2XML = strip_VariableItems(cff2XML)
diff --git a/Tests/ttLib/tables/C_F_F_test.py b/Tests/ttLib/tables/C_F_F_test.py
index 63f767c..cb8d8c5 100644
--- a/Tests/ttLib/tables/C_F_F_test.py
+++ b/Tests/ttLib/tables/C_F_F_test.py
@@ -1,9 +1,7 @@
 """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
+from io import StringIO
 import re
 import os
 import unittest
@@ -33,7 +31,7 @@
         font = TTFont(sfntVersion='OTTO')
         cffTable = font['CFF '] = newTable('CFF ')
         cffTable.decompile(self.cffData, font)
-        out = UnicodeIO()
+        out = StringIO()
         font.saveXML(out)
         cffXML = strip_ttLibVersion(out.getvalue()).splitlines()
         self.assertEqual(cffXML, self.cffXML)
diff --git a/Tests/ttLib/tables/C_O_L_R_test.py b/Tests/ttLib/tables/C_O_L_R_test.py
new file mode 100644
index 0000000..aaf3300
--- /dev/null
+++ b/Tests/ttLib/tables/C_O_L_R_test.py
@@ -0,0 +1,625 @@
+from fontTools import ttLib
+from fontTools.misc.testTools import getXML, parseXML
+from fontTools.ttLib.tables.C_O_L_R_ import table_C_O_L_R_
+
+import binascii
+import pytest
+
+
+COLR_V0_SAMPLE = (
+    (b"\x00\x00", "Version (0)"),
+    (b"\x00\x01", "BaseGlyphRecordCount (1)"),
+    (
+        b"\x00\x00\x00\x0e",
+        "Offset to BaseGlyphRecordArray from beginning of table (14)",
+    ),
+    (b"\x00\x00\x00\x14", "Offset to LayerRecordArray from beginning of table (20)"),
+    (b"\x00\x03", "LayerRecordCount (3)"),
+    (b"\x00\x06", "BaseGlyphRecord[0].BaseGlyph (6)"),
+    (b"\x00\x00", "BaseGlyphRecord[0].FirstLayerIndex (0)"),
+    (b"\x00\x03", "BaseGlyphRecord[0].NumLayers (3)"),
+    (b"\x00\x07", "LayerRecord[0].LayerGlyph (7)"),
+    (b"\x00\x00", "LayerRecord[0].PaletteIndex (0)"),
+    (b"\x00\x08", "LayerRecord[1].LayerGlyph (8)"),
+    (b"\x00\x01", "LayerRecord[1].PaletteIndex (1)"),
+    (b"\x00\t", "LayerRecord[2].LayerGlyph (9)"),
+    (b"\x00\x02", "LayerRecord[3].PaletteIndex (2)"),
+)
+
+COLR_V0_DATA = b"".join(t[0] for t in COLR_V0_SAMPLE)
+
+
+COLR_V0_XML = [
+    '<version value="0"/>',
+    '<ColorGlyph name="glyph00006">',
+    '  <layer colorID="0" name="glyph00007"/>',
+    '  <layer colorID="1" name="glyph00008"/>',
+    '  <layer colorID="2" name="glyph00009"/>',
+    "</ColorGlyph>",
+]
+
+
+def dump(table, ttFont=None):
+    print("\n".join(getXML(table.toXML, ttFont)))
+
+
+def diff_binary_fragments(font_bytes, expected_fragments):
+    pos = 0
+    prev_desc = ""
+    errors = 0
+    for expected_bytes, description in expected_fragments:
+        actual_bytes = font_bytes[pos : pos + len(expected_bytes)]
+        if actual_bytes != expected_bytes:
+            print(
+                f'{description} (previous "{prev_desc}", actual_bytes: {"".join("%02x" % v for v in actual_bytes)} bytes: {str(font_bytes[pos:pos+16])}'
+            )
+            errors += 1
+        pos += len(expected_bytes)
+        prev_desc = description
+    assert errors == 0
+    assert pos == len(
+        font_bytes
+    ), f"Leftover font bytes, used {pos} of {len(font_bytes)}"
+
+
+@pytest.fixture
+def font():
+    font = ttLib.TTFont()
+    font.setGlyphOrder(["glyph%05d" % i for i in range(30)])
+    return font
+
+
+class COLR_V0_Test(object):
+    def test_decompile_and_compile(self, font):
+        colr = table_C_O_L_R_()
+        colr.decompile(COLR_V0_DATA, font)
+        diff_binary_fragments(colr.compile(font), COLR_V0_SAMPLE)
+
+    def test_decompile_and_dump_xml(self, font):
+        colr = table_C_O_L_R_()
+        colr.decompile(COLR_V0_DATA, font)
+
+        dump(colr, font)
+        assert getXML(colr.toXML, font) == COLR_V0_XML
+
+    def test_load_from_xml_and_compile(self, font):
+        colr = table_C_O_L_R_()
+        for name, attrs, content in parseXML(COLR_V0_XML):
+            colr.fromXML(name, attrs, content, font)
+
+        diff_binary_fragments(colr.compile(font), COLR_V0_SAMPLE)
+
+    def test_round_trip_xml(self, font):
+        colr = table_C_O_L_R_()
+        for name, attrs, content in parseXML(COLR_V0_XML):
+            colr.fromXML(name, attrs, content, font)
+        compiled = colr.compile(font)
+
+        colr = table_C_O_L_R_()
+        colr.decompile(compiled, font)
+        assert getXML(colr.toXML, font) == COLR_V0_XML
+
+
+COLR_V1_SAMPLE = (
+    (b"\x00\x01", "Version (1)"),
+    (b"\x00\x01", "BaseGlyphRecordCount (1)"),
+    (
+        b"\x00\x00\x00\x22",
+        "Offset to BaseGlyphRecordArray from beginning of table (34)",
+    ),
+    (b"\x00\x00\x00\x28", "Offset to LayerRecordArray from beginning of table (40)"),
+    (b"\x00\x03", "LayerRecordCount (3)"),
+    (b"\x00\x00\x00\x34", "Offset to BaseGlyphList from beginning of table (52)"),
+    (b"\x00\x00\x00\x9f", "Offset to LayerList from beginning of table (159)"),
+    (b"\x00\x00\x01\x62", "Offset to ClipList (354)"),
+    (b"\x00\x00\x00\x00", "Offset to DeltaSetIndexMap (NULL)"),
+    (b"\x00\x00\x00\x00", "Offset to VarStore (NULL)"),
+    (b"\x00\x06", "BaseGlyphRecord[0].BaseGlyph (6)"),
+    (b"\x00\x00", "BaseGlyphRecord[0].FirstLayerIndex (0)"),
+    (b"\x00\x03", "BaseGlyphRecord[0].NumLayers (3)"),
+    (b"\x00\x07", "LayerRecord[0].LayerGlyph (7)"),
+    (b"\x00\x00", "LayerRecord[0].PaletteIndex (0)"),
+    (b"\x00\x08", "LayerRecord[1].LayerGlyph (8)"),
+    (b"\x00\x01", "LayerRecord[1].PaletteIndex (1)"),
+    (b"\x00\t", "LayerRecord[2].LayerGlyph (9)"),
+    (b"\x00\x02", "LayerRecord[2].PaletteIndex (2)"),
+    # BaseGlyphList
+    (b"\x00\x00\x00\x03", "BaseGlyphList.BaseGlyphCount (3)"),
+    (b"\x00\n", "BaseGlyphList.BaseGlyphPaintRecord[0].BaseGlyph (10)"),
+    (
+        b"\x00\x00\x00\x16",
+        "Offset to Paint table from beginning of BaseGlyphList (22)",
+    ),
+    (b"\x00\x0e", "BaseGlyphList.BaseGlyphPaintRecord[1].BaseGlyph (14)"),
+    (
+        b"\x00\x00\x00\x1c",
+        "Offset to Paint table from beginning of BaseGlyphList (28)",
+    ),
+    (b"\x00\x0f", "BaseGlyphList.BaseGlyphPaintRecord[2].BaseGlyph (15)"),
+    (
+        b"\x00\x00\x00\x4a",
+        "Offset to Paint table from beginning of BaseGlyphList (74)",
+    ),
+    # BaseGlyphPaintRecord[0]
+    (b"\x01", "BaseGlyphPaintRecord[0].Paint.Format (1)"),
+    (b"\x04", "BaseGlyphPaintRecord[0].Paint.NumLayers (4)"),
+    (b"\x00\x00\x00\x00", "BaseGlyphPaintRecord[0].Paint.FirstLayerIndex (0)"),
+    # BaseGlyphPaintRecord[1]
+    (b"\x20", "BaseGlyphPaintRecord[1].Paint.Format (32)"),
+    (b"\x00\x00\x0f", "Offset to SourcePaint from beginning of PaintComposite (15)"),
+    (b"\x03", "BaseGlyphPaintRecord[1].Paint.CompositeMode [SRC_OVER] (3)"),
+    (b"\x00\x00\x08", "Offset to BackdropPaint from beginning of PaintComposite (8)"),
+    (b"\x0d", "BaseGlyphPaintRecord[1].Paint.BackdropPaint.Format (13)"),
+    (b"\x00\x00\x07", "Offset to Paint from beginning of PaintVarTransform (7)"),
+    (b"\x00\x00\x0a", "Offset to VarAffine2x3 from beginning of PaintVarTransform (10)"),
+    (b"\x0b", "BaseGlyphPaintRecord[1].Paint.BackdropPaint.Format (11)"),
+    (b"\x00\x0a", "BaseGlyphPaintRecord[1].Paint.BackdropPaint.Glyph (10)"),
+    (b"\x00\x01\x00\x00", "VarAffine2x3.xx (1.0)"),
+    (b"\x00\x00\x00\x00", "VarAffine2x3.xy (0.0)"),
+    (b"\x00\x00\x00\x00", "VarAffine2x3.yx (0.0)"),
+    (b"\x00\x01\x00\x00", "VarAffine2x3.yy (1.0)"),
+    (b"\x01\x2c\x00\x00", "VarAffine2x3.dx (300.0)"),
+    (b"\x00\x00\x00\x00", "VarAffine2x3.dy (0.0)"),
+    (b"\x00\x00\x00\x00", "VarIndexBase (0)"),
+    (b"\x0a", "BaseGlyphPaintRecord[1].Paint.SourcePaint.Format (10)"),
+    (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"),
+    (b"\x00\x0b", "BaseGlyphPaintRecord[1].Paint.SourcePaint.Glyph (11)"),
+    (b"\x08", "BaseGlyphPaintRecord[1].Paint.SourcePaint.Paint.Format (8)"),
+    (b"\x00\x00\x0c", "Offset to ColorLine from beginning of PaintSweepGradient (12)"),
+    (b"\x01\x03", "centerX (259)"),
+    (b"\x01\x2c", "centerY (300)"),
+    (b"\x10\x00", "startAngle (0.25)"),
+    (b"\x30\x00", "endAngle (0.75)"),
+    (b"\x00", "ColorLine.Extend (0; pad)"),
+    (b"\x00\x02", "ColorLine.StopCount (2)"),
+    (b"\x00\x00", "ColorLine.ColorStop[0].StopOffset (0.0)"),
+    (b"\x00\x03", "ColorLine.ColorStop[0].PaletteIndex (3)"),
+    (b"@\x00", "ColorLine.ColorStop[0].Alpha (1.0)"),
+    (b"@\x00", "ColorLine.ColorStop[1].StopOffset (1.0)"),
+    (b"\x00\x05", "ColorLine.ColorStop[1].PaletteIndex (5)"),
+    (b"@\x00", "ColorLine.ColorStop[1].Alpha (1.0)"),
+    # LayerList
+    (b"\x00\x00\x00\x04", "LayerList.LayerCount (4)"),
+    (
+        b"\x00\x00\x00\x14",
+        "First Offset to Paint table from beginning of LayerList (20)",
+    ),
+    (
+        b"\x00\x00\x00\x23",
+        "Second Offset to Paint table from beginning of LayerList (35)",
+    ),
+    (
+        b"\x00\x00\x00\x4e",
+        "Third Offset to Paint table from beginning of LayerList (78)",
+    ),
+    (
+        b"\x00\x00\x00\x9e",
+        "Fourth Offset to Paint table from beginning of LayerList (158)",
+    ),
+    # BaseGlyphPaintRecord[2]
+    (b"\x0a", "BaseGlyphPaintRecord[2].Paint.Format (10)"),
+    (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"),
+    (b"\x00\x0b", "BaseGlyphPaintRecord[2].Paint.Glyph (11)"),
+    # PaintVarSolid
+    (b"\x03", "LayerList.Paint[0].Paint.Format (3)"),
+    (b"\x00\x02", "Paint.PaletteIndex (2)"),
+    (b" \x00", "Paint.Alpha.value (0.5)"),
+    (b"\x00\x00\x00\x06", "VarIndexBase (6)"),
+    # PaintGlyph glyph00012
+    (b"\x0a", "LayerList.Paint[1].Format (10)"),
+    (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"),
+    (b"\x00\x0c", "LayerList.Paint[1].Glyph (glyph00012)"),
+    (b"\x04", "LayerList.Paint[1].Paint.Format (4)"),
+    (b"\x00\x00\x10", "Offset to ColorLine from beginning of PaintLinearGradient (16)"),
+    (b"\x00\x01", "Paint.x0 (1)"),
+    (b"\x00\x02", "Paint.y0 (2)"),
+    (b"\xff\xfd", "Paint.x1 (-3)"),
+    (b"\xff\xfc", "Paint.y1 (-4)"),
+    (b"\x00\x05", "Paint.x2 (5)"),
+    (b"\x00\x06", "Paint.y2 (6)"),
+    (b"\x01", "ColorLine.Extend (1; repeat)"),
+    (b"\x00\x03", "ColorLine.StopCount (3)"),
+    (b"\x00\x00", "ColorLine.ColorStop[0].StopOffset (0.0)"),
+    (b"\x00\x03", "ColorLine.ColorStop[0].PaletteIndex (3)"),
+    (b"@\x00", "ColorLine.ColorStop[0].Alpha (1.0)"),
+    (b" \x00", "ColorLine.ColorStop[1].StopOffset (0.5)"),
+    (b"\x00\x04", "ColorLine.ColorStop[1].PaletteIndex (4)"),
+    (b"@\x00", "ColorLine.ColorStop[1].Alpha (1.0)"),
+    (b"@\x00", "ColorLine.ColorStop[2].StopOffset (1.0)"),
+    (b"\x00\x05", "ColorLine.ColorStop[2].PaletteIndex (5)"),
+    (b"@\x00", "ColorLine.ColorStop[2].Alpha (1.0)"),
+    # PaintGlyph glyph00013
+    (b"\x0a", "LayerList.Paint[2].Format (10)"),
+    (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"),
+    (b"\x00\x0d", "LayerList.Paint[2].Glyph (13)"),
+    (b"\x0c", "LayerList.Paint[2].Paint.Format (12)"),
+    (b"\x00\x00\x07", "Offset to Paint subtable from beginning of PaintTransform (7)"),
+    (b"\x00\x00\x32", "Offset to Affine2x3 subtable from beginning of PaintTransform (50)"),
+    (b"\x07", "LayerList.Paint[2].Paint.Paint.Format (7)"),
+    (b"\x00\x00\x14", "Offset to ColorLine from beginning of PaintVarRadialGradient (20)"),
+    (b"\x00\x07", "Paint.x0.value (7)"),
+    (b"\x00\x08", "Paint.y0.value (8)"),
+    (b"\x00\t", "Paint.r0.value (9)"),
+    (b"\x00\n", "Paint.x1.value (10)"),
+    (b"\x00\x0b", "Paint.y1.value (11)"),
+    (b"\x00\x0c", "Paint.r1.value (12)"),
+    (b"\xff\xff\xff\xff", "VarIndexBase (0xFFFFFFFF)"),
+    (b"\x00", "ColorLine.Extend (0; pad)"),
+    (b"\x00\x02", "ColorLine.StopCount (2)"),
+    (b"\x00\x00", "ColorLine.ColorStop[0].StopOffset.value (0.0)"),
+    (b"\x00\x06", "ColorLine.ColorStop[0].PaletteIndex (6)"),
+    (b"@\x00", "ColorLine.ColorStop[0].Alpha.value (1.0)"),
+    (b"\xff\xff\xff\xff", "VarIndexBase (0xFFFFFFFF)"),
+    (b"@\x00", "ColorLine.ColorStop[1].StopOffset.value (1.0)"),
+    (b"\x00\x07", "ColorLine.ColorStop[1].PaletteIndex (7)"),
+    (b"\x19\x9a", "ColorLine.ColorStop[1].Alpha.value (0.4)"),
+
+    (b"\x00\x00\x00\x07", "VarIndexBase (7)"),
+    (b"\xff\xf3\x00\x00", "Affine2x3.xx (-13)"),
+    (b"\x00\x0e\x00\x00", "Affine2x3.xy (14)"),
+    (b"\x00\x0f\x00\x00", "Affine2x3.yx (15)"),
+    (b"\xff\xef\x00\x00", "Affine2x3.yy (-17)"),
+    (b"\x00\x12\x00\x00", "Affine2x3.yy (18)"),
+    (b"\x00\x13\x00\x00", "Affine2x3.yy (19)"),
+
+    # PaintTranslate
+    (b"\x0e", "LayerList.Paint[3].Format (14)"),
+    (b"\x00\x00\x08", "Offset to Paint subtable from beginning of PaintTranslate (8)"),
+    (b"\x01\x01", "dx (257)"),
+    (b"\x01\x02", "dy (258)"),
+
+    # PaintRotateAroundCenter
+    (b"\x1a", "LayerList.Paint[3].Paint.Format (26)"),
+    (
+        b"\x00\x00\x0a",
+        "Offset to Paint subtable from beginning of PaintRotateAroundCenter (11)",
+    ),
+    (b"\x10\x00", "angle (0.25)"),
+    (b"\x00\xff", "centerX (255)"),
+    (b"\x01\x00", "centerY (256)"),
+
+    # PaintSkew
+    (b"\x1c", "LayerList.Paint[3].Paint.Paint.Format (28)"),
+    (
+        b"\x00\x00\x08",
+        "Offset to Paint subtable from beginning of PaintSkew (8)",
+    ),
+    (b"\xfc\x17", "xSkewAngle (-0.0611)"),
+    (b"\x01\xc7", "ySkewAngle (0.0278)"),
+
+    # PaintGlyph
+    (b"\x0a", "LayerList.Paint[3].Paint.Paint.Paint.Format (10)"),
+    (b"\x00\x00\x06", "Offset to Paint subtable from beginning of PaintGlyph (6)"),
+    (b"\x00\x0b", "LayerList.Paint[2].Glyph (11)"),
+
+    # PaintSolid
+    (b"\x02", "LayerList.Paint[0].Paint.Paint.Paint.Paint.Format (2)"),
+    (b"\x00\x02", "Paint.PaletteIndex (2)"),
+    (b" \x00", "Paint.Alpha (0.5)"),
+
+    # ClipList
+    (b'\x01', "ClipList.Format (1)"),
+    (b'\x00\x00\x00\x02', "ClipList.ClipCount (2)"),
+    (b'\x00\x0a', "ClipRecord[0].StartGlyphID (10)"),
+    (b'\x00\x0a', "ClipRecord[0].EndGlyphID (10)"),
+    (b'\x00\x00\x13', "Offset to ClipBox subtable from beginning of ClipList (19)"),
+    (b'\x00\x0e', "ClipRecord[1].StartGlyphID (14)"),
+    (b'\x00\x0f', "ClipRecord[1].EndGlyphID (15)"),
+    (b'\x00\x00\x20', "Offset to ClipBox subtable from beginning of ClipList (32)"),
+
+    (b'\x02', "ClipBox.Format (2)"),
+    (b'\x00\x00', "ClipBox.xMin (0)"),
+    (b'\x00\x00', "ClipBox.yMin (0)"),
+    (b'\x01\xf4', "ClipBox.xMax (500)"),
+    (b'\x01\xf4', "ClipBox.yMax (500)"),
+    (b'\x00\x00\x00\t', "ClipBox.VarIndexBase (9)"),
+
+    (b'\x01', "ClipBox.Format (1)"),
+    (b'\x00\x00', "ClipBox.xMin (0)"),
+    (b'\x00\x00', "ClipBox.yMin (0)"),
+    (b'\x03\xe8', "ClipBox.xMax (1000)"),
+    (b'\x03\xe8', "ClipBox.yMax (1000)"),
+)
+
+COLR_V1_DATA = b"".join(t[0] for t in COLR_V1_SAMPLE)
+
+COLR_V1_XML = [
+    '<Version value="1"/>',
+    "<!-- BaseGlyphRecordCount=1 -->",
+    "<BaseGlyphRecordArray>",
+    '  <BaseGlyphRecord index="0">',
+    '    <BaseGlyph value="glyph00006"/>',
+    '    <FirstLayerIndex value="0"/>',
+    '    <NumLayers value="3"/>',
+    "  </BaseGlyphRecord>",
+    "</BaseGlyphRecordArray>",
+    "<LayerRecordArray>",
+    '  <LayerRecord index="0">',
+    '    <LayerGlyph value="glyph00007"/>',
+    '    <PaletteIndex value="0"/>',
+    "  </LayerRecord>",
+    '  <LayerRecord index="1">',
+    '    <LayerGlyph value="glyph00008"/>',
+    '    <PaletteIndex value="1"/>',
+    "  </LayerRecord>",
+    '  <LayerRecord index="2">',
+    '    <LayerGlyph value="glyph00009"/>',
+    '    <PaletteIndex value="2"/>',
+    "  </LayerRecord>",
+    "</LayerRecordArray>",
+    "<!-- LayerRecordCount=3 -->",
+    "<BaseGlyphList>",
+    "  <!-- BaseGlyphCount=3 -->",
+    '  <BaseGlyphPaintRecord index="0">',
+    '    <BaseGlyph value="glyph00010"/>',
+    '    <Paint Format="1"><!-- PaintColrLayers -->',
+    '      <NumLayers value="4"/>',
+    '      <FirstLayerIndex value="0"/>',
+    "    </Paint>",
+    "  </BaseGlyphPaintRecord>",
+    '  <BaseGlyphPaintRecord index="1">',
+    '    <BaseGlyph value="glyph00014"/>',
+    '    <Paint Format="32"><!-- PaintComposite -->',
+    '      <SourcePaint Format="11"><!-- PaintColrGlyph -->',
+    '        <Glyph value="glyph00010"/>',
+    "      </SourcePaint>",
+    '      <CompositeMode value="src_over"/>',
+    '      <BackdropPaint Format="13"><!-- PaintVarTransform -->',
+    '        <Paint Format="11"><!-- PaintColrGlyph -->',
+    '          <Glyph value="glyph00010"/>',
+    "        </Paint>",
+    "        <Transform>",
+    '          <xx value="1.0"/>',
+    '          <yx value="0.0"/>',
+    '          <xy value="0.0"/>',
+    '          <yy value="1.0"/>',
+    '          <dx value="300.0"/>',
+    '          <dy value="0.0"/>',
+    '          <VarIndexBase value="0"/>',
+    "        </Transform>",
+    "      </BackdropPaint>",
+    "    </Paint>",
+    "  </BaseGlyphPaintRecord>",
+    '  <BaseGlyphPaintRecord index="2">',
+    '    <BaseGlyph value="glyph00015"/>',
+    '    <Paint Format="10"><!-- PaintGlyph -->',
+    '      <Paint Format="8"><!-- PaintSweepGradient -->',
+    "        <ColorLine>",
+    '          <Extend value="pad"/>',
+    "          <!-- StopCount=2 -->",
+    '          <ColorStop index="0">',
+    '            <StopOffset value="0.0"/>',
+    '            <PaletteIndex value="3"/>',
+    '            <Alpha value="1.0"/>',
+    "          </ColorStop>",
+    '          <ColorStop index="1">',
+    '            <StopOffset value="1.0"/>',
+    '            <PaletteIndex value="5"/>',
+    '            <Alpha value="1.0"/>',
+    "          </ColorStop>",
+    "        </ColorLine>",
+    '        <centerX value="259"/>',
+    '        <centerY value="300"/>',
+    '        <startAngle value="45.0"/>',
+    '        <endAngle value="135.0"/>',
+    "      </Paint>",
+    '      <Glyph value="glyph00011"/>',
+    "    </Paint>",
+    "  </BaseGlyphPaintRecord>",
+    "</BaseGlyphList>",
+    "<LayerList>",
+    "  <!-- LayerCount=4 -->",
+    '  <Paint index="0" Format="10"><!-- PaintGlyph -->',
+    '    <Paint Format="3"><!-- PaintVarSolid -->',
+    '      <PaletteIndex value="2"/>',
+    '      <Alpha value="0.5"/>',
+    '      <VarIndexBase value="6"/>',
+    "    </Paint>",
+    '    <Glyph value="glyph00011"/>',
+    "  </Paint>",
+    '  <Paint index="1" Format="10"><!-- PaintGlyph -->',
+    '    <Paint Format="4"><!-- PaintLinearGradient -->',
+    "      <ColorLine>",
+    '        <Extend value="repeat"/>',
+    "        <!-- StopCount=3 -->",
+    '        <ColorStop index="0">',
+    '          <StopOffset value="0.0"/>',
+    '          <PaletteIndex value="3"/>',
+    '          <Alpha value="1.0"/>',
+    "        </ColorStop>",
+    '        <ColorStop index="1">',
+    '          <StopOffset value="0.5"/>',
+    '          <PaletteIndex value="4"/>',
+    '          <Alpha value="1.0"/>',
+    "        </ColorStop>",
+    '        <ColorStop index="2">',
+    '          <StopOffset value="1.0"/>',
+    '          <PaletteIndex value="5"/>',
+    '          <Alpha value="1.0"/>',
+    "        </ColorStop>",
+    "      </ColorLine>",
+    '      <x0 value="1"/>',
+    '      <y0 value="2"/>',
+    '      <x1 value="-3"/>',
+    '      <y1 value="-4"/>',
+    '      <x2 value="5"/>',
+    '      <y2 value="6"/>',
+    "    </Paint>",
+    '    <Glyph value="glyph00012"/>',
+    "  </Paint>",
+    '  <Paint index="2" Format="10"><!-- PaintGlyph -->',
+    '    <Paint Format="12"><!-- PaintTransform -->',
+    '      <Paint Format="7"><!-- PaintVarRadialGradient -->',
+    "        <ColorLine>",
+    '          <Extend value="pad"/>',
+    "          <!-- StopCount=2 -->",
+    '          <ColorStop index="0">',
+    '            <StopOffset value="0.0"/>',
+    '            <PaletteIndex value="6"/>',
+    '            <Alpha value="1.0"/>',
+    "            <VarIndexBase/>",
+    "          </ColorStop>",
+    '          <ColorStop index="1">',
+    '            <StopOffset value="1.0"/>',
+    '            <PaletteIndex value="7"/>',
+    '            <Alpha value="0.4"/>',
+    '            <VarIndexBase value="7"/>',
+    "          </ColorStop>",
+    "        </ColorLine>",
+    '        <x0 value="7"/>',
+    '        <y0 value="8"/>',
+    '        <r0 value="9"/>',
+    '        <x1 value="10"/>',
+    '        <y1 value="11"/>',
+    '        <r1 value="12"/>',
+    "        <VarIndexBase/>",
+    "      </Paint>",
+    "      <Transform>",
+    '        <xx value="-13.0"/>',
+    '        <yx value="14.0"/>',
+    '        <xy value="15.0"/>',
+    '        <yy value="-17.0"/>',
+    '        <dx value="18.0"/>',
+    '        <dy value="19.0"/>',
+    "      </Transform>",
+    "    </Paint>",
+    '    <Glyph value="glyph00013"/>',
+    "  </Paint>",
+    '  <Paint index="3" Format="14"><!-- PaintTranslate -->',
+    '    <Paint Format="26"><!-- PaintRotateAroundCenter -->',
+    '      <Paint Format="28"><!-- PaintSkew -->',
+    '        <Paint Format="10"><!-- PaintGlyph -->',
+    '          <Paint Format="2"><!-- PaintSolid -->',
+    '            <PaletteIndex value="2"/>',
+    '            <Alpha value="0.5"/>',
+    "          </Paint>",
+    '          <Glyph value="glyph00011"/>',
+    "        </Paint>",
+    '        <xSkewAngle value="-11.0"/>',
+    '        <ySkewAngle value="5.0"/>',
+    "      </Paint>",
+    '      <angle value="45.0"/>',
+    '      <centerX value="255"/>',
+    '      <centerY value="256"/>',
+    "    </Paint>",
+    '    <dx value="257"/>',
+    '    <dy value="258"/>',
+    "  </Paint>",
+    "</LayerList>",
+    '<ClipList Format="1">',
+    "  <Clip>",
+    '    <Glyph value="glyph00010"/>',
+    '    <ClipBox Format="2">',
+    '      <xMin value="0"/>',
+    '      <yMin value="0"/>',
+    '      <xMax value="500"/>',
+    '      <yMax value="500"/>',
+    '      <VarIndexBase value="9"/>',
+    "    </ClipBox>",
+    "  </Clip>",
+    "  <Clip>",
+    '    <Glyph value="glyph00014"/>',
+    '    <Glyph value="glyph00015"/>',
+    '    <ClipBox Format="1">',
+    '      <xMin value="0"/>',
+    '      <yMin value="0"/>',
+    '      <xMax value="1000"/>',
+    '      <yMax value="1000"/>',
+    "    </ClipBox>",
+    "  </Clip>",
+    "</ClipList>",
+]
+
+COLR_V1_VAR_XML = [
+    '<VarIndexMap Format="0">',
+    '  <Map index="0" outer="1" inner="1"/>',
+    '  <Map index="1" outer="1" inner="0"/>',
+    '  <Map index="2" outer="1" inner="0"/>',
+    '  <Map index="3" outer="1" inner="1"/>',
+    '  <Map index="4" outer="1" inner="0"/>',
+    '  <Map index="5" outer="1" inner="0"/>',
+    '  <Map index="6" outer="0" inner="2"/>',
+    '  <Map index="7" outer="0" inner="0"/>',
+    '  <Map index="8" outer="0" inner="1"/>',
+    '  <Map index="9" outer="1" inner="0"/>',
+    '  <Map index="10" outer="1" inner="0"/>',
+    '  <Map index="11" outer="0" inner="3"/>',
+    '  <Map index="12" outer="0" inner="3"/>',
+    "</VarIndexMap>",
+    '<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=2 -->",
+    '  <VarData index="0">',
+    "    <!-- ItemCount=4 -->",
+    '    <NumShorts value="1"/>',
+    "    <!-- VarRegionCount=1 -->",
+    '    <VarRegionIndex index="0" value="0"/>',
+    '    <Item index="0" value="[-3277]"/>',
+    '    <Item index="1" value="[6553]"/>',
+    '    <Item index="2" value="[8192]"/>',
+    '    <Item index="3" value="[500]"/>',
+    "  </VarData>",
+    '  <VarData index="1">',
+    "    <!-- ItemCount=2 -->",
+    '    <NumShorts value="32769"/>',
+    "    <!-- VarRegionCount=1 -->",
+    '    <VarRegionIndex index="0" value="0"/>',
+    '    <Item index="0" value="[0]"/>',
+    '    <Item index="1" value="[65536]"/>',
+    "  </VarData>",
+    "</VarStore>",
+]
+
+
+class COLR_V1_Test(object):
+    def test_decompile_and_compile(self, font):
+        colr = table_C_O_L_R_()
+        colr.decompile(COLR_V1_DATA, font)
+        diff_binary_fragments(colr.compile(font), COLR_V1_SAMPLE)
+
+    def test_decompile_and_dump_xml(self, font):
+        colr = table_C_O_L_R_()
+        colr.decompile(COLR_V1_DATA, font)
+
+        dump(colr, font)
+        assert getXML(colr.toXML, font) == COLR_V1_XML
+
+    def test_load_from_xml_and_compile(self, font):
+        colr = table_C_O_L_R_()
+        for name, attrs, content in parseXML(COLR_V1_XML):
+            colr.fromXML(name, attrs, content, font)
+        diff_binary_fragments(colr.compile(font), COLR_V1_SAMPLE)
+
+    def test_round_trip_xml(self, font):
+        colr = table_C_O_L_R_()
+        for name, attrs, content in parseXML(COLR_V1_XML):
+            colr.fromXML(name, attrs, content, font)
+        compiled = colr.compile(font)
+
+        colr = table_C_O_L_R_()
+        colr.decompile(compiled, font)
+        assert getXML(colr.toXML, font) == COLR_V1_XML
+
+
+class COLR_V1_Variable_Test(object):
+    def test_round_trip_xml(self, font):
+        colr = table_C_O_L_R_()
+        xml = COLR_V1_XML + COLR_V1_VAR_XML
+        for name, attrs, content in parseXML(xml):
+            colr.fromXML(name, attrs, content, font)
+        compiled = colr.compile(font)
+
+        colr = table_C_O_L_R_()
+        colr.decompile(compiled, font)
+        assert getXML(colr.toXML, font) == xml
diff --git a/Tests/ttLib/tables/C_P_A_L_test.py b/Tests/ttLib/tables/C_P_A_L_test.py
index bacd4a8..10c8ea0 100644
--- a/Tests/ttLib/tables/C_P_A_L_test.py
+++ b/Tests/ttLib/tables/C_P_A_L_test.py
@@ -1,5 +1,3 @@
-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
@@ -67,9 +65,6 @@
         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')
@@ -81,9 +76,6 @@
             '[#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')
@@ -93,9 +85,10 @@
         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.paletteLabels, [cpal.NO_NAME_ID] * len(cpal.palettes))
         self.assertEqual(cpal.paletteTypes, [0, 0])
-        self.assertEqual(cpal.paletteEntryLabels, [0, 0, 0])
+        self.assertEqual(cpal.paletteEntryLabels,
+                        [cpal.NO_NAME_ID] * cpal.numPaletteEntries)
 
     def test_decompile_v1(self):
         cpal = newTable('CPAL')
@@ -195,9 +188,6 @@
         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')
@@ -219,7 +209,8 @@
                          '[[#12345678, #FEDCBA98, #CAFECAFE]]')
         self.assertEqual(cpal.paletteLabels, [259])
         self.assertEqual(cpal.paletteTypes, [2])
-        self.assertEqual(cpal.paletteEntryLabels, [0, 262, 0])
+        self.assertEqual(cpal.paletteEntryLabels,
+                        [cpal.NO_NAME_ID, 262, cpal.NO_NAME_ID])
 
 
 if __name__ == "__main__":
diff --git a/Tests/ttLib/tables/M_V_A_R_test.py b/Tests/ttLib/tables/M_V_A_R_test.py
index 05a92e8..a8b092e 100644
--- a/Tests/ttLib/tables/M_V_A_R_test.py
+++ b/Tests/ttLib/tables/M_V_A_R_test.py
@@ -1,5 +1,3 @@
-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
@@ -10,8 +8,8 @@
 MVAR_DATA = deHexStr(
     '0001 0000 '  # 0:   version=1.0
     '0000 0008 '  # 4:   reserved=0, valueRecordSize=8
-    '0007 '       # 8:   valueRecordCount=7
-    '0044 '       # 10:  offsetToItemVariationStore=68
+    '0009 '       # 8:   valueRecordCount=9
+    '0054 '       # 10:  offsetToItemVariationStore=84
     '6861 7363 '  # 12:  ValueRecord.valueTag="hasc"
     '0000 '       # 16:  ValueRecord.deltaSetOuterIndex
     '0003 '       # 18:  ValueRecord.deltaSetInnerIndex
@@ -33,30 +31,36 @@
     '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
+    '7465 7374 '  # 68:  ValueRecord.valueTag="test"
+    '0000 '       # 72:  ValueRecord.deltaSetOuterIndex
+    '0002 '       # 74:  ValueRecord.deltaSetInnerIndex
+    '7465 7332 '  # 76:  ValueRecord.valueTag="tes2"
+    '0000 '       # 78:  ValueRecord.deltaSetOuterIndex
+    '0002 '       # 82:  ValueRecord.deltaSetInnerIndex
+    '0001 '       # 84:  VarStore.format=1
+    '0000 000C '  # 86:  VarStore.offsetToVariationRegionList=12
+    '0001 '       # 90:  VarStore.itemVariationDataCount=1
+    '0000 0016 '  # 92:  VarStore.itemVariationDataOffsets[0]=22
+    '0001 '       # 96:  VarRegionList.axisCount=1
+    '0001 '       # 98:  VarRegionList.regionCount=1
+    '0000 '       # 100:  variationRegions[0].regionAxes[0].startCoord=0.0
+    '4000 '       # 102:  variationRegions[0].regionAxes[0].peakCoord=1.0
+    '4000 '       # 104:  variationRegions[0].regionAxes[0].endCoord=1.0
+    '0004 '       # 106:  VarData.ItemCount=4
+    '0001 '       # 108:  VarData.NumShorts=1
+    '0001 '       # 110:  VarData.VarRegionCount=1
+    '0000 '       # 112:  VarData.VarRegionIndex[0]=0
+    'FF38 '       # 114:  VarData.deltaSets[0]=-200
+    'FFCE '       # 116: VarData.deltaSets[0]=-50
+    '0064 '       # 118: VarData.deltaSets[0]=100
+    '00C8 '       # 120: VarData.deltaSets[0]=200
 )
 
 MVAR_XML = [
     '<Version value="0x00010000"/>',
     '<Reserved value="0"/>',
     '<ValueRecordSize value="8"/>',
-    '<!-- ValueRecordCount=7 -->',
+    '<!-- ValueRecordCount=9 -->',
     '<VarStore Format="1">',
     '  <Format value="1"/>',
     '  <VarRegionList>',
@@ -110,6 +114,14 @@
     '  <ValueTag value="spyo"/>',
     '  <VarIdx value="2"/>',
     '</ValueRecord>',
+    '<ValueRecord index="7">',
+    '  <ValueTag value="test"/>',
+    '  <VarIdx value="2"/>',
+    '</ValueRecord>',
+    '<ValueRecord index="8">',
+    '  <ValueTag value="tes2"/>',
+    '  <VarIdx value="2"/>',
+    '</ValueRecord>',
 ]
 
 
@@ -125,6 +137,13 @@
         mvar.decompile(MVAR_DATA, font)
         self.assertEqual(getXML(mvar.toXML), MVAR_XML)
 
+
+    def test_decompile_toXML_lazy(self):
+        mvar = newTable('MVAR')
+        font = TTFont(lazy=True)
+        mvar.decompile(MVAR_DATA, font)
+        self.assertEqual(getXML(mvar.toXML), MVAR_XML)
+
     def test_compile_fromXML(self):
         mvar = newTable('MVAR')
         font = TTFont()
diff --git a/Tests/ttLib/tables/O_S_2f_2_test.py b/Tests/ttLib/tables/O_S_2f_2_test.py
index 116e82e..1f12309 100644
--- a/Tests/ttLib/tables/O_S_2f_2_test.py
+++ b/Tests/ttLib/tables/O_S_2f_2_test.py
@@ -1,4 +1,3 @@
-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
diff --git a/Tests/ttLib/tables/S_T_A_T_test.py b/Tests/ttLib/tables/S_T_A_T_test.py
index e851f98..c5c1234 100644
--- a/Tests/ttLib/tables/S_T_A_T_test.py
+++ b/Tests/ttLib/tables/S_T_A_T_test.py
@@ -1,5 +1,3 @@
-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
@@ -148,7 +146,7 @@
     '<AxisValueArray>',
     '  <AxisValue index="0" Format="3">',
     '    <AxisIndex value="0"/>',
-    '    <Flags value="2"/>',
+    '    <Flags value="2"/>  <!-- ElidableAxisValueName -->',
     '    <ValueNameID value="2"/>',
     '    <Value value="400.0"/>',
     '    <LinkedValue value="700.0"/>',
@@ -192,7 +190,7 @@
     '<AxisValueArray>',
     '  <AxisValue index="0" Format="3">',
     '    <AxisIndex value="0"/>',
-    '    <Flags value="2"/>',
+    '    <Flags value="2"/>  <!-- ElidableAxisValueName -->',
     '    <ValueNameID value="2"/>',
     '    <Value value="400.0"/>',
     '    <LinkedValue value="700.0"/>',
diff --git a/Tests/ttLib/tables/T_S_I__0_test.py b/Tests/ttLib/tables/T_S_I__0_test.py
index 1de6b34..44ca44e 100644
--- a/Tests/ttLib/tables/T_S_I__0_test.py
+++ b/Tests/ttLib/tables/T_S_I__0_test.py
@@ -1,5 +1,4 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import SimpleNamespace
+from types 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
diff --git a/Tests/ttLib/tables/T_S_I__1_test.py b/Tests/ttLib/tables/T_S_I__1_test.py
index e529425..3a565ad 100644
--- a/Tests/ttLib/tables/T_S_I__1_test.py
+++ b/Tests/ttLib/tables/T_S_I__1_test.py
@@ -1,7 +1,4 @@
-from __future__ import (
-    print_function, division, absolute_import, unicode_literals
-)
-from fontTools.misc.py23 import unichr, tobytes
+from fontTools.misc.py23 import 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
@@ -36,7 +33,7 @@
     # ['a', 'b', 'c', ...]
     ch = 0x61
     n = len(indextable.indices)
-    font.glyphOrder = [unichr(i) for i in range(ch, ch+n)]
+    font.glyphOrder = [chr(i) for i in range(ch, ch+n)]
     font['TSI0'] = indextable
     return font
 
diff --git a/Tests/ttLib/tables/TupleVariation_test.py b/Tests/ttLib/tables/TupleVariation_test.py
index 6986d5b..99b9491 100644
--- a/Tests/ttLib/tables/TupleVariation_test.py
+++ b/Tests/ttLib/tables/TupleVariation_test.py
@@ -1,6 +1,3 @@
-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
@@ -8,6 +5,7 @@
 from fontTools.ttLib.tables.TupleVariation import \
 	log, TupleVariation, compileSharedTuples, decompileSharedTuples, \
 	compileTupleVariationStore, decompileTupleVariationStore, inferRegion_
+from io import BytesIO
 import random
 import unittest
 
@@ -18,9 +16,9 @@
 
 
 AXES = {
-	"wdth": (0.3, 0.4, 0.5),
+	"wdth": (0.25, 0.375, 0.5),
 	"wght": (0.0, 1.0, 1.0),
-	"opsz": (-0.7, -0.7, 0.0)
+	"opsz": (-0.75, -0.75, 0.0)
 }
 
 
@@ -69,6 +67,13 @@
 
 
 class TupleVariationTest(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_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)])
@@ -107,7 +112,7 @@
 		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"/>',
+			  '<coord axis="wdth" min="0.25" value="0.375" max="0.5"/>',
 			  '<!-- bad delta #0 -->',
 			'</tuple>',
 		], TupleVariationTest.xml_lines(writer))
@@ -118,9 +123,9 @@
 		g.toXML(writer, ["wdth", "wght", "opsz"])
 		self.assertEqual([
 			'<tuple>',
-			  '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
+			  '<coord axis="wdth" min="0.25" value="0.375" max="0.5"/>',
 			  '<coord axis="wght" value="1.0"/>',
-			  '<coord axis="opsz" value="-0.7"/>',
+			  '<coord axis="opsz" value="-0.75"/>',
 			  '<delta cvt="0" value="42"/>',
 			  '<delta cvt="2" value="23"/>',
 			  '<delta cvt="3" value="0"/>',
@@ -134,9 +139,9 @@
 		g.toXML(writer, ["wdth", "wght", "opsz"])
 		self.assertEqual([
 			'<tuple>',
-			  '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
+			  '<coord axis="wdth" min="0.25" value="0.375" max="0.5"/>',
 			  '<coord axis="wght" value="1.0"/>',
-			  '<coord axis="opsz" value="-0.7"/>',
+			  '<coord axis="opsz" value="-0.75"/>',
 			  '<delta pt="0" x="9" y="8"/>',
 			  '<delta pt="2" x="7" y="6"/>',
 			  '<delta pt="3" x="0" y="0"/>',
@@ -156,6 +161,22 @@
 			'</tuple>'
 		], TupleVariationTest.xml_lines(writer))
 
+	def test_toXML_axes_floats(self):
+		writer = XMLWriter(BytesIO())
+		axes = {
+			"wght": (0.0, 0.2999878, 0.7000122),
+			"wdth": (0.0, 0.4000244, 0.4000244),
+		}
+		g = TupleVariation(axes, [None] * 5)
+		g.toXML(writer, ["wght", "wdth"])
+		self.assertEqual(
+			[
+				'<coord axis="wght" min="0.0" value="0.3" max="0.7"/>',
+				'<coord axis="wdth" value="0.4"/>',
+			],
+			TupleVariationTest.xml_lines(writer)[1:3]
+		)
+
 	def test_fromXML_badDeltaFormat(self):
 		g = TupleVariation({}, [])
 		with CapturingLogHandler(log, "WARNING") as captor:
@@ -167,9 +188,9 @@
 	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="wdth" min="0.25" value="0.375" max="0.5"/>'
 				'<coord axis="wght" value="1.0"/>'
-				'<coord axis="opsz" value="-0.7"/>'
+				'<coord axis="opsz" value="-0.75"/>'
 				'<delta cvt="1" value="42"/>'
 				'<delta cvt="2" value="-23"/>'):
 			g.fromXML(name, attrs, content)
@@ -179,23 +200,38 @@
 	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="wdth" min="0.25" value="0.375" max="0.5"/>'
 				'<coord axis="wght" value="1.0"/>'
-				'<coord axis="opsz" value="-0.7"/>'
+				'<coord axis="opsz" value="-0.75"/>'
 				'<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_fromXML_axes_floats(self):
+		g = TupleVariation({}, [None] * 4)
+		for name, attrs, content in parseXML(
+			'<coord axis="wght" min="0.0" value="0.3" max="0.7"/>'
+			'<coord axis="wdth" value="0.4"/>'
+		):
+			g.fromXML(name, attrs, content)
+
+		self.assertEqual(g.axes["wght"][0], 0)
+		self.assertAlmostEqual(g.axes["wght"][1], 0.2999878)
+		self.assertAlmostEqual(g.axes["wght"][2], 0.7000122)
+
+		self.assertEqual(g.axes["wdth"][0], 0)
+		self.assertAlmostEqual(g.axes["wdth"][1], 0.4000244)
+		self.assertAlmostEqual(g.axes["wdth"][2], 0.4000244)
+
 	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})
+		tup, deltas = var.compile(axisTags, sharedPeakIndices, pointData=b'')
 		# len(deltas)=8; flags=None; tupleIndex=0x77
 		# embeddedPeaks=[]; intermediateCoord=[]
 		self.assertEqual("00 08 00 77", hexencode(tup))
@@ -209,8 +245,7 @@
 			[(7,4), (8,5), (9,6)])
 		axisTags = ["wght", "wdth"]
 		sharedPeakIndices = { var.compileCoord(axisTags): 0x77 }
-		tup, deltas, _ = var.compile(axisTags, sharedPeakIndices,
-		                          sharedPoints={0,1,2})
+		tup, deltas = var.compile(axisTags, sharedPeakIndices, pointData=b'')
 		# 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))
@@ -224,8 +259,7 @@
 			[(7,4), (8,5), (9,6)])
 		axisTags = ["wght", "wdth"]
 		sharedPeakIndices = { var.compileCoord(axisTags): 0x77 }
-		tup, deltas, _ = var.compile(axisTags, sharedPeakIndices,
-		                          sharedPoints=None)
+		tup, deltas = var.compile(axisTags, sharedPeakIndices)
 		# len(deltas)=9; flags=PRIVATE_POINT_NUMBERS; tupleIndex=0x77
 		# embeddedPeak=[]; intermediateCoord=[]
 		self.assertEqual("00 09 20 77", hexencode(tup))
@@ -240,8 +274,7 @@
 			[(7,4), (8,5), (9,6)])
 		axisTags = ["wght", "wdth"]
 		sharedPeakIndices = { var.compileCoord(axisTags): 0x77 }
-		tuple, deltas, _ = var.compile(axisTags,
-		                            sharedPeakIndices, sharedPoints=None)
+		tuple, deltas = var.compile(axisTags, sharedPeakIndices)
 		# 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",
@@ -255,8 +288,7 @@
 		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})
+		tup, deltas = var.compile(axisTags=["wght", "wdth"], pointData=b'')
 		# 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))
@@ -268,8 +300,7 @@
 		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})
+		tup, deltas = var.compile(axisTags=["wght", "wdth"], pointData=b'')
 		# 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))
@@ -280,9 +311,7 @@
 		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})
+		tup, deltas = var.compile(axisTags=["wght", "wdth"], pointData=b'')
 		# 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",
@@ -295,8 +324,7 @@
 		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)
+		tup, deltas = var.compile(axisTags=["wght", "wdth"])
 		# 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))
@@ -309,8 +337,7 @@
 		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)
+		tup, deltas = var.compile(axisTags=["wght", "wdth"])
 		# 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))
@@ -322,9 +349,7 @@
 		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)
+		tup, deltas = var.compile(axisTags = ["wght", "wdth"])
 		# 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)]
@@ -339,9 +364,7 @@
 		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)
+		tup, deltas = var.compile(axisTags = ["wght", "wdth"])
 		# 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)]
@@ -370,7 +393,7 @@
 		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
+		# Make sure we are not affected by https://github.com/fonttools/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()}
@@ -378,8 +401,8 @@
 		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
+		compilePoints = lambda p: TupleVariation.compilePoints(set(p))
+		self.assertEqual("00", hexencode(compilePoints(set())))  # 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])))
@@ -451,7 +474,7 @@
 
 	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)
+		compile = lambda points: TupleVariation.compilePoints(points)
 		decompile = lambda data: set(TupleVariation.decompilePoints_(numPointsInGlyph, data, 0, "gvar")[0])
 		for i in range(50):
 			points = set(random.sample(range(numPointsInGlyph), 30))
@@ -459,18 +482,17 @@
 					    "failed round-trip decompile/compilePoints; points=%s" % points)
 		allPoints = set(range(numPointsInGlyph))
 		self.assertSetEqual(allPoints, decompile(compile(allPoints)))
+		self.assertSetEqual(allPoints, decompile(compile(set())))
 
 	def test_compileDeltas_points(self):
-		var = TupleVariation({}, [(0,0), (1, 0), (2, 0), None, (4, 0), (5, 0)])
-		points = {1, 2, 3, 4}
+		var = TupleVariation({}, [None, (1, 0), (2, 0), None, (4, 0), None])
 		# deltaX for points: [1, 2, 4]; deltaY for points: [0, 0, 0]
-		self.assertEqual("02 01 02 04 82", hexencode(var.compileDeltas(points)))
+		self.assertEqual("02 01 02 04 82", hexencode(var.compileDeltas()))
 
 	def test_compileDeltas_constants(self):
-		var = TupleVariation({}, [0, 1, 2, None, 4, 5])
-		cvts = {1, 2, 3, 4}
+		var = TupleVariation({}, [None, 1, 2, None, 4, None])
 		# delta for cvts: [1, 2, 4]
-		self.assertEqual("02 01 02 04", hexencode(var.compileDeltas(cvts)))
+		self.assertEqual("02 01 02 04", hexencode(var.compileDeltas()))
 
 	def test_compileDeltaValues(self):
 		compileDeltaValues = lambda values: hexencode(TupleVariation.compileDeltaValues_(values))
@@ -512,11 +534,6 @@
 		# 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_
@@ -542,8 +559,12 @@
 			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.
+		# Below, the peak coordinate {"wght": 1.0, "wdth": 0.8} appears
+		# three times (most frequent sorted first); {"wght": 1.0, "wdth": 0.5}
+		# and {"wght": 1.0, "wdth": 0.7} both appears two times (tie) and
+		# are sorted alphanumerically to ensure determinism.
+		# The peak coordinate {"wght": 1.0, "wdth": 0.9} appears only once
+		# and is thus ignored.
 		# Because the start and end of variation ranges is not encoded
 		# into the shared pool, they should get ignored.
 		deltas = [None] * 4
@@ -562,7 +583,7 @@
 			}, deltas),
 			TupleVariation({
 				"wght": (1.0, 1.0, 1.0),
-				"wdth": (0.3, 0.7, 1.0)
+				"wdth": (0.3, 0.5, 1.0)
 			}, deltas),
 			TupleVariation({
 				"wght": (1.0, 1.0, 1.0),
@@ -571,11 +592,19 @@
 			TupleVariation({
 				"wght": (1.0, 1.0, 1.0),
 				"wdth": (0.3, 0.9, 1.0)
-            }, deltas)
+			}, deltas),
+			TupleVariation({
+				"wght": (1.0, 1.0, 1.0),
+				"wdth": (0.4, 0.8, 1.0)
+			}, deltas),
+			TupleVariation({
+				"wght": (1.0, 1.0, 1.0),
+				"wdth": (0.5, 0.5, 1.0)
+			}, deltas),
 		]
 		result = compileSharedTuples(["wght", "wdth"], variations)
 		self.assertEqual([hexencode(c) for c in result],
-		                 ["40 00 2C CD", "40 00 33 33"])
+		                 ["40 00 33 33", "40 00 20 00", "40 00 2C CD"])
 
 	def test_decompileSharedTuples_Skia(self):
 		sharedTuples = decompileSharedTuples(
@@ -681,6 +710,167 @@
 		content = writer.file.getvalue().decode("utf-8")
 		return [line.strip() for line in content.splitlines()][1:]
 
+	def test_getCoordWidth(self):
+		empty = TupleVariation({}, [])
+		self.assertEqual(empty.getCoordWidth(), 0)
+
+		empty = TupleVariation({}, [None])
+		self.assertEqual(empty.getCoordWidth(), 0)
+
+		gvarTuple = TupleVariation({}, [None, (0, 0)])
+		self.assertEqual(gvarTuple.getCoordWidth(), 2)
+
+		cvarTuple = TupleVariation({}, [None, 0])
+		self.assertEqual(cvarTuple.getCoordWidth(), 1)
+
+		cvarTuple.coordinates[1] *= 1.0
+		self.assertEqual(cvarTuple.getCoordWidth(), 1)
+
+		with self.assertRaises(TypeError):
+			TupleVariation({}, [None, "a"]).getCoordWidth()
+
+	def test_scaleDeltas_cvar(self):
+		var = TupleVariation({}, [100, None])
+
+		var.scaleDeltas(1.0)
+		self.assertEqual(var.coordinates, [100, None])
+
+		var.scaleDeltas(0.333)
+		self.assertAlmostEqual(var.coordinates[0], 33.3)
+		self.assertIsNone(var.coordinates[1])
+
+		var.scaleDeltas(0.0)
+		self.assertEqual(var.coordinates, [0, None])
+
+	def test_scaleDeltas_gvar(self):
+		var = TupleVariation({}, [(100, 200), None])
+
+		var.scaleDeltas(1.0)
+		self.assertEqual(var.coordinates, [(100, 200), None])
+
+		var.scaleDeltas(0.333)
+		self.assertAlmostEqual(var.coordinates[0][0], 33.3)
+		self.assertAlmostEqual(var.coordinates[0][1], 66.6)
+		self.assertIsNone(var.coordinates[1])
+
+		var.scaleDeltas(0.0)
+		self.assertEqual(var.coordinates, [(0, 0), None])
+
+	def test_roundDeltas_cvar(self):
+		var = TupleVariation({}, [55.5, None, 99.9])
+		var.roundDeltas()
+		self.assertEqual(var.coordinates, [56, None, 100])
+
+	def test_roundDeltas_gvar(self):
+		var = TupleVariation({}, [(55.5, 100.0), None, (99.9, 100.0)])
+		var.roundDeltas()
+		self.assertEqual(var.coordinates, [(56, 100), None, (100, 100)])
+
+	def test_calcInferredDeltas(self):
+		var = TupleVariation({}, [(0, 0), None, None, None])
+		coords = [(1, 1), (1, 1), (1, 1), (1, 1)]
+
+		var.calcInferredDeltas(coords, [])
+
+		self.assertEqual(
+			var.coordinates,
+			[(0, 0), (0, 0), (0, 0), (0, 0)]
+		)
+
+	def test_calcInferredDeltas_invalid(self):
+		# cvar tuples can't have inferred deltas
+		with self.assertRaises(TypeError):
+			TupleVariation({}, [0]).calcInferredDeltas([], [])
+
+		# origCoords must have same length as self.coordinates
+		with self.assertRaises(ValueError):
+			TupleVariation({}, [(0, 0), None]).calcInferredDeltas([], [])
+
+		# at least 4 phantom points required
+		with self.assertRaises(AssertionError):
+			TupleVariation({}, [(0, 0), None]).calcInferredDeltas([(0, 0), (0, 0)], [])
+
+		with self.assertRaises(AssertionError):
+			TupleVariation({}, [(0, 0)] + [None]*5).calcInferredDeltas(
+				[(0, 0)]*6,
+				[1, 0]  # endPts not in increasing order
+			)
+
+	def test_optimize(self):
+		var = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0)]*5)
+
+		var.optimize([(0, 0)]*5, [0])
+
+		self.assertEqual(var.coordinates, [None, None, None, None, None])
+
+	def test_optimize_isComposite(self):
+		# when a composite glyph's deltas are all (0, 0), we still want
+		# to write out an entry in gvar, else macOS doesn't apply any
+		# variations to the composite glyph (even if its individual components
+		# do vary).
+		# https://github.com/fonttools/fonttools/issues/1381
+		var = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0)]*5)
+		var.optimize([(0, 0)]*5, [0], isComposite=True)
+		self.assertEqual(var.coordinates, [(0, 0)]*5)
+
+		# it takes more than 128 (0, 0) deltas before the optimized tuple with
+		# (None) inferred deltas (except for the first) becomes smaller than
+		# the un-optimized one that has all deltas explicitly set to (0, 0).
+		var = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0)]*129)
+		var.optimize([(0, 0)]*129, list(range(129-4)), isComposite=True)
+		self.assertEqual(var.coordinates, [(0, 0)] + [None]*128)
+
+	def test_sum_deltas_gvar(self):
+		var1 = TupleVariation(
+			{},
+			[
+				(-20, 0), (-20, 0), (20, 0), (20, 0),
+				(0, 0), (0, 0), (0, 0), (0, 0),
+			]
+		)
+		var2 = TupleVariation(
+			{},
+			[
+				(-10, 0), (-10, 0), (10, 0), (10, 0),
+				(0, 0), (20, 0), (0, 0), (0, 0),
+			]
+		)
+
+		var1 += var2
+
+		self.assertEqual(
+			var1.coordinates,
+			[
+				(-30, 0), (-30, 0), (30, 0), (30, 0),
+				(0, 0), (20, 0), (0, 0), (0, 0),
+			]
+		)
+
+	def test_sum_deltas_gvar_invalid_length(self):
+		var1 = TupleVariation({}, [(1, 2)])
+		var2 = TupleVariation({}, [(1, 2), (3, 4)])
+
+		with self.assertRaisesRegex(ValueError, "deltas with different lengths"):
+			var1 += var2
+
+	def test_sum_deltas_gvar_with_inferred_points(self):
+		var1 = TupleVariation({}, [(1, 2), None])
+		var2 = TupleVariation({}, [(2, 3), None])
+
+		with self.assertRaisesRegex(ValueError, "deltas with inferred points"):
+			var1 += var2
+
+	def test_sum_deltas_cvar(self):
+		axes = {"wght": (0.0, 1.0, 1.0)}
+		var1 = TupleVariation(axes, [0, 1, None, None])
+		var2 = TupleVariation(axes, [None, 2, None, 3])
+		var3 = TupleVariation(axes, [None, None, None, 4])
+
+		var1 += var2
+		var1 += var3
+
+		self.assertEqual(var1.coordinates, [0, 3, None, 7])
+
 
 if __name__ == "__main__":
 	import sys
diff --git a/Tests/ttLib/tables/_a_n_k_r_test.py b/Tests/ttLib/tables/_a_n_k_r_test.py
index 0327e46..6c9be16 100644
--- a/Tests/ttLib/tables/_a_n_k_r_test.py
+++ b/Tests/ttLib/tables/_a_n_k_r_test.py
@@ -1,6 +1,4 @@
 # 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
diff --git a/Tests/ttLib/tables/_a_v_a_r_test.py b/Tests/ttLib/tables/_a_v_a_r_test.py
index 7b92118..429ca2e 100644
--- a/Tests/ttLib/tables/_a_v_a_r_test.py
+++ b/Tests/ttLib/tables/_a_v_a_r_test.py
@@ -1,13 +1,10 @@
-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
+from io import BytesIO
 import unittest
 
 
@@ -18,6 +15,17 @@
 
 
 class AxisVariationTableTest(unittest.TestCase):
+    def assertAvarAlmostEqual(self, segments1, segments2):
+        self.assertSetEqual(set(segments1.keys()), set(segments2.keys()))
+        for axisTag, mapping1 in segments1.items():
+            mapping2 = segments2[axisTag]
+            self.assertEqual(len(mapping1), len(mapping2))
+            for (k1, v1), (k2, v2) in zip(
+                sorted(mapping1.items()), sorted(mapping2.items())
+            ):
+                self.assertAlmostEqual(k1, k2)
+                self.assertAlmostEqual(v1, v2)
+
     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}
@@ -27,8 +35,8 @@
     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},
+        self.assertAvarAlmostEqual({
+            "wdth": {-1.0: -1.0, 0.0: 0.0, 0.2999878: 0.7999878, 1.0: 1.0},
             "wght": {-1.0: -1.0, 0.0: 0.0, 1.0: 1.0}
         }, avar.segments)
 
@@ -39,7 +47,7 @@
 
     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}
+        avar.segments["opsz"] = {-1.0: -1.0, 0.0: 0.0, 0.2999878: 0.7999878, 1.0: 1.0}
         writer = XMLWriter(BytesIO())
         avar.toXML(writer, self.makeFont(["opsz"]))
         self.assertEqual([
@@ -61,8 +69,10 @@
                 '    <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)
+        self.assertAvarAlmostEqual(
+            {"wdth": {-1: -1, 0: 0, 0.7000122: 0.2000122, 1.0: 1.0}},
+            avar.segments
+        )
 
     @staticmethod
     def makeFont(axisTags):
diff --git a/Tests/ttLib/tables/_b_s_l_n_test.py b/Tests/ttLib/tables/_b_s_l_n_test.py
index 97ffa6a..e40c1bd 100644
--- a/Tests/ttLib/tables/_b_s_l_n_test.py
+++ b/Tests/ttLib/tables/_b_s_l_n_test.py
@@ -1,5 +1,3 @@
-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
diff --git a/Tests/ttLib/tables/_c_i_d_g_test.py b/Tests/ttLib/tables/_c_i_d_g_test.py
index c6e8d3c..11c1fc0 100644
--- a/Tests/ttLib/tables/_c_i_d_g_test.py
+++ b/Tests/ttLib/tables/_c_i_d_g_test.py
@@ -1,6 +1,3 @@
-# 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
diff --git a/Tests/ttLib/tables/_c_m_a_p_test.py b/Tests/ttLib/tables/_c_m_a_p_test.py
index 6647345..6328504 100644
--- a/Tests/ttLib/tables/_c_m_a_p_test.py
+++ b/Tests/ttLib/tables/_c_m_a_p_test.py
@@ -1,9 +1,21 @@
-from __future__ import print_function, division, absolute_import, unicode_literals
-from fontTools.misc.py23 import *
+import io
+import os
+import re
 from fontTools import ttLib
+from fontTools.fontBuilder import FontBuilder
 import unittest
 from fontTools.ttLib.tables._c_m_a_p import CmapSubtable, table__c_m_a_p
 
+CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+DATA_DIR = os.path.join(CURR_DIR, 'data')
+CMAP_FORMAT_14_TTX = os.path.join(DATA_DIR, "_c_m_a_p_format_14.ttx")
+CMAP_FORMAT_14_BW_COMPAT_TTX = os.path.join(DATA_DIR, "_c_m_a_p_format_14_bw_compat.ttx")
+
+def strip_VariableItems(string):
+    # ttlib changes with the fontTools version
+    string = re.sub(' ttLibVersion=".*"', '', string)
+    return string
+
 class CmapSubtableTest(unittest.TestCase):
 
 	def makeSubtable(self, cmapFormat, platformID, platEncID, langID):
@@ -37,6 +49,47 @@
 		self.assertEqual(subtable.getEncoding("ascii"), "ascii")
 		self.assertEqual(subtable.getEncoding(default="xyz"), "xyz")
 
+	def test_compile_2(self):
+		subtable = self.makeSubtable(2, 1, 2, 0)
+		subtable.cmap = {c: "cid%05d" % c for c in range(32, 8192)}
+		font = ttLib.TTFont()
+		font.setGlyphOrder([".notdef"] + list(subtable.cmap.values()))
+		data = subtable.compile(font)
+
+		subtable2 = CmapSubtable.newSubtable(2)
+		subtable2.decompile(data, font)
+		self.assertEqual(subtable2.cmap, subtable.cmap)
+
+	def test_compile_2_rebuild_rev_glyph_order(self):
+		for fmt in [2, 4, 12]:
+			subtable = self.makeSubtable(fmt, 1, 2, 0)
+			subtable.cmap = {c: "cid%05d" % c for c in range(32, 8192)}
+			font = ttLib.TTFont()
+			font.setGlyphOrder([".notdef"] + list(subtable.cmap.values()))
+			font._reverseGlyphOrderDict = {}  # force first KeyError branch in subtable.compile()
+			data = subtable.compile(font)
+			subtable2 = CmapSubtable.newSubtable(fmt)
+			subtable2.decompile(data, font)
+			self.assertEqual(subtable2.cmap, subtable.cmap, str(fmt))
+
+	def test_compile_2_gids(self):
+		for fmt in [2, 4, 12]:
+			subtable = self.makeSubtable(fmt, 1, 3, 0)
+			subtable.cmap = {0x0041:'gid001', 0x0042:'gid002'}
+			font = ttLib.TTFont()
+			font.setGlyphOrder([".notdef"])
+			data = subtable.compile(font)
+
+	def test_compile_decompile_4_empty(self):
+		subtable = self.makeSubtable(4, 3, 1, 0)
+		subtable.cmap = {}
+		font = ttLib.TTFont()
+		font.setGlyphOrder([])
+		data = subtable.compile(font)
+		subtable2 = CmapSubtable.newSubtable(4)
+		subtable2.decompile(data, font)
+		self.assertEqual(subtable2.cmap, {})
+
 	def test_decompile_4(self):
 		subtable = CmapSubtable.newSubtable(4)
 		font = ttLib.TTFont()
@@ -82,6 +135,37 @@
 		self.assertEqual(font.getBestCmap(cmapPreferences=[(3, 1)]), {0x0041:'A', 0x0391:'A'})
 		self.assertEqual(font.getBestCmap(cmapPreferences=[(0, 4)]), None)
 
+	def test_format_14(self):
+		subtable = self.makeSubtable(14, 0, 5, 0)
+		subtable.cmap = {}  # dummy
+		subtable.uvsDict = {
+			0xFE00: [(0x0030, "zero.slash")],
+			0xFE01: [(0x0030, None)],
+		}
+		fb = FontBuilder(1024, isTTF=True)
+		font = fb.font
+		fb.setupGlyphOrder([".notdef", "zero.slash"])
+		fb.setupMaxp()
+		fb.setupPost()
+		cmap = table__c_m_a_p()
+		cmap.tableVersion = 0
+		cmap.tables = [subtable]
+		font["cmap"] = cmap
+		f = io.BytesIO()
+		font.save(f)
+		f.seek(0)
+		font = ttLib.TTFont(f)
+		self.assertEqual(font["cmap"].getcmap(0, 5).uvsDict, subtable.uvsDict)
+		f = io.StringIO(newline=None)
+		font.saveXML(f, tables=["cmap"])
+		ttx = strip_VariableItems(f.getvalue())
+		with open(CMAP_FORMAT_14_TTX) as f:
+			expected = strip_VariableItems(f.read())
+		self.assertEqual(ttx, expected)
+		with open(CMAP_FORMAT_14_BW_COMPAT_TTX) as f:
+			font.importXML(f)
+		self.assertEqual(font["cmap"].getcmap(0, 5).uvsDict, subtable.uvsDict)
+
 
 if __name__ == "__main__":
 	import sys
diff --git a/Tests/ttLib/tables/_c_v_a_r_test.py b/Tests/ttLib/tables/_c_v_a_r_test.py
index 0fd5531..31c1953 100644
--- a/Tests/ttLib/tables/_c_v_a_r_test.py
+++ b/Tests/ttLib/tables/_c_v_a_r_test.py
@@ -1,6 +1,3 @@
-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
@@ -53,12 +50,23 @@
 
 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)},
+    TupleVariation({"wght": (-1, -1.0, 0.0), "wdth": (0.0, 0.7999878, 0.7999878)},
                    [None, None, 9, 7, 8]),
 ]
 
 
 class CVARTableTest(unittest.TestCase):
+    def assertVariationsAlmostEqual(self, variations1, variations2):
+        self.assertEqual(len(variations1), len(variations2))
+        for (v1, v2) in zip(variations1, variations2):
+            self.assertSetEqual(set(v1.axes), set(v2.axes))
+            for axisTag, support1 in v1.axes.items():
+                support2 = v2.axes[axisTag]
+                self.assertEqual(len(support1), len(support2))
+                for s1, s2 in zip(support1, support2):
+                    self.assertAlmostEqual(s1, s2)
+                self.assertEqual(v1.coordinates, v2.coordinates)
+
     def makeFont(self):
         cvt, cvar, fvar = newTable("cvt "), newTable("cvar"), newTable("fvar")
         font = {"cvt ": cvt, "cvar": cvar, "fvar": fvar}
@@ -83,14 +91,14 @@
         cvar.decompile(CVAR_PRIVATE_POINT_DATA, font)
         self.assertEqual(cvar.majorVersion, 1)
         self.assertEqual(cvar.minorVersion, 0)
-        self.assertEqual(cvar.variations, CVAR_VARIATIONS)
+        self.assertVariationsAlmostEqual(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)
+        self.assertVariationsAlmostEqual(cvar.variations, CVAR_VARIATIONS)
 
     def test_fromXML(self):
         font, cvar = self.makeFont()
@@ -98,7 +106,7 @@
             cvar.fromXML(name, attrs, content, ttFont=font)
         self.assertEqual(cvar.majorVersion, 1)
         self.assertEqual(cvar.minorVersion, 0)
-        self.assertEqual(cvar.variations, CVAR_VARIATIONS)
+        self.assertVariationsAlmostEqual(cvar.variations, CVAR_VARIATIONS)
 
     def test_toXML(self):
         font, cvar = self.makeFont()
diff --git a/Tests/ttLib/tables/_f_p_g_m_test.py b/Tests/ttLib/tables/_f_p_g_m_test.py
index 71fec48..ff233dd 100644
--- a/Tests/ttLib/tables/_f_p_g_m_test.py
+++ b/Tests/ttLib/tables/_f_p_g_m_test.py
@@ -1,5 +1,3 @@
-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
 
diff --git a/Tests/ttLib/tables/_f_v_a_r_test.py b/Tests/ttLib/tables/_f_v_a_r_test.py
index 4a8e767..2ac5237 100644
--- a/Tests/ttLib/tables/_f_v_a_r_test.py
+++ b/Tests/ttLib/tables/_f_v_a_r_test.py
@@ -1,11 +1,10 @@
-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
+from io import BytesIO
 import unittest
 
 
@@ -120,7 +119,7 @@
         self.assertEqual("opsz", axis.axisTag)
         self.assertEqual(345, axis.axisNameID)
         self.assertEqual(-0.5, axis.minValue)
-        self.assertEqual(1.3, axis.defaultValue)
+        self.assertAlmostEqual(1.3000031, axis.defaultValue)
         self.assertEqual(1.5, axis.maxValue)
 
     def test_toXML(self):
@@ -166,6 +165,11 @@
 
 
 class NamedInstanceTest(unittest.TestCase):
+    def assertDictAlmostEqual(self, dict1, dict2):
+        self.assertEqual(set(dict1.keys()), set(dict2.keys()))
+        for key in dict1:
+            self.assertAlmostEqual(dict1[key], dict2[key])
+
     def test_compile_withPostScriptName(self):
         inst = NamedInstance()
         inst.subfamilyNameID = 345
@@ -187,14 +191,14 @@
         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)
+        self.assertDictAlmostEqual({"wght": 0.6999969, "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)
+        self.assertDictAlmostEqual({"wght": 0.6999969, "wdth": 0.5}, inst.coordinates)
 
     def test_toXML_withPostScriptName(self):
         font = MakeFont()
@@ -244,7 +248,7 @@
             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)
+        self.assertDictAlmostEqual({"wght": 0.6999969, "wdth": 0.5}, inst.coordinates)
 
     def test_fromXML_withoutPostScriptName(self):
         inst = NamedInstance()
@@ -256,7 +260,7 @@
             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)
+        self.assertDictAlmostEqual({"wght": 0.6999969, "wdth": 0.5}, inst.coordinates)
 
 
 if __name__ == "__main__":
diff --git a/Tests/ttLib/tables/_g_c_i_d_test.py b/Tests/ttLib/tables/_g_c_i_d_test.py
index 1ec92fb..e766677 100644
--- a/Tests/ttLib/tables/_g_c_i_d_test.py
+++ b/Tests/ttLib/tables/_g_c_i_d_test.py
@@ -1,6 +1,3 @@
-# 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
diff --git a/Tests/ttLib/tables/_g_l_y_f_test.py b/Tests/ttLib/tables/_g_l_y_f_test.py
index 38ea8e5..84f30dc 100644
--- a/Tests/ttLib/tables/_g_l_y_f_test.py
+++ b/Tests/ttLib/tables/_g_l_y_f_test.py
@@ -1,8 +1,29 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
+from fontTools.misc.fixedTools import otRound
+from fontTools.misc.testTools import getXML, parseXML
+from fontTools.pens.ttGlyphPen import TTGlyphPen
+from fontTools.pens.recordingPen import RecordingPen, RecordingPointPen
+from fontTools.pens.pointPen import PointToSegmentPen
+from fontTools.ttLib import TTFont, newTable, TTLibError
+from fontTools.ttLib.tables._g_l_y_f import (
+    Glyph,
+    GlyphCoordinates,
+    GlyphComponent,
+    ARGS_ARE_XY_VALUES,
+    SCALED_COMPONENT_OFFSET,
+    UNSCALED_COMPONENT_OFFSET,
+    WE_HAVE_A_SCALE,
+    WE_HAVE_A_TWO_BY_TWO,
+    WE_HAVE_AN_X_AND_Y_SCALE,
+)
+from fontTools.ttLib.tables import ttProgram
 import sys
+import array
+from io import StringIO
+import itertools
 import pytest
+import re
+import os
+import unittest
 
 
 class GlyphCoordinatesTest(object):
@@ -55,7 +76,7 @@
     def test__round__(self):
         g = GlyphCoordinates([(-1.5,2)])
         g2 = round(g)
-        assert g2 == GlyphCoordinates([(-2,2)])
+        assert g2 == GlyphCoordinates([(-1,2)])
 
     def test__add__(self):
         g1 = GlyphCoordinates([(1,2)])
@@ -149,4 +170,489 @@
         # 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] == round(afloat)
+        assert g[0][0] == otRound(afloat)
+
+    def test__checkFloat_overflow(self):
+        g = GlyphCoordinates([(1, 1)])
+        g.append((0x8000, 0))
+        assert list(g.array) == [1.0, 1.0, 32768.0, 0.0]
+
+
+CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+DATA_DIR = os.path.join(CURR_DIR, 'data')
+
+GLYF_TTX = os.path.join(DATA_DIR, "_g_l_y_f_outline_flag_bit6.ttx")
+GLYF_BIN = os.path.join(DATA_DIR, "_g_l_y_f_outline_flag_bit6.glyf.bin")
+HEAD_BIN = os.path.join(DATA_DIR, "_g_l_y_f_outline_flag_bit6.head.bin")
+LOCA_BIN = os.path.join(DATA_DIR, "_g_l_y_f_outline_flag_bit6.loca.bin")
+MAXP_BIN = os.path.join(DATA_DIR, "_g_l_y_f_outline_flag_bit6.maxp.bin")
+
+
+def strip_ttLibVersion(string):
+    return re.sub(' ttLibVersion=".*"', '', string)
+
+
+class GlyfTableTest(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):
+        with open(GLYF_BIN, 'rb') as f:
+            cls.glyfData = f.read()
+        with open(HEAD_BIN, 'rb') as f:
+            cls.headData = f.read()
+        with open(LOCA_BIN, 'rb') as f:
+            cls.locaData = f.read()
+        with open(MAXP_BIN, 'rb') as f:
+            cls.maxpData = f.read()
+        with open(GLYF_TTX, 'r') as f:
+            cls.glyfXML = strip_ttLibVersion(f.read()).splitlines()
+
+    def test_toXML(self):
+        font = TTFont(sfntVersion="\x00\x01\x00\x00")
+        glyfTable = font['glyf'] = newTable('glyf')
+        font['head'] = newTable('head')
+        font['loca'] = newTable('loca')
+        font['maxp'] = newTable('maxp')
+        font['maxp'].decompile(self.maxpData, font)
+        font['head'].decompile(self.headData, font)
+        font['loca'].decompile(self.locaData, font)
+        glyfTable.decompile(self.glyfData, font)
+        out = StringIO()
+        font.saveXML(out)
+        glyfXML = strip_ttLibVersion(out.getvalue()).splitlines()
+        self.assertEqual(glyfXML, self.glyfXML)
+
+    def test_fromXML(self):
+        font = TTFont(sfntVersion="\x00\x01\x00\x00")
+        font.importXML(GLYF_TTX)
+        glyfTable = font['glyf']
+        glyfData = glyfTable.compile(font)
+        self.assertEqual(glyfData, self.glyfData)
+
+    def test_recursiveComponent(self):
+        glyphSet = {}
+        pen_dummy = TTGlyphPen(glyphSet)
+        glyph_dummy = pen_dummy.glyph()
+        glyphSet["A"] = glyph_dummy
+        glyphSet["B"] = glyph_dummy
+        pen_A = TTGlyphPen(glyphSet)
+        pen_A.addComponent("B", (1, 0, 0, 1, 0, 0))
+        pen_B = TTGlyphPen(glyphSet)
+        pen_B.addComponent("A", (1, 0, 0, 1, 0, 0))
+        glyph_A = pen_A.glyph()
+        glyph_B = pen_B.glyph()
+        glyphSet["A"] = glyph_A
+        glyphSet["B"] = glyph_B
+        with self.assertRaisesRegex(TTLibError, "glyph '.' contains a recursive component reference"):
+            glyph_A.getCoordinates(glyphSet)
+
+    def test_trim_remove_hinting_composite_glyph(self):
+        glyphSet = {"dummy": TTGlyphPen(None).glyph()}
+
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("dummy", (1, 0, 0, 1, 0, 0))
+        composite = pen.glyph()
+        p = ttProgram.Program()
+        p.fromAssembly(['SVTCA[0]'])
+        composite.program = p
+        glyphSet["composite"] = composite
+
+        glyfTable = newTable("glyf")
+        glyfTable.glyphs = glyphSet
+        glyfTable.glyphOrder = sorted(glyphSet)
+
+        composite.compact(glyfTable)
+
+        self.assertTrue(hasattr(composite, "data"))
+
+        # remove hinting from the compacted composite glyph, without expanding it
+        composite.trim(remove_hinting=True)
+
+        # check that, after expanding the glyph, we have no instructions
+        composite.expand(glyfTable)
+        self.assertFalse(hasattr(composite, "program"))
+
+        # now remove hinting from expanded composite glyph
+        composite.program = p
+        composite.trim(remove_hinting=True)
+
+        # check we have no instructions
+        self.assertFalse(hasattr(composite, "program"))
+
+        composite.compact(glyfTable)
+
+    def test_bit6_draw_to_pen_issue1771(self):
+        # https://github.com/fonttools/fonttools/issues/1771
+        font = TTFont(sfntVersion="\x00\x01\x00\x00")
+        # glyph00003 contains a bit 6 flag on the first point,
+        # which triggered the issue
+        font.importXML(GLYF_TTX)
+        glyfTable = font['glyf']
+        pen = RecordingPen()
+        glyfTable["glyph00003"].draw(pen, glyfTable=glyfTable)
+        expected = [('moveTo', ((501, 1430),)),
+                    ('lineTo', ((683, 1430),)),
+                    ('lineTo', ((1172, 0),)),
+                    ('lineTo', ((983, 0),)),
+                    ('lineTo', ((591, 1193),)),
+                    ('lineTo', ((199, 0),)),
+                    ('lineTo', ((12, 0),)),
+                    ('closePath', ()),
+                    ('moveTo', ((249, 514),)),
+                    ('lineTo', ((935, 514),)),
+                    ('lineTo', ((935, 352),)),
+                    ('lineTo', ((249, 352),)),
+                    ('closePath', ())]
+        self.assertEqual(pen.value, expected)
+
+    def test_bit6_draw_to_pointpen(self):
+        # https://github.com/fonttools/fonttools/issues/1771
+        font = TTFont(sfntVersion="\x00\x01\x00\x00")
+        # glyph00003 contains a bit 6 flag on the first point
+        # which triggered the issue
+        font.importXML(GLYF_TTX)
+        glyfTable = font['glyf']
+        pen = RecordingPointPen()
+        glyfTable["glyph00003"].drawPoints(pen, glyfTable=glyfTable)
+        expected = [
+            ('beginPath', (), {}),
+            ('addPoint', ((501, 1430), 'line', False, None), {}),
+            ('addPoint', ((683, 1430), 'line', False, None), {}),
+            ('addPoint', ((1172, 0), 'line', False, None), {}),
+            ('addPoint', ((983, 0), 'line', False, None), {}),
+        ]
+        self.assertEqual(pen.value[:len(expected)], expected)
+
+    def test_draw_vs_drawpoints(self):
+        font = TTFont(sfntVersion="\x00\x01\x00\x00")
+        font.importXML(GLYF_TTX)
+        glyfTable = font['glyf']
+        pen1 = RecordingPen()
+        pen2 = RecordingPen()
+        glyfTable["glyph00003"].draw(pen1, glyfTable)
+        glyfTable["glyph00003"].drawPoints(PointToSegmentPen(pen2), glyfTable)
+        self.assertEqual(pen1.value, pen2.value)
+
+    def test_compile_empty_table(self):
+        font = TTFont(sfntVersion="\x00\x01\x00\x00")
+        font.importXML(GLYF_TTX)
+        glyfTable = font['glyf']
+        # set all glyphs to zero contours
+        glyfTable.glyphs = {glyphName: Glyph() for glyphName in font.getGlyphOrder()}
+        glyfData = glyfTable.compile(font)
+        self.assertEqual(glyfData, b"\x00")
+        self.assertEqual(list(font["loca"]), [0] * (font["maxp"].numGlyphs+1))
+
+    def test_decompile_empty_table(self):
+        font = TTFont()
+        glyphNames = [".notdef", "space"]
+        font.setGlyphOrder(glyphNames)
+        font["loca"] = newTable("loca")
+        font["loca"].locations = [0] * (len(glyphNames) + 1)
+        font["glyf"] = newTable("glyf")
+        font["glyf"].decompile(b"\x00", font)
+        self.assertEqual(len(font["glyf"]), 2)
+        self.assertEqual(font["glyf"][".notdef"].numberOfContours, 0)
+        self.assertEqual(font["glyf"]["space"].numberOfContours, 0)
+
+    def test_getPhantomPoints(self):
+        # https://github.com/fonttools/fonttools/issues/2295
+        font = TTFont()
+        glyphNames = [".notdef"]
+        font.setGlyphOrder(glyphNames)
+        font["loca"] = newTable("loca")
+        font["loca"].locations = [0] * (len(glyphNames) + 1)
+        font["glyf"] = newTable("glyf")
+        font["glyf"].decompile(b"\x00", font)
+        font["hmtx"] = newTable("hmtx")
+        font["hmtx"].metrics = {".notdef": (100,0)}
+        font["head"] = newTable("head")
+        font["head"].unitsPerEm = 1000
+        self.assertEqual(
+            font["glyf"].getPhantomPoints(".notdef", font, 0), 
+            [(0, 0), (100, 0), (0, 0), (0, -1000)]
+        )
+
+class GlyphTest:
+
+    def test_getCoordinates(self):
+        glyphSet = {}
+        pen = TTGlyphPen(glyphSet)
+        pen.moveTo((0, 0))
+        pen.lineTo((100, 0))
+        pen.lineTo((100, 100))
+        pen.lineTo((0, 100))
+        pen.closePath()
+        # simple contour glyph
+        glyphSet["a"] = a = pen.glyph()
+
+        assert a.getCoordinates(glyphSet) == (
+            GlyphCoordinates([(0, 0), (100, 0), (100, 100), (0, 100)]),
+            [3],
+            array.array("B", [1, 1, 1, 1]),
+        )
+
+        # composite glyph with only XY offset
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("a", (1, 0, 0, 1, 10, 20))
+        glyphSet["b"] = b = pen.glyph()
+
+        assert b.getCoordinates(glyphSet) == (
+            GlyphCoordinates([(10, 20), (110, 20), (110, 120), (10, 120)]),
+            [3],
+            array.array("B", [1, 1, 1, 1]),
+        )
+
+        # composite glyph with a scale (and referencing another composite glyph)
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("b", (0.5, 0, 0, 0.5, 0, 0))
+        glyphSet["c"] = c = pen.glyph()
+
+        assert c.getCoordinates(glyphSet) == (
+            GlyphCoordinates([(5, 10), (55, 10), (55, 60), (5, 60)]),
+            [3],
+            array.array("B", [1, 1, 1, 1]),
+        )
+
+        # composite glyph with unscaled offset (MS-style)
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("a", (0.5, 0, 0, 0.5, 10, 20))
+        glyphSet["d"] = d = pen.glyph()
+        d.components[0].flags |= UNSCALED_COMPONENT_OFFSET
+
+        assert d.getCoordinates(glyphSet) == (
+            GlyphCoordinates([(10, 20), (60, 20), (60, 70), (10, 70)]),
+            [3],
+            array.array("B", [1, 1, 1, 1]),
+        )
+
+        # composite glyph with a scaled offset (Apple-style)
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("a", (0.5, 0, 0, 0.5, 10, 20))
+        glyphSet["e"] = e = pen.glyph()
+        e.components[0].flags |= SCALED_COMPONENT_OFFSET
+
+        assert e.getCoordinates(glyphSet) == (
+            GlyphCoordinates([(5, 10), (55, 10), (55, 60), (5, 60)]),
+            [3],
+            array.array("B", [1, 1, 1, 1]),
+        )
+
+        # composite glyph where the 2nd and 3rd components use anchor points
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("a", (1, 0, 0, 1, 0, 0))
+        glyphSet["f"] = f = pen.glyph()
+
+        comp1 = GlyphComponent()
+        comp1.glyphName = "a"
+        # aling the new component's pt 0 to pt 2 of contour points added so far
+        comp1.firstPt = 2
+        comp1.secondPt = 0
+        comp1.flags = 0
+        f.components.append(comp1)
+
+        comp2 = GlyphComponent()
+        comp2.glyphName = "a"
+        # aling the new component's pt 0 to pt 6 of contour points added so far
+        comp2.firstPt = 6
+        comp2.secondPt = 0
+        comp2.transform = [[0.707107, 0.707107], [-0.707107, 0.707107]]  # rotate 45 deg
+        comp2.flags = WE_HAVE_A_TWO_BY_TWO
+        f.components.append(comp2)
+
+        coords, end_pts, flags = f.getCoordinates(glyphSet)
+        assert end_pts == [3, 7, 11]
+        assert flags == array.array("B", [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
+        assert list(sum(coords, ())) == pytest.approx(
+            [
+                0, 0,
+                100, 0,
+                100, 100,
+                0, 100,
+                100, 100,
+                200, 100,
+                200, 200,
+                100, 200,
+                200, 200,
+                270.7107, 270.7107,
+                200.0, 341.4214,
+                129.2893, 270.7107,
+            ]
+        )
+
+    def test_getCompositeMaxpValues(self):
+        # https://github.com/fonttools/fonttools/issues/2044
+        glyphSet = {}
+        pen = TTGlyphPen(glyphSet)  # empty non-composite glyph
+        glyphSet["fraction"] = pen.glyph()
+        glyphSet["zero.numr"] = pen.glyph()
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("zero.numr", (1, 0, 0, 1, 0, 0))
+        glyphSet["zero.dnom"] = pen.glyph()
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("zero.numr", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("fraction", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("zero.dnom", (1, 0, 0, 1, 0, 0))
+        glyphSet["percent"] = pen.glyph()
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("zero.numr", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("fraction", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("zero.dnom", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("zero.dnom", (1, 0, 0, 1, 0, 0))
+        glyphSet["perthousand"] = pen.glyph()
+        assert glyphSet["zero.dnom"].getCompositeMaxpValues(glyphSet)[2] == 1
+        assert glyphSet["percent"].getCompositeMaxpValues(glyphSet)[2] == 2
+        assert glyphSet["perthousand"].getCompositeMaxpValues(glyphSet)[2] == 2
+
+
+class GlyphComponentTest:
+
+    def test_toXML_no_transform(self):
+        comp = GlyphComponent()
+        comp.glyphName = "a"
+        comp.flags = ARGS_ARE_XY_VALUES
+        comp.x, comp.y = 1, 2
+
+        assert getXML(comp.toXML) == [
+            '<component glyphName="a" x="1" y="2" flags="0x2"/>'
+        ]
+
+    def test_toXML_transform_scale(self):
+        comp = GlyphComponent()
+        comp.glyphName = "a"
+        comp.flags = ARGS_ARE_XY_VALUES | WE_HAVE_A_SCALE
+        comp.x, comp.y = 1, 2
+
+        comp.transform = [[0.2999878, 0], [0, 0.2999878]]
+        assert getXML(comp.toXML) == [
+            '<component glyphName="a" x="1" y="2" scale="0.3" flags="0xa"/>'
+        ]
+
+    def test_toXML_transform_xy_scale(self):
+        comp = GlyphComponent()
+        comp.glyphName = "a"
+        comp.flags = ARGS_ARE_XY_VALUES | WE_HAVE_AN_X_AND_Y_SCALE
+        comp.x, comp.y = 1, 2
+
+        comp.transform = [[0.5999756, 0], [0, 0.2999878]]
+        assert getXML(comp.toXML) == [
+            '<component glyphName="a" x="1" y="2" scalex="0.6" '
+            'scaley="0.3" flags="0x42"/>'
+        ]
+
+    def test_toXML_transform_2x2_scale(self):
+        comp = GlyphComponent()
+        comp.glyphName = "a"
+        comp.flags = ARGS_ARE_XY_VALUES | WE_HAVE_A_TWO_BY_TWO
+        comp.x, comp.y = 1, 2
+
+        comp.transform = [[0.5999756, -0.2000122], [0.2000122, 0.2999878]]
+        assert getXML(comp.toXML) == [
+            '<component glyphName="a" x="1" y="2" scalex="0.6" scale01="-0.2" '
+            'scale10="0.2" scaley="0.3" flags="0x82"/>'
+        ]
+
+    def test_fromXML_no_transform(self):
+        comp = GlyphComponent()
+        for name, attrs, content in parseXML(
+            ['<component glyphName="a" x="1" y="2" flags="0x2"/>']
+        ):
+            comp.fromXML(name, attrs, content, ttFont=None)
+
+        assert comp.glyphName == "a"
+        assert comp.flags & ARGS_ARE_XY_VALUES != 0
+        assert (comp.x, comp.y) == (1, 2)
+        assert not hasattr(comp, "transform")
+
+    def test_fromXML_transform_scale(self):
+        comp = GlyphComponent()
+        for name, attrs, content in parseXML(
+            ['<component glyphName="a" x="1" y="2" scale="0.3" flags="0xa"/>']
+        ):
+            comp.fromXML(name, attrs, content, ttFont=None)
+
+        assert comp.glyphName == "a"
+        assert comp.flags & ARGS_ARE_XY_VALUES != 0
+        assert comp.flags & WE_HAVE_A_SCALE != 0
+        assert (comp.x, comp.y) == (1, 2)
+        assert hasattr(comp, "transform")
+        for value, expected in zip(
+            itertools.chain(*comp.transform), [0.2999878, 0, 0, 0.2999878]
+        ):
+            assert value == pytest.approx(expected)
+
+    def test_fromXML_transform_xy_scale(self):
+        comp = GlyphComponent()
+        for name, attrs, content in parseXML(
+            [
+                '<component glyphName="a" x="1" y="2" scalex="0.6" '
+                'scaley="0.3" flags="0x42"/>'
+            ]
+        ):
+            comp.fromXML(name, attrs, content, ttFont=None)
+
+        assert comp.glyphName == "a"
+        assert comp.flags & ARGS_ARE_XY_VALUES != 0
+        assert comp.flags & WE_HAVE_AN_X_AND_Y_SCALE != 0
+        assert (comp.x, comp.y) == (1, 2)
+        assert hasattr(comp, "transform")
+        for value, expected in zip(
+            itertools.chain(*comp.transform), [0.5999756, 0, 0, 0.2999878]
+        ):
+            assert value == pytest.approx(expected)
+
+    def test_fromXML_transform_2x2_scale(self):
+        comp = GlyphComponent()
+        for name, attrs, content in parseXML(
+            [
+                '<component glyphName="a" x="1" y="2" scalex="0.6" scale01="-0.2" '
+                'scale10="0.2" scaley="0.3" flags="0x82"/>'
+            ]
+        ):
+            comp.fromXML(name, attrs, content, ttFont=None)
+
+        assert comp.glyphName == "a"
+        assert comp.flags & ARGS_ARE_XY_VALUES != 0
+        assert comp.flags & WE_HAVE_A_TWO_BY_TWO != 0
+        assert (comp.x, comp.y) == (1, 2)
+        assert hasattr(comp, "transform")
+        for value, expected in zip(
+            itertools.chain(*comp.transform),
+            [0.5999756, -0.2000122, 0.2000122, 0.2999878]
+        ):
+            assert value == pytest.approx(expected)
+
+    def test_toXML_reference_points(self):
+        comp = GlyphComponent()
+        comp.glyphName = "a"
+        comp.flags = 0
+        comp.firstPt = 1
+        comp.secondPt = 2
+
+        assert getXML(comp.toXML) == [
+            '<component glyphName="a" firstPt="1" secondPt="2" flags="0x0"/>'
+        ]
+
+    def test_fromXML_reference_points(self):
+        comp = GlyphComponent()
+        for name, attrs, content in parseXML(
+            ['<component glyphName="a" firstPt="1" secondPt="2" flags="0x0"/>']
+        ):
+            comp.fromXML(name, attrs, content, ttFont=None)
+
+        assert comp.glyphName == "a"
+        assert comp.flags == 0
+        assert (comp.firstPt, comp.secondPt) == (1, 2)
+        assert not hasattr(comp, "transform")
+
+
+if __name__ == "__main__":
+    import sys
+    sys.exit(unittest.main())
diff --git a/Tests/ttLib/tables/_g_v_a_r_test.py b/Tests/ttLib/tables/_g_v_a_r_test.py
index 64adbb6..077bb63 100644
--- a/Tests/ttLib/tables/_g_v_a_r_test.py
+++ b/Tests/ttLib/tables/_g_v_a_r_test.py
@@ -1,5 +1,3 @@
-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
@@ -65,7 +63,7 @@
     ],
     "space": [
         TupleVariation(
-            {"wdth": (0.0, 0.7, 0.7)},
+            {"wdth": (0.0, 0.7000122, 0.7000122)},
             [(1, 11), (2, 22), (3, 33), (4, 44)]),
     ],
     "I": [
@@ -73,7 +71,7 @@
             {"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)},
+            {"wght": (-1.0, -1.0, 0.0), "wdth": (0.0, 0.7999878, 0.7999878)},
             [(-8,-88), (7,77), None, None, (-4,44), (3,33), (-2,-22), (1,11)]),
     ],
 }
@@ -82,18 +80,9 @@
 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"/>',
+    '    <coord axis="wght" min="0.0" value="0.5" max="1.0"/>',
     '    <delta pt="0" x="3" y="3"/>',
     '    <delta pt="1" x="1" y="1"/>',
     '    <delta pt="2" x="4" y="4"/>',
@@ -114,6 +103,15 @@
     '    <delta pt="7" x="1" y="11"/>',
     '  </tuple>',
     '</glyphVariations>',
+    '<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>',
 ]
 
 
@@ -133,6 +131,20 @@
 
 
 class GVARTableTest(unittest.TestCase):
+	def assertVariationsAlmostEqual(self, vars1, vars2):
+		self.assertSetEqual(set(vars1.keys()), set(vars2.keys()))
+		for glyphName, variations1 in vars1.items():
+			variations2 = vars2[glyphName]
+			self.assertEqual(len(variations1), len(variations2))
+			for (v1, v2) in zip(variations1, variations2):
+				self.assertSetEqual(set(v1.axes), set(v2.axes))
+				for axisTag, support1 in v1.axes.items():
+					support2 = v2.axes[axisTag]
+					self.assertEqual(len(support1), len(support2))
+					for s1, s2 in zip(support1, support2):
+						self.assertAlmostEqual(s1, s2)
+				self.assertEqual(v1.coordinates, v2.coordinates)
+
 	def makeFont(self, variations):
 		glyphs=[".notdef", "space", "I"]
 		Axis = getTableModule("fvar").Axis
@@ -164,7 +176,7 @@
 	def test_decompile(self):
 		font, gvar = self.makeFont({})
 		gvar.decompile(GVAR_DATA, font)
-		self.assertEqual(gvar.variations, GVAR_VARIATIONS)
+		self.assertVariationsAlmostEqual(gvar.variations, GVAR_VARIATIONS)
 
 	def test_decompile_noVariations(self):
 		font, gvar = self.makeFont({})
@@ -176,8 +188,10 @@
 		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})
+		self.assertVariationsAlmostEqual(
+			gvar.variations,
+			{g:v for g,v in GVAR_VARIATIONS.items() if v}
+		)
 
 	def test_toXML(self):
 		font, gvar = self.makeFont(GVAR_VARIATIONS)
diff --git a/Tests/ttLib/tables/_h_h_e_a_test.py b/Tests/ttLib/tables/_h_h_e_a_test.py
index 64849e9..e04fd7b 100644
--- a/Tests/ttLib/tables/_h_h_e_a_test.py
+++ b/Tests/ttLib/tables/_h_h_e_a_test.py
@@ -1,5 +1,3 @@
-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
@@ -127,6 +125,18 @@
             len([r for r in captor.records
                  if "Table version value is a float" in r.msg]) == 1)
 
+    def test_aliases(self):
+        hhea = self.font['hhea']
+        self.assertEqual(hhea.ascent, hhea.ascender)
+        self.assertEqual(hhea.descent, hhea.descender)
+        hhea.ascender = 800
+        self.assertEqual(hhea.ascent, 800)
+        hhea.ascent = 750
+        self.assertEqual(hhea.ascender, 750)
+        hhea.descender = -300
+        self.assertEqual(hhea.descent, -300)
+        hhea.descent = -299
+        self.assertEqual(hhea.descender, -299)
 
 class HheaDecompileOrFromXMLTest(unittest.TestCase):
 
diff --git a/Tests/ttLib/tables/_h_m_t_x_test.py b/Tests/ttLib/tables/_h_m_t_x_test.py
index 0584a31..79d0cb7 100644
--- a/Tests/ttLib/tables/_h_m_t_x_test.py
+++ b/Tests/ttLib/tables/_h_m_t_x_test.py
@@ -1,6 +1,3 @@
-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
@@ -107,6 +104,27 @@
             len([r for r in captor.records
                 if "has a huge advance" in r.msg]) == 1)
 
+    def test_decompile_no_header_table(self):
+        font = TTFont()
+        maxp = font['maxp'] = newTable('maxp')
+        maxp.numGlyphs = 3
+        font.glyphOrder = ["A", "B", "C"]
+
+        self.assertNotIn(self.tableClass.headerTag, font)
+
+        data = deHexStr("0190 001E 0190 0028 0190 0032")
+        mtxTable = newTable(self.tag)
+        mtxTable.decompile(data, font)
+
+        self.assertEqual(
+            mtxTable.metrics,
+            {
+                "A": (400, 30),
+                "B": (400, 40),
+                "C": (400, 50),
+            }
+        )
+
     def test_compile(self):
         # we set the wrong 'numberOfMetrics' to check it gets adjusted
         font = self.makeFont(numGlyphs=3, numberOfMetrics=4)
@@ -164,14 +182,32 @@
         font = self.makeFont(numGlyphs=3, numberOfMetrics=2)
         mtxTable = font[self.tag] = newTable(self.tag)
         mtxTable.metrics = {
-            'A': (0.5, 0.5),  # round -> (0, 0)
+            '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("0000 0000 0000 0001 0000"))
+        self.assertEqual(data, deHexStr("0001 0001 0000 0001 0000"))
+
+    def test_compile_no_header_table(self):
+        font = TTFont()
+        maxp = font['maxp'] = newTable('maxp')
+        maxp.numGlyphs = 3
+        font.glyphOrder = [chr(i) for i in range(65, 68)]
+        mtxTable = font[self.tag] = newTable(self.tag)
+        mtxTable.metrics = {
+            "A": (400, 30),
+            "B": (400, 40),
+            "C": (400, 50),
+        }
+
+        self.assertNotIn(self.tableClass.headerTag, font)
+
+        data = mtxTable.compile(font)
+
+        self.assertEqual(data, deHexStr("0190 001E 0190 0028 0190 0032"))
 
     def test_toXML(self):
         font = self.makeFont(numGlyphs=2, numberOfMetrics=2)
diff --git a/Tests/ttLib/tables/_k_e_r_n_test.py b/Tests/ttLib/tables/_k_e_r_n_test.py
index 8ab1b1c..eb48bae 100644
--- a/Tests/ttLib/tables/_k_e_r_n_test.py
+++ b/Tests/ttLib/tables/_k_e_r_n_test.py
@@ -1,10 +1,9 @@
-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 itertools
 import pytest
 
 
@@ -120,12 +119,32 @@
     '</kernsubtable>',
 ]
 
+KERN_VER_0_FMT_0_OVERFLOWING_DATA = deHexStr(
+    '0000 '  #  0: version=0
+    '0001 '  #  2: nTables=1
+    '0000 '  #  4: version=0 (bogus field, unused)
+    '0274 '  #  6: length=628 (bogus value for 66164 % 0x10000)
+    '00 '    #  8: format=0
+    '01 '    #  9: coverage=1
+    '2B11 '  # 10: nPairs=11025
+    'C000 '  # 12: searchRange=49152
+    '000D '  # 14: entrySelector=13
+    '4266 '  # 16: rangeShift=16998
+) + deHexStr(' '.join(
+    '%04X %04X %04X' % (a, b, 0)
+    for (a, b) in itertools.product(range(105), repeat=2)
+))
+
 
 @pytest.fixture
 def font():
     return FakeFont(list("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                          "abcdefghijklmnopqrstuvwxyz"))
 
+@pytest.fixture
+def overflowing_font():
+    return FakeFont(["glyph%i" % i for i in range(105)])
+
 
 class KernTableTest(object):
 
@@ -364,6 +383,36 @@
         assert subtable[("B", "D")] == 1
         assert subtable[("B", "glyph65535")] == 2
 
+    def test_compileOverflowingSubtable(self, overflowing_font):
+        font = overflowing_font
+        kern = newTable("kern")
+        kern.version = 0
+        st = KernTable_format_0(0)
+        kern.kernTables = [st]
+        st.coverage = 1
+        st.tupleIndex = None
+        st.kernTable = {
+            (a, b): 0
+            for (a, b) in itertools.product(
+                font.getGlyphOrder(), repeat=2)
+        }
+        assert len(st.kernTable) == 11025
+        data = kern.compile(font)
+        assert data == KERN_VER_0_FMT_0_OVERFLOWING_DATA
+
+    def test_decompileOverflowingSubtable(self, overflowing_font):
+        font = overflowing_font
+        data = KERN_VER_0_FMT_0_OVERFLOWING_DATA
+        kern = newTable("kern")
+        kern.decompile(data, font)
+
+        st = kern.kernTables[0]
+        assert st.kernTable == {
+            (a, b): 0
+            for (a, b) in itertools.product(
+                font.getGlyphOrder(), repeat=2)
+        }
+
 
 if __name__ == "__main__":
     import sys
diff --git a/Tests/ttLib/tables/_l_c_a_r_test.py b/Tests/ttLib/tables/_l_c_a_r_test.py
index 4525def..5837a07 100644
--- a/Tests/ttLib/tables/_l_c_a_r_test.py
+++ b/Tests/ttLib/tables/_l_c_a_r_test.py
@@ -1,5 +1,3 @@
-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
diff --git a/Tests/ttLib/tables/_l_t_a_g_test.py b/Tests/ttLib/tables/_l_t_a_g_test.py
index a4c5a0e..2911990 100644
--- a/Tests/ttLib/tables/_l_t_a_g_test.py
+++ b/Tests/ttLib/tables/_l_t_a_g_test.py
@@ -1,8 +1,6 @@
-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
+from io import BytesIO
 import struct
 import unittest
 from fontTools.ttLib import newTable
@@ -47,14 +45,14 @@
 		table = newTable("ltag")
 		table.decompile(self.DATA_, ttFont=None)
 		table.toXML(writer, ttFont=None)
-		expected = os.linesep.join([
+		expected = "\n".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
+		]) + "\n"
 		self.assertEqual(expected.encode("utf_8"), writer.file.getvalue())
 
 
diff --git a/Tests/ttLib/tables/_m_e_t_a_test.py b/Tests/ttLib/tables/_m_e_t_a_test.py
index dc0f8cd..f05ff57 100644
--- a/Tests/ttLib/tables/_m_e_t_a_test.py
+++ b/Tests/ttLib/tables/_m_e_t_a_test.py
@@ -1,10 +1,9 @@
-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
+from io import BytesIO
 import unittest
 
 
@@ -59,6 +58,19 @@
             '</hexdata>'
         ], [line.strip() for line in xml.splitlines()][1:])
 
+    def test_toXML_ascii_data(self):
+        table = table__m_e_t_a()
+        table.data["TEST"] = b"Hello!"
+        writer = XMLWriter(BytesIO())
+        table.toXML(writer, {"meta": table})
+        xml = writer.file.getvalue().decode("utf-8")
+        self.assertEqual([
+            '<hexdata tag="TEST">',
+                '<!-- ascii: Hello! -->',
+                '48656c6c 6f21',
+            '</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(
diff --git a/Tests/ttLib/tables/_m_o_r_t_test.py b/Tests/ttLib/tables/_m_o_r_t_test.py
index 70696e8..3e7169b 100644
--- a/Tests/ttLib/tables/_m_o_r_t_test.py
+++ b/Tests/ttLib/tables/_m_o_r_t_test.py
@@ -1,5 +1,3 @@
-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
diff --git a/Tests/ttLib/tables/_m_o_r_x_test.py b/Tests/ttLib/tables/_m_o_r_x_test.py
index 7deb39f..0b807f8 100644
--- a/Tests/ttLib/tables/_m_o_r_x_test.py
+++ b/Tests/ttLib/tables/_m_o_r_x_test.py
@@ -1,6 +1,4 @@
-# coding: utf-8
-from __future__ import print_function, division, absolute_import, unicode_literals
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytechr, bytesjoin
 from fontTools.misc.testTools import FakeFont, getXML, parseXML
 from fontTools.misc.textTools import deHexStr, hexStr
 from fontTools.ttLib import newTable
@@ -708,6 +706,160 @@
 ]
 
 
+# Taken from the `morx` table of the second font in DevanagariSangamMN.ttc
+# on macOS X 10.12.6; manually pruned to just contain the insertion lookup.
+MORX_INSERTION_DATA = deHexStr(
+    '0002 0000 '  #  0: Version=2, Reserved=0
+    '0000 0001 '  #  4: MorphChainCount=1
+    '0000 0001 '  #  8: DefaultFlags=1
+    '0000 00A4 '  # 12: StructLength=164 (+8=172)
+    '0000 0000 '  # 16: MorphFeatureCount=0
+    '0000 0001 '  # 20: MorphSubtableCount=1
+    '0000 0094 '  # 24: Subtable[0].StructLength=148 (+24=172)
+    '00 '         # 28: Subtable[0].CoverageFlags=0x00
+    '00 00 '      # 29: Subtable[0].Reserved=0
+    '05 '         # 31: Subtable[0].MorphType=5/InsertionMorph
+    '0000 0001 '  # 32: Subtable[0].SubFeatureFlags=0x1
+    '0000 0006 '  # 36: STXHeader.ClassCount=6
+    '0000 0014 '  # 40: STXHeader.ClassTableOffset=20 (+36=56)
+    '0000 004A '  # 44: STXHeader.StateArrayOffset=74 (+36=110)
+    '0000 006E '  # 48: STXHeader.EntryTableOffset=110 (+36=146)
+    '0000 0086 '  # 52: STXHeader.InsertionActionOffset=134 (+36=170)
+     # Glyph class table.
+    '0002 0006 '       #  56: ClassTable.LookupFormat=2, .UnitSize=6
+    '0006 0018 '       #  60:   .NUnits=6, .SearchRange=24
+    '0002 000C '       #  64:   .EntrySelector=2, .RangeShift=12
+    '00AC 00AC 0005 '  #  68: GlyphID 172..172 -> GlyphClass 5
+    '01EB 01E6 0005 '  #  74: GlyphID 486..491 -> GlyphClass 5
+    '01F0 01F0 0004 '  #  80: GlyphID 496..496 -> GlyphClass 4
+    '01F8 01F6 0004 '  #  88: GlyphID 502..504 -> GlyphClass 4
+    '01FC 01FA 0004 '  #  92: GlyphID 506..508 -> GlyphClass 4
+    '0250 0250 0005 '  #  98: GlyphID 592..592 -> GlyphClass 5
+    'FFFF FFFF 0000 '  # 104: <end of lookup>
+    # State array.
+    '0000 0000 0000 0000 0001 0000 '  # 110: State[0][0..5]
+    '0000 0000 0000 0000 0001 0000 '  # 122: State[1][0..5]
+    '0000 0000 0001 0000 0001 0002 '  # 134: State[2][0..5]
+    # Entry table.
+    '0000 0000 '  # 146: Entries[0].NewState=0, .Flags=0
+    'FFFF '       # 150: Entries[0].CurrentInsertIndex=<None>
+    'FFFF '       # 152: Entries[0].MarkedInsertIndex=<None>
+    '0002 0000 '  # 154: Entries[1].NewState=0, .Flags=0
+    'FFFF '       # 158: Entries[1].CurrentInsertIndex=<None>
+    'FFFF '       # 160: Entries[1].MarkedInsertIndex=<None>
+    '0000 '       # 162: Entries[2].NewState=0
+    '2820 '       # 164:   .Flags=CurrentIsKashidaLike,CurrentInsertBefore
+                  #        .CurrentInsertCount=1, .MarkedInsertCount=0
+    '0000 '       # 166: Entries[1].CurrentInsertIndex=0
+    'FFFF '       # 168: Entries[1].MarkedInsertIndex=<None>
+    # Insertion action table.
+    '022F'        # 170: InsertionActionTable[0]=GlyphID 559
+)   # 172: <end>
+assert len(MORX_INSERTION_DATA) == 172, len(MORX_INSERTION_DATA)
+
+
+MORX_INSERTION_XML = [
+    '<Version value="2"/>',
+    '<Reserved value="0"/>',
+    '<!-- MorphChainCount=1 -->',
+    '<MorphChain index="0">',
+    '  <DefaultFlags value="0x00000001"/>',
+    '  <!-- StructLength=164 -->',
+    '  <!-- MorphFeatureCount=0 -->',
+    '  <!-- MorphSubtableCount=1 -->',
+    '  <MorphSubtable index="0">',
+    '    <!-- StructLength=148 -->',
+    '    <TextDirection value="Horizontal"/>',
+    '    <ProcessingOrder value="LayoutOrder"/>',
+    '    <!-- MorphType=5 -->',
+    '    <SubFeatureFlags value="0x00000001"/>',
+    '    <InsertionMorph>',
+    '      <StateTable>',
+    '        <!-- GlyphClassCount=6 -->',
+    '        <GlyphClass glyph="g.172" value="5"/>',
+    '        <GlyphClass glyph="g.486" value="5"/>',
+    '        <GlyphClass glyph="g.487" value="5"/>',
+    '        <GlyphClass glyph="g.488" value="5"/>',
+    '        <GlyphClass glyph="g.489" value="5"/>',
+    '        <GlyphClass glyph="g.490" value="5"/>',
+    '        <GlyphClass glyph="g.491" value="5"/>',
+    '        <GlyphClass glyph="g.496" value="4"/>',
+    '        <GlyphClass glyph="g.502" value="4"/>',
+    '        <GlyphClass glyph="g.503" value="4"/>',
+    '        <GlyphClass glyph="g.504" value="4"/>',
+    '        <GlyphClass glyph="g.506" value="4"/>',
+    '        <GlyphClass glyph="g.507" value="4"/>',
+    '        <GlyphClass glyph="g.508" value="4"/>',
+    '        <GlyphClass glyph="g.592" value="5"/>',
+    '        <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"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <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"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <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="2"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="3">',
+    '            <NewState value="0"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="4">',
+    '            <NewState value="2"/>',
+    '          </Transition>',
+    '          <Transition onGlyphClass="5">',
+    '            <NewState value="0"/>',
+    '            <Flags value="CurrentIsKashidaLike,CurrentInsertBefore"/>',
+    '            <CurrentInsertionAction glyph="g.559"/>',
+    '          </Transition>',
+    '        </State>',
+    '      </StateTable>',
+    '    </InsertionMorph>',
+    '  </MorphSubtable>',
+    '</MorphChain>',
+]
+
+
 class MORXNoncontextualGlyphSubstitutionTest(unittest.TestCase):
 
     @classmethod
@@ -802,6 +954,26 @@
                          hexStr(MORX_LIGATURE_DATA))
 
 
+class MORXGlyphInsertionTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.maxDiff = None
+        cls.font = FakeFont(['.notdef'] + ['g.%d' % i for i in range (1, 910)])
+
+    def test_decompile_toXML(self):
+        table = newTable('morx')
+        table.decompile(MORX_INSERTION_DATA, self.font)
+        self.assertEqual(getXML(table.toXML), MORX_INSERTION_XML)
+
+    def test_compile_fromXML(self):
+        table = newTable('morx')
+        for name, attrs, content in parseXML(MORX_INSERTION_XML):
+            table.fromXML(name, attrs, content, font=self.font)
+        self.assertEqual(hexStr(table.compile(self.font)),
+                         hexStr(MORX_INSERTION_DATA))
+
+
 class MORXCoverageFlagsTest(unittest.TestCase):
 
     @classmethod
diff --git a/Tests/ttLib/tables/_n_a_m_e_test.py b/Tests/ttLib/tables/_n_a_m_e_test.py
index eb21148..8e82970 100644
--- a/Tests/ttLib/tables/_n_a_m_e_test.py
+++ b/Tests/ttLib/tables/_n_a_m_e_test.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
-from __future__ import print_function, division, absolute_import, unicode_literals
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import bytesjoin, tostr
 from fontTools.misc import sstruct
 from fontTools.misc.loggingTools import CapturingLogHandler
 from fontTools.misc.testTools import FakeFont
 from fontTools.misc.xmlWriter import XMLWriter
+from io import BytesIO
 import struct
 import unittest
 from fontTools.ttLib import newTable
@@ -54,6 +53,26 @@
 		with self.assertRaises(TypeError):
 			table.setName(1.000, 5, 1, 0, 0)
 
+	def test_names_sort_bytes_str(self):
+		# Corner case: If a user appends a name record directly to `names`, the
+		# `__lt__` method on NameRecord may run into duplicate name records where
+		# one `string` is a str and the other one bytes, leading to an exception.
+		table = table__n_a_m_e()
+		table.names = [
+			makeName("Test", 25, 3, 1, 0x409),
+			makeName("Test".encode("utf-16be"), 25, 3, 1, 0x409),
+		]
+		table.compile(None)
+
+	def test_names_sort_bytes_str_encoding_error(self):
+		table = table__n_a_m_e()
+		table.names = [
+			makeName("Test寬", 25, 1, 0, 0),
+			makeName("Test鬆鬆", 25, 1, 0, 0),
+		]
+		with self.assertRaises(TypeError):
+			table.names.sort()
+
 	def test_addName(self):
 		table = table__n_a_m_e()
 		nameIDs = []
@@ -76,6 +95,111 @@
 		with self.assertRaises(TypeError):
 			table.addName(b"abc")  # must be unicode string
 
+	def test_removeNames(self):
+		table = table__n_a_m_e()
+		table.setName("Regular", 2, 1, 0, 0)
+		table.setName("Regular", 2, 3, 1, 0x409)
+		table.removeNames(nameID=2)
+		self.assertEqual(table.names, [])
+
+		table = table__n_a_m_e()
+		table.setName("FamilyName", 1, 1, 0, 0)
+		table.setName("Regular", 2, 1, 0, 0)
+		table.setName("FamilyName", 1, 3, 1, 0x409)
+		table.setName("Regular", 2, 3, 1, 0x409)
+		table.removeNames(platformID=1)
+		self.assertEqual(len(table.names), 2)
+		self.assertIsNone(table.getName(1, 1, 0, 0))
+		self.assertIsNone(table.getName(2, 1, 0, 0))
+		rec1 = table.getName(1, 3, 1, 0x409)
+		self.assertEqual(str(rec1), "FamilyName")
+		rec2 = table.getName(2, 3, 1, 0x409)
+		self.assertEqual(str(rec2), "Regular")
+
+		table = table__n_a_m_e()
+		table.setName("FamilyName", 1, 1, 0, 0)
+		table.setName("Regular", 2, 1, 0, 0)
+		table.removeNames(nameID=1)
+		self.assertEqual(len(table.names), 1)
+		self.assertIsNone(table.getName(1, 1, 0, 0))
+		rec = table.getName(2, 1, 0, 0)
+		self.assertEqual(str(rec), "Regular")
+
+		table = table__n_a_m_e()
+		table.setName("FamilyName", 1, 1, 0, 0)
+		table.setName("Regular", 2, 1, 0, 0)
+		table.removeNames(2, 1, 0, 0)
+		self.assertEqual(len(table.names), 1)
+		self.assertIsNone(table.getName(2, 1, 0, 0))
+		rec = table.getName(1, 1, 0, 0)
+		self.assertEqual(str(rec), "FamilyName")
+
+		table = table__n_a_m_e()
+		table.setName("FamilyName", 1, 1, 0, 0)
+		table.setName("Regular", 2, 1, 0, 0)
+		table.removeNames()
+		self.assertEqual(len(table.names), 2)
+		rec1 = table.getName(1, 1, 0, 0)
+		self.assertEqual(str(rec1), "FamilyName")
+		rec2 = table.getName(2, 1, 0, 0)
+		self.assertEqual(str(rec2), "Regular")
+
+	@staticmethod
+	def _get_test_names():
+		names = {
+			"en": "Width",
+			"de-CH": "Breite",
+			"gsw-LI": "Bräiti",
+		}
+		namesSubSet = names.copy()
+		del namesSubSet["gsw-LI"]
+		namesSuperSet = names.copy()
+		namesSuperSet["nl"] = "Breedte"
+		return names, namesSubSet, namesSuperSet
+
+	def test_findMultilingualName(self):
+		table = table__n_a_m_e()
+		names, namesSubSet, namesSuperSet = self._get_test_names()
+		nameID = table.addMultilingualName(names)
+		assert nameID is not None
+		self.assertEqual(nameID, table.findMultilingualName(names))
+		self.assertEqual(nameID, table.findMultilingualName(namesSubSet))
+		self.assertEqual(None, table.findMultilingualName(namesSuperSet))
+
+	def test_findMultilingualName_compiled(self):
+		table = table__n_a_m_e()
+		names, namesSubSet, namesSuperSet = self._get_test_names()
+		nameID = table.addMultilingualName(names)
+		assert nameID is not None
+		# After compile/decompile, name.string is a bytes sequence, which
+		# findMultilingualName() should also handle
+		data = table.compile(None)
+		table = table__n_a_m_e()
+		table.decompile(data, None)
+		self.assertEqual(nameID, table.findMultilingualName(names))
+		self.assertEqual(nameID, table.findMultilingualName(namesSubSet))
+		self.assertEqual(None, table.findMultilingualName(namesSuperSet))
+
+	def test_addMultilingualNameReuse(self):
+		table = table__n_a_m_e()
+		names, namesSubSet, namesSuperSet = self._get_test_names()
+		nameID = table.addMultilingualName(names)
+		assert nameID is not None
+		self.assertEqual(nameID, table.addMultilingualName(names))
+		self.assertEqual(nameID, table.addMultilingualName(namesSubSet))
+		self.assertNotEqual(None, table.addMultilingualName(namesSuperSet))
+
+	def test_findMultilingualNameNoMac(self):
+		table = table__n_a_m_e()
+		names, namesSubSet, namesSuperSet = self._get_test_names()
+		nameID = table.addMultilingualName(names, mac=False)
+		assert nameID is not None
+		self.assertEqual(nameID, table.findMultilingualName(names, mac=False))
+		self.assertEqual(None, table.findMultilingualName(names))
+		self.assertEqual(nameID, table.findMultilingualName(namesSubSet, mac=False))
+		self.assertEqual(None, table.findMultilingualName(namesSubSet))
+		self.assertEqual(None, table.findMultilingualName(namesSuperSet))
+
 	def test_addMultilingualName(self):
 		# Microsoft Windows has language codes for “English” (en)
 		# and for “Standard German as used in Switzerland” (de-CH).
@@ -93,12 +217,12 @@
 				"en": "Width",
 				"de-CH": "Breite",
 				"gsw-LI": "Bräiti",
-			}, ttFont=font)
+			}, ttFont=font, mac=False)
 			self.assertEqual(widthID, 256)
 			xHeightID = nameTable.addMultilingualName({
 				"en": "X-Height",
 				"gsw-LI": "X-Hööchi"
-			}, ttFont=font)
+			}, ttFont=font, mac=False)
 			self.assertEqual(xHeightID, 257)
 		captor.assertRegex("cannot add Windows name in language gsw-LI")
 		self.assertEqual(names(nameTable), [
@@ -170,8 +294,19 @@
 			nameTable.addMultilingualName({"en": "A", "la": "ⱾƤℚⱤ"})
 		captor.assertRegex("cannot store language la into 'ltag' table")
 
+	def test_addMultilingualName_minNameID(self):
+		table = table__n_a_m_e()
+		names, namesSubSet, namesSuperSet = self._get_test_names()
+		nameID = table.addMultilingualName(names, nameID=2)
+		self.assertEqual(nameID, 2)
+		nameID = table.addMultilingualName(names)
+		self.assertEqual(nameID, 2)
+		nameID = table.addMultilingualName(names, minNameID=256)
+		self.assertGreaterEqual(nameID, 256)
+		self.assertEqual(nameID, table.findMultilingualName(names, minNameID=256))
+
 	def test_decompile_badOffset(self):
-                # https://github.com/behdad/fonttools/issues/525
+                # https://github.com/fonttools/fonttools/issues/525
 		table = table__n_a_m_e()
 		badRecord = {
 			"platformID": 1,
@@ -182,7 +317,7 @@
 			"offset": 8765  # out of range
 		}
 		data = bytesjoin([
-                        struct.pack(">HHH", 1, 1, 6 + nameRecordSize),
+                        struct.pack(tostr(">HHH"), 1, 1, 6 + nameRecordSize),
                         sstruct.pack(nameRecordFormat, badRecord)])
 		table.decompile(data, ttFont=None)
 		self.assertEqual(table.names, [])
@@ -203,13 +338,18 @@
 	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())
+		self.assertEqual("Foo Italic"+chr(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 test_toUnicode_singleChar(self):
+		# https://github.com/fonttools/fonttools/issues/1997
+		name = makeName("A", 256, 3, 1, 0x409)
+		self.assertEqual(name.toUnicode(), "A")
+
 	def toXML(self, name):
 		writer = XMLWriter(BytesIO())
 		name.toXML(writer, ttFont=None)
@@ -290,7 +430,19 @@
 
 	def test_extended_mac_encodings(self):
 		name = makeName(b'\xfe', 123, 1, 1, 0) # Mac Japanese
-		self.assertEqual(name.toUnicode(), unichr(0x2122))
+		self.assertEqual(name.toUnicode(), chr(0x2122))
+
+	def test_extended_mac_encodings_errors(self):
+		s = "汉仪彩云体简"
+		name = makeName(s.encode("x_mac_simp_chinese_ttx"), 123, 1, 25, 0)
+		# first check we round-trip with 'strict'
+		self.assertEqual(name.toUnicode(errors="strict"), s)
+
+		# append an incomplete invalid sequence and check that we handle
+		# errors with the requested error handler
+		name.string += b"\xba"
+		self.assertEqual(name.toUnicode(errors="backslashreplace"), s + "\\xba")
+		self.assertEqual(name.toUnicode(errors="replace"), s + "�")
 
 	def test_extended_unknown(self):
 		name = makeName(b'\xfe', 123, 10, 11, 12)
diff --git a/Tests/ttLib/tables/_o_p_b_d_test.py b/Tests/ttLib/tables/_o_p_b_d_test.py
index 131d3f9..d62ada8 100644
--- a/Tests/ttLib/tables/_o_p_b_d_test.py
+++ b/Tests/ttLib/tables/_o_p_b_d_test.py
@@ -1,5 +1,3 @@
-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
diff --git a/Tests/ttLib/tables/_p_r_o_p_test.py b/Tests/ttLib/tables/_p_r_o_p_test.py
index edac915..63c2924 100644
--- a/Tests/ttLib/tables/_p_r_o_p_test.py
+++ b/Tests/ttLib/tables/_p_r_o_p_test.py
@@ -1,5 +1,3 @@
-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
diff --git a/Tests/ttLib/tables/_t_r_a_k_test.py b/Tests/ttLib/tables/_t_r_a_k_test.py
index c223c81..2ea6cf5 100644
--- a/Tests/ttLib/tables/_t_r_a_k_test.py
+++ b/Tests/ttLib/tables/_t_r_a_k_test.py
@@ -1,5 +1,3 @@
-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
diff --git a/Tests/ttLib/tables/_v_h_e_a_test.py b/Tests/ttLib/tables/_v_h_e_a_test.py
index 8979e92..c601863 100644
--- a/Tests/ttLib/tables/_v_h_e_a_test.py
+++ b/Tests/ttLib/tables/_v_h_e_a_test.py
@@ -1,5 +1,3 @@
-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
diff --git a/Tests/ttLib/tables/_v_m_t_x_test.py b/Tests/ttLib/tables/_v_m_t_x_test.py
index f55dbec..5ea2d24 100644
--- a/Tests/ttLib/tables/_v_m_t_x_test.py
+++ b/Tests/ttLib/tables/_v_m_t_x_test.py
@@ -1,6 +1,3 @@
-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
diff --git a/Tests/ttLib/tables/data/C_F_F__2.bin b/Tests/ttLib/tables/data/C_F_F__2.bin
index faa0e91..52b0177 100644
--- a/Tests/ttLib/tables/data/C_F_F__2.bin
+++ 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
index c86252b..95f840f 100644
--- a/Tests/ttLib/tables/data/C_F_F__2.ttx
+++ b/Tests/ttLib/tables/data/C_F_F__2.ttx
@@ -21,7 +21,7 @@
     <xMin value="51"/>
     <yMin value="-115"/>
     <xMax value="560"/>
-    <yMax value="762"/>
+    <yMax value="731"/>
     <macStyle value="00000000 00000000"/>
     <lowestRecPPEM value="3"/>
     <fontDirectionHint value="2"/>
@@ -106,6 +106,8 @@
                 <blend value="190 -110 -162 0 -110 -162"/>
                 <blend value="200 0 -6 0 0 -6"/>
             </StemSnapV>
+            <LanguageGroup value="0"/>
+            <ExpansionFactor value="0.06"/>
           </Private>
         </FontDict>
       </FDArray>
diff --git a/Tests/ttLib/tables/data/NotoColorEmoji.subset.index_format_3.ttx b/Tests/ttLib/tables/data/NotoColorEmoji.subset.index_format_3.ttx
new file mode 100644
index 0000000..6a1728e
--- /dev/null
+++ b/Tests/ttLib/tables/data/NotoColorEmoji.subset.index_format_3.ttx
@@ -0,0 +1,705 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.44">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="eight"/>
+    <GlyphID id="2" name="registered"/>
+    <GlyphID id="3" name="uni2049"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.011"/>
+    <checkSumAdjustment value="0x73631d70"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00001011"/>
+    <unitsPerEm value="2048"/>
+    <created value="Wed May 22 20:00:43 2013"/>
+    <modified value="Thu Jan 30 14:31:14 2020"/>
+    <xMin value="0"/>
+    <yMin value="-500"/>
+    <xMax value="2550"/>
+    <yMax value="1900"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="8"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1900"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="2550"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="2550"/>
+    <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>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="4"/>
+    <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="2550"/>
+    <usWeightClass value="400"/>
+    <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 01000000"/>
+    <usFirstCharIndex value="56"/>
+    <usLastCharIndex value="8265"/>
+    <sTypoAscender value="1900"/>
+    <sTypoDescender value="-500"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1900"/>
+    <usWinDescent value="500"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="0"/>
+    <sCapHeight value="1900"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="2550" lsb="0"/>
+    <mtx name="eight" width="2550" lsb="0"/>
+    <mtx name="registered" width="2550" lsb="0"/>
+    <mtx name="uni2049" width="2550" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uv="0x38" uvs="0xfe0f"/>
+      <map uv="0xae" uvs="0xfe0f"/>
+      <map uv="0x2049" uvs="0xfe0f"/>
+    </cmap_format_14>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="52" language="0" nGroups="3">
+      <map code="0x38" name="eight"/><!-- DIGIT EIGHT -->
+      <map code="0xae" name="registered"/><!-- REGISTERED SIGN -->
+      <map code="0x2049" name="uni2049"/><!-- EXCLAMATION QUESTION MARK -->
+    </cmap_format_12>
+  </cmap>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2013 Google Inc.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Noto Color Emoji
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      Noto Color Emoji
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Noto Color Emoji
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.011;GOOG;noto-emoji:20180424; pistol
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NotoColorEmoji
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.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"/>
+  </post>
+
+  <CBDT>
+    <header version="3.0"/>
+    <strikedata index="0">
+      <cbdt_bitmap_format_17 name="registered">
+        <SmallGlyphMetrics>
+          <height value="128"/>
+          <width value="136"/>
+          <BearingX value="0"/>
+          <BearingY value="101"/>
+          <Advance value="136"/>
+        </SmallGlyphMetrics>
+        <rawimagedata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000088 00000080 08030000 00e737d1
+          0d000000 ff504c54 454c6971 74747475
+          75757878 78777777 7b7b7b7e 7e7e6868
+          68787878 79797977 77777777 77787878
+          6969697b 7b7b7777 77737373 6f6f6f6c
+          6c6c6767 67787878 6a6a6a64 64646161
+          617d7d7d 7a7a7a60 60607575 75797979
+          6666665e 5e5e7171 716e6e6e 6565655c
+          5c5c5a5a 5a6c6c6c 58585857 57577373
+          73555555 53535352 52526e6e 6e505050
+          4e4e4e6b 6b6b4c4c 4c666666 4b4b4b66
+          66666565 65494949 65656547 47474646
+          465b5b5b 4444445a 5a5a4242 42606060
+          5757573f 3f3f6060 60575757 3d3d3d58
+          58583a3a 3a5c5c5c 5d5d5d39 39393737
+          37353535 33333353 53535252 52525252
+          2f2f2f52 52524f4f 4f2b2b2b 28282825
+          25252323 23212121 f9766a48 00000050
+          74524e53 00406180 bfefff20 9fcf508e
+          df30ffff ffffffff 10ffffff ffffffff
+          afffffff ffffffff 70ffffef ffffff80
+          ffffc0ff 8fffefa4 ffdfffff 50ff10ff
+          c162ff80 40ffa7ff d6efffff ffff8fdb
+          7fffefbf 08ff47de 00000bbe 49444154
+          7801ecd5 d77eab30 0c067033 dcb0629b
+          8d11a381 aef77fc2 23b70c93 09745c9d
+          ff5533ac 7e96941f e4bfff7e 8d615ab6
+          4d9f26d4 b62dd320 7fea603a eed30dae
+          631ec89f f0fce0e9 81c0f7c8 2f3bfaf3
+          30181761 144fa250 70360fca 3f925f93
+          98d34078 9866f915 591af269 4866f23b
+          312c5a7c 9222cdcb 3bf254c8 e213b592
+          5f8bc144 5cae100b f63b5186 181c7bb1
+          529ef221 0af93946 50281097 9bc45028
+          81f15353 a976c5d0 a2543f32 1f8f1648
+          d6cd2eb5 2c10f5be df0ebf40 2cbaf63f
+          a210a098 0084d1b5 b4112b90 ffcda61c
+          dc0241d6 9c89435e 5cc5c38b 301914c8
+          3d7c6b4b e9b576c4 2d2bee60 6d7cad29
+          d420bb99 0cf1653b f267c906 d4f62dcf
+          1878966f 533690cf f9b2299c 2193ec64
+          31d4968d e624d897 407fc8ea 8fe5807d
+          11a74653 b60c5964 1787a168 39ed2185
+          7fbcf75c 1cb240b6 1c0f7276 e788bb59
+          13b24fce c3611bea 2c0a9b6e 16ef4ca2
+          6ac9ac9b a592216a 252b1f4d 0cc9b49b
+          65724f12 4b95c9bb 492ef418 eba308bd
+          86dcbe27 e6598e5a 32541dc8 06878a21
+          59eb49b6 fe760c89 b4b94412 05c6f687
+          a544913e 1db4a1cc 814a29e3 794b8544
+          4e42364b 1c89c4bc b331bea4 87d5c75d
+          29b5356b 409d36c9 2ea6ba13 cc49527c
+          e9aebd92 affad98f 4ad58fe0 88350379
+          07b52bcb bb72d7a3 3a25ca7e a4a6ec93
+          553c75b2 9b72f0af 3b24b65c 21f0bdab
+          fde55392 4eddcb5b 35187a71 cec65e3a
+          7225ea1f ce0ada17 37a36b86 53a9455d
+          e4705487 d5a8b3fc b6380d85 1c386751
+          9c4512b5 b0d5ba5f 6edb8f5a d50f822c
+          2979d33f d0652948 85be9005 7b5974d5
+          6f38e01c bad741ca 397793a1 54d4af91
+          4752b193 e59e60a1 f475d001 e70179c0
+          c213f578 22e77822 19ef94f6 eb349144
+          f4b84812 60a9fc75 50e30beb d1a6722e
+          f4e4586f 631094c3 459223d5 3b2db070
+          f2b021e5 db20c417 e63865ce e3d7d5fa
+          673cba4c 62e23be1 58b97cd4 92e45dfb
+          f609bfed 903d4150 346cd7cc c1774eda
+          1ddf937b 0d0180e6 e3cbab00 08123dc8
+          db166acf fde59a00 88d7a178 0300d6bd
+          86003c7f 0c520030 c8ad20fd 29d4a531
+          0e74a9c5 2406d118 00908ed5 9f01eeb4
+          c4d41ad2 01404526 3640fd31 eb045c88
+          f0a8a607 009be82a 00e8b496 98e41657
+          6bc83f52 ca43c759 1c8ac2c0 163205c9
+          258e2524 c7941448 4fa6effc efff5c7b
+          b35c6106 dfb059f6 53733807 fbb3cd8c
+          00e59814 c16b23c8 601987c1 236d89d7
+          60db3992 87e00609 bca9dd81 e025b622
+          b95b23bf eef6b70e bf3f372a ba72acc0
+          76e27d82 05a61ac6 4940f304 5f535537
+          28bc435a 4474ae0d 897f7bee 9b4898c3
+          fb06152e 5081e653 40033589 b5a53b10
+          5ac4a56e 99eb8659 f9f32b79 f48e6489
+          4ba026c1 2363acc0 9681f1b4 2f52b7a0
+          08f5b18b da61fb9b 9ec2b406 c302c68f
+          f4cd30c6 db29189b 043d91d9 90084e01
+          2685ab49 f777834c 18b335c2 19a3efe6
+          196c370d 57dbf0a7 08631d11 c5182932
+          5d33265d 4d33d63b fdf07aea b8089cfa
+          73401013 9d6111fa 54adab95 30e5f06e
+          63ea82e1 798d1d0b a7e68b60 0880c816
+          037fc755 5babf060 7ba698d6 904681cf
+          c45ab56b 28adb589 27a20745 1098bc74
+          3d5f2481 a94b5c46 593ba1fe ad5aabb1
+          31b3b67f 7bbfb729 4e714364 6d6dea7a
+          b06ad8bf 1b6b6798 6a6ba97f ae84ea08
+          11ec1122 f4c113b7 0b4feb7d 030ca3c1
+          05e47891 089ee132 b51f030b 6b332cac
+          a010fb0b a4840879 ae832231 3c5bb90d
+          2f823e5b ce25e605 e7eb8010 d9b7d022
+          6e197247 c89af302 73c9b93f cd81f319
+          e69af303 11cf3126 674022ce f9aead2d
+          e117b590 c67c060b f97996cd 8f0d26cb
+          4e2345a0 a65c6d4e 2d74ca32 830bcdb3
+          cccfd759 563891ed 8027d540 1eb37616
+          ac4dfc6f c0891459 b6f67221 448d390c
+          1f478924 b01bb16f 5b7bd08a 7c59981e
+          0b350cff 4524f444 84e86c35 17624b79
+          c09b956b 95f033f6 5ae11d22 e7867122
+          d39300f4 d1a18438 04a4082e 3456e4dc
+          a285b874 2bd370f1 22aea467 c70a7e47
+          ff592486 679bd77f 38c230a1 445e5b36
+          82022bc8 f540de02 5a042b3b fa44ba22
+          2129e248 0541ba7f eda0f19b 1f10d950
+          224aa91d e630f445 942a5f3b 94aac76c
+          797ced17 0e012502 01767630 1c2be2d8
+          7539bff6 a86092cb 74acc87b c32d91f7
+          fb291590 04b744b0 754b64ff d100c34f
+          4264f971 2faf7305 4401c927 44d8db53
+          2217a52a ccb552db ff23b2cf 490f64ab
+          94c66205 d7e7e507 29575f0d a9942732
+          be8f4202 9724b8c1 49ca149b 2b290f94
+          48817949 e41057f7 79ec2470 f82ba0c0
+          994aa7ec 8b6c9d68 0d1b1a2f a2e1ed45
+          709b8b94 b53bfaad 972fa4cc bf1b8eb0
+          a59810f9 be0b7839 1af08821 3f623597
+          d2570ea1 f04ece45 8bec2ac7 7b4f241c
+          108920c7 e63b5d35 c6ecb191 1af3d217
+          31a6fe76 bc42a5c3 a613cddc cb142fc6
+          a4d8dcc3 9b44e397 5baa32e6 ad9fe679
+          5744e73f d9bb6809 3f07be91 37632a6c
+          d6c6fca2 54f3bcc0 c619e64a 86447650
+          787b796a 8028d75f 3f242f71 708304ca
+          672c1679 4e9ddddf 8456c792 eb2010f4
+          4da77785 2a0e1c98 924b72ce 56708e9b
+          feff7b5e d72e3028 d9fddeee 4a9aeea6
+          67c0612f 84280f7f 9042cc6a 4184181c
+          3c8c1041 2f199486 8b53c1d5 06664248
+          cb2bc1db b71d673c 9f5a4e22 c4fa5510
+          12e2180e 13d23957 53c1e53a d6422441
+          e0e80d67 0ecea91e 64d419a4 58a0cfd2
+          574b0d79 fb3beb89 2373b72d 53d307de
+          9b6523c8 d98383f0 e6f4b9bc e9dc9c25
+          efcc4157 f7bfeab6 b15629ae 8b4a1029
+          8320b194 d5d95fa4 942bae8f 24134214
+          b04d83b4 59c79baf 948e55b2 930f327e
+          11a48056 1d9840d0 9f7a0d1c f1b874bd
+          4ab9e83a d1815902 5ad119a4 df6838c3
+          12092a41 236bafe7 b84c3a28 29675daf
+          f160be79 6d243ba5 c6578fbe 52f5c95f
+          94525b66 8c713b6b 1b486e17 58e1baf3
+          abc24d29 0ad6ba47 9520535e 26690629
+          ee4ae973 85a26a9b 1381d277 7552ead6
+          ebc21ee2 dc124b5c 5faa411e 1e1c8491
+          419032e5 a0d149d1 185a69ed 735cefb9
+          d4d255e2 9c069596 765a4f9f 1e89d61c
+          c4e1a2b5 ce99b3c2 ed25ac9f 6038e056
+          38660b8e 1097d6e8 6cb4be15 4190392f
+          928641b8 0dadcdb5 42d25950 bea17cb6
+          b552571c 5abd92b0 a5e5fb20 8c0c8211
+          931ea475 70cc96a8 ae8291f2 405e8c84
+          5bdabb20 c6ac3e3c 5263dafa b918634a
+          6695b8dd f9f307b3 f4f97620 3c126362
+          67742663 eeffda82 0c10a443 4d4fa68d
+          90e464df 1a50a2b3 2bc4c6f0 40da7184
+          761bb674 2b7c904f 8f6a1046 06c18869
+          cfc4982f 7b40c261 6d0deb3b 01053d9c
+          62ee9320 c8865718 358d7873 0ecc5b61
+          6f7c8eb9 737d105c 7bef7082 6410b46e
+          ad2e4463 5e8088f6 adeae84e 147f78de
+          98e862db a8999e7a 6f812529 779a8f94
+          febcf6f8 fb704f4b dcb4bd3d 5bdedcf1
+          1e84c0d6 32f5f172 6bf90e05 9aea5f2b
+          4976452f 8ae338b1 5f1256b8 feee92ef
+          501c1f7e 799b04d7 51afd855 725cfb44
+          7c525f21 838ee7f8 808e704e 7ee20a3a
+          471bdd2b bc1f9c0f 42670fde 18dcfe6f
+          af5e7694 85a1008e 9f1d2b5e 83585342
+          4c8cdc02 2975a2f4 4a19bff7 7f96ef94
+          4174eee0 e8ace647 5c68da93 bf674306
+          b3481cb0 356747ea 1710828c 2e6c059f
+          0a6d7421 2154789d 1ecd995f a784798a
+          273c5c1a 235e1ee1 4b6c058c ab68f024
+          832fafeb 2e1a28ce a0b2be43 4cc372fc
+          8afb9d29 f07faa36 67228e10 2f60b182
+          472816e6 acf6eb0c 603616a1 8399e411
+          520c1662 2a42b999 1c22b468 4c851748
+          6b267b12 211ec002 018f10d9 9b49eba7
+          54b0885e ad56a475 1391ac90 d505cc54
+          68bb4289 709396e0 0f1a16e2 bee4e8cc
+          f8385793 29657606 a99d9b46 1cfd000e
+          8b71821a 77617232 e00cbec1 3819e4c6
+          5d34c35d 801b4b6a 77456cc9 40c9103e
+          154a4506 5be1aed4 b776204d 50eeaeb5
+          2979a178 15c03b41 c5157991 b6ee5a4e
+          90861b55 0425a2bf 664a4a46 b6933a63
+          28c44fa6 6567c988 96a6bf26 12822ab8
+          19b3ebf5 9aeefbd7 da92aebf 40cbb67f
+          6defcf5b 063f10a8 35dab9fe 0d516fd7
+          1fdad6a2 7fc3edd6 4805f023 85a4283e
+          f4ef8943 9da67492 a6f541f4 ef1d628a
+          64013f95 598a36e6 f9266643 91cde00e
+          0a4ebddc dc909153 8f17701f 4cc55e6e
+          4e8b983c f61483fb d136f652 f17c9ae9
+          59a4b167 35dc5531 a6248dfb 37836b92
+          31a3807b c39464b0 6bcce9ab 88936976
+          c9e01119 5e51a964 541e4dff 51446f8e
+          65325255 010f134a 9b9c6dcb a615ceeb
+          1d126d53 26132b43 78b04caa cd3794cc
+          e0570415 ff346678 2dff2aa6 75d7d9cd
+          c4769dd6 0cfefc79 94ff183d 2a93948d
+          64820000 00004945 4e44ae42 6082
+        </rawimagedata>
+      </cbdt_bitmap_format_17>
+      <cbdt_bitmap_format_17 name="eight">
+        <SmallGlyphMetrics>
+          <height value="128"/>
+          <width value="136"/>
+          <BearingX value="0"/>
+          <BearingY value="101"/>
+          <Advance value="136"/>
+        </SmallGlyphMetrics>
+        <rawimagedata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000088 00000080 08030000 00e737d1
+          0d000000 36504c54 454c6971 40404040
+          40404040 40404040 40404071 7171c3c3
+          c3dfdfdf efefeff4 f4f4fafa fad0d0d0
+          f8f8f8b6 b6b6e7e7 e7909090 a6a6a6ad
+          4143d100 00001274 524e5300 0a131e29
+          3340739e c8dfff84 f266b34d 59e71538
+          bc000003 04494441 547801ed 9ac1b29d
+          200c860b f02902a2 beffcb76 3a3d73ab
+          a15e4f17 c959946f ed8cbf49 0249cc8f
+          c1603018 0c0683c1 6030b0c7 f910232f
+          620cde7d 44458874 c460adc5 456e88ce
+          5ec634a7 25971779 49f3642c 25005097
+          d2b15400 828d8e08 5073f92b 6b028856
+          3a5a2eb7 e406441b 1da97c4b 02a2858e
+          ad3cb04d ea4a82d0 71c3a21d b1eece2f
+          920d70ba 8e99cb5b ccaacef1 30e5f216
+          eb045ed3 20a93b37 e61df639 ad7dea44
+          c50899d6 ee752fa6 d49bc4e9 a54cbdbe
+          ece0c47c 155921e8 79662967 1a10bdfb
+          559944a4 caace71b a0f38b3f 994b0490
+          9a6f1c34 190527e3 0719414d 2b6fbc30
+          fe266c1f c5a19bb4 822408db cfe085d0
+          6623447c f10e4e9e ffe2c289 26490374
+          c1bc7e5e 887ce07f 102282f5 d9355659
+          b3839342 4cb2c6c3 fc2fe95b c1ab5dbe
+          0f075a6f 311522e4 72e27ac4 7b71c467
+          c0a80c48 9c947820 c932c0aa 30aad732
+          e0d02b8c 04519864 9d3971ac 7aa5a2c0
+          21a2a4a4 e9a654cc 80576d27 f6b59c59
+          b7b9c121 8be7f580 a8dc60b5 37db1a54
+          c7241ea8 e5910a78 fde6b7ad 0fcd55b3
+          18d60460 5fca372c bba20ea1 847a6b94
+          5cb1d1e1 7930c9b6 03380b1d d31b1323
+          bcbe8e23 9707f204 78751d6b 79643d74
+          bde3a48e 4f2989bd 8e9c5a83 36a72c94
+          68cef37c 77e72d8d 2f5a36bb f5ba7e3f
+          7161331a 1979d8bb 1b85f0bb 300a208b
+          955dcb24 7db70fd1 5dfe586c b2b8b628
+          e2f3a576 160312cd 625196ce b5fbe0d8
+          3f11f45b df1570bd cd54e612 cf438767
+          ad2a21b2 f79ded73 77ac3eca 9bc13f75
+          c74d5f88 7c897c66 083116f2 744878a8
+          e642b6e7 ac5113b2 cb6b5ebe 05c8327d
+          f57f09b4 ce24415e cf80c174 7301bcb0
+          199b3ce2 2d7e1b55 e4e88a26 1f081675
+          d17afcd9 18713ed2 8d2cb42a 2360e99a
+          6d313292 bed36a7a 5bdf5e22 4746fad3
+          3c072cfd c6480338 eaf63287 c86f2d93
+          1ce56d0e d5f126b5 bc49d26c f5bc3c29
+          eed980f0 a9ad0da1 23daefb1 98e9104a
+          ead330af 9aecf6bc 37cc8b46 c3bc762b
+          6569669b 681e803d e5d291d3 0180375d
+          cd63afd7 15c1ba83 b239242e 708bf502
+          a70ff410 fca7166b f9e2338b b583c160
+          30180c06 83c160f0 13e55b78 1b5bd5fd
+          5a000000 0049454e 44ae4260 82
+        </rawimagedata>
+      </cbdt_bitmap_format_17>
+      <cbdt_bitmap_format_17 name="uni2049">
+        <SmallGlyphMetrics>
+          <height value="128"/>
+          <width value="136"/>
+          <BearingX value="0"/>
+          <BearingY value="101"/>
+          <Advance value="136"/>
+        </SmallGlyphMetrics>
+        <rawimagedata>
+          89504e47 0d0a1a0a 0000000d 49484452
+          00000088 00000080 08030000 00e737d1
+          0d000001 3b504c54 454c6971 d73b3bd9
+          4141da44 44d94141 d94242d5 3737d83f
+          3fd84040 d94040d9 4141d941 41d53838
+          d13030d8 3f3fee4a 4af74d4d e34545eb
+          4747ff50 50fe5050 fe4e4eda 4242fe4f
+          4ffb4747 d94040d3 3434fd4c 4ce14040
+          f34747fc 4b4bd83e 3eeb4242 fb4949fa
+          4545d639 39d02e2e dc3a3aef 3f3fd333
+          33f94343 d43737f8 4141f740 40ed3939
+          e13434d3 3434cf2b 2bf63e3e f53c3cd2
+          3131f439 39d02c2c cc2626d0 2d2df337
+          37f23535 e83030cf 2b2bf132 32ce2929
+          dd2b2bcb 2222f030 30cb2323 d42525cd
+          2626ef2d 2dc81d1d c31313cb 2222eb25
+          25ee2929 c81b1bdb 2121c91f 1fec2525
+          ea2222eb 2424c71a 1ac10c0c c61919c0
+          0a0ac00a 0ac20c0c c10c0cd1 0b0bda0c
+          0cc00a0a e10c0cc1 0808be06 06df0909
+          cb0202de 0606d403 03bc0202 dc0303b9
+          0000b800 00da0101 b80000b9 0000b800
+          00b70000 fbff4a1d 00000068 74524e53
+          0040cfff df583080 afbfef8f 10209fff
+          ffffffff ffffffff ffff70ff ffffffff
+          ffffffff 60ffffef ffffffff ffffffbf
+          ffffffff 9fd5ffff ffffffff ffff80ff
+          8fffffff ef40ffff ffbfffff ffffffff
+          a0ff50df ff8fffff 8fffffff ffffffff
+          ffffdf9f ff50ff8f 9e461cce 00000655
+          49444154 7801ec96 e9e2e22a 0c476ff7
+          55812254 dcfabfef ff929320 636b6c71
+          c3d98fdf f0977808 a8fdef1f ff788128
+          4e6e48b3 7b557956 94553d56 c4555344
+          ed3b1e59 328bcf64 95954e81 1237d9cb
+          2269324b ba946fb3 2af151af f3d744a0
+          96710283 c5d5bc46 719985e8 189b9430
+          7171a9b2 1745b824 7058cc7d 1a827179
+          0be79d70 2a513891 b96b9d3a 8b8d5c04
+          a78934ed c744dae6 ac31492b 3e833c1f
+          529a7f48 64156b00 34b81f6c c6120d3c
+          7953a0e2 56041649 2caf612d 614b6320
+          6c843509 2f821ebe 7110140c c59a0417
+          b11e5dbf d928fe20 ea691314 d910a848
+          6b3dfc16 0437933c ac486c3d 36923f07
+          54a56d48 9102ef47 3f734b7b c63a21ec
+          4f6ccf29 b2c79934 0f8ba45a b39e702d
+          92a387ea a949dfe9 291da32e 4a8a670e
+          a79a1161 30d3ab84 e654830b 4d49ae55
+          94b41baa de1419eb 236d1357 26dbef1a
+          55d514f8 60a22d82 13153c9c 28944865
+          8c564a49 b5e5eea5 943640dd 8c9fb1da
+          c506d05c f131266d 70fda888 319d2274
+          c65c4472 e3025272 47af5123 bb79d23b
+          9b8c833b 77326d18 9106baef ad08771b
+          e5e851ce b45f1b9b e5e34014 87a52c8c
+          486a8c50 167749e4 c14ce74d 4d84cd21
+          0a398073 10113c99 e3dea2e0 fced9bcb
+          e75eda34 de26f4c6 224c3f2e b2274c44
+          0a9cb643 49789da0 73bcd4ac ad8d1198
+          430d2b82 67138510 29b1f3c5 449db798
+          2f35436f c3210749 87366617 4224a56f
+          fbbf906d 4df3c298 e62191c6 18e111b1
+          873ee1e8 ff1ae07d 3d2cf5f2 53cc8888
+          cbae231c 35e95bfb da655040 45d27744
+          0a27320c 43779c70 1806ef06 5750703c
+          4d38c2c2 6322c370 3811e0d3 5004a987
+          1bfc77ef 5322d940 895b6fbf 7818bec2
+          8ba0493c 4ca9d7c4 8352bd23 f23fc18a
+          bc088a4c 7bfd13f9 65446a10 d94ef87a
+          46644b78 43a4c5af 2f11a9be 114717ba
+          11c34010 40b74cee 89a10ca6 630ef9ff
+          3fac7315 d99b8241 ca8d78a5 993c3951
+          cd0b340b 217cee93 6d8da29a 2752ca4f
+          96372915 e5e54e4a cdb71eb3 21389d50
+          5ecea57c f6a70cb6 2e8f0051 a81a7fea
+          594a4b47 80dc48a9 c77eb494 a378c898
+          251ba2d0 7cf7970c 9f1a0622 ec0f0f72
+          4e9190c9 64c22138 6541ae51 34c183e0
+          f0383ce4 113d3df5 a327132b 12205396
+          3c889a20 337fc7e0 70454343 2e2d6ac6
+          9f99c984 0721c1ea 6910e698 07330b5c
+          144567b9 5c9a5910 83934876 a034efcd
+          aca81492 e358042b eb094e97 89907590
+          68087384 2b1aa72b 4a83ccc3 89394ea5
+          8ed922f1 c7144098 6313e4b0 614532a4
+          3f52ec40 2e694808 1cdbedb6 e7c04d51
+          5a50e110 9cca1c5b e4913220 bb202910
+          f1ede07d 644f8342 4455e060 59fdb814
+          5baeeb7a d104996f 71cb7060 8b4f2dea
+          ba8aebee f1cd2d73 d4dc5104 594555d5
+          c1d1963b 4a21f716 1f9db77e 921d0cd2
+          068985a0 597741b3 2b70d0ca 390e712e
+          06f2e878 b373c89e 068608eb 5cdd777c
+          1143c74a 0ac25014 865359b1 2f604d6f
+          2305eb05 f50a27c8 d1d57dff 9759ab4d
+          120b329b ccf20fed c97c97ad 290639a4
+          41f622d2 1ebcda4f 11a9cddf 2122dd21
+          a8135986 5412efd2 1d4521bd 881ca395
+          9cccff43 9ad7ece8 d5cad22a e1b4f331
+          2805b251 d53618a9 ea471e44 35829c55
+          fb8495fa 9b8bba51 06e4e297 04d9a976
+          fea8531d aa321057 0a442388 aaeecd0a
+          90118075 8ba905b0 c9850031 045882d4
+          0026af0e 684c01c8 14e420cb 2337d917
+          825cdc97 00d94510 00f52a7f e40accde
+          c202d864 43c81842 2e411af2 176227db
+          91340520 b8f9d904 08c9bb37 99c9abc9
+          6d24790b 421ac4ba 8a416c10 c9716134
+          909c5d24 4f45205f 410990fe 1137e643
+          5eafdc83 129ead9a c8d19b22 90d977cc
+          49f7d5cf abeb399a fcaae1f1 5e6556a8
+          7f77f466 95b6c377 d090e9f8 698f0e76
+          1c048130 8e73da5b 1f8b3b11 4b5b953a
+          8222efff 043b241b 56d9c334 0e7be377
+          2bc9ff8b 50869b3c b889a669 9a0b3aa5
+          7ba455c7 6f18a4ee 332d990d c3bd3f51
+          179abba8 40f58979 20437c09 aba13c7b
+          645e3fcc 47abcf61 188e0dfe 7c0a2699
+          361fafec 91563ba2 19c77138 36031e48
+          c1a30f9b 7955d76f 281ddec5 bc4e0c1e
+          7564339d 900d49e1 e5a6025e 4f5d69b8
+          ff8c9d0a 16df996a de05aa21 e193ceef
+          c28c87b5 1b1200fc 1dc5c3af 0bcdbf7c
+          c88d6896 02e34388 d1ca0dc9 01d8a560
+          015ced86 e401c6a5 300278aa 590b5443
+          da420876 3db178b4 d56e682e 0498d783
+          194270f5 1b920c79 356f86ed 422305d3
+          9e566d7e e3b4e9c9 26c6187e 9b803f77
+          c1e62302 3b230b11 f98f9b24 377c7b3c
+          d9990d83 74317392 df306cde 45e4fc56
+          af691a86 6f38d2f4 13a054ea b3000000
+          0049454e 44ae4260 82 
+        </rawimagedata>
+      </cbdt_bitmap_format_17>
+    </strikedata>
+  </CBDT>
+
+  <CBLC>
+    <header version="3.0"/>
+    <strike index="0">
+      <bitmapSizeTable>
+        <sbitLineMetrics direction="hori">
+          <ascender value="101"/>
+          <descender value="-27"/>
+          <widthMax value="136"/>
+          <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="101"/>
+          <descender value="-27"/>
+          <widthMax value="136"/>
+          <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="1"/>
+        <endGlyphIndex value="3"/>
+        <ppemX value="109"/>
+        <ppemY value="109"/>
+        <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_3 imageFormat="17" firstGlyphIndex="1" lastGlyphIndex="2">
+        <glyphLoc id="1" name="eight"/>
+        <glyphLoc id="2" name="registered"/>
+      </eblc_index_sub_table_3>
+      <eblc_index_sub_table_3 imageFormat="17" firstGlyphIndex="3" lastGlyphIndex="3">
+        <glyphLoc id="3" name="uni2049"/>
+      </eblc_index_sub_table_3>
+    </strike>
+  </CBLC>
+
+  <vhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1275"/>
+    <descent value="-1275"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="2500"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="2400"/>
+    <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="2500" tsb="0"/>
+    <mtx name="eight" height="2500" tsb="0"/>
+    <mtx name="registered" height="2500" tsb="0"/>
+    <mtx name="uni2049" height="2500" tsb="0"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/_c_m_a_p_format_14.ttx b/Tests/ttLib/tables/data/_c_m_a_p_format_14.ttx
new file mode 100644
index 0000000..73bc6bf
--- /dev/null
+++ b/Tests/ttLib/tables/data/_c_m_a_p_format_14.ttx
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uv="0x30" uvs="0xfe00" name="zero.slash"/>
+      <map uv="0x30" uvs="0xfe01"/>
+    </cmap_format_14>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/_c_m_a_p_format_14_bw_compat.ttx b/Tests/ttLib/tables/data/_c_m_a_p_format_14_bw_compat.ttx
new file mode 100644
index 0000000..00be8ca
--- /dev/null
+++ b/Tests/ttLib/tables/data/_c_m_a_p_format_14_bw_compat.ttx
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uvs="0xfe00" uv="0x30" name="zero.slash"/>
+      <map uvs="0xfe01" uv="0x30" name="None"/><!-- testing whether the old format that uses name="None" is still accepted -->
+    </cmap_format_14>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.glyf.bin b/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.glyf.bin
new file mode 100644
index 0000000..4e18ddb
--- /dev/null
+++ b/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.glyf.bin
Binary files differ
diff --git a/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.head.bin b/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.head.bin
new file mode 100644
index 0000000..3114f38
--- /dev/null
+++ b/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.head.bin
Binary files differ
diff --git a/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.loca.bin b/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.loca.bin
new file mode 100644
index 0000000..d0a95fd
--- /dev/null
+++ b/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.loca.bin
Binary files differ
diff --git a/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.maxp.bin b/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.maxp.bin
new file mode 100644
index 0000000..7fbd12e
--- /dev/null
+++ b/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.maxp.bin
Binary files differ
diff --git a/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.ttx b/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.ttx
new file mode 100644
index 0000000..d0194d6
--- /dev/null
+++ b/Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.ttx
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.29">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="glyph00001"/>
+    <GlyphID id="2" name="glyph00002"/>
+    <GlyphID id="3" name="glyph00003"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x6e4f1ccf"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00100000 00011011"/>
+    <unitsPerEm value="2048"/>
+    <created value="Thu Sep 13 14:22:20 2018"/>
+    <modified value="Fri Sep 14 09:25:13 2018"/>
+    <xMin value="12"/>
+    <yMin value="0"/>
+    <xMax value="1172"/>
+    <yMax value="1430"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="9"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="4"/>
+    <maxPoints value="11"/>
+    <maxContours value="2"/>
+    <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="353"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <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="glyph00001"/><!-- contains no outline data -->
+
+    <TTGlyph name="glyph00002"/><!-- contains no outline data -->
+
+    <TTGlyph name="glyph00003" xMin="12" yMin="0" xMax="1172" yMax="1430">
+      <contour>
+        <pt x="501" y="1430" on="1" overlap="1"/>
+        <pt x="683" y="1430" on="1"/>
+        <pt x="1172" y="0" on="1"/>
+        <pt x="983" y="0" on="1"/>
+        <pt x="591" y="1193" on="1"/>
+        <pt x="199" y="0" on="1"/>
+        <pt x="12" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="249" y="514" on="1"/>
+        <pt x="935" y="514" on="1"/>
+        <pt x="935" y="352" on="1"/>
+        <pt x="249" y="352" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/classdef1_font1.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef1_font1.ttx.GSUB
index 21f1ab1..7a8f510 100644
--- a/Tests/ttLib/tables/data/aots/classdef1_font1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/classdef1_font1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g3"/>
           <Substitution in="g1" out="g4"/>
           <Substitution in="g10" out="g13"/>
@@ -140,7 +140,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g4"/>
           <Substitution in="g1" out="g5"/>
           <Substitution in="g10" out="g14"/>
@@ -247,7 +247,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g5"/>
           <Substitution in="g1" out="g6"/>
           <Substitution in="g10" out="g15"/>
@@ -355,7 +355,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value=".notdef"/>
             <Glyph value="g1"/>
             <Glyph value="g2"/>
@@ -457,7 +457,7 @@
             <Glyph value="g98"/>
             <Glyph value="g99"/>
           </Coverage>
-          <ClassDef Format="1">
+          <ClassDef>
             <ClassDef glyph="g18" class="1"/>
             <ClassDef glyph="g19" class="1"/>
             <ClassDef glyph="g20" class="1"/>
diff --git a/Tests/ttLib/tables/data/aots/classdef1_font2.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef1_font2.ttx.GSUB
index ec7278e..563d6b6 100644
--- a/Tests/ttLib/tables/data/aots/classdef1_font2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/classdef1_font2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g3"/>
           <Substitution in="g1" out="g4"/>
           <Substitution in="g10" out="g13"/>
@@ -140,7 +140,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g4"/>
           <Substitution in="g1" out="g5"/>
           <Substitution in="g10" out="g14"/>
@@ -247,7 +247,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g5"/>
           <Substitution in="g1" out="g6"/>
           <Substitution in="g10" out="g15"/>
@@ -355,7 +355,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value=".notdef"/>
             <Glyph value="g1"/>
             <Glyph value="g2"/>
@@ -457,7 +457,7 @@
             <Glyph value="g98"/>
             <Glyph value="g99"/>
           </Coverage>
-          <ClassDef Format="1">
+          <ClassDef>
             <ClassDef glyph="g18" class="2"/>
             <ClassDef glyph="g19" class="2"/>
             <ClassDef glyph="g20" class="2"/>
diff --git a/Tests/ttLib/tables/data/aots/classdef1_font3.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef1_font3.ttx.GSUB
index 9bc1e43..0bcbc72 100644
--- a/Tests/ttLib/tables/data/aots/classdef1_font3.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/classdef1_font3.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g3"/>
           <Substitution in="g1" out="g4"/>
           <Substitution in="g10" out="g13"/>
@@ -140,7 +140,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g4"/>
           <Substitution in="g1" out="g5"/>
           <Substitution in="g10" out="g14"/>
@@ -247,7 +247,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g5"/>
           <Substitution in="g1" out="g6"/>
           <Substitution in="g10" out="g15"/>
@@ -355,7 +355,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value=".notdef"/>
             <Glyph value="g1"/>
             <Glyph value="g2"/>
@@ -457,7 +457,7 @@
             <Glyph value="g98"/>
             <Glyph value="g99"/>
           </Coverage>
-          <ClassDef Format="1">
+          <ClassDef>
             <ClassDef glyph="g18" class="2"/>
             <ClassDef glyph="g19" class="2"/>
             <ClassDef glyph="g20" class="2"/>
diff --git a/Tests/ttLib/tables/data/aots/classdef1_font4.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef1_font4.ttx.GSUB
index 681240d..dce2706 100644
--- a/Tests/ttLib/tables/data/aots/classdef1_font4.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/classdef1_font4.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g3"/>
           <Substitution in="g1" out="g4"/>
           <Substitution in="g10" out="g13"/>
@@ -140,7 +140,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g4"/>
           <Substitution in="g1" out="g5"/>
           <Substitution in="g10" out="g14"/>
@@ -247,7 +247,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g5"/>
           <Substitution in="g1" out="g6"/>
           <Substitution in="g10" out="g15"/>
@@ -355,7 +355,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value=".notdef"/>
             <Glyph value="g1"/>
             <Glyph value="g2"/>
@@ -457,7 +457,7 @@
             <Glyph value="g98"/>
             <Glyph value="g99"/>
           </Coverage>
-          <ClassDef Format="1">
+          <ClassDef>
           </ClassDef>
           <!-- SubClassSetCount=1 -->
           <SubClassSet index="0" empty="1"/>
diff --git a/Tests/ttLib/tables/data/aots/classdef2_font1.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef2_font1.ttx.GSUB
index f602354..7a8f510 100644
--- a/Tests/ttLib/tables/data/aots/classdef2_font1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/classdef2_font1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g3"/>
           <Substitution in="g1" out="g4"/>
           <Substitution in="g10" out="g13"/>
@@ -140,7 +140,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g4"/>
           <Substitution in="g1" out="g5"/>
           <Substitution in="g10" out="g14"/>
@@ -247,7 +247,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g5"/>
           <Substitution in="g1" out="g6"/>
           <Substitution in="g10" out="g15"/>
@@ -355,7 +355,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value=".notdef"/>
             <Glyph value="g1"/>
             <Glyph value="g2"/>
@@ -457,7 +457,7 @@
             <Glyph value="g98"/>
             <Glyph value="g99"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g18" class="1"/>
             <ClassDef glyph="g19" class="1"/>
             <ClassDef glyph="g20" class="1"/>
diff --git a/Tests/ttLib/tables/data/aots/classdef2_font2.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef2_font2.ttx.GSUB
index d4650bd..563d6b6 100644
--- a/Tests/ttLib/tables/data/aots/classdef2_font2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/classdef2_font2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g3"/>
           <Substitution in="g1" out="g4"/>
           <Substitution in="g10" out="g13"/>
@@ -140,7 +140,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g4"/>
           <Substitution in="g1" out="g5"/>
           <Substitution in="g10" out="g14"/>
@@ -247,7 +247,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g5"/>
           <Substitution in="g1" out="g6"/>
           <Substitution in="g10" out="g15"/>
@@ -355,7 +355,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value=".notdef"/>
             <Glyph value="g1"/>
             <Glyph value="g2"/>
@@ -457,7 +457,7 @@
             <Glyph value="g98"/>
             <Glyph value="g99"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g18" class="2"/>
             <ClassDef glyph="g19" class="2"/>
             <ClassDef glyph="g20" class="2"/>
diff --git a/Tests/ttLib/tables/data/aots/classdef2_font3.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef2_font3.ttx.GSUB
index 7cb045f..0bcbc72 100644
--- a/Tests/ttLib/tables/data/aots/classdef2_font3.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/classdef2_font3.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g3"/>
           <Substitution in="g1" out="g4"/>
           <Substitution in="g10" out="g13"/>
@@ -140,7 +140,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g4"/>
           <Substitution in="g1" out="g5"/>
           <Substitution in="g10" out="g14"/>
@@ -247,7 +247,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g5"/>
           <Substitution in="g1" out="g6"/>
           <Substitution in="g10" out="g15"/>
@@ -355,7 +355,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value=".notdef"/>
             <Glyph value="g1"/>
             <Glyph value="g2"/>
@@ -457,7 +457,7 @@
             <Glyph value="g98"/>
             <Glyph value="g99"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g18" class="2"/>
             <ClassDef glyph="g19" class="2"/>
             <ClassDef glyph="g20" class="2"/>
diff --git a/Tests/ttLib/tables/data/aots/classdef2_font4.ttx.GSUB b/Tests/ttLib/tables/data/aots/classdef2_font4.ttx.GSUB
index 90f0b93..dce2706 100644
--- a/Tests/ttLib/tables/data/aots/classdef2_font4.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/classdef2_font4.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g3"/>
           <Substitution in="g1" out="g4"/>
           <Substitution in="g10" out="g13"/>
@@ -140,7 +140,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g4"/>
           <Substitution in="g1" out="g5"/>
           <Substitution in="g10" out="g14"/>
@@ -247,7 +247,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in=".notdef" out="g5"/>
           <Substitution in="g1" out="g6"/>
           <Substitution in="g10" out="g15"/>
@@ -355,7 +355,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value=".notdef"/>
             <Glyph value="g1"/>
             <Glyph value="g2"/>
@@ -457,7 +457,7 @@
             <Glyph value="g98"/>
             <Glyph value="g99"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
           </ClassDef>
           <!-- SubClassSetCount=1 -->
           <SubClassSet index="0" empty="1"/>
diff --git a/Tests/ttLib/tables/data/aots/cmap14_font1.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap14_font1.ttx.cmap
index ced8c8a..acead7b 100644
--- a/Tests/ttLib/tables/data/aots/cmap14_font1.ttx.cmap
+++ b/Tests/ttLib/tables/data/aots/cmap14_font1.ttx.cmap
@@ -3,14 +3,14 @@
 
   <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 platformID="0" platEncID="5">
+      <map uv="0x4e00" uvs="0xe0100"/>
+      <map uv="0x4e03" uvs="0xe0100"/>
+      <map uv="0x4e04" uvs="0xe0100"/>
+      <map uv="0x4e05" uvs="0xe0100"/>
+      <map uv="0x4e06" uvs="0xe0100"/>
+      <map uv="0x4e10" uvs="0xe0100" name="g25"/>
+      <map uv="0x4e11" uvs="0xe0100" name="g26"/>
     </cmap_format_14>
     <cmap_format_4 platformID="3" platEncID="1" language="0">
       <map code="0x4e00" name="g10"/><!-- CJK UNIFIED IDEOGRAPH-4E00 -->
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
index 971a3f1..08e65de 100644
--- a/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g18" class="1"/>
     </GlyphClassDef>
   </GDEF>
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
index 93d3a05..3ab19a6 100644
--- a/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_lookupflag_f1.ttx.GPOS
@@ -31,10 +31,10 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="1"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
             <Glyph value="g20"/>
           </Coverage>
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
index 00eacc3..508d14e 100644
--- a/Tests/ttLib/tables/data/aots/gpos1_1_simple_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
             <Glyph value="g20"/>
           </Coverage>
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
index 1eff021..ef78ece 100644
--- a/Tests/ttLib/tables/data/aots/gpos1_1_simple_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
             <Glyph value="g20"/>
           </Coverage>
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
index c3850df..523b139 100644
--- a/Tests/ttLib/tables/data/aots/gpos1_1_simple_f3.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f3.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
             <Glyph value="g20"/>
           </Coverage>
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
index f80286c..027d687 100644
--- a/Tests/ttLib/tables/data/aots/gpos1_1_simple_f4.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos1_1_simple_f4.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
             <Glyph value="g20"/>
           </Coverage>
diff --git a/Tests/ttLib/tables/data/aots/gpos1_2_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos1_2_font1.ttx.GPOS
index 11351a6..058c302 100644
--- a/Tests/ttLib/tables/data/aots/gpos1_2_font1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos1_2_font1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
             <Glyph value="g20"/>
           </Coverage>
diff --git a/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GDEF
index 971a3f1..08e65de 100644
--- a/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g18" class="1"/>
     </GlyphClassDef>
   </GDEF>
diff --git a/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GPOS
index 88257ac..2b557f7 100644
--- a/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos1_2_font2.ttx.GPOS
@@ -31,10 +31,10 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="1"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
             <Glyph value="g20"/>
           </Coverage>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_font6.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_1_font6.ttx.GPOS
index db1315b..e27e72b 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_1_font6.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_font6.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
           </Coverage>
           <ValueFormat1 value="1"/>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_1_font7.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_1_font7.ttx.GPOS
index 8b22294..01f6b45 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_1_font7.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_font7.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
             <Glyph value="g21"/>
           </Coverage>
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
index 971a3f1..08e65de 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g18" class="1"/>
     </GlyphClassDef>
   </GDEF>
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
index 06b0691..329315c 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f1.ttx.GPOS
@@ -31,10 +31,10 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g19"/>
           </Coverage>
           <ValueFormat1 value="1"/>
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
index 971a3f1..08e65de 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g18" class="1"/>
     </GlyphClassDef>
   </GDEF>
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
index 03e9f8b..5650616 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_lookupflag_f2.ttx.GPOS
@@ -31,10 +31,10 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g19"/>
           </Coverage>
           <ValueFormat1 value="4"/>
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
index d8b4e83..e5f8cc7 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
           </Coverage>
           <ValueFormat1 value="1"/>
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
index cf71f47..820bea6 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
           </Coverage>
           <ValueFormat1 value="1"/>
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
index 81b1720..55b8402 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_1_simple_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos2_1_simple_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
           </Coverage>
           <ValueFormat1 value="1"/>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_2_font1.ttx.GPOS
index 5595b99..d41d02d 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_2_font1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font1.ttx.GPOS
@@ -34,15 +34,15 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
           </Coverage>
           <ValueFormat1 value="1"/>
           <ValueFormat2 value="2"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
             <ClassDef glyph="g18" class="1"/>
           </ClassDef1>
-          <ClassDef2 Format="2">
+          <ClassDef2>
             <ClassDef glyph="g19" class="1"/>
           </ClassDef2>
           <!-- Class1Count=2 -->
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GDEF
index 971a3f1..08e65de 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g18" class="1"/>
     </GlyphClassDef>
   </GDEF>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GPOS
index 7896be3..c7f3f32 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font2.ttx.GPOS
@@ -31,18 +31,18 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g19"/>
           </Coverage>
           <ValueFormat1 value="1"/>
           <ValueFormat2 value="2"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
             <ClassDef glyph="g19" class="1"/>
           </ClassDef1>
-          <ClassDef2 Format="2">
+          <ClassDef2>
             <ClassDef glyph="g20" class="1"/>
           </ClassDef2>
           <!-- Class1Count=2 -->
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GDEF
index 971a3f1..08e65de 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g18" class="1"/>
     </GlyphClassDef>
   </GDEF>
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GPOS
index 216a591..25ac07c 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font3.ttx.GPOS
@@ -31,18 +31,18 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g19"/>
           </Coverage>
           <ValueFormat1 value="4"/>
           <ValueFormat2 value="2"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
             <ClassDef glyph="g19" class="1"/>
           </ClassDef1>
-          <ClassDef2 Format="2">
+          <ClassDef2>
             <ClassDef glyph="g20" class="1"/>
           </ClassDef2>
           <!-- Class1Count=2 -->
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font4.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_2_font4.ttx.GPOS
index a2e6017..46f3e6e 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_2_font4.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font4.ttx.GPOS
@@ -34,15 +34,15 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
           </Coverage>
           <ValueFormat1 value="1"/>
           <ValueFormat2 value="2"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
             <ClassDef glyph="g18" class="1"/>
           </ClassDef1>
-          <ClassDef2 Format="2">
+          <ClassDef2>
             <ClassDef glyph="g18" class="1"/>
           </ClassDef2>
           <!-- Class1Count=2 -->
diff --git a/Tests/ttLib/tables/data/aots/gpos2_2_font5.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos2_2_font5.ttx.GPOS
index d269735..1d589ca 100644
--- a/Tests/ttLib/tables/data/aots/gpos2_2_font5.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos2_2_font5.ttx.GPOS
@@ -34,15 +34,15 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
           </Coverage>
           <ValueFormat1 value="1"/>
           <ValueFormat2 value="0"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
             <ClassDef glyph="g18" class="1"/>
           </ClassDef1>
-          <ClassDef2 Format="2">
+          <ClassDef2>
             <ClassDef glyph="g18" class="1"/>
           </ClassDef2>
           <!-- Class1Count=2 -->
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos3_font1.ttx.GPOS
index 8cbdbc7..8babfbf 100644
--- a/Tests/ttLib/tables/data/aots/gpos3_font1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos3_font1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <CursivePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
             <Glyph value="g19"/>
           </Coverage>
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GDEF
index b5ca1ed..d2981b4 100644
--- a/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g21" class="1"/>
     </GlyphClassDef>
   </GDEF>
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GPOS
index 3ded3a7..378af37 100644
--- a/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos3_font2.ttx.GPOS
@@ -31,10 +31,10 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="3"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
         <CursivePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
             <Glyph value="g19"/>
           </Coverage>
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GDEF
index b5ca1ed..d2981b4 100644
--- a/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g21" class="1"/>
     </GlyphClassDef>
   </GDEF>
diff --git a/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GPOS
index e6a1c04..7da5f5e 100644
--- a/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos3_font3.ttx.GPOS
@@ -31,10 +31,10 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="3"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
         <CursivePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
             <Glyph value="g19"/>
             <Glyph value="g20"/>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GDEF
index 5118ad8..e6b3946 100644
--- a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GDEF
@@ -3,13 +3,13 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g17" class="1"/>
       <ClassDef glyph="g18" class="1"/>
       <ClassDef glyph="g19" class="3"/>
       <ClassDef glyph="g20" class="3"/>
     </GlyphClassDef>
-    <MarkAttachClassDef Format="2">
+    <MarkAttachClassDef>
       <ClassDef glyph="g19" class="1"/>
       <ClassDef glyph="g20" class="2"/>
     </MarkAttachClassDef>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GPOS
index 744eaaa..d7526b5 100644
--- a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f1.ttx.GPOS
@@ -31,13 +31,13 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="4"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
         <MarkBasePos index="0" Format="1">
-          <MarkCoverage Format="1">
+          <MarkCoverage>
             <Glyph value="g19"/>
           </MarkCoverage>
-          <BaseCoverage Format="1">
+          <BaseCoverage>
             <Glyph value="g18"/>
           </BaseCoverage>
           <!-- ClassCount=1 -->
diff --git a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GDEF
index 2627f21..8184af2 100644
--- a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g17" class="1"/>
       <ClassDef glyph="g18" class="1"/>
       <ClassDef glyph="g19" class="3"/>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GPOS
index 7817336..3efed82 100644
--- a/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos4_lookupflag_f2.ttx.GPOS
@@ -31,13 +31,13 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <MarkBasePos index="0" Format="1">
-          <MarkCoverage Format="1">
+          <MarkCoverage>
             <Glyph value="g19"/>
           </MarkCoverage>
-          <BaseCoverage Format="1">
+          <BaseCoverage>
             <Glyph value="g18"/>
           </BaseCoverage>
           <!-- ClassCount=1 -->
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
index 4f9f64a..a8df0fd 100644
--- a/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g17" class="1"/>
       <ClassDef glyph="g18" class="1"/>
       <ClassDef glyph="g19" class="3"/>
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
index afe7e6a..cfd3ddb 100644
--- a/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos4_multiple_anchors_1.ttx.GPOS
@@ -34,13 +34,13 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <MarkBasePos index="0" Format="1">
-          <MarkCoverage Format="1">
+          <MarkCoverage>
             <Glyph value="g19"/>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
           </MarkCoverage>
-          <BaseCoverage Format="1">
+          <BaseCoverage>
             <Glyph value="g17"/>
             <Glyph value="g18"/>
           </BaseCoverage>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GDEF
index 2627f21..8184af2 100644
--- a/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g17" class="1"/>
       <ClassDef glyph="g18" class="1"/>
       <ClassDef glyph="g19" class="3"/>
diff --git a/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GPOS
index 3a2e623..ccbc784 100644
--- a/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos4_simple_1.ttx.GPOS
@@ -34,10 +34,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <MarkBasePos index="0" Format="1">
-          <MarkCoverage Format="1">
+          <MarkCoverage>
             <Glyph value="g19"/>
           </MarkCoverage>
-          <BaseCoverage Format="1">
+          <BaseCoverage>
             <Glyph value="g18"/>
           </BaseCoverage>
           <!-- ClassCount=1 -->
diff --git a/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GDEF
index ff2dc98..461a982 100644
--- a/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g17" class="1"/>
       <ClassDef glyph="g18" class="2"/>
       <ClassDef glyph="g19" class="3"/>
diff --git a/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GPOS
index b5017b3..d5abadc 100644
--- a/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GPOS
@@ -34,10 +34,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <MarkLigPos index="0" Format="1">
-          <MarkCoverage Format="1">
+          <MarkCoverage>
             <Glyph value="g19"/>
           </MarkCoverage>
-          <LigatureCoverage Format="1">
+          <LigatureCoverage>
             <Glyph value="g18"/>
           </LigatureCoverage>
           <!-- ClassCount=1 -->
diff --git a/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GSUB
index 85d3308..f81552a 100644
--- a/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gpos5_font1.ttx.GSUB
@@ -31,9 +31,9 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g30">
             <Ligature components="g31" glyph="g18"/>
           </LigatureSet>
diff --git a/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GDEF b/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GDEF
index 640f3eb..f07a29b 100644
--- a/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g17" class="1"/>
       <ClassDef glyph="g18" class="3"/>
       <ClassDef glyph="g19" class="3"/>
diff --git a/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GPOS
index d4c4da5..f2fd253 100644
--- a/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos6_font1.ttx.GPOS
@@ -34,10 +34,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <MarkMarkPos index="0" Format="1">
-          <Mark1Coverage Format="1">
+          <Mark1Coverage>
             <Glyph value="g19"/>
           </Mark1Coverage>
-          <Mark2Coverage Format="1">
+          <Mark2Coverage>
             <Glyph value="g18"/>
           </Mark2Coverage>
           <!-- ClassCount=1 -->
diff --git a/Tests/ttLib/tables/data/aots/gpos7_1_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos7_1_font1.ttx.GPOS
index 3d82e68..db3b76e 100644
--- a/Tests/ttLib/tables/data/aots/gpos7_1_font1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos7_1_font1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
             <Glyph value="g19"/>
             <Glyph value="g20"/>
@@ -51,7 +51,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g18"/>
           </Coverage>
           <!-- PosRuleSetCount=1 -->
diff --git a/Tests/ttLib/tables/data/aots/gpos9_font1.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos9_font1.ttx.GPOS
index bbc1c38..9bcef94 100644
--- a/Tests/ttLib/tables/data/aots/gpos9_font1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos9_font1.ttx.GPOS
@@ -36,7 +36,7 @@
         <ExtensionPos index="0" Format="1">
           <ExtensionLookupType value="1"/>
           <SinglePos Format="1">
-            <Coverage Format="1">
+            <Coverage>
               <Glyph value="g18"/>
               <Glyph value="g20"/>
             </Coverage>
diff --git a/Tests/ttLib/tables/data/aots/gpos9_font2.ttx.GPOS b/Tests/ttLib/tables/data/aots/gpos9_font2.ttx.GPOS
index ac6d6af..ffb993b 100644
--- a/Tests/ttLib/tables/data/aots/gpos9_font2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos9_font2.ttx.GPOS
@@ -36,7 +36,7 @@
         <ExtensionPos index="0" Format="1">
           <ExtensionLookupType value="1"/>
           <SinglePos Format="1">
-            <Coverage Format="1">
+            <Coverage>
               <Glyph value="g18"/>
               <Glyph value="g20"/>
             </Coverage>
@@ -47,7 +47,7 @@
         <ExtensionPos index="1" Format="1">
           <ExtensionLookupType value="1"/>
           <SinglePos Format="1">
-            <Coverage Format="1">
+            <Coverage>
               <Glyph value="g19"/>
               <Glyph value="g21"/>
             </Coverage>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index ea85d9f..a701d03 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <!-- ChainPosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index bd176ba..865a69b 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <!-- ChainPosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index b189067..f36b48a 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f3.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <!-- ChainPosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index f750652..d497f58 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_boundary_f4.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g22"/>
           </Coverage>
           <!-- ChainPosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index b07a9ba..540a00e 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_lookupflag_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -97,10 +97,10 @@
       </Lookup>
       <Lookup index="4">
         <LookupType value="8"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g22"/>
           </Coverage>
           <!-- ChainPosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 09b5bd8..81374bc 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <!-- ChainPosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index e8fb516..404e929 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <!-- ChainPosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 1f7539e..87be738 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
             <Glyph value="g23"/>
           </Coverage>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 1780fda..c755f87 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <!-- ChainPosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index c2d3411..e211d85 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_simple_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g22"/>
           </Coverage>
           <!-- ChainPosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 3960d8a..a2da259 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining1_successive_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- ChainPosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index fe06077..f8b5eac 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -109,7 +109,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -118,7 +118,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -127,7 +127,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index c529371..aa832de 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -109,7 +109,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -118,7 +118,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -127,7 +127,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index fa55cf1..6ee0bc9 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f3.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -109,7 +109,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -118,7 +118,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -127,7 +127,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index f943402..1acf6c4 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_boundary_f4.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -109,7 +109,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -118,7 +118,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -127,7 +127,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index b26d93b..a5b4dec 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_lookupflag_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -97,10 +97,10 @@
       </Lookup>
       <Lookup index="4">
         <LookupType value="8"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -109,7 +109,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -118,7 +118,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -127,7 +127,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 33399ba..c5b5253 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -109,7 +109,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -118,7 +118,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -127,7 +127,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index dc10c2f..b9512fa 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -109,7 +109,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -118,7 +118,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -127,7 +127,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 9c18a9b..b9e864d 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -109,7 +109,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -118,7 +118,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -127,7 +127,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index decc575..3fddfb2 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -109,7 +109,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -118,7 +118,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -127,7 +127,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index a35678d..248c52a 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_simple_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -109,7 +109,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -118,7 +118,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -127,7 +127,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 6775e5b..4914552 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining2_successive_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -109,7 +109,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -118,7 +118,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -127,7 +127,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index c362427..4a4f076 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -101,18 +101,18 @@
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="3">
           <!-- BacktrackGlyphCount=1 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g20"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=2 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g21"/>
           </InputCoverage>
-          <InputCoverage index="1" Format="1">
+          <InputCoverage index="1">
             <Glyph value="g22"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=1 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g23"/>
           </LookAheadCoverage>
           <!-- PosCount=0 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 7b27f90..4f38aec 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -101,18 +101,18 @@
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="3">
           <!-- BacktrackGlyphCount=1 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g20"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=1 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g21"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=2 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g22"/>
           </LookAheadCoverage>
-          <LookAheadCoverage index="1" Format="1">
+          <LookAheadCoverage index="1">
             <Glyph value="g23"/>
           </LookAheadCoverage>
           <!-- PosCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 73df34c..4cde228 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f3.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -102,14 +102,14 @@
         <ChainContextPos index="0" Format="3">
           <!-- BacktrackGlyphCount=0 -->
           <!-- InputGlyphCount=1 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g21"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=2 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g22"/>
           </LookAheadCoverage>
-          <LookAheadCoverage index="1" Format="1">
+          <LookAheadCoverage index="1">
             <Glyph value="g23"/>
           </LookAheadCoverage>
           <!-- PosCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 67bfc0e..ab46ecb 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_boundary_f4.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -101,14 +101,14 @@
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="3">
           <!-- BacktrackGlyphCount=2 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g21"/>
           </BacktrackCoverage>
-          <BacktrackCoverage index="1" Format="1">
+          <BacktrackCoverage index="1">
             <Glyph value="g20"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=1 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g22"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=0 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index b12b665..2bc6f6b 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_lookupflag_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -97,31 +97,31 @@
       </Lookup>
       <Lookup index="4">
         <LookupType value="8"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="3">
           <!-- BacktrackGlyphCount=2 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g21"/>
           </BacktrackCoverage>
-          <BacktrackCoverage index="1" Format="1">
+          <BacktrackCoverage index="1">
             <Glyph value="g20"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=3 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g22"/>
           </InputCoverage>
-          <InputCoverage index="1" Format="1">
+          <InputCoverage index="1">
             <Glyph value="g23"/>
           </InputCoverage>
-          <InputCoverage index="2" Format="1">
+          <InputCoverage index="2">
             <Glyph value="g24"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=2 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g25"/>
           </LookAheadCoverage>
-          <LookAheadCoverage index="1" Format="1">
+          <LookAheadCoverage index="1">
             <Glyph value="g26"/>
           </LookAheadCoverage>
           <!-- PosCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index d5b0c1f..76be0ca 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -101,18 +101,18 @@
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="3">
           <!-- BacktrackGlyphCount=1 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g22"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=2 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g21"/>
           </InputCoverage>
-          <InputCoverage index="1" Format="1">
+          <InputCoverage index="1">
             <Glyph value="g22"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=1 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g21"/>
           </LookAheadCoverage>
           <!-- PosCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index b9b0ce5..4a3d10a 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -101,18 +101,18 @@
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="3">
           <!-- BacktrackGlyphCount=1 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g20"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=2 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g21"/>
           </InputCoverage>
-          <InputCoverage index="1" Format="1">
+          <InputCoverage index="1">
             <Glyph value="g22"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=1 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g23"/>
           </LookAheadCoverage>
           <!-- PosCount=2 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 6d023ec..1a1f57b 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_simple_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -101,27 +101,27 @@
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="3">
           <!-- BacktrackGlyphCount=2 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g21"/>
           </BacktrackCoverage>
-          <BacktrackCoverage index="1" Format="1">
+          <BacktrackCoverage index="1">
             <Glyph value="g20"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=3 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g22"/>
           </InputCoverage>
-          <InputCoverage index="1" Format="1">
+          <InputCoverage index="1">
             <Glyph value="g23"/>
           </InputCoverage>
-          <InputCoverage index="2" Format="1">
+          <InputCoverage index="2">
             <Glyph value="g24"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=2 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g25"/>
           </LookAheadCoverage>
-          <LookAheadCoverage index="1" Format="1">
+          <LookAheadCoverage index="1">
             <Glyph value="g26"/>
           </LookAheadCoverage>
           <!-- PosCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index f7c85b6..2c6ebb6 100644
--- a/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_chaining3_successive_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -101,24 +101,24 @@
         <!-- SubTableCount=1 -->
         <ChainContextPos index="0" Format="3">
           <!-- BacktrackGlyphCount=1 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g25"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=4 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g20"/>
           </InputCoverage>
-          <InputCoverage index="1" Format="1">
+          <InputCoverage index="1">
             <Glyph value="g21"/>
           </InputCoverage>
-          <InputCoverage index="2" Format="1">
+          <InputCoverage index="2">
             <Glyph value="g22"/>
           </InputCoverage>
-          <InputCoverage index="3" Format="1">
+          <InputCoverage index="3">
             <Glyph value="g23"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=1 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g24"/>
           </LookAheadCoverage>
           <!-- PosCount=2 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 662ae54..e61cc4e 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- PosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 56a4f7b..70c9250 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_boundary_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- PosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 77d52eb..ba6c146 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_expansion_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- PosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 2d5f796..8688a39 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -97,10 +97,10 @@
       </Lookup>
       <Lookup index="4">
         <LookupType value="7"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- PosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index ef419ff..8e2621f 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_lookupflag_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -97,10 +97,10 @@
       </Lookup>
       <Lookup index="4">
         <LookupType value="7"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- PosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 82750d5..03cec90 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- PosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 764703b..652850f 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- PosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index ac00f86..6ad207d 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- PosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 031f56f..a4c824f 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- PosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index bb3d01d..15faa22 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_simple_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- PosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 2d17ca5..11ae1c7 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context1_successive_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,7 +100,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- PosRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index b830138..08af7f3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,10 +100,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
           </ClassDef>
           <!-- PosClassSetCount=2 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index a48dc6a..832e37b 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_boundary_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,10 +100,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
           </ClassDef>
           <!-- PosClassSetCount=2 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 7573e48..b06382d 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,11 +100,11 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="1"/>
             <ClassDef glyph="g22" class="1"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 4435c02..a266ada 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_classes_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,13 +100,13 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
             <Glyph value="g24"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="1"/>
             <ClassDef glyph="g22" class="1"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 584892f..48833f7 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_expansion_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,10 +100,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 99546b5..d45c280 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -97,13 +97,13 @@
       </Lookup>
       <Lookup index="4">
         <LookupType value="7"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 4a0fcb8..6f3c016 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_lookupflag_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -97,13 +97,13 @@
       </Lookup>
       <Lookup index="4">
         <LookupType value="7"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index d2f38c3..1a9b1e2 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,10 +100,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index e5d0a63..f3f9d9d 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,10 +100,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index c171781..655d1d3 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,10 +100,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
           </ClassDef>
           <!-- PosClassSetCount=2 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 266f31b..bc06db5 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,10 +100,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 8001658..c2964bb 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_simple_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,10 +100,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
           </ClassDef>
           <!-- PosClassSetCount=2 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 2de586f..797b37b 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context2_successive_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -100,10 +100,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index fd85d19..f709b3b 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -102,10 +102,10 @@
         <ContextPos index="0" Format="3">
           <!-- GlyphCount=2 -->
           <!-- PosCount=0 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
-          <Coverage index="1" Format="1">
+          <Coverage index="1">
             <Glyph value="g20"/>
           </Coverage>
         </ContextPos>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index bee96ff..a885d1a 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_boundary_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -102,7 +102,7 @@
         <ContextPos index="0" Format="3">
           <!-- GlyphCount=1 -->
           <!-- PosCount=1 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
           <PosLookupRecord index="0">
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index e9c854c..286a200 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -97,18 +97,18 @@
       </Lookup>
       <Lookup index="4">
         <LookupType value="7"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="3">
           <!-- GlyphCount=3 -->
           <!-- PosCount=3 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
-          <Coverage index="1" Format="1">
+          <Coverage index="1">
             <Glyph value="g21"/>
           </Coverage>
-          <Coverage index="2" Format="1">
+          <Coverage index="2">
             <Glyph value="g22"/>
           </Coverage>
           <PosLookupRecord index="0">
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index e2b4a23..ea697fc 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_lookupflag_f2.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -97,18 +97,18 @@
       </Lookup>
       <Lookup index="4">
         <LookupType value="7"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ContextPos index="0" Format="3">
           <!-- GlyphCount=3 -->
           <!-- PosCount=1 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
-          <Coverage index="1" Format="1">
+          <Coverage index="1">
             <Glyph value="g21"/>
           </Coverage>
-          <Coverage index="2" Format="1">
+          <Coverage index="2">
             <Glyph value="g22"/>
           </Coverage>
           <PosLookupRecord index="0">
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 44ce072..c4b9d8a 100644
--- 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
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -102,10 +102,10 @@
         <ContextPos index="0" Format="3">
           <!-- GlyphCount=2 -->
           <!-- PosCount=1 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
-          <Coverage index="1" Format="1">
+          <Coverage index="1">
             <Glyph value="g20"/>
           </Coverage>
           <PosLookupRecord index="0">
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index c559a7d..7804fb8 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_simple_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -102,13 +102,13 @@
         <ContextPos index="0" Format="3">
           <!-- GlyphCount=3 -->
           <!-- PosCount=3 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
-          <Coverage index="1" Format="1">
+          <Coverage index="1">
             <Glyph value="g21"/>
           </Coverage>
-          <Coverage index="2" Format="1">
+          <Coverage index="2">
             <Glyph value="g22"/>
           </Coverage>
           <PosLookupRecord index="0">
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 8ff7eea..17b5ed2 100644
--- a/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.ttx.GPOS
+++ b/Tests/ttLib/tables/data/aots/gpos_context3_successive_f1.ttx.GPOS
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -55,7 +55,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -72,10 +72,10 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <ValueFormat1 value="1"/>
@@ -102,16 +102,16 @@
         <ContextPos index="0" Format="3">
           <!-- GlyphCount=4 -->
           <!-- PosCount=2 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
-          <Coverage index="1" Format="1">
+          <Coverage index="1">
             <Glyph value="g21"/>
           </Coverage>
-          <Coverage index="2" Format="1">
+          <Coverage index="2">
             <Glyph value="g22"/>
           </Coverage>
-          <Coverage index="3" Format="1">
+          <Coverage index="3">
             <Glyph value="g23"/>
           </Coverage>
           <PosLookupRecord index="0">
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
index 971a3f1..08e65de 100644
--- a/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g18" class="1"/>
     </GlyphClassDef>
   </GDEF>
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
index ba7b68f..d5e0bf7 100644
--- a/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub1_1_lookupflag_f1.ttx.GSUB
@@ -31,9 +31,9 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="1"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g18" out="g23"/>
           <Substitution in="g19" out="g24"/>
         </SingleSubst>
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
index b9923da..f145dd3 100644
--- a/Tests/ttLib/tables/data/aots/gsub1_1_modulo_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub1_1_modulo_f1.ttx.GSUB
@@ -34,11 +34,11 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=2 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g21" out="glyph32787"/>
           <Substitution in="g22" out="glyph32788"/>
         </SingleSubst>
-        <SingleSubst index="1" Format="1">
+        <SingleSubst index="1">
           <Substitution in="g19" out="glyph32789"/>
           <Substitution in="g20" out="glyph32790"/>
         </SingleSubst>
@@ -47,16 +47,16 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=4 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="glyph32787" out="g23"/>
         </SingleSubst>
-        <SingleSubst index="1" Format="1">
+        <SingleSubst index="1">
           <Substitution in="glyph32788" out="g18"/>
         </SingleSubst>
-        <SingleSubst index="2" Format="1">
+        <SingleSubst index="2">
           <Substitution in="glyph32789" out="g17"/>
         </SingleSubst>
-        <SingleSubst index="3" Format="1">
+        <SingleSubst index="3">
           <Substitution in="glyph32790" out="g24"/>
         </SingleSubst>
       </Lookup>
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
index e76ba74..7de19b0 100644
--- a/Tests/ttLib/tables/data/aots/gsub1_1_simple_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub1_1_simple_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g18" out="g23"/>
           <Substitution in="g19" out="g24"/>
         </SingleSubst>
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
index 971a3f1..08e65de 100644
--- a/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g18" class="1"/>
     </GlyphClassDef>
   </GDEF>
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
index 51bfbb1..a518afb 100644
--- a/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub1_2_lookupflag_f1.ttx.GSUB
@@ -31,9 +31,9 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="1"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="2">
+        <SingleSubst index="0">
           <Substitution in="g18" out="g22"/>
           <Substitution in="g20" out="g25"/>
         </SingleSubst>
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
index 55649d2..392ff3c 100644
--- a/Tests/ttLib/tables/data/aots/gsub1_2_simple_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub1_2_simple_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="2">
+        <SingleSubst index="0">
           <Substitution in="g18" out="g22"/>
           <Substitution in="g20" out="g25"/>
         </SingleSubst>
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
index 971a3f1..08e65de 100644
--- a/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g18" class="1"/>
     </GlyphClassDef>
   </GDEF>
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
index 2c59793..d3c37dc 100644
--- a/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub2_1_lookupflag_f1.ttx.GSUB
@@ -31,9 +31,9 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g18" out="g20,g21"/>
           <Substitution in="g19" out="g22,g23"/>
         </MultipleSubst>
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
index 5ec800e..1c4cd4c 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g18" out="g20,g21"/>
           <Substitution in="g19" out="g22,g23"/>
         </MultipleSubst>
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
index cae4f66..b2453ec 100644
--- a/Tests/ttLib/tables/data/aots/gsub2_1_simple_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub2_1_simple_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g18" out="g20,g21,g22"/>
         </MultipleSubst>
       </Lookup>
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
index 971a3f1..08e65de 100644
--- a/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g18" class="1"/>
     </GlyphClassDef>
   </GDEF>
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
index 63c53b4..531c608 100644
--- a/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub3_1_lookupflag_f1.ttx.GSUB
@@ -31,9 +31,9 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="3"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
-        <AlternateSubst index="0" Format="1">
+        <AlternateSubst index="0">
           <AlternateSet glyph="g18">
             <Alternate glyph="g20"/>
             <Alternate glyph="g21"/>
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
index 1eae117..54a5219 100644
--- a/Tests/ttLib/tables/data/aots/gsub3_1_multiple_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub3_1_multiple_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="3"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <AlternateSubst index="0" Format="1">
+        <AlternateSubst index="0">
           <AlternateSet glyph="g18">
             <Alternate glyph="g20"/>
             <Alternate glyph="g21"/>
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
index 7372cd0..9a516b2 100644
--- a/Tests/ttLib/tables/data/aots/gsub3_1_simple_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub3_1_simple_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="3"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <AlternateSubst index="0" Format="1">
+        <AlternateSubst index="0">
           <AlternateSet glyph="g18">
             <Alternate glyph="g20"/>
             <Alternate glyph="g21"/>
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
index ffcc7a1..269092d 100644
--- a/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g24" class="1"/>
     </GlyphClassDef>
   </GDEF>
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
index 0982ef6..7dc3472 100644
--- a/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_lookupflag_f1.ttx.GSUB
@@ -31,9 +31,9 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="4"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g18">
             <Ligature components="g19,g20" glyph="g23"/>
           </LigatureSet>
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
index 9466f91..e9eb9ea 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g18">
             <Ligature components="g19,g20" glyph="g23"/>
             <Ligature components="g19" glyph="g24"/>
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
index bc25e84..6756dfd 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g18">
             <Ligature components="g19" glyph="g24"/>
             <Ligature components="g19,g20" glyph="g23"/>
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
index 0ea6495..ab04368 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g18">
             <Ligature components="g19" glyph="g23"/>
           </LigatureSet>
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
index ecc8fa6..7f3012a 100644
--- a/Tests/ttLib/tables/data/aots/gsub4_1_simple_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub4_1_simple_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g18">
             <Ligature components="g19,g20" glyph="g23"/>
           </LigatureSet>
diff --git a/Tests/ttLib/tables/data/aots/gsub7_font1.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub7_font1.ttx.GSUB
index 3f65d93..cf43ec6 100644
--- a/Tests/ttLib/tables/data/aots/gsub7_font1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub7_font1.ttx.GSUB
@@ -35,7 +35,7 @@
         <!-- SubTableCount=1 -->
         <ExtensionSubst index="0" Format="1">
           <ExtensionLookupType value="1"/>
-          <SingleSubst Format="1">
+          <SingleSubst>
             <Substitution in="g18" out="g23"/>
             <Substitution in="g19" out="g24"/>
           </SingleSubst>
diff --git a/Tests/ttLib/tables/data/aots/gsub7_font2.ttx.GSUB b/Tests/ttLib/tables/data/aots/gsub7_font2.ttx.GSUB
index 98338ed..1f488f3 100644
--- a/Tests/ttLib/tables/data/aots/gsub7_font2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub7_font2.ttx.GSUB
@@ -35,13 +35,13 @@
         <!-- SubTableCount=2 -->
         <ExtensionSubst index="0" Format="1">
           <ExtensionLookupType value="1"/>
-          <SingleSubst Format="1">
+          <SingleSubst>
             <Substitution in="g18" out="g23"/>
           </SingleSubst>
         </ExtensionSubst>
         <ExtensionSubst index="1" Format="1">
           <ExtensionLookupType value="1"/>
-          <SingleSubst Format="1">
+          <SingleSubst>
             <Substitution in="g19" out="g29"/>
           </SingleSubst>
         </ExtensionSubst>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 49ed83d..897946e 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <!-- ChainSubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 7790bf9..896736c 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <!-- ChainSubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index c58e3d5..43a5914 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f3.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <!-- ChainSubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 43a1181..d649b63 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_boundary_f4.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g22"/>
           </Coverage>
           <!-- ChainSubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index e3bdf7f..dd83bd7 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_lookupflag_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,16 +70,16 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
       <Lookup index="4">
         <LookupType value="6"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g22"/>
           </Coverage>
           <!-- ChainSubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index a8ceb2b..b446f62 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <!-- ChainSubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index f73b4ec..9b6b835 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <!-- ChainSubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 3858160..8b554ab 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
             <Glyph value="g23"/>
           </Coverage>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index d7dbd6b..f7dbe58 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g21"/>
           </Coverage>
           <!-- ChainSubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index e97a6b1..a8894e5 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_simple_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g22"/>
           </Coverage>
           <!-- ChainSubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 146dd2a..52cf963 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining1_successive_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- ChainSubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index ecd0a6b..9109e59 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -88,7 +88,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -97,7 +97,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -106,7 +106,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 076cb30..ead59dc 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -88,7 +88,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -97,7 +97,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -106,7 +106,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index c6fc1dc..8163db1 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f3.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -88,7 +88,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -97,7 +97,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -106,7 +106,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 4221c6c..48f9483 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_boundary_f4.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -88,7 +88,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -97,7 +97,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -106,7 +106,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index eaccba0..a7f4ce6 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_lookupflag_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,16 +70,16 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
       <Lookup index="4">
         <LookupType value="6"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -88,7 +88,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -97,7 +97,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -106,7 +106,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index c0447c2..34cea46 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -88,7 +88,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -97,7 +97,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -106,7 +106,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 6067dfd..21cbe2a 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -88,7 +88,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -97,7 +97,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -106,7 +106,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index f3dcb64..d14ac1b 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -88,7 +88,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -97,7 +97,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -106,7 +106,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 7997e3a..da6a915 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -88,7 +88,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -97,7 +97,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -106,7 +106,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index bd3645a..94f6217 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_simple_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -88,7 +88,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -97,7 +97,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -106,7 +106,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index fd6ead4..7dbc5e0 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining2_successive_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
@@ -88,7 +88,7 @@
             <Glyph value="g25"/>
             <Glyph value="g26"/>
           </Coverage>
-          <BacktrackClassDef Format="2">
+          <BacktrackClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -97,7 +97,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </BacktrackClassDef>
-          <InputClassDef Format="2">
+          <InputClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
@@ -106,7 +106,7 @@
             <ClassDef glyph="g25" class="25"/>
             <ClassDef glyph="g26" class="26"/>
           </InputClassDef>
-          <LookAheadClassDef Format="2">
+          <LookAheadClassDef>
             <ClassDef glyph="g20" class="20"/>
             <ClassDef glyph="g21" class="21"/>
             <ClassDef glyph="g22" class="22"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 579d6ce..82eb133 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -80,18 +80,18 @@
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=1 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g20"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=2 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g21"/>
           </InputCoverage>
-          <InputCoverage index="1" Format="1">
+          <InputCoverage index="1">
             <Glyph value="g22"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=1 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g23"/>
           </LookAheadCoverage>
           <!-- SubstCount=0 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 5098ceb..b5c1636 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -80,18 +80,18 @@
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=1 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g20"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=1 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g21"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=2 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g22"/>
           </LookAheadCoverage>
-          <LookAheadCoverage index="1" Format="1">
+          <LookAheadCoverage index="1">
             <Glyph value="g23"/>
           </LookAheadCoverage>
           <!-- SubstCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 3d6b966..76d5a46 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f3.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -81,14 +81,14 @@
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=0 -->
           <!-- InputGlyphCount=1 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g21"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=2 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g22"/>
           </LookAheadCoverage>
-          <LookAheadCoverage index="1" Format="1">
+          <LookAheadCoverage index="1">
             <Glyph value="g23"/>
           </LookAheadCoverage>
           <!-- SubstCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 9cf6009..9346d47 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_boundary_f4.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -80,14 +80,14 @@
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=2 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g21"/>
           </BacktrackCoverage>
-          <BacktrackCoverage index="1" Format="1">
+          <BacktrackCoverage index="1">
             <Glyph value="g20"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=1 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g22"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=0 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 9cc951e..c329936 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_lookupflag_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,37 +70,37 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
       <Lookup index="4">
         <LookupType value="6"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=2 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g21"/>
           </BacktrackCoverage>
-          <BacktrackCoverage index="1" Format="1">
+          <BacktrackCoverage index="1">
             <Glyph value="g20"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=3 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g22"/>
           </InputCoverage>
-          <InputCoverage index="1" Format="1">
+          <InputCoverage index="1">
             <Glyph value="g23"/>
           </InputCoverage>
-          <InputCoverage index="2" Format="1">
+          <InputCoverage index="2">
             <Glyph value="g24"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=2 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g25"/>
           </LookAheadCoverage>
-          <LookAheadCoverage index="1" Format="1">
+          <LookAheadCoverage index="1">
             <Glyph value="g26"/>
           </LookAheadCoverage>
           <!-- SubstCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 00ff357..46d1f85 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -80,18 +80,18 @@
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=1 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g22"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=2 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g21"/>
           </InputCoverage>
-          <InputCoverage index="1" Format="1">
+          <InputCoverage index="1">
             <Glyph value="g22"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=1 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g21"/>
           </LookAheadCoverage>
           <!-- SubstCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 6cbf5f9..722dcad 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -80,18 +80,18 @@
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=1 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g20"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=2 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g21"/>
           </InputCoverage>
-          <InputCoverage index="1" Format="1">
+          <InputCoverage index="1">
             <Glyph value="g22"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=1 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g23"/>
           </LookAheadCoverage>
           <!-- SubstCount=2 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 65fea5d..5842c55 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_simple_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -80,27 +80,27 @@
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=2 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g21"/>
           </BacktrackCoverage>
-          <BacktrackCoverage index="1" Format="1">
+          <BacktrackCoverage index="1">
             <Glyph value="g20"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=3 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g22"/>
           </InputCoverage>
-          <InputCoverage index="1" Format="1">
+          <InputCoverage index="1">
             <Glyph value="g23"/>
           </InputCoverage>
-          <InputCoverage index="2" Format="1">
+          <InputCoverage index="2">
             <Glyph value="g24"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=2 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g25"/>
           </LookAheadCoverage>
-          <LookAheadCoverage index="1" Format="1">
+          <LookAheadCoverage index="1">
             <Glyph value="g26"/>
           </LookAheadCoverage>
           <!-- SubstCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 6e8918f..610ea56 100644
--- a/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_chaining3_successive_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -80,24 +80,24 @@
         <!-- SubTableCount=1 -->
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=1 -->
-          <BacktrackCoverage index="0" Format="1">
+          <BacktrackCoverage index="0">
             <Glyph value="g25"/>
           </BacktrackCoverage>
           <!-- InputGlyphCount=4 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="g20"/>
           </InputCoverage>
-          <InputCoverage index="1" Format="1">
+          <InputCoverage index="1">
             <Glyph value="g21"/>
           </InputCoverage>
-          <InputCoverage index="2" Format="1">
+          <InputCoverage index="2">
             <Glyph value="g22"/>
           </InputCoverage>
-          <InputCoverage index="3" Format="1">
+          <InputCoverage index="3">
             <Glyph value="g23"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=1 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="g24"/>
           </LookAheadCoverage>
           <!-- SubstCount=2 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 5ce2dbc..6de6506 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- SubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index f4df5df..7d5772c 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_boundary_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- SubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index e03d5c3..e8dca8a 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_expansion_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- SubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 7f7dbae..579294f 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,16 +70,16 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
       <Lookup index="4">
         <LookupType value="5"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- SubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 921576c..e583f6a 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_lookupflag_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,16 +70,16 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
       <Lookup index="4">
         <LookupType value="5"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- SubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 3bcb882..2202320 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- SubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 04e9696..df568c3 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- SubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 1276fb9..518e754 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- SubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 4e135b5..2906b06 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- SubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 787c216..554168b 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_simple_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- SubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index e0b1c54..fc66abd 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context1_successive_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,7 +79,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
           <!-- SubRuleSetCount=1 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 705389b..e1e12f8 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,10 +79,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
           </ClassDef>
           <!-- SubClassSetCount=2 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 3a35d50..d2598d9 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_boundary_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,10 +79,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
           </ClassDef>
           <!-- SubClassSetCount=2 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 6716203..394df78 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,11 +79,11 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="1"/>
             <ClassDef glyph="g22" class="1"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index deeea52..4de5bc6 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_classes_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,13 +79,13 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
             <Glyph value="g21"/>
             <Glyph value="g22"/>
             <Glyph value="g24"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="1"/>
             <ClassDef glyph="g22" class="1"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 3952840..5ab7d08 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_expansion_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,10 +79,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 80a6237..4a8d749 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,19 +70,19 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
       <Lookup index="4">
         <LookupType value="5"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 715a1cd..a672edf 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_lookupflag_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,19 +70,19 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
       <Lookup index="4">
         <LookupType value="5"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 827db06..07b5f5e 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,10 +79,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 83d6af5..2252761 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,10 +79,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 09e9dc9..b78220b 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,10 +79,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
           </ClassDef>
           <!-- SubClassSetCount=2 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 6bc8a46..35fff94 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,10 +79,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index f65bf4d..28ab11e 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_simple_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,10 +79,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
           </ClassDef>
           <!-- SubClassSetCount=2 -->
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 3c7e0f9..a364207 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context2_successive_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -79,10 +79,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="g20"/>
           </Coverage>
-          <ClassDef Format="2">
+          <ClassDef>
             <ClassDef glyph="g20" class="1"/>
             <ClassDef glyph="g21" class="2"/>
             <ClassDef glyph="g22" class="3"/>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 843e88d..9a73fac 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -81,10 +81,10 @@
         <ContextSubst index="0" Format="3">
           <!-- GlyphCount=2 -->
           <!-- SubstCount=0 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
-          <Coverage index="1" Format="1">
+          <Coverage index="1">
             <Glyph value="g20"/>
           </Coverage>
         </ContextSubst>
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index cba8c31..d45e67b 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_boundary_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -81,7 +81,7 @@
         <ContextSubst index="0" Format="3">
           <!-- GlyphCount=1 -->
           <!-- SubstCount=1 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
           <SubstLookupRecord index="0">
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index d485a04..90b53f4 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,24 +70,24 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
       <Lookup index="4">
         <LookupType value="5"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="3">
           <!-- GlyphCount=3 -->
           <!-- SubstCount=3 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
-          <Coverage index="1" Format="1">
+          <Coverage index="1">
             <Glyph value="g21"/>
           </Coverage>
-          <Coverage index="2" Format="1">
+          <Coverage index="2">
             <Glyph value="g22"/>
           </Coverage>
           <SubstLookupRecord index="0">
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 016350f..a82c3c0 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_lookupflag_f2.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,24 +70,24 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
       <Lookup index="4">
         <LookupType value="5"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <ContextSubst index="0" Format="3">
           <!-- GlyphCount=3 -->
           <!-- SubstCount=1 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
-          <Coverage index="1" Format="1">
+          <Coverage index="1">
             <Glyph value="g21"/>
           </Coverage>
-          <Coverage index="2" Format="1">
+          <Coverage index="2">
             <Glyph value="g22"/>
           </Coverage>
           <SubstLookupRecord index="0">
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
index dba5550..b5c2ac3 100644
--- 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
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 10fcba5..a4f5add 100644
--- 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
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -81,10 +81,10 @@
         <ContextSubst index="0" Format="3">
           <!-- GlyphCount=2 -->
           <!-- SubstCount=1 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
-          <Coverage index="1" Format="1">
+          <Coverage index="1">
             <Glyph value="g20"/>
           </Coverage>
           <SubstLookupRecord index="0">
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 93f4bdd..15d6c08 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_simple_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -81,13 +81,13 @@
         <ContextSubst index="0" Format="3">
           <!-- GlyphCount=3 -->
           <!-- SubstCount=3 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
-          <Coverage index="1" Format="1">
+          <Coverage index="1">
             <Glyph value="g21"/>
           </Coverage>
-          <Coverage index="2" Format="1">
+          <Coverage index="2">
             <Glyph value="g22"/>
           </Coverage>
           <SubstLookupRecord index="0">
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
index dba5550..b5c2ac3 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g80" class="1"/>
       <ClassDef glyph="g81" class="1"/>
       <ClassDef glyph="g82" class="1"/>
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
index 73e3567..6558c69 100644
--- a/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/gsub_context3_successive_f1.ttx.GSUB
@@ -33,7 +33,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="g20" out="g60"/>
           <Substitution in="g21" out="g61"/>
           <Substitution in="g22" out="g62"/>
@@ -50,7 +50,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -58,9 +58,9 @@
       </Lookup>
       <Lookup index="2">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g21">
             <Ligature components="g22" glyph="g61"/>
           </LigatureSet>
@@ -70,7 +70,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="g21" out="g61,g62,g63"/>
         </MultipleSubst>
       </Lookup>
@@ -81,16 +81,16 @@
         <ContextSubst index="0" Format="3">
           <!-- GlyphCount=4 -->
           <!-- SubstCount=2 -->
-          <Coverage index="0" Format="1">
+          <Coverage index="0">
             <Glyph value="g20"/>
           </Coverage>
-          <Coverage index="1" Format="1">
+          <Coverage index="1">
             <Glyph value="g21"/>
           </Coverage>
-          <Coverage index="2" Format="1">
+          <Coverage index="2">
             <Glyph value="g22"/>
           </Coverage>
-          <Coverage index="3" Format="1">
+          <Coverage index="3">
             <Glyph value="g23"/>
           </Coverage>
           <SubstLookupRecord index="0">
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
index 802351a..736e2d8 100644
--- a/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g10" class="1"/>
       <ClassDef glyph="g11" class="1"/>
       <ClassDef glyph="g12" class="1"/>
@@ -21,7 +21,7 @@
       <ClassDef glyph="g28" class="3"/>
       <ClassDef glyph="g29" class="3"/>
     </GlyphClassDef>
-    <MarkAttachClassDef Format="2">
+    <MarkAttachClassDef>
       <ClassDef glyph="g20" class="1"/>
       <ClassDef glyph="g21" class="1"/>
       <ClassDef glyph="g22" class="1"/>
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
index f8064cb..42876ea 100644
--- a/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_attach_f1.ttx.GSUB
@@ -31,9 +31,9 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="4"/>
-        <LookupFlag value="512"/>
+        <LookupFlag value="512"/><!-- markAttachmentType[2] -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g11">
             <Ligature components="g13,g26" glyph="g15"/>
           </LigatureSet>
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
index 2b0a686..c6abc87 100644
--- a/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g24" class="1"/>
       <ClassDef glyph="g25" class="1"/>
     </GlyphClassDef>
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
index 0982ef6..7dc3472 100644
--- a/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_base_f1.ttx.GSUB
@@ -31,9 +31,9 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="4"/>
-        <LookupFlag value="2"/>
+        <LookupFlag value="2"/><!-- ignoreBaseGlyphs -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g18">
             <Ligature components="g19,g20" glyph="g23"/>
           </LigatureSet>
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
index b67d75e..e81490b 100644
--- a/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g24" class="1"/>
       <ClassDef glyph="g25" class="1"/>
       <ClassDef glyph="g26" class="1"/>
@@ -14,7 +14,7 @@
       <ClassDef glyph="g31" class="3"/>
       <ClassDef glyph="g32" class="3"/>
     </GlyphClassDef>
-    <MarkAttachClassDef Format="2">
+    <MarkAttachClassDef>
       <ClassDef glyph="g25" class="1"/>
       <ClassDef glyph="g26" class="2"/>
       <ClassDef glyph="g28" class="1"/>
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
index 95047b3..ffdc2ae 100644
--- a/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_combination_f1.ttx.GSUB
@@ -31,9 +31,9 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="4"/>
-        <LookupFlag value="514"/>
+        <LookupFlag value="514"/><!-- ignoreBaseGlyphs markAttachmentType[2] -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g18">
             <Ligature components="g19,g20" glyph="g23"/>
           </LigatureSet>
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
index 19aa7aa..b914431 100644
--- a/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g24" class="1"/>
       <ClassDef glyph="g25" class="1"/>
       <ClassDef glyph="g26" class="2"/>
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
index 3bc1e8d..db7fcc0 100644
--- a/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_ligatures_f1.ttx.GSUB
@@ -31,9 +31,9 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="4"/>
-        <LookupFlag value="4"/>
+        <LookupFlag value="4"/><!-- ignoreLigatures -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g18">
             <Ligature components="g19,g20" glyph="g23"/>
           </LigatureSet>
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
index 19aa7aa..b914431 100644
--- a/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.ttx.GDEF
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.ttx.GDEF
@@ -3,7 +3,7 @@
 
   <GDEF>
     <Version value="0x00010000"/>
-    <GlyphClassDef Format="2">
+    <GlyphClassDef>
       <ClassDef glyph="g24" class="1"/>
       <ClassDef glyph="g25" class="1"/>
       <ClassDef glyph="g26" class="2"/>
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
index 28b4682..b3fd500 100644
--- a/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.ttx.GSUB
+++ b/Tests/ttLib/tables/data/aots/lookupflag_ignore_marks_f1.ttx.GSUB
@@ -31,9 +31,9 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="4"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="g18">
             <Ligature components="g19,g20" glyph="g23"/>
           </LigatureSet>
diff --git a/Tests/ttLib/tables/otBase_test.py b/Tests/ttLib/tables/otBase_test.py
index f1fa5b0..ce0416e 100644
--- a/Tests/ttLib/tables/otBase_test.py
+++ b/Tests/ttLib/tables/otBase_test.py
@@ -1,5 +1,3 @@
-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
diff --git a/Tests/ttLib/tables/otConverters_test.py b/Tests/ttLib/tables/otConverters_test.py
index 7f76d71..1aff03b 100644
--- a/Tests/ttLib/tables/otConverters_test.py
+++ b/Tests/ttLib/tables/otConverters_test.py
@@ -1,7 +1,3 @@
-# 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
@@ -114,6 +110,7 @@
     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):
@@ -148,6 +145,14 @@
             '<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([])
diff --git a/Tests/ttLib/tables/otTables_test.py b/Tests/ttLib/tables/otTables_test.py
index e02b22f..b169776 100644
--- a/Tests/ttLib/tables/otTables_test.py
+++ b/Tests/ttLib/tables/otTables_test.py
@@ -1,11 +1,9 @@
-# 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
+from io import StringIO
 import unittest
 
 
@@ -166,11 +164,11 @@
                          {'c_t': ['c', 't'], 'f_f_i': ['f', 'f', 'i']})
 
     def test_fromXML_oldFormat_bug385(self):
-        # https://github.com/behdad/fonttools/issues/385
+        # https://github.com/fonttools/fonttools/issues/385
         table = otTables.MultipleSubst()
         table.Format = 1
         for name, attrs, content in parseXML(
-                '<Coverage Format="1">'
+                '<Coverage>'
                 '  <Glyph value="o"/>'
                 '  <Glyph value="l"/>'
                 '</Coverage>'
@@ -383,6 +381,10 @@
         r.compile(writer, self.font, actionIndex=None)
         self.assertEqual(hexStr(writer.getAllData()), "1234fffd")
 
+    def testCompileActions(self):
+        act = otTables.RearrangementMorphAction()
+        self.assertEqual(act.compileActions(self.font, []), (None, None))
+
     def testDecompileToXML(self):
         r = otTables.RearrangementMorphAction()
         r.decompile(OTTableReader(deHexStr("1234fffd")),
@@ -411,6 +413,10 @@
         a.compile(writer, self.font, actionIndex=None)
         self.assertEqual(hexStr(writer.getAllData()), "1234f117deadbeef")
 
+    def testCompileActions(self):
+        act = otTables.ContextualMorphAction()
+        self.assertEqual(act.compileActions(self.font, []), (None, None))
+
     def testDecompileToXML(self):
         a = otTables.ContextualMorphAction()
         a.decompile(OTTableReader(deHexStr("1234f117deadbeef")),
@@ -447,6 +453,32 @@
                 '</Transition>',
         ])
 
+    def testCompileActions_empty(self):
+        act = otTables.LigatureMorphAction()
+        actions, actionIndex = act.compileActions(self.font, [])
+        self.assertEqual(actions, b'')
+        self.assertEqual(actionIndex, {})
+
+    def testCompileActions_shouldShareSubsequences(self):
+        state = otTables.AATState()
+        t = state.Transitions = {i: otTables.LigatureMorphAction()
+                                 for i in range(3)}
+        ligs = [otTables.LigAction() for _ in range(3)]
+        for i, lig in enumerate(ligs):
+            lig.GlyphIndexDelta = i
+        t[0].Actions = ligs[1:2]
+        t[1].Actions = ligs[0:3]
+        t[2].Actions = ligs[1:3]
+        actions, actionIndex = t[0].compileActions(self.font, [state])
+        self.assertEqual(actions,
+                         deHexStr("00000000 00000001 80000002 80000001"))
+        self.assertEqual(actionIndex, {
+            deHexStr("00000000 00000001 80000002"): 0,
+            deHexStr("00000001 80000002"): 1,
+            deHexStr("80000002"): 2,
+            deHexStr("80000001"): 3,
+        })
+
 
 class InsertionMorphActionTest(unittest.TestCase):
     MORPH_ACTION_XML = [
@@ -480,10 +512,180 @@
         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})
+        a.compile(
+            writer,
+            self.font,
+            actionIndex={('B', 'C'): 9, ('B', 'A', 'D'): 7},
+        )
         self.assertEqual(hexStr(writer.getAllData()), "1234fc4300090007")
 
+    def testCompileActions_empty(self):
+        act = otTables.InsertionMorphAction()
+        actions, actionIndex = act.compileActions(self.font, [])
+        self.assertEqual(actions, b'')
+        self.assertEqual(actionIndex, {})
+
+    def testCompileActions_shouldShareSubsequences(self):
+        state = otTables.AATState()
+        t = state.Transitions = {i: otTables.InsertionMorphAction()
+                                 for i in range(3)}
+        t[1].CurrentInsertionAction = []
+        t[0].MarkedInsertionAction = ['A']
+        t[1].CurrentInsertionAction = ['C', 'D']
+        t[1].MarkedInsertionAction = ['B']
+        t[2].CurrentInsertionAction = ['B', 'C', 'D']
+        t[2].MarkedInsertionAction = ['C', 'D']
+        actions, actionIndex = t[0].compileActions(self.font, [state])
+        self.assertEqual(actions, deHexStr('0002 0003 0004 0001'))
+        self.assertEqual(actionIndex, {
+            ('A',): 3,
+            ('B',): 0,
+            ('B', 'C'): 0,
+            ('B', 'C', 'D'): 0,
+            ('C',): 1,
+            ('C', 'D'): 1,
+            ('D',): 2,
+        })
+
+
+class SplitMultipleSubstTest:
+    def overflow(self, itemName, itemRecord):
+        from fontTools.otlLib.builder import buildMultipleSubstSubtable
+        from fontTools.ttLib.tables.otBase import OverflowErrorRecord
+
+        oldSubTable = buildMultipleSubstSubtable({'e': 1, 'a': 2, 'b': 3, 'c': 4, 'd': 5})
+        newSubTable = otTables.MultipleSubst()
+
+        ok = otTables.splitMultipleSubst(oldSubTable, newSubTable, OverflowErrorRecord((None, None, None, itemName, itemRecord)))
+
+        assert ok
+        return oldSubTable.mapping, newSubTable.mapping
+
+    def test_Coverage(self):
+        oldMapping, newMapping = self.overflow('Coverage', None)
+        assert oldMapping == {'a': 2, 'b': 3}
+        assert newMapping == {'c': 4, 'd': 5, 'e': 1}
+
+    def test_RangeRecord(self):
+        oldMapping, newMapping = self.overflow('RangeRecord', None)
+        assert oldMapping == {'a': 2, 'b': 3}
+        assert newMapping == {'c': 4, 'd': 5, 'e': 1}
+
+    def test_Sequence(self):
+        oldMapping, newMapping = self.overflow('Sequence', 4)
+        assert oldMapping == {'a': 2, 'b': 3,'c': 4}
+        assert newMapping == {'d': 5, 'e': 1}
+
+
+def test_splitMarkBasePos():
+    from fontTools.otlLib.builder import buildAnchor, buildMarkBasePosSubtable
+
+    marks = {
+        "acutecomb": (0, buildAnchor(0, 600)),
+        "gravecomb": (0, buildAnchor(0, 590)),
+        "cedillacomb": (1, buildAnchor(0, 0)),
+    }
+    bases = {
+        "a": {
+            0: buildAnchor(350, 500),
+            1: None,
+        },
+        "c": {
+            0: buildAnchor(300, 700),
+            1: buildAnchor(300, 0),
+        },
+    }
+    glyphOrder = ["a", "c", "acutecomb", "gravecomb", "cedillacomb"]
+    glyphMap = {g: i for i, g in enumerate(glyphOrder)}
+
+    oldSubTable = buildMarkBasePosSubtable(marks, bases, glyphMap)
+    newSubTable = otTables.MarkBasePos()
+
+    ok = otTables.splitMarkBasePos(oldSubTable, newSubTable, overflowRecord=None)
+
+    assert ok
+
+    assert getXML(oldSubTable.toXML) == [
+        '<MarkBasePos Format="1">',
+        '  <MarkCoverage>',
+        '    <Glyph value="acutecomb"/>',
+        '    <Glyph value="gravecomb"/>',
+        '  </MarkCoverage>',
+        '  <BaseCoverage>',
+        '    <Glyph value="a"/>',
+        '    <Glyph value="c"/>',
+        '  </BaseCoverage>',
+        '  <!-- ClassCount=1 -->',
+        '  <MarkArray>',
+        '    <!-- MarkCount=2 -->',
+        '    <MarkRecord index="0">',
+        '      <Class value="0"/>',
+        '      <MarkAnchor Format="1">',
+        '        <XCoordinate value="0"/>',
+        '        <YCoordinate value="600"/>',
+        '      </MarkAnchor>',
+        '    </MarkRecord>',
+        '    <MarkRecord index="1">',
+        '      <Class value="0"/>',
+        '      <MarkAnchor Format="1">',
+        '        <XCoordinate value="0"/>',
+        '        <YCoordinate value="590"/>',
+        '      </MarkAnchor>',
+        '    </MarkRecord>',
+        '  </MarkArray>',
+        '  <BaseArray>',
+        '    <!-- BaseCount=2 -->',
+        '    <BaseRecord index="0">',
+        '      <BaseAnchor index="0" Format="1">',
+        '        <XCoordinate value="350"/>',
+        '        <YCoordinate value="500"/>',
+        '      </BaseAnchor>',
+        '    </BaseRecord>',
+        '    <BaseRecord index="1">',
+        '      <BaseAnchor index="0" Format="1">',
+        '        <XCoordinate value="300"/>',
+        '        <YCoordinate value="700"/>',
+        '      </BaseAnchor>',
+        '    </BaseRecord>',
+        '  </BaseArray>',
+        '</MarkBasePos>',
+    ]
+
+    assert getXML(newSubTable.toXML) == [
+        '<MarkBasePos Format="1">',
+        '  <MarkCoverage>',
+        '    <Glyph value="cedillacomb"/>',
+        '  </MarkCoverage>',
+        '  <BaseCoverage>',
+        '    <Glyph value="a"/>',
+        '    <Glyph value="c"/>',
+        '  </BaseCoverage>',
+        '  <!-- ClassCount=1 -->',
+        '  <MarkArray>',
+        '    <!-- MarkCount=1 -->',
+        '    <MarkRecord index="0">',
+        '      <Class value="0"/>',
+        '      <MarkAnchor Format="1">',
+        '        <XCoordinate value="0"/>',
+        '        <YCoordinate value="0"/>',
+        '      </MarkAnchor>',
+        '    </MarkRecord>',
+        '  </MarkArray>',
+        '  <BaseArray>',
+        '    <!-- BaseCount=2 -->',
+        '    <BaseRecord index="0">',
+        '      <BaseAnchor index="0" empty="1"/>',
+        '    </BaseRecord>',
+        '    <BaseRecord index="1">',
+        '      <BaseAnchor index="0" Format="1">',
+        '        <XCoordinate value="300"/>',
+        '        <YCoordinate value="0"/>',
+        '      </BaseAnchor>',
+        '    </BaseRecord>',
+        '  </BaseArray>',
+        '</MarkBasePos>',
+    ]
+
 
 if __name__ == "__main__":
     import sys
diff --git a/Tests/ttLib/tables/tables_test.py b/Tests/ttLib/tables/tables_test.py
index 9d03814..be8c63e 100644
--- a/Tests/ttLib/tables/tables_test.py
+++ b/Tests/ttLib/tables/tables_test.py
@@ -1,7 +1,5 @@
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
-from fontTools.misc.py23 import *
 from fontTools.ttLib import TTFont, tagToXML
+from io import StringIO
 import os
 import sys
 import re
@@ -255,13 +253,13 @@
 
 
 def dump_ttx(font, tableTag):
-    f = UnicodeIO()
-    font.saveXML(f, newlinestr='\n', tables=[tableTag])
+    f = StringIO()
+    font.saveXML(f, tables=[tableTag])
     return ttLibVersion_RE.sub('', f.getvalue())
 
 
 def load_ttx(ttx):
-    f = UnicodeIO()
+    f = StringIO()
     f.write(ttx)
     f.seek(0)
     font = TTFont()
diff --git a/Tests/ttLib/tables/ttProgram_test.py b/Tests/ttLib/tables/ttProgram_test.py
index 9a7d232..13d1ba8 100644
--- a/Tests/ttLib/tables/ttProgram_test.py
+++ b/Tests/ttLib/tables/ttProgram_test.py
@@ -1,11 +1,9 @@
-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
+from io import StringIO
 import os
-import re
 import unittest
 
 CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
@@ -105,8 +103,8 @@
         p = Program()
         p.fromBytecode(BYTECODE)
         ttfont = TestFont()
-        buf = UnicodeIO()
-        writer = XMLWriter(buf, newlinestr='\n')
+        buf = StringIO()
+        writer = XMLWriter(buf)
         try:
             p.toXML(writer, ttfont)
         finally:
diff --git a/Tests/ttLib/ttFont_test.py b/Tests/ttLib/ttFont_test.py
new file mode 100644
index 0000000..33dcc09
--- /dev/null
+++ b/Tests/ttLib/ttFont_test.py
@@ -0,0 +1,80 @@
+import io
+from fontTools.ttLib import TTFont, newTable, registerCustomTableClass, unregisterCustomTableClass
+from fontTools.ttLib.tables.DefaultTable import DefaultTable
+
+
+class CustomTableClass(DefaultTable):
+
+    def decompile(self, data, ttFont):
+        self.numbers = list(data)
+
+    def compile(self, ttFont):
+        return bytes(self.numbers)
+
+    # not testing XML read/write
+
+
+table_C_U_S_T_ = CustomTableClass  # alias for testing
+
+
+TABLETAG = "CUST"
+
+
+def test_registerCustomTableClass():
+    font = TTFont()
+    font[TABLETAG] = newTable(TABLETAG)
+    font[TABLETAG].data = b"\x00\x01\xff"
+    f = io.BytesIO()
+    font.save(f)
+    f.seek(0)
+    assert font[TABLETAG].data == b"\x00\x01\xff"
+    registerCustomTableClass(TABLETAG, "ttFont_test", "CustomTableClass")
+    try:
+        font = TTFont(f)
+        assert font[TABLETAG].numbers == [0, 1, 255]
+        assert font[TABLETAG].compile(font) == b"\x00\x01\xff"
+    finally:
+        unregisterCustomTableClass(TABLETAG)
+
+
+def test_registerCustomTableClassStandardName():
+    registerCustomTableClass(TABLETAG, "ttFont_test")
+    try:
+        font = TTFont()
+        font[TABLETAG] = newTable(TABLETAG)
+        font[TABLETAG].numbers = [4, 5, 6]
+        assert font[TABLETAG].compile(font) == b"\x04\x05\x06"
+    finally:
+        unregisterCustomTableClass(TABLETAG)
+
+
+ttxTTF = r"""<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.9.0">
+  <hmtx>
+    <mtx name=".notdef" width="300" lsb="0"/>
+  </hmtx>
+</ttFont>
+"""
+
+
+ttxOTF = """<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.9.0">
+  <hmtx>
+    <mtx name=".notdef" width="300" lsb="0"/>
+  </hmtx>
+</ttFont>
+"""
+
+
+def test_sfntVersionFromTTX():
+    # https://github.com/fonttools/fonttools/issues/2370
+    font = TTFont()
+    assert font.sfntVersion == "\x00\x01\x00\x00"
+    ttx = io.StringIO(ttxOTF)
+    # Font is "empty", TTX file will determine sfntVersion
+    font.importXML(ttx)
+    assert font.sfntVersion == "OTTO"
+    ttx = io.StringIO(ttxTTF)
+    # Font is not "empty", sfntVersion in TTX file will be ignored
+    font.importXML(ttx)
+    assert font.sfntVersion == "OTTO"
diff --git a/Tests/ttLib/woff2_test.py b/Tests/ttLib/woff2_test.py
index b3ec6b2..c0d60ce 100644
--- a/Tests/ttLib/woff2_test.py
+++ b/Tests/ttLib/woff2_test.py
@@ -1,23 +1,32 @@
-from __future__ import print_function, division, absolute_import, unicode_literals
-from fontTools.misc.py23 import *
+from fontTools.misc.py23 import Tag, bytechr, byteord
 from fontTools import ttLib
+from fontTools.ttLib import woff2
+from fontTools.ttLib.tables import _g_l_y_f
 from fontTools.ttLib.woff2 import (
 	WOFF2Reader, woff2DirectorySize, woff2DirectoryFormat,
 	woff2FlagsSize, woff2UnknownTagSize, woff2Base128MaxSize, WOFF2DirectoryEntry,
 	getKnownTagIndex, packBase128, base128Size, woff2UnknownTagIndex,
 	WOFF2FlavorData, woff2TransformedTableTags, WOFF2GlyfTable, WOFF2LocaTable,
-	WOFF2Writer, unpackBase128, unpack255UShort, pack255UShort)
+	WOFF2HmtxTable, WOFF2Writer, unpackBase128, unpack255UShort, pack255UShort)
 import unittest
 from fontTools.misc import sstruct
+from fontTools import fontBuilder
+from fontTools.pens.ttGlyphPen import TTGlyphPen
+from io import BytesIO
 import struct
 import os
 import random
 import copy
 from collections import OrderedDict
+from functools import partial
+import pytest
 
 haveBrotli = False
 try:
-	import brotli
+	try:
+		import brotlicffi as brotli
+	except ImportError:
+		import brotli
 	haveBrotli = True
 except ImportError:
 	pass
@@ -122,7 +131,7 @@
 	def test_reconstruct_unknown(self):
 		reader = WOFF2Reader(self.file)
 		with self.assertRaisesRegex(ttLib.TTLibError, 'transform for table .* unknown'):
-			reader.reconstructTable('ZZZZ')
+			reader.reconstructTable('head')
 
 
 class WOFF2ReaderTTFTest(WOFF2ReaderTest):
@@ -145,6 +154,7 @@
 	def test_reconstruct_loca(self):
 		woff2Reader = WOFF2Reader(self.file)
 		reconstructedData = woff2Reader['loca']
+		self.font.getTableData("glyf")  # 'glyf' needs to be compiled before 'loca'
 		self.assertEqual(self.font.getTableData('loca'), reconstructedData)
 		self.assertTrue(hasattr(woff2Reader.tables['glyf'], 'data'))
 
@@ -195,7 +205,7 @@
 	# drop DSIG but keep a copy
 	DSIG_copy = copy.deepcopy(font['DSIG'])
 	del font['DSIG']
-	# ovverride TTFont attributes
+	# override TTFont attributes
 	origFlavor = font.flavor
 	origRecalcBBoxes = font.recalcBBoxes
 	origRecalcTimestamp = font.recalcTimestamp
@@ -242,10 +252,6 @@
 		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
@@ -291,6 +297,35 @@
 		data = self.entry.toString()
 		self.assertEqual(len(data), expectedSize)
 
+	def test_glyf_loca_transform_flags(self):
+		for tag in ("glyf", "loca"):
+			entry = WOFF2DirectoryEntry()
+			entry.tag = Tag(tag)
+			entry.flags = getKnownTagIndex(entry.tag)
+
+			self.assertEqual(entry.transformVersion, 0)
+			self.assertTrue(entry.transformed)
+
+			entry.transformed = False
+
+			self.assertEqual(entry.transformVersion, 3)
+			self.assertEqual(entry.flags & 0b11000000, (3 << 6))
+			self.assertFalse(entry.transformed)
+
+	def test_other_transform_flags(self):
+		entry = WOFF2DirectoryEntry()
+		entry.tag = Tag('ZZZZ')
+		entry.flags = woff2UnknownTagIndex
+
+		self.assertEqual(entry.transformVersion, 0)
+		self.assertFalse(entry.transformed)
+
+		entry.transformed = True
+
+		self.assertEqual(entry.transformVersion, 1)
+		self.assertEqual(entry.flags & 0b11000000, (1 << 6))
+		self.assertTrue(entry.transformed)
+
 
 class DummyReader(WOFF2Reader):
 
@@ -299,6 +334,7 @@
 		for attr in ('majorVersion', 'minorVersion', 'metaOffset', 'metaLength',
 				'metaOrigLength', 'privLength', 'privOffset'):
 			setattr(self, attr, 0)
+		self.tables = {}
 
 
 class WOFF2FlavorDataTest(unittest.TestCase):
@@ -353,6 +389,27 @@
 		self.assertEqual(flavorData.majorVersion, 1)
 		self.assertEqual(flavorData.minorVersion, 1)
 
+	def test_mutually_exclusive_args(self):
+		msg = "arguments are mutually exclusive"
+		reader = DummyReader(self.file)
+		with self.assertRaisesRegex(TypeError, msg):
+			WOFF2FlavorData(reader, transformedTables={"hmtx"})
+		with self.assertRaisesRegex(TypeError, msg):
+			WOFF2FlavorData(reader, data=WOFF2FlavorData())
+
+	def test_transformedTables_default(self):
+		flavorData = WOFF2FlavorData()
+		self.assertEqual(flavorData.transformedTables, set(woff2TransformedTableTags))
+
+	def test_transformedTables_invalid(self):
+		msg = r"'glyf' and 'loca' must be transformed \(or not\) together"
+
+		with self.assertRaisesRegex(ValueError, msg):
+			WOFF2FlavorData(transformedTables={"glyf"})
+
+		with self.assertRaisesRegex(ValueError, msg):
+			WOFF2FlavorData(transformedTables={"loca"})
+
 
 class WOFF2WriterTest(unittest.TestCase):
 
@@ -360,7 +417,7 @@
 	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.tags = sorted(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)
@@ -511,6 +568,30 @@
 		flavorData.majorVersion, flavorData.minorVersion = (10, 11)
 		self.assertEqual((10, 11), self.writer._getVersion())
 
+	def test_hmtx_trasform(self):
+		tableTransforms = {"glyf", "loca", "hmtx"}
+
+		writer = WOFF2Writer(BytesIO(), self.numTables, self.font.sfntVersion)
+		writer.flavorData = WOFF2FlavorData(transformedTables=tableTransforms)
+
+		for tag in self.tags:
+			writer[tag] = self.font.getTableData(tag)
+		writer.close()
+
+		# enabling hmtx transform has no effect when font has no glyf table
+		self.assertEqual(writer.file.getvalue(), CFF_WOFF2.getvalue())
+
+	def test_no_transforms(self):
+		writer = WOFF2Writer(BytesIO(), self.numTables, self.font.sfntVersion)
+		writer.flavorData = WOFF2FlavorData(transformedTables=())
+
+		for tag in self.tags:
+			writer[tag] = self.font.getTableData(tag)
+		writer.close()
+
+		# transforms settings have no effect when font is CFF-flavored, since
+		# all the current transforms only apply to TrueType-flavored fonts.
+		self.assertEqual(writer.file.getvalue(), CFF_WOFF2.getvalue())
 
 class WOFF2WriterTTFTest(WOFF2WriterTest):
 
@@ -518,7 +599,7 @@
 	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.tags = sorted(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)
@@ -539,6 +620,35 @@
 		for tag in normTables:
 			self.assertEqual(self.writer.tables[tag].data, normTables[tag])
 
+	def test_hmtx_trasform(self):
+		tableTransforms = {"glyf", "loca", "hmtx"}
+
+		writer = WOFF2Writer(BytesIO(), self.numTables, self.font.sfntVersion)
+		writer.flavorData = WOFF2FlavorData(transformedTables=tableTransforms)
+
+		for tag in self.tags:
+			writer[tag] = self.font.getTableData(tag)
+		writer.close()
+
+		length = len(writer.file.getvalue())
+
+		# enabling optional hmtx transform shaves off a few bytes
+		self.assertLess(length, len(TT_WOFF2.getvalue()))
+
+	def test_no_transforms(self):
+		writer = WOFF2Writer(BytesIO(), self.numTables, self.font.sfntVersion)
+		writer.flavorData = WOFF2FlavorData(transformedTables=())
+
+		for tag in self.tags:
+			writer[tag] = self.font.getTableData(tag)
+		writer.close()
+
+		self.assertNotEqual(writer.file.getvalue(), TT_WOFF2.getvalue())
+
+		writer.file.seek(0)
+		reader = WOFF2Reader(writer.file)
+		self.assertEqual(len(reader.flavorData.transformedTables), 0)
+
 
 class WOFF2LocaTableTest(unittest.TestCase):
 
@@ -708,28 +818,6 @@
 		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)
@@ -747,6 +835,521 @@
 		self.assertEqual(normGlyfData, reconstructedData)
 
 
+@pytest.fixture(scope="module")
+def fontfile():
+
+	class Glyph(object):
+		def __init__(self, empty=False, **kwargs):
+			if not empty:
+				self.draw = partial(self.drawRect, **kwargs)
+			else:
+				self.draw = lambda pen: None
+
+		@staticmethod
+		def drawRect(pen, xMin, xMax):
+			pen.moveTo((xMin, 0))
+			pen.lineTo((xMin, 1000))
+			pen.lineTo((xMax, 1000))
+			pen.lineTo((xMax, 0))
+			pen.closePath()
+
+	class CompositeGlyph(object):
+		def __init__(self, components):
+			self.components = components
+
+		def draw(self, pen):
+			for baseGlyph, (offsetX, offsetY) in self.components:
+				pen.addComponent(baseGlyph, (1, 0, 0, 1, offsetX, offsetY))
+
+	fb = fontBuilder.FontBuilder(unitsPerEm=1000, isTTF=True)
+	fb.setupGlyphOrder(
+		[".notdef", "space", "A", "acutecomb", "Aacute", "zero", "one", "two"]
+	)
+	fb.setupCharacterMap(
+		{
+			0x20: "space",
+			0x41: "A",
+			0x0301: "acutecomb",
+			0xC1: "Aacute",
+			0x30: "zero",
+			0x31: "one",
+			0x32: "two",
+		}
+	)
+	fb.setupHorizontalMetrics(
+		{
+			".notdef": (500, 50),
+			"space": (600, 0),
+			"A": (550, 40),
+			"acutecomb": (0, -40),
+			"Aacute": (550, 40),
+			"zero": (500, 30),
+			"one": (500, 50),
+			"two": (500, 40),
+		}
+	)
+	fb.setupHorizontalHeader(ascent=1000, descent=-200)
+
+	srcGlyphs = {
+		".notdef": Glyph(xMin=50, xMax=450),
+		"space": Glyph(empty=True),
+		"A": Glyph(xMin=40, xMax=510),
+		"acutecomb": Glyph(xMin=-40, xMax=60),
+		"Aacute": CompositeGlyph([("A", (0, 0)), ("acutecomb", (200, 0))]),
+		"zero": Glyph(xMin=30, xMax=470),
+		"one": Glyph(xMin=50, xMax=450),
+		"two": Glyph(xMin=40, xMax=460),
+	}
+	pen = TTGlyphPen(srcGlyphs)
+	glyphSet = {}
+	for glyphName, glyph in srcGlyphs.items():
+		glyph.draw(pen)
+		glyphSet[glyphName] = pen.glyph()
+	fb.setupGlyf(glyphSet)
+
+	fb.setupNameTable(
+		{
+			"familyName": "TestWOFF2",
+			"styleName": "Regular",
+			"uniqueFontIdentifier": "TestWOFF2 Regular; Version 1.000; ABCD",
+			"fullName": "TestWOFF2 Regular",
+			"version": "Version 1.000",
+			"psName": "TestWOFF2-Regular",
+		}
+	)
+	fb.setupOS2()
+	fb.setupPost()
+
+	buf = BytesIO()
+	fb.save(buf)
+	buf.seek(0)
+
+	assert fb.font["maxp"].numGlyphs == 8
+	assert fb.font["hhea"].numberOfHMetrics == 6
+	for glyphName in fb.font.getGlyphOrder():
+		xMin = getattr(fb.font["glyf"][glyphName], "xMin", 0)
+		assert xMin == fb.font["hmtx"][glyphName][1]
+
+	return buf
+
+
+@pytest.fixture
+def ttFont(fontfile):
+	return ttLib.TTFont(fontfile, recalcBBoxes=False, recalcTimestamp=False)
+
+
+class WOFF2HmtxTableTest(object):
+	def test_transform_no_sidebearings(self, ttFont):
+		hmtxTable = WOFF2HmtxTable()
+		hmtxTable.metrics = ttFont["hmtx"].metrics
+
+		data = hmtxTable.transform(ttFont)
+
+		assert data == (
+			b"\x03"  # 00000011 | bits 0 and 1 are set (no sidebearings arrays)
+
+			# advanceWidthArray
+			b'\x01\xf4'  # .notdef: 500
+			b'\x02X'     # space: 600
+			b'\x02&'     # A: 550
+			b'\x00\x00'  # acutecomb: 0
+			b'\x02&'     # Aacute: 550
+			b'\x01\xf4'  # zero: 500
+		)
+
+	def test_transform_proportional_sidebearings(self, ttFont):
+		hmtxTable = WOFF2HmtxTable()
+		metrics = ttFont["hmtx"].metrics
+		# force one of the proportional glyphs to have its left sidebearing be
+		# different from its xMin (40)
+		metrics["A"] = (550, 39)
+		hmtxTable.metrics = metrics
+
+		assert ttFont["glyf"]["A"].xMin != metrics["A"][1]
+
+		data = hmtxTable.transform(ttFont)
+
+		assert data == (
+			b"\x02"  # 00000010 | bits 0 unset: explicit proportional sidebearings
+
+			# advanceWidthArray
+			b'\x01\xf4'  # .notdef: 500
+			b'\x02X'     # space: 600
+			b'\x02&'     # A: 550
+			b'\x00\x00'  # acutecomb: 0
+			b'\x02&'     # Aacute: 550
+			b'\x01\xf4'  # zero: 500
+
+			# lsbArray
+			b'\x002'     # .notdef: 50
+			b'\x00\x00'  # space: 0
+			b"\x00'"     # A: 39 (xMin: 40)
+			b'\xff\xd8'  # acutecomb: -40
+			b'\x00('     # Aacute: 40
+			b'\x00\x1e'  # zero: 30
+		)
+
+	def test_transform_monospaced_sidebearings(self, ttFont):
+		hmtxTable = WOFF2HmtxTable()
+		metrics = ttFont["hmtx"].metrics
+		hmtxTable.metrics = metrics
+
+		# force one of the monospaced glyphs at the end of hmtx table to have
+		# its xMin different from its left sidebearing (50)
+		ttFont["glyf"]["one"].xMin = metrics["one"][1] + 1
+
+		data = hmtxTable.transform(ttFont)
+
+		assert data == (
+			b"\x01"  # 00000001 | bits 1 unset: explicit monospaced sidebearings
+
+			# advanceWidthArray
+			b'\x01\xf4'  # .notdef: 500
+			b'\x02X'     # space: 600
+			b'\x02&'     # A: 550
+			b'\x00\x00'  # acutecomb: 0
+			b'\x02&'     # Aacute: 550
+			b'\x01\xf4'  # zero: 500
+
+			# leftSideBearingArray
+			b'\x002'     # one: 50 (xMin: 51)
+			b'\x00('     # two: 40
+		)
+
+	def test_transform_not_applicable(self, ttFont):
+		hmtxTable = WOFF2HmtxTable()
+		metrics = ttFont["hmtx"].metrics
+		# force both a proportional and monospaced glyph to have sidebearings
+		# different from the respective xMin coordinates
+		metrics["A"] = (550, 39)
+		metrics["one"] = (500, 51)
+		hmtxTable.metrics = metrics
+
+		# 'None' signals to fall back using untransformed hmtx table data
+		assert hmtxTable.transform(ttFont) is None
+
+	def test_reconstruct_no_sidebearings(self, ttFont):
+		hmtxTable = WOFF2HmtxTable()
+
+		data = (
+			b"\x03"  # 00000011 | bits 0 and 1 are set (no sidebearings arrays)
+
+			# advanceWidthArray
+			b'\x01\xf4'  # .notdef: 500
+			b'\x02X'     # space: 600
+			b'\x02&'     # A: 550
+			b'\x00\x00'  # acutecomb: 0
+			b'\x02&'     # Aacute: 550
+			b'\x01\xf4'  # zero: 500
+		)
+
+		hmtxTable.reconstruct(data, ttFont)
+
+		assert hmtxTable.metrics == {
+			".notdef": (500, 50),
+			"space": (600, 0),
+			"A": (550, 40),
+			"acutecomb": (0, -40),
+			"Aacute": (550, 40),
+			"zero": (500, 30),
+			"one": (500, 50),
+			"two": (500, 40),
+		}
+
+	def test_reconstruct_proportional_sidebearings(self, ttFont):
+		hmtxTable = WOFF2HmtxTable()
+
+		data = (
+			b"\x02"  # 00000010 | bits 0 unset: explicit proportional sidebearings
+
+			# advanceWidthArray
+			b'\x01\xf4'  # .notdef: 500
+			b'\x02X'     # space: 600
+			b'\x02&'     # A: 550
+			b'\x00\x00'  # acutecomb: 0
+			b'\x02&'     # Aacute: 550
+			b'\x01\xf4'  # zero: 500
+
+			# lsbArray
+			b'\x002'     # .notdef: 50
+			b'\x00\x00'  # space: 0
+			b"\x00'"     # A: 39 (xMin: 40)
+			b'\xff\xd8'  # acutecomb: -40
+			b'\x00('     # Aacute: 40
+			b'\x00\x1e'  # zero: 30
+		)
+
+		hmtxTable.reconstruct(data, ttFont)
+
+		assert hmtxTable.metrics == {
+			".notdef": (500, 50),
+			"space": (600, 0),
+			"A": (550, 39),
+			"acutecomb": (0, -40),
+			"Aacute": (550, 40),
+			"zero": (500, 30),
+			"one": (500, 50),
+			"two": (500, 40),
+		}
+
+		assert ttFont["glyf"]["A"].xMin == 40
+
+	def test_reconstruct_monospaced_sidebearings(self, ttFont):
+		hmtxTable = WOFF2HmtxTable()
+
+		data = (
+			b"\x01"  # 00000001 | bits 1 unset: explicit monospaced sidebearings
+
+			# advanceWidthArray
+			b'\x01\xf4'  # .notdef: 500
+			b'\x02X'     # space: 600
+			b'\x02&'     # A: 550
+			b'\x00\x00'  # acutecomb: 0
+			b'\x02&'     # Aacute: 550
+			b'\x01\xf4'  # zero: 500
+
+			# leftSideBearingArray
+			b'\x003'     # one: 51 (xMin: 50)
+			b'\x00('     # two: 40
+		)
+
+		hmtxTable.reconstruct(data, ttFont)
+
+		assert hmtxTable.metrics == {
+			".notdef": (500, 50),
+			"space": (600, 0),
+			"A": (550, 40),
+			"acutecomb": (0, -40),
+			"Aacute": (550, 40),
+			"zero": (500, 30),
+			"one": (500, 51),
+			"two": (500, 40),
+		}
+
+		assert ttFont["glyf"]["one"].xMin == 50
+
+	def test_reconstruct_flags_reserved_bits(self):
+		hmtxTable = WOFF2HmtxTable()
+
+		with pytest.raises(
+			ttLib.TTLibError, match="Bits 2-7 of 'hmtx' flags are reserved"
+		):
+			hmtxTable.reconstruct(b"\xFF", ttFont=None)
+
+	def test_reconstruct_flags_required_bits(self):
+		hmtxTable = WOFF2HmtxTable()
+
+		with pytest.raises(ttLib.TTLibError, match="either bits 0 or 1 .* must set"):
+			hmtxTable.reconstruct(b"\x00", ttFont=None)
+
+	def test_reconstruct_too_much_data(self, ttFont):
+		ttFont["hhea"].numberOfHMetrics = 2
+		data = b'\x03\x01\xf4\x02X\x02&'
+		hmtxTable = WOFF2HmtxTable()
+
+		with pytest.raises(ttLib.TTLibError, match="too much 'hmtx' table data"):
+			hmtxTable.reconstruct(data, ttFont)
+
+
+class WOFF2RoundtripTest(object):
+	@staticmethod
+	def roundtrip(infile):
+		infile.seek(0)
+		ttFont = ttLib.TTFont(infile, recalcBBoxes=False, recalcTimestamp=False)
+		outfile = BytesIO()
+		ttFont.save(outfile)
+		return outfile, ttFont
+
+	def test_roundtrip_default_transforms(self, ttFont):
+		ttFont.flavor = "woff2"
+		# ttFont.flavorData = None
+		tmp = BytesIO()
+		ttFont.save(tmp)
+
+		tmp2, ttFont2 = self.roundtrip(tmp)
+
+		assert tmp.getvalue() == tmp2.getvalue()
+		assert ttFont2.reader.flavorData.transformedTables == {"glyf", "loca"}
+
+	def test_roundtrip_no_transforms(self, ttFont):
+		ttFont.flavor = "woff2"
+		ttFont.flavorData = WOFF2FlavorData(transformedTables=[])
+		tmp = BytesIO()
+		ttFont.save(tmp)
+
+		tmp2, ttFont2 = self.roundtrip(tmp)
+
+		assert tmp.getvalue() == tmp2.getvalue()
+		assert not ttFont2.reader.flavorData.transformedTables
+
+	def test_roundtrip_all_transforms(self, ttFont):
+		ttFont.flavor = "woff2"
+		ttFont.flavorData = WOFF2FlavorData(transformedTables=["glyf", "loca", "hmtx"])
+		tmp = BytesIO()
+		ttFont.save(tmp)
+
+		tmp2, ttFont2 = self.roundtrip(tmp)
+
+		assert tmp.getvalue() == tmp2.getvalue()
+		assert ttFont2.reader.flavorData.transformedTables == {"glyf", "loca", "hmtx"}
+
+	def test_roundtrip_only_hmtx_no_glyf_transform(self, ttFont):
+		ttFont.flavor = "woff2"
+		ttFont.flavorData = WOFF2FlavorData(transformedTables=["hmtx"])
+		tmp = BytesIO()
+		ttFont.save(tmp)
+
+		tmp2, ttFont2 = self.roundtrip(tmp)
+
+		assert tmp.getvalue() == tmp2.getvalue()
+		assert ttFont2.reader.flavorData.transformedTables == {"hmtx"}
+
+	def test_roundtrip_no_glyf_and_loca_tables(self):
+		ttx = os.path.join(
+			os.path.dirname(current_dir), "subset", "data", "google_color.ttx"
+		)
+		ttFont = ttLib.TTFont()
+		ttFont.importXML(ttx)
+
+		assert "glyf" not in ttFont
+		assert "loca" not in ttFont
+
+		ttFont.flavor = "woff2"
+		tmp = BytesIO()
+		ttFont.save(tmp)
+
+		tmp2, ttFont2 = self.roundtrip(tmp)
+		assert tmp.getvalue() == tmp2.getvalue()
+		assert ttFont.flavor == "woff2"
+
+	def test_roundtrip_off_curve_despite_overlap_bit(self):
+		ttx = os.path.join(data_dir, "woff2_overlap_offcurve_in.ttx")
+		ttFont = ttLib.TTFont()
+		ttFont.importXML(ttx)
+
+		assert ttFont["glyf"]["A"].flags[0] == _g_l_y_f.flagOverlapSimple
+
+		ttFont.flavor = "woff2"
+		tmp = BytesIO()
+		ttFont.save(tmp)
+
+		_, ttFont2 = self.roundtrip(tmp)
+		assert ttFont2.flavor == "woff2"
+		assert ttFont2["glyf"]["A"].flags[0] == 0
+
+class MainTest(object):
+
+	@staticmethod
+	def make_ttf(tmpdir):
+		ttFont = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False)
+		ttFont.importXML(TTX)
+		filename = str(tmpdir / "TestTTF-Regular.ttf")
+		ttFont.save(filename)
+		return filename
+
+	def test_compress_ttf(self, tmpdir):
+		input_file = self.make_ttf(tmpdir)
+
+		assert woff2.main(["compress", input_file]) is None
+
+		assert (tmpdir / "TestTTF-Regular.woff2").check(file=True)
+
+	def test_compress_ttf_no_glyf_transform(self, tmpdir):
+		input_file = self.make_ttf(tmpdir)
+
+		assert woff2.main(["compress", "--no-glyf-transform", input_file]) is None
+
+		assert (tmpdir / "TestTTF-Regular.woff2").check(file=True)
+
+	def test_compress_ttf_hmtx_transform(self, tmpdir):
+		input_file = self.make_ttf(tmpdir)
+
+		assert woff2.main(["compress", "--hmtx-transform", input_file]) is None
+
+		assert (tmpdir / "TestTTF-Regular.woff2").check(file=True)
+
+	def test_compress_ttf_no_glyf_transform_hmtx_transform(self, tmpdir):
+		input_file = self.make_ttf(tmpdir)
+
+		assert woff2.main(
+			["compress", "--no-glyf-transform", "--hmtx-transform", input_file]
+		) is None
+
+		assert (tmpdir / "TestTTF-Regular.woff2").check(file=True)
+
+	def test_compress_output_file(self, tmpdir):
+		input_file = self.make_ttf(tmpdir)
+		output_file = tmpdir / "TestTTF.woff2"
+
+		assert woff2.main(
+			["compress", "-o", str(output_file), str(input_file)]
+		) is None
+
+		assert output_file.check(file=True)
+
+	def test_compress_otf(self, tmpdir):
+		ttFont = ttLib.TTFont(recalcBBoxes=False, recalcTimestamp=False)
+		ttFont.importXML(OTX)
+		input_file = str(tmpdir / "TestOTF-Regular.otf")
+		ttFont.save(input_file)
+
+		assert woff2.main(["compress", input_file]) is None
+
+		assert (tmpdir / "TestOTF-Regular.woff2").check(file=True)
+
+	def test_recompress_woff2_keeps_flavorData(self, tmpdir):
+		woff2_font = ttLib.TTFont(BytesIO(TT_WOFF2.getvalue()))
+		woff2_font.flavorData.privData = b"FOOBAR"
+		woff2_file = tmpdir / "TestTTF-Regular.woff2"
+		woff2_font.save(str(woff2_file))
+
+		assert woff2_font.flavorData.transformedTables == {"glyf", "loca"}
+
+		woff2.main(["compress", "--hmtx-transform", str(woff2_file)])
+
+		output_file = tmpdir / "TestTTF-Regular#1.woff2"
+		assert output_file.check(file=True)
+
+		new_woff2_font = ttLib.TTFont(str(output_file))
+
+		assert new_woff2_font.flavorData.transformedTables == {"glyf", "loca", "hmtx"}
+		assert new_woff2_font.flavorData.privData == b"FOOBAR"
+
+	def test_decompress_ttf(self, tmpdir):
+		input_file = tmpdir / "TestTTF-Regular.woff2"
+		input_file.write_binary(TT_WOFF2.getvalue())
+
+		assert woff2.main(["decompress", str(input_file)]) is None
+
+		assert (tmpdir / "TestTTF-Regular.ttf").check(file=True)
+
+	def test_decompress_otf(self, tmpdir):
+		input_file = tmpdir / "TestTTF-Regular.woff2"
+		input_file.write_binary(CFF_WOFF2.getvalue())
+
+		assert woff2.main(["decompress", str(input_file)]) is None
+
+		assert (tmpdir / "TestTTF-Regular.otf").check(file=True)
+
+	def test_decompress_output_file(self, tmpdir):
+		input_file = tmpdir / "TestTTF-Regular.woff2"
+		input_file.write_binary(TT_WOFF2.getvalue())
+		output_file = tmpdir / "TestTTF.ttf"
+
+		assert woff2.main(
+			["decompress", "-o", str(output_file), str(input_file)]
+		) is None
+
+		assert output_file.check(file=True)
+
+	def test_no_subcommand_show_help(self, capsys):
+		with pytest.raises(SystemExit):
+			woff2.main(["--help"])
+
+		captured = capsys.readouterr()
+		assert "usage: fonttools ttLib.woff2" in captured.out
+
+
 class Base128Test(unittest.TestCase):
 
 	def test_unpackBase128(self):
@@ -765,7 +1368,7 @@
 
 		self.assertRaisesRegex(
 			ttLib.TTLibError,
-			"UIntBase128 value exceeds 2\*\*32-1",
+			r"UIntBase128 value exceeds 2\*\*32-1",
 			unpackBase128, b'\x90\x80\x80\x80\x00')
 
 		self.assertRaisesRegex(
@@ -783,11 +1386,11 @@
 		self.assertEqual(packBase128(2**32-1), b'\x8f\xff\xff\xff\x7f')
 		self.assertRaisesRegex(
 			ttLib.TTLibError,
-			"UIntBase128 format requires 0 <= integer <= 2\*\*32-1",
+			r"UIntBase128 format requires 0 <= integer <= 2\*\*32-1",
 			packBase128, 2**32+1)
 		self.assertRaisesRegex(
 			ttLib.TTLibError,
-			"UIntBase128 format requires 0 <= integer <= 2\*\*32-1",
+			r"UIntBase128 format requires 0 <= integer <= 2\*\*32-1",
 			packBase128, -1)
 
 
diff --git a/Tests/ttx/data/TestOTF.ttx b/Tests/ttx/data/TestOTF.ttx
index 852dacf..96f1844 100644
--- a/Tests/ttx/data/TestOTF.ttx
+++ b/Tests/ttx/data/TestOTF.ttx
@@ -142,13 +142,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
     <namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
       Test TTF
@@ -184,13 +184,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
   </name>
 
diff --git a/Tests/ttx/data/TestTTF.ttx b/Tests/ttx/data/TestTTF.ttx
index c283a29..66caf6c 100644
--- a/Tests/ttx/data/TestTTF.ttx
+++ b/Tests/ttx/data/TestTTF.ttx
@@ -462,13 +462,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
     <namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
       Test TTF
@@ -504,13 +504,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
   </name>
 
diff --git a/Tests/ttx/ttx_test.py b/Tests/ttx/ttx_test.py
index 4d9372d..c246347 100644
--- a/Tests/ttx/ttx_test.py
+++ b/Tests/ttx/ttx_test.py
@@ -1,15 +1,32 @@
-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:
+    try:
+        import brotlicffi as brotli
+    except ImportError:
+        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,
@@ -40,148 +57,969 @@
         shutil.copy2(font_path, temppath)
         return temppath
 
-# -----
-# Tests
-# -----
+    @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))
+        self.assertTrue(
+            "Must specify at least one input file" in str(cm.exception)
+        )
 
     def test_parseOptions_invalid_path(self):
-        file_path = 'invalid_font_path'
+        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'
+        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')))
+        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'
+        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
+        _, _ = 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')))
+        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'
+        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')))
+        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'
+        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
+        _, _ = 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')))
+        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']
+        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)]
+        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')))
+            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']
+        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)]
+        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])))
+            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'
+        file_name = "TestTTF.ttf"
         font_path = self.getpath(file_name)
-        self.assertEqual(ttx.guessFileType(font_path), 'TTF')
+        self.assertEqual(ttx.guessFileType(font_path), "TTF")
 
     def test_guessFileType_otf(self):
-        file_name = 'TestOTF.otf'
+        file_name = "TestOTF.otf"
         font_path = self.getpath(file_name)
-        self.assertEqual(ttx.guessFileType(font_path), 'OTF')
+        self.assertEqual(ttx.guessFileType(font_path), "OTF")
 
     def test_guessFileType_woff(self):
-        file_name = 'TestWOFF.woff'
+        file_name = "TestWOFF.woff"
         font_path = self.getpath(file_name)
-        self.assertEqual(ttx.guessFileType(font_path), 'WOFF')
+        self.assertEqual(ttx.guessFileType(font_path), "WOFF")
 
     def test_guessFileType_woff2(self):
-        file_name = 'TestWOFF2.woff2'
+        file_name = "TestWOFF2.woff2"
         font_path = self.getpath(file_name)
-        self.assertEqual(ttx.guessFileType(font_path), 'WOFF2')
+        self.assertEqual(ttx.guessFileType(font_path), "WOFF2")
 
     def test_guessFileType_ttc(self):
-        file_name = 'TestTTC.ttc'
+        file_name = "TestTTC.ttc"
         font_path = self.getpath(file_name)
-        self.assertEqual(ttx.guessFileType(font_path), 'TTC')
+        self.assertEqual(ttx.guessFileType(font_path), "TTC")
 
     def test_guessFileType_dfont(self):
-        file_name = 'TestDFONT.dfont'
+        file_name = "TestDFONT.dfont"
         font_path = self.getpath(file_name)
-        self.assertEqual(ttx.guessFileType(font_path), 'TTF')
+        self.assertEqual(ttx.guessFileType(font_path), "TTF")
 
     def test_guessFileType_ttx_ttf(self):
-        file_name = 'TestTTF.ttx'
+        file_name = "TestTTF.ttx"
         font_path = self.getpath(file_name)
-        self.assertEqual(ttx.guessFileType(font_path), 'TTX')
+        self.assertEqual(ttx.guessFileType(font_path), "TTX")
 
     def test_guessFileType_ttx_otf(self):
-        file_name = 'TestOTF.ttx'
+        file_name = "TestOTF.ttx"
         font_path = self.getpath(file_name)
-        self.assertEqual(ttx.guessFileType(font_path), 'OTX')
+        self.assertEqual(ttx.guessFileType(font_path), "OTX")
 
     def test_guessFileType_ttx_bom(self):
-        file_name = 'TestBOM.ttx'
+        file_name = "TestBOM.ttx"
         font_path = self.getpath(file_name)
-        self.assertEqual(ttx.guessFileType(font_path), 'TTX')
+        self.assertEqual(ttx.guessFileType(font_path), "TTX")
 
     def test_guessFileType_ttx_no_sfntVersion(self):
-        file_name = 'TestNoSFNT.ttx'
+        file_name = "TestNoSFNT.ttx"
         font_path = self.getpath(file_name)
-        self.assertEqual(ttx.guessFileType(font_path), 'TTX')
+        self.assertEqual(ttx.guessFileType(font_path), "TTX")
 
     def test_guessFileType_ttx_no_xml(self):
-        file_name = 'TestNoXML.ttx'
+        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'
+        font_path = "invalid_font_path"
         self.assertIsNone(ttx.guessFileType(font_path))
 
 
-if __name__ == "__main__":
-    sys.exit(unittest.main())
+# -----------------------
+# 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_recalc_timestamp():
+    tto = ttx.Options([("--no-recalc-timestamp", "")], 1)
+    assert tto.recalcTimestamp is False
+
+
+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
+
+    # --no-recalc-timestamp will keep original timestamp
+    options.recalcTimestamp = False
+    ttx.ttCompile(inttx, str(outttf2), options)
+    assert outttf2.check(file=True)
+    inttf = TTFont()
+    inttf.importXML(inttx)
+    assert inttf["head"].modified == TTFont(str(outttf2))["head"].modified
+
+
+# -------------------------
+# 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, caplog):
+    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)
+
+    assert "(Cancelled.)" in caplog.text
+
+
+@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, caplog):
+    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)
+
+    assert "Test error" in caplog.text
+
+
+@pytest.mark.skipif(
+    sys.platform == "win32",
+    reason="waitForKeyPress function causes test to hang on Windows platform",
+)
+def test_main_base_exception(tmpdir, monkeypatch, caplog):
+    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)
+
+    assert "Unhandled exception has occurred" in caplog.text
+
+
+# ---------------------------
+# support functions for tests
+# ---------------------------
+
+
+def raise_exception(exception):
+    raise exception
diff --git a/Tests/ufoLib/GLIF1_test.py b/Tests/ufoLib/GLIF1_test.py
new file mode 100644
index 0000000..85fcc71
--- /dev/null
+++ b/Tests/ufoLib/GLIF1_test.py
@@ -0,0 +1,1331 @@
+import unittest
+from fontTools.ufoLib.glifLib import GlifLibError, readGlyphFromString, writeGlyphToString
+from .testSupport import Glyph, stripText
+from itertools import islice
+
+# ----------
+# Test Cases
+# ----------
+
+class TestGLIF1(unittest.TestCase):
+
+	def assertEqual(self, first, second, msg=None):
+		if isinstance(first, str):
+			first = stripText(first)
+		if isinstance(second, str):
+			second = stripText(second)
+		return super().assertEqual(first, second, msg=msg)
+
+	def pyToGLIF(self, py):
+		py = stripText(py)
+		glyph = Glyph()
+		exec(py, {"glyph" : glyph, "pointPen" : glyph})
+		glif = writeGlyphToString(glyph.name, glyphObject=glyph, drawPointsFunc=glyph.drawPoints, formatVersion=1, validate=True)
+		# discard the first line containing the xml declaration
+		return "\n".join(islice(glif.splitlines(), 1, None))
+
+	def glifToPy(self, glif):
+		glif = stripText(glif)
+		glif = "<?xml version=\"1.0\"?>\n" + glif
+		glyph = Glyph()
+		readGlyphFromString(glif, glyphObject=glyph, pointPen=glyph, validate=True)
+		return glyph.py()
+
+	def testTopElement(self):
+		# not glyph
+		glif = """
+		<notglyph name="a" format="1">
+			<outline>
+			</outline>
+		</notglyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testName_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testName_empty(self):
+		# empty
+		glif = """
+		<glyph name="" format="1">
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = ""
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testName_not_a_string(self):
+		# not a string
+		py = """
+		glyph.name = 1
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+
+	def testFormat_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testFormat_wrong_number(self):
+		# wrong number
+		glif = """
+		<glyph name="a" format="-1">
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testFormat_not_an_int(self):
+		# not an int
+		glif = """
+		<glyph name="a" format="A">
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testBogusGlyphStructure_unknown_element(self):
+		# unknown element
+		glif = """
+		<glyph name="a" format="1">
+			<unknown />
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testBogusGlyphStructure_content(self):
+		# content
+		glif = """
+		<glyph name="a" format="1">
+			Hello World.
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testAdvance_legal_width_and_height(self):
+		# legal: width and height
+		glif = """
+		<glyph name="a" format="1">
+			<advance height="200" width="100"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.width = 100
+		glyph.height = 200
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testAdvance_legal_width_and_height_floats(self):
+		# legal: width and height floats
+		glif = """
+		<glyph name="a" format="1">
+			<advance height="200.1" width="100.1"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.width = 100.1
+		glyph.height = 200.1
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testAdvance_legal_width(self):
+		# legal: width
+		glif = """
+		<glyph name="a" format="1">
+			<advance width="100"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.width = 100
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testAdvance_legal_height(self):
+		# legal: height
+		glif = """
+		<glyph name="a" format="1">
+			<advance height="200"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.height = 200
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testAdvance_illegal_width(self):
+		# illegal: not a number
+		glif = """
+		<glyph name="a" format="1">
+			<advance width="a"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.width = "a"
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testAdvance_illegal_height(self):
+		glif = """
+		<glyph name="a" format="1">
+			<advance height="a"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.height = "a"
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testUnicodes_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="1">
+			<unicode hex="0061"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.unicodes = [97]
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testUnicodes_legal_multiple(self):
+		glif = """
+		<glyph name="a" format="1">
+			<unicode hex="0062"/>
+			<unicode hex="0063"/>
+			<unicode hex="0061"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.unicodes = [98, 99, 97]
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testUnicodes_illegal(self):
+		# illegal
+		glif = """
+		<glyph name="a" format="1">
+			<unicode hex="1.1"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "zzzzzz"
+		glyph.unicodes = ["1.1"]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testNote(self):
+		glif = """
+		<glyph name="a" format="1">
+			<note>
+				\U0001F4A9
+			</note>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.note = "💩"
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testLib_legal(self):
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+			</outline>
+			<lib>
+				<dict>
+					<key>dict</key>
+					<dict>
+						<key>hello</key>
+						<string>world</string>
+					</dict>
+					<key>float</key>
+					<real>2.5</real>
+					<key>int</key>
+					<integer>1</integer>
+					<key>list</key>
+					<array>
+						<string>a</string>
+						<string>b</string>
+						<integer>1</integer>
+						<real>2.5</real>
+					</array>
+					<key>string</key>
+					<string>a</string>
+				</dict>
+			</lib>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.lib = {"dict" : {"hello" : "world"}, "float" : 2.5, "int" : 1, "list" : ["a", "b", 1, 2.5], "string" : "a"}
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testOutline_unknown_element(self):
+		# unknown element
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<unknown/>
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testOutline_content(self):
+		# content
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				hello
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testComponent_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<component base="x" xScale="2" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", (2, 3, 6, 5, 1, 4)])
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testComponent_illegal_no_base(self):
+		# no base
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<component xScale="2" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testComponent_bogus_transformation(self):
+		# bogus values in transformation
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<component base="x" xScale="a" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", ("a", 3, 6, 5, 1, 4)])
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<component base="x" xScale="a" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", (2, "a", 6, 5, 1, 4)])
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<component base="x" xScale="2" xyScale="3" yxScale="a" yScale="5" xOffset="1" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", (2, 3, "a", 5, 1, 4)])
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<component base="x" xScale="2" xyScale="3" yxScale="6" yScale="a" xOffset="1" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", (2, 3, 6, "a", 1, 4)])
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<component base="x" xScale="2" xyScale="3" yxScale="6" yScale="5" xOffset="a" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", (2, 3, 6, 5, "a", 4)])
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<component base="x" xScale="2" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="a"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", (2, 3, 6, 5, 1, "a")])
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testContour_legal_one_contour(self):
+		# legal: one contour
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testContour_legal_two_contours(self):
+		# legal: two contours
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="1" y="2" type="move"/>
+					<point x="10" y="20" type="line"/>
+				</contour>
+				<contour>
+					<point x="1" y="2" type="move"/>
+					<point x="10" y="20" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(10, 20)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(10, 20)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testContour_illegal_unkonwn_element(self):
+		# unknown element
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<unknown/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointCoordinates_legal_int(self):
+		# legal: int
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="move"/>
+					<point x="0" y="0" type="line" name="this is here so that the contour isn't seen as an anchor"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 0)], **{"name" : "this is here so that the contour isn't seen as an anchor", "segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointCoordinates_legal_float(self):
+		# legal: float
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="1.1" y="-2.2" type="move"/>
+					<point x="0" y="0" type="line" name="this is here so that the contour isn't seen as an anchor"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1.1, -2.2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 0)], **{"name" : "this is here so that the contour isn't seen as an anchor", "segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointCoordinates_illegal_x(self):
+		# illegal: string
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="a" y="2" type="move"/>
+					<point x="0" y="0" type="line" name="this is here so that the contour isn't seen as an anchor"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[("a", 2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 0)], **{"name" : "this is here so that the contour isn't seen as an anchor", "segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointCoordinates_illegal_y(self):
+		# legal: int
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="1" y="a" type="move"/>
+					<point x="0" y="0" type="line" name="this is here so that the contour isn't seen as an anchor"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, "a")], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 0)], **{"name" : "this is here so that the contour isn't seen as an anchor", "segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointTypeMove_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="move"/>
+					<point x="3" y="-4" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeMove_legal_smooth(self):
+		# legal: smooth=True
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="move" smooth="yes"/>
+					<point x="3" y="-4" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : True})
+		pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeMove_illegal_not_at_start(self):
+		# illegal: not at start
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="3" y="-4" type="line"/>
+					<point x="1" y="-2" type="move"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointTypeLine_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="move"/>
+					<point x="3" y="-4" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeLine_legal_start_of_contour(self):
+		# legal: start of contour
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="line"/>
+					<point x="3" y="-4" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeLine_legal_smooth(self):
+		# legal: smooth=True
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="move"/>
+					<point x="3" y="-4" type="line" smooth="yes"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : True})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeCurve_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeCurve_legal_start_of_contour(self):
+		# legal: start of contour
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="100" y="200" type="curve"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeCurve_legal_smooth(self):
+		# legal: smooth=True
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="curve" smooth="yes"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : True})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeCurve_legal_no_off_curves(self):
+		# legal: no off-curves
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="100" y="200" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeCurve_legal_1_off_curve(self):
+		# legal: 1 off-curve
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="50" y="100"/>
+					<point x="100" y="200" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(50, 100)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeCurve_illegal_3_off_curves(self):
+		# illegal: 3 off-curves
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="100"/>
+					<point x="35" y="125"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 100)], **{"smooth" : False})
+		pointPen.addPoint(*[(35, 125)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointQCurve_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="qcurve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointQCurve_legal_start_of_contour(self):
+		# legal: start of contour
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="100" y="200" type="qcurve"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointQCurve_legal_smooth(self):
+		# legal: smooth=True
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="qcurve" smooth="yes"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : True})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointQCurve_legal_no_off_curves(self):
+		# legal: no off-curves
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="100" y="200" type="qcurve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointQCurve_legal_one_off_curve(self):
+		# legal: 1 off-curve
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="50" y="100"/>
+					<point x="100" y="200" type="qcurve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(50, 100)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointQCurve_legal_3_off_curves(self):
+		# legal: 3 off-curves
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="100"/>
+					<point x="35" y="125"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="qcurve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 100)], **{"smooth" : False})
+		pointPen.addPoint(*[(35, 125)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testSpecialCaseQCurve(self):
+		# contour with no on curve
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="0"/>
+					<point x="0" y="100"/>
+					<point x="100" y="100"/>
+					<point x="100" y="0"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"smooth" : False})
+		pointPen.addPoint(*[(0, 100)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 100)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 0)], **{"smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeOffCurve_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeOffCurve_legal_start_of_contour(self):
+		# legal: start of contour
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeOffCurve_illegal_before_move(self):
+		# before move
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="65"/>
+					<point x="0" y="0" type="move"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointTypeOffCurve_illegal_before_line(self):
+		# before line
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="65"/>
+					<point x="0" y="0" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointTypeOffCurve_illegal_smooth(self):
+		# smooth=True
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="0" y="65" smooth="yes"/>
+					<point x="0" y="0" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : True})
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testSinglePoint_legal_without_name(self):
+		# legal
+		# glif format 1 single point without a name was not an anchor
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="1" y="2" type="move"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testAnchor_legal_with_name(self):
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="1" y="2" type="move" name="test"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.anchors = [{"name" : "test", "x" : 1, "y" : 2}]
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testOpenContourLooseOffCurves_legal(self):
+		# a piece of software was writing this kind of structure
+		glif = """
+		<glyph name="a" format="1">
+			<outline>
+				<contour>
+					<point x="1" y="2" type="move"/>
+					<point x="1" y="2"/>
+					<point x="1" y="2"/>
+					<point x="1" y="2" type="curve"/>
+					<point x="1" y="2"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		expectedPy = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, 2)], **{"smooth" : False})
+		pointPen.addPoint(*[(1, 2)], **{"smooth" : False})
+		pointPen.addPoint(*[(1, 2)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(resultPy, expectedPy)
+
+	def testOpenContourLooseOffCurves_illegal(self):
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, 2)], **{"smooth" : False})
+		pointPen.addPoint(*[(1, 2)], **{"smooth" : False})
+		pointPen.addPoint(*[(1, 2)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, 2)], **{"smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
diff --git a/Tests/ufoLib/GLIF2_test.py b/Tests/ufoLib/GLIF2_test.py
new file mode 100644
index 0000000..ab9495d
--- /dev/null
+++ b/Tests/ufoLib/GLIF2_test.py
@@ -0,0 +1,2366 @@
+import unittest
+from fontTools.ufoLib.glifLib import GlifLibError, readGlyphFromString, writeGlyphToString
+from .testSupport import Glyph, stripText
+from itertools import islice
+
+# ----------
+# Test Cases
+# ----------
+
+class TestGLIF2(unittest.TestCase):
+
+	def assertEqual(self, first, second, msg=None):
+		if isinstance(first, str):
+			first = stripText(first)
+		if isinstance(second, str):
+			second = stripText(second)
+		return super().assertEqual(first, second, msg=msg)
+
+	def pyToGLIF(self, py):
+		py = stripText(py)
+		glyph = Glyph()
+		exec(py, {"glyph" : glyph, "pointPen" : glyph})
+		glif = writeGlyphToString(glyph.name, glyphObject=glyph, drawPointsFunc=glyph.drawPoints, formatVersion=2, validate=True)
+		# discard the first line containing the xml declaration
+		return "\n".join(islice(glif.splitlines(), 1, None))
+
+	def glifToPy(self, glif):
+		glif = stripText(glif)
+		glif = "<?xml version=\"1.0\"?>\n" + glif
+		glyph = Glyph()
+		readGlyphFromString(glif, glyphObject=glyph, pointPen=glyph, validate=True)
+		return glyph.py()
+
+	def testTopElement(self):
+		# not glyph
+		glif = """
+		<notglyph name="a" format="2">
+			<outline>
+			</outline>
+		</notglyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testName_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testName_empty(self):
+		# empty
+		glif = """
+		<glyph name="" format="2">
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = ""
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testName_not_a_string(self):
+		# not a string
+		py = """
+		glyph.name = 1
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+
+	def testFormat_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testFormat_illegal_wrong_number(self):
+		# wrong number
+		glif = """
+		<glyph name="a" format="-1">
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testFormat_illegal_not_int(self):
+		# not an int
+		glif = """
+		<glyph name="a" format="A">
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testBogusGlyphStructure_unknown_element(self):
+		# unknown element
+		glif = """
+		<glyph name="a" format="2">
+			<unknown />
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testBogusGlyphStructure_content(self):
+		# content
+		glif = """
+		<glyph name="a" format="2">
+			Hello World.
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testAdvance_legal_widht_and_height(self):
+		# legal: width and height
+		glif = """
+		<glyph name="a" format="2">
+			<advance height="200" width="100"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.width = 100
+		glyph.height = 200
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testAdvance_legal_width_and_height_floats(self):
+		# legal: width and height floats
+		glif = """
+		<glyph name="a" format="2">
+			<advance height="200.1" width="100.1"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.width = 100.1
+		glyph.height = 200.1
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testAdvance_legal_width(self):
+		# legal: width
+		glif = """
+		<glyph name="a" format="2">
+			<advance width="100"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.width = 100
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testAdvance_legal_height(self):
+		# legal: height
+		glif = """
+		<glyph name="a" format="2">
+			<advance height="200"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.height = 200
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testAdvance_illegal_width(self):
+		# illegal: not a number
+		glif = """
+		<glyph name="a" format="2">
+			<advance width="a"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.width = "a"
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testAdvance_illegal_height(self):
+		glif = """
+		<glyph name="a" format="2">
+			<advance height="a"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.height = "a"
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testUnicodes_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="2">
+			<unicode hex="0061"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.unicodes = [97]
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testUnicodes_legal_multiple(self):
+		glif = """
+		<glyph name="a" format="2">
+			<unicode hex="0062"/>
+			<unicode hex="0063"/>
+			<unicode hex="0061"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.unicodes = [98, 99, 97]
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testUnicodes_illegal(self):
+		# illegal
+		glif = """
+		<glyph name="a" format="2">
+			<unicode hex="1.1"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "zzzzzz"
+		glyph.unicodes = ["1.1"]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testNote(self):
+		glif = """
+		<glyph name="a" format="2">
+			<note>
+				hëllö
+			</note>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.note = "hëllö"
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testLib(self):
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+			</outline>
+			<lib>
+				<dict>
+					<key>dict</key>
+					<dict>
+						<key>hello</key>
+						<string>world</string>
+					</dict>
+					<key>float</key>
+					<real>2.5</real>
+					<key>int</key>
+					<integer>1</integer>
+					<key>list</key>
+					<array>
+						<string>a</string>
+						<string>b</string>
+						<integer>1</integer>
+						<real>2.5</real>
+					</array>
+					<key>string</key>
+					<string>a</string>
+				</dict>
+			</lib>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.lib = {"dict" : {"hello" : "world"}, "float" : 2.5, "int" : 1, "list" : ["a", "b", 1, 2.5], "string" : "a"}
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testGuidelines_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="1"/>
+			<guideline y="1"/>
+			<guideline x="1" y="1" angle="0"/>
+			<guideline x="1" y="1" angle="360"/>
+			<guideline x="1.1" y="1.1" angle="45.5"/>
+			<guideline x="1" name="a"/>
+			<guideline x="1" color="1,1,1,1"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"x" : 1}, {"y" : 1}, {"angle" : 0, "x" : 1, "y" : 1}, {"angle" : 360, "x" : 1, "y" : 1}, {"angle" : 45.5, "x" : 1.1, "y" : 1.1}, {"name" : "a", "x" : 1}, {"color" : "1,1,1,1", "x" : 1}]
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testGuidelines_illegal_x(self):
+		# x not an int or float
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="a" y="1" angle="45"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"angle" : 45, "x" : "a", "y" : 1}]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testGuidelines_illegal_y(self):
+		# y not an int or float
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="1" y="y" angle="45"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"angle" : 45, "x" : 1, "y" : "a"}]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testGuidelines_illegal_angle(self):
+		# angle not an int or float
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="1" y="1" angle="a"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"angle" : "a", "x" : 1, "y" : 1}]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testGuidelines_illegal_x_missing(self):
+		# x missing
+		glif = """
+		<glyph name="a" format="2">
+			<guideline y="1" angle="45"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"angle" : 45, "y" : 1}]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testGuidelines_illegal_y_missing(self):
+		# y missing
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="1" angle="45"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"angle" : 45, "x" : 1}]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testGuidelines_illegal_angle_missing(self):
+		# angle missing
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="1" y="1"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"x" : 1, "y" : 1}]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testGuidelines_illegal_angle_out_of_range(self):
+		# angle out of range
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="1" y="1" angle="-1"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"angle" : -1, "x" : "1", "y" : 1}]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="1" y="1" angle="361"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"angle" : 361, "x" : "1", "y" : 1}]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testAnchors_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="2">
+			<anchor x="1" y="2" name="test" color="1,0,0,1"/>
+			<anchor x="1" y="2"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.anchors = [{"color" : "1,0,0,1", "name" : "test", "x" : 1, "y" : 2}, {"x" : 1, "y" : 2}]
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testAnchors_illegal_x(self):
+		# x not an int or float
+		glif = """
+		<glyph name="a" format="2">
+			<anchor x="a" y="1"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.anchors = [{"x" : "a", "y" : 1}]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testAnchors_illegal_y(self):
+		# y not an int or float
+		glif = """
+		<glyph name="a" format="2">
+			<anchor x="1" y="a"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.anchors = [{"x" : 1, "y" : "a"}]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testAnchors_illegal_x_missing(self):
+		# x missing
+		glif = """
+		<glyph name="a" format="2">
+			<anchor y="1"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.anchors = [{"y" : 1}]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testAnchors_illegal_y_missing(self):
+		# y missing
+		glif = """
+		<glyph name="a" format="2">
+			<anchor x="1"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.anchors = [{"x" : 1}]
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testImage_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="2">
+			<image fileName="test.png" xScale="2" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="4" color="1,1,1,1"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.image = {"color" : "1,1,1,1", "fileName" : "test.png", "xOffset" : 1, "xScale" : 2, "xyScale" : 3, "yOffset" : 4, "yScale" : 5, "yxScale" : 6}
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testImage_legal_no_color_or_transformation(self):
+		# legal: no color or transformation
+		glif = """
+		<glyph name="a" format="2">
+			<image fileName="test.png"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.image = {"fileName" : "test.png", "xOffset" : 0, "xScale" : 1, "xyScale" : 0, "yOffset" : 0, "yScale" : 1, "yxScale" : 0}
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testImage_illegal_no_file_name(self):
+		# no file name
+		glif = """
+		<glyph name="a" format="2">
+			<image xScale="2" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="4" color="1,1,1,1"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.image = {"color" : "1,1,1,1", "xOffset" : 1, "xScale" : 2, "xyScale" : 3, "yOffset" : 4, "yScale" : 5, "yxScale" : 6}
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testImage_bogus_transformation(self):
+		# bogus transformation
+		glif = """
+		<glyph name="a" format="2">
+			<image fileName="test.png" xScale="a" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="4"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.image = {"fileName" : "test.png", "xOffset" : 1, "xScale" : "a", "xyScale" : 3, "yOffset" : 4, "yScale" : 5, "yxScale" : 6}
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="2">
+			<image fileName="test.png" xScale="2" xyScale="a" yxScale="6" yScale="5" xOffset="1" yOffset="4"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.image = {"fileName" : "test.png", "xOffset" : 1, "xScale" : 2, "xyScale" : "a", "yOffset" : 4, "yScale" : 5, "yxScale" : 6}
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="2">
+			<image fileName="test.png" xScale="2" xyScale="3" yxScale="a" yScale="5" xOffset="1" yOffset="4"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.image = {"fileName" : "test.png", "xOffset" : 1, "xScale" : 2, "xyScale" : 3, "yOffset" : 4, "yScale" : 5, "yxScale" : "a"}
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="2">
+			<image fileName="test.png" xScale="2" xyScale="3" yxScale="6" yScale="a" xOffset="1" yOffset="4"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.image = {"fileName" : "test.png", "xOffset" : 1, "xScale" : 2, "xyScale" : 3, "yOffset" : 4, "yScale" : "a", "yxScale" : 6}
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="2">
+			<image fileName="test.png" xScale="2" xyScale="3" yxScale="6" yScale="5" xOffset="a" yOffset="4"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.image = {"fileName" : "test.png", "xOffset" : "a", "xScale" : 2, "xyScale" : 3, "yOffset" : 4, "yScale" : 5, "yxScale" : 6}
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="2">
+			<image fileName="test.png" xScale="2" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="a"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.image = {"fileName" : "test.png", "xOffset" : 1, "xScale" : 2, "xyScale" : 3, "yOffset" : "a", "yScale" : 5, "yxScale" : 6}
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testImage_bogus_color(self):
+		# bogus color
+		glif = """
+		<glyph name="a" format="2">
+			<image fileName="test.png" color="1,1,1,x"/>
+			<outline>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.image = {"color" : "1,1,1,x"}
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testOutline_unknown_element(self):
+		# unknown element
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<unknown/>
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testOutline_content(self):
+		# content
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				hello
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testComponent_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<component base="x" xScale="2" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", (2, 3, 6, 5, 1, 4)])
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testComponent_illegal_no_base(self):
+		# no base
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<component xScale="2" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testComponent_illegal_bogus_transformation(self):
+		# bogus values in transformation
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<component base="x" xScale="a" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", ("a", 3, 6, 5, 1, 4)])
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<component base="x" xScale="a" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", (2, "a", 6, 5, 1, 4)])
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<component base="x" xScale="2" xyScale="3" yxScale="a" yScale="5" xOffset="1" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", (2, 3, "a", 5, 1, 4)])
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<component base="x" xScale="2" xyScale="3" yxScale="6" yScale="a" xOffset="1" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", (2, 3, 6, "a", 1, 4)])
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<component base="x" xScale="2" xyScale="3" yxScale="6" yScale="5" xOffset="a" yOffset="4"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", (2, 3, 6, 5, "a", 4)])
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<component base="x" xScale="2" xyScale="3" yxScale="6" yScale="5" xOffset="1" yOffset="a"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.addComponent(*["x", (2, 3, 6, 5, 1, "a")])
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testContour_legal_one_contour(self):
+		# legal: one contour
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testContour_legal_two_contours(self):
+		# legal: two contours
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="1" y="2" type="move"/>
+				</contour>
+				<contour>
+					<point x="1" y="2" type="move"/>
+					<point x="10" y="20" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(10, 20)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testContour_illegal_unkonwn_element(self):
+		# unknown element
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<unknown/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testContourIdentifier(self):
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour identifier="foo">
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath(**{"identifier" : "foo"})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointCoordinates_legal_int(self):
+		# legal: int
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="move"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointCoordinates_legal_float(self):
+		# legal: float
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="1.1" y="-2.2" type="move"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1.1, -2.2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointCoordinates_illegal_x(self):
+		# illegal: x as string
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="a" y="2" type="move"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[("a", 2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointCoordinates_illegal_y(self):
+		# illegal: y as string
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="1" y="a" type="move"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, "a")], **{"segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointTypeMove_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="move"/>
+					<point x="3" y="-4" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeMove_legal_smooth(self):
+		# legal: smooth=True
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="move" smooth="yes"/>
+					<point x="3" y="-4" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : True})
+		pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeMove_illegal_not_at_start(self):
+		# illegal: not at start
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="3" y="-4" type="line"/>
+					<point x="1" y="-2" type="move"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointTypeLine_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="move"/>
+					<point x="3" y="-4" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeLine_legal_start_of_contour(self):
+		# legal: start of contour
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="line"/>
+					<point x="3" y="-4" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeLine_legal_smooth(self):
+		# legal: smooth=True
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="move"/>
+					<point x="3" y="-4" type="line" smooth="yes"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(3, -4)], **{"segmentType" : "line", "smooth" : True})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeCurve_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeCurve_legal_start_of_contour(self):
+		# legal: start of contour
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="100" y="200" type="curve"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeCurve_legal_smooth(self):
+		# legal: smooth=True
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="curve" smooth="yes"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : True})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeCurve_legal_no_off_curves(self):
+		# legal: no off-curves
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="100" y="200" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeCurve_legal_1_off_curve(self):
+		# legal: 1 off-curve
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="50" y="100"/>
+					<point x="100" y="200" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(50, 100)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeCurve_illegal_3_off_curves(self):
+		# illegal: 3 off-curves
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="100"/>
+					<point x="35" y="125"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 100)], **{"smooth" : False})
+		pointPen.addPoint(*[(35, 125)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointQCurve_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="qcurve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointQCurve_legal_start_of_contour(self):
+		# legal: start of contour
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="100" y="200" type="qcurve"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointQCurve_legal_smooth(self):
+		# legal: smooth=True
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="qcurve" smooth="yes"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : True})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointQCurve_legal_no_off_curves(self):
+		# legal: no off-curves
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="100" y="200" type="qcurve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointQCurve_legal_one_off_curve(self):
+		# legal: 1 off-curve
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="50" y="100"/>
+					<point x="100" y="200" type="qcurve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(50, 100)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointQCurve_legal_3_off_curves(self):
+		# legal: 3 off-curves
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="100"/>
+					<point x="35" y="125"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="qcurve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 100)], **{"smooth" : False})
+		pointPen.addPoint(*[(35, 125)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testSpecialCaseQCurve_legal_no_on_curve(self):
+		# contour with no on curve
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="0"/>
+					<point x="0" y="100"/>
+					<point x="100" y="100"/>
+					<point x="100" y="0"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"smooth" : False})
+		pointPen.addPoint(*[(0, 100)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 100)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 0)], **{"smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeOffCurve_legal(self):
+		# legal
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="0" type="move"/>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeOffCurve_legal_start_of_contour(self):
+		# legal: start of contour
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="65"/>
+					<point x="65" y="200"/>
+					<point x="100" y="200" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(65, 200)], **{"smooth" : False})
+		pointPen.addPoint(*[(100, 200)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testPointTypeOffCurve_illegal_before_move(self):
+		# before move
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="65"/>
+					<point x="0" y="0" type="move"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointTypeOffCurve_illegal_before_line(self):
+		# before line
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="65"/>
+					<point x="0" y="0" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : False})
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "line", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testPointTypeOffCurve_illegal_smooth(self):
+		# smooth=True
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="0" y="65" smooth="yess"/>
+					<point x="0" y="0" type="curve"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(0, 65)], **{"smooth" : True})
+		pointPen.addPoint(*[(0, 0)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testOpenContourLooseOffCurves(self):
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="1" y="2" type="move"/>
+					<point x="1" y="2"/>
+					<point x="1" y="2"/>
+					<point x="1" y="2" type="curve"/>
+					<point x="1" y="2"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, 2)], **{"segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, 2)], **{"smooth" : False})
+		pointPen.addPoint(*[(1, 2)], **{"smooth" : False})
+		pointPen.addPoint(*[(1, 2)], **{"segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, 2)], **{"smooth" : False})
+		pointPen.endPath()
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+
+	def testPointIdentifier(self):
+		glif = """
+		<glyph name="a" format="2">
+			<outline>
+				<contour>
+					<point x="1" y="-2" type="move" identifier="1"/>
+					<point x="1" y="-2" type="line" identifier="2"/>
+					<point x="1" y="-2" type="curve" identifier="3"/>
+					<point x="1" y="-2" type="qcurve" identifier="4"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		pointPen.beginPath()
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testIdentifierConflict_legal_no_conflict(self):
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="point1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		resultGlif = self.pyToGLIF(py)
+		resultPy = self.glifToPy(glif)
+		self.assertEqual(glif, resultGlif)
+		self.assertEqual(py, resultPy)
+
+	def testIdentifierConflict_point_point(self):
+		# point - point
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="point1"/>
+					<point x="1" y="-2" type="line" identifier="point1"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_point_contour(self):
+		# point - contour
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="contour1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "contour1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_point_component(self):
+		# point - component
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="component1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "component1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_point_guideline(self):
+		# point - guideline
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="guideline1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "guideline1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_point_anchor(self):
+		# point - anchor
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="anchor1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "anchor1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_contour_contour(self):
+		# contour - contour
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="point1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_contour_component(self):
+		# contour - component
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="point1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="contour1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "contour1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_contour_guideline(self):
+		# contour - guideline
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="contour1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="point1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "contour1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_contour_anchor(self):
+		# contour - anchor
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="anchor1">
+					<point x="1" y="-2" type="move" identifier="point1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "anchor1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_component_component(self):
+		# component - component
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="point1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_component_guideline(self):
+		# component - guideline
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="component1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="point1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "component1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_component_anchor(self):
+		# component - anchor
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="point1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="anchor1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "anchor1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_guideline_guideline(self):
+		# guideline - guideline
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline1"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="point1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline1", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_guideline_anchor(self):
+		# guideline - anchor
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="anchor1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor2"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="point1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "anchor1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor2", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
+
+	def testIdentifierConflict_anchor_anchor(self):
+		# anchor - anchor
+		glif = """
+		<glyph name="a" format="2">
+			<guideline x="0" identifier="guideline1"/>
+			<guideline x="0" identifier="guideline2"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<anchor x="0" y="0" identifier="anchor1"/>
+			<outline>
+				<contour identifier="contour1">
+					<point x="1" y="-2" type="move" identifier="point1"/>
+					<point x="1" y="-2" type="line" identifier="point2"/>
+					<point x="1" y="-2" type="curve" identifier="point3"/>
+					<point x="1" y="-2" type="qcurve" identifier="point4"/>
+				</contour>
+				<contour identifier="contour2">
+					<point x="1" y="-2" type="move" identifier="point5"/>
+				</contour>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component1"/>
+				<component base="x" xyScale="1" yxScale="1" xOffset="1" yOffset="1" identifier="component2"/>
+			</outline>
+		</glyph>
+		"""
+		py = """
+		glyph.name = "a"
+		glyph.guidelines = [{"identifier" : "guideline1", "x" : 0}, {"identifier" : "guideline2", "x" : 0}]
+		glyph.anchors = [{"identifier" : "anchor1", "x" : 0, "y" : 0}, {"identifier" : "anchor1", "x" : 0, "y" : 0}]
+		pointPen.beginPath(**{"identifier" : "contour1"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point1", "segmentType" : "move", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point2", "segmentType" : "line", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point3", "segmentType" : "curve", "smooth" : False})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point4", "segmentType" : "qcurve", "smooth" : False})
+		pointPen.endPath()
+		pointPen.beginPath(**{"identifier" : "contour2"})
+		pointPen.addPoint(*[(1, -2)], **{"identifier" : "point5", "segmentType" : "move", "smooth" : False})
+		pointPen.endPath()
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component1"})
+		pointPen.addComponent(*["x", (1, 1, 1, 1, 1, 1)], **{"identifier" : "component2"})
+		"""
+		self.assertRaises(GlifLibError, self.pyToGLIF, py)
+		self.assertRaises(GlifLibError, self.glifToPy, glif)
diff --git a/Tests/ufoLib/UFO1_test.py b/Tests/ufoLib/UFO1_test.py
new file mode 100644
index 0000000..5feb045
--- /dev/null
+++ b/Tests/ufoLib/UFO1_test.py
@@ -0,0 +1,150 @@
+import os
+import shutil
+import unittest
+import tempfile
+from io import open
+from fontTools.ufoLib import UFOReader, UFOWriter, UFOLibError
+from fontTools.ufoLib import plistlib
+from .testSupport import fontInfoVersion1, fontInfoVersion2
+
+
+class TestInfoObject: pass
+
+
+class ReadFontInfoVersion1TestCase(unittest.TestCase):
+
+	def setUp(self):
+		self.dstDir = tempfile.mktemp()
+		os.mkdir(self.dstDir)
+		metaInfo = {
+			"creator": "test",
+			"formatVersion": 1
+		}
+		path = os.path.join(self.dstDir, "metainfo.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(metaInfo, f)
+
+	def tearDown(self):
+		shutil.rmtree(self.dstDir)
+
+	def _writeInfoToPlist(self, info):
+		path = os.path.join(self.dstDir, "fontinfo.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(info, f)
+
+	def testRead(self):
+		originalData = dict(fontInfoVersion1)
+		self._writeInfoToPlist(originalData)
+		infoObject = TestInfoObject()
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(infoObject)
+		for attr in dir(infoObject):
+			if attr not in fontInfoVersion2:
+				continue
+			originalValue = fontInfoVersion2[attr]
+			readValue = getattr(infoObject, attr)
+			self.assertEqual(originalValue, readValue)
+
+	def testFontStyleConversion(self):
+		fontStyle1To2 = {
+			64 : "regular",
+			1  : "italic",
+			32 : "bold",
+			33 : "bold italic"
+		}
+		for old, new in list(fontStyle1To2.items()):
+			info = dict(fontInfoVersion1)
+			info["fontStyle"] = old
+			self._writeInfoToPlist(info)
+			reader = UFOReader(self.dstDir, validate=True)
+			infoObject = TestInfoObject()
+			reader.readInfo(infoObject)
+			self.assertEqual(new, infoObject.styleMapStyleName)
+
+	def testWidthNameConversion(self):
+		widthName1To2 = {
+			"Ultra-condensed" : 1,
+			"Extra-condensed" : 2,
+			"Condensed"		  : 3,
+			"Semi-condensed"  : 4,
+			"Medium (normal)" : 5,
+			"Semi-expanded"	  : 6,
+			"Expanded"		  : 7,
+			"Extra-expanded"  : 8,
+			"Ultra-expanded"  : 9
+		}
+		for old, new in list(widthName1To2.items()):
+			info = dict(fontInfoVersion1)
+			info["widthName"] = old
+			self._writeInfoToPlist(info)
+			reader = UFOReader(self.dstDir, validate=True)
+			infoObject = TestInfoObject()
+			reader.readInfo(infoObject)
+			self.assertEqual(new, infoObject.openTypeOS2WidthClass)
+
+
+class WriteFontInfoVersion1TestCase(unittest.TestCase):
+
+	def setUp(self):
+		self.tempDir = tempfile.mktemp()
+		os.mkdir(self.tempDir)
+		self.dstDir = os.path.join(self.tempDir, "test.ufo")
+
+	def tearDown(self):
+		shutil.rmtree(self.tempDir)
+
+	def makeInfoObject(self):
+		infoObject = TestInfoObject()
+		for attr, value in list(fontInfoVersion2.items()):
+			setattr(infoObject, attr, value)
+		return infoObject
+
+	def readPlist(self):
+		path = os.path.join(self.dstDir, "fontinfo.plist")
+		with open(path, "rb") as f:
+			plist = plistlib.load(f)
+		return plist
+
+	def testWrite(self):
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=1)
+		writer.writeInfo(infoObject)
+		writtenData = self.readPlist()
+		for attr, originalValue in list(fontInfoVersion1.items()):
+			newValue = writtenData[attr]
+			self.assertEqual(newValue, originalValue)
+
+	def testFontStyleConversion(self):
+		fontStyle1To2 = {
+			64 : "regular",
+			1  : "italic",
+			32 : "bold",
+			33 : "bold italic"
+		}
+		for old, new in list(fontStyle1To2.items()):
+			infoObject = self.makeInfoObject()
+			infoObject.styleMapStyleName = new
+			writer = UFOWriter(self.dstDir, formatVersion=1)
+			writer.writeInfo(infoObject)
+			writtenData = self.readPlist()
+			self.assertEqual(writtenData["fontStyle"], old)
+
+	def testWidthNameConversion(self):
+		widthName1To2 = {
+			"Ultra-condensed" : 1,
+			"Extra-condensed" : 2,
+			"Condensed"		  : 3,
+			"Semi-condensed"  : 4,
+			"Medium (normal)" : 5,
+			"Semi-expanded"	  : 6,
+			"Expanded"		  : 7,
+			"Extra-expanded"  : 8,
+			"Ultra-expanded"  : 9
+		}
+		for old, new in list(widthName1To2.items()):
+			infoObject = self.makeInfoObject()
+			infoObject.openTypeOS2WidthClass = new
+			writer = UFOWriter(self.dstDir, formatVersion=1)
+			writer.writeInfo(infoObject)
+			writtenData = self.readPlist()
+			self.assertEqual(writtenData["widthName"], old)
diff --git a/Tests/ufoLib/UFO2_test.py b/Tests/ufoLib/UFO2_test.py
new file mode 100644
index 0000000..68b4baf
--- /dev/null
+++ b/Tests/ufoLib/UFO2_test.py
@@ -0,0 +1,1412 @@
+import os
+import shutil
+import unittest
+import tempfile
+from io import open
+from fontTools.ufoLib import UFOReader, UFOWriter, UFOLibError
+from fontTools.ufoLib import plistlib
+from .testSupport import fontInfoVersion2
+
+
+class TestInfoObject: pass
+
+
+class ReadFontInfoVersion2TestCase(unittest.TestCase):
+
+	def setUp(self):
+		self.dstDir = tempfile.mktemp()
+		os.mkdir(self.dstDir)
+		metaInfo = {
+			"creator": "test",
+			"formatVersion": 2
+		}
+		path = os.path.join(self.dstDir, "metainfo.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(metaInfo, f)
+
+	def tearDown(self):
+		shutil.rmtree(self.dstDir)
+
+	def _writeInfoToPlist(self, info):
+		path = os.path.join(self.dstDir, "fontinfo.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(info, f)
+
+	def testRead(self):
+		originalData = dict(fontInfoVersion2)
+		self._writeInfoToPlist(originalData)
+		infoObject = TestInfoObject()
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(infoObject)
+		readData = {}
+		for attr in list(fontInfoVersion2.keys()):
+			readData[attr] = getattr(infoObject, attr)
+		self.assertEqual(originalData, readData)
+
+	def testGenericRead(self):
+		# familyName
+		info = dict(fontInfoVersion2)
+		info["familyName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# styleName
+		info = dict(fontInfoVersion2)
+		info["styleName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# styleMapFamilyName
+		info = dict(fontInfoVersion2)
+		info["styleMapFamilyName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# styleMapStyleName
+		## not a string
+		info = dict(fontInfoVersion2)
+		info["styleMapStyleName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out of range
+		info = dict(fontInfoVersion2)
+		info["styleMapStyleName"] = "REGULAR"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# versionMajor
+		info = dict(fontInfoVersion2)
+		info["versionMajor"] = "1"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# versionMinor
+		info = dict(fontInfoVersion2)
+		info["versionMinor"] = "0"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# copyright
+		info = dict(fontInfoVersion2)
+		info["copyright"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# trademark
+		info = dict(fontInfoVersion2)
+		info["trademark"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# unitsPerEm
+		info = dict(fontInfoVersion2)
+		info["unitsPerEm"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# descender
+		info = dict(fontInfoVersion2)
+		info["descender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# xHeight
+		info = dict(fontInfoVersion2)
+		info["xHeight"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# capHeight
+		info = dict(fontInfoVersion2)
+		info["capHeight"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# ascender
+		info = dict(fontInfoVersion2)
+		info["ascender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# italicAngle
+		info = dict(fontInfoVersion2)
+		info["italicAngle"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testHeadRead(self):
+		# openTypeHeadCreated
+		## not a string
+		info = dict(fontInfoVersion2)
+		info["openTypeHeadCreated"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## invalid format
+		info = dict(fontInfoVersion2)
+		info["openTypeHeadCreated"] = "2000-Jan-01 00:00:00"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHeadLowestRecPPEM
+		info = dict(fontInfoVersion2)
+		info["openTypeHeadLowestRecPPEM"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHeadFlags
+		info = dict(fontInfoVersion2)
+		info["openTypeHeadFlags"] = [-1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testHheaRead(self):
+		# openTypeHheaAscender
+		info = dict(fontInfoVersion2)
+		info["openTypeHheaAscender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHheaDescender
+		info = dict(fontInfoVersion2)
+		info["openTypeHheaDescender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHheaLineGap
+		info = dict(fontInfoVersion2)
+		info["openTypeHheaLineGap"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHheaCaretSlopeRise
+		info = dict(fontInfoVersion2)
+		info["openTypeHheaCaretSlopeRise"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHheaCaretSlopeRun
+		info = dict(fontInfoVersion2)
+		info["openTypeHheaCaretSlopeRun"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHheaCaretOffset
+		info = dict(fontInfoVersion2)
+		info["openTypeHheaCaretOffset"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testNameRead(self):
+		# openTypeNameDesigner
+		info = dict(fontInfoVersion2)
+		info["openTypeNameDesigner"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameDesignerURL
+		info = dict(fontInfoVersion2)
+		info["openTypeNameDesignerURL"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameManufacturer
+		info = dict(fontInfoVersion2)
+		info["openTypeNameManufacturer"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameManufacturerURL
+		info = dict(fontInfoVersion2)
+		info["openTypeNameManufacturerURL"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameLicense
+		info = dict(fontInfoVersion2)
+		info["openTypeNameLicense"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameLicenseURL
+		info = dict(fontInfoVersion2)
+		info["openTypeNameLicenseURL"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameVersion
+		info = dict(fontInfoVersion2)
+		info["openTypeNameVersion"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameUniqueID
+		info = dict(fontInfoVersion2)
+		info["openTypeNameUniqueID"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameDescription
+		info = dict(fontInfoVersion2)
+		info["openTypeNameDescription"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNamePreferredFamilyName
+		info = dict(fontInfoVersion2)
+		info["openTypeNamePreferredFamilyName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNamePreferredSubfamilyName
+		info = dict(fontInfoVersion2)
+		info["openTypeNamePreferredSubfamilyName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameCompatibleFullName
+		info = dict(fontInfoVersion2)
+		info["openTypeNameCompatibleFullName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameSampleText
+		info = dict(fontInfoVersion2)
+		info["openTypeNameSampleText"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameWWSFamilyName
+		info = dict(fontInfoVersion2)
+		info["openTypeNameWWSFamilyName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameWWSSubfamilyName
+		info = dict(fontInfoVersion2)
+		info["openTypeNameWWSSubfamilyName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testOS2Read(self):
+		# openTypeOS2WidthClass
+		## not an int
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2WidthClass"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out or range
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2WidthClass"] = 15
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2WeightClass
+		info = dict(fontInfoVersion2)
+		## not an int
+		info["openTypeOS2WeightClass"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out of range
+		info["openTypeOS2WeightClass"] = -50
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2Selection
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2Selection"] = [-1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2VendorID
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2VendorID"] = 1234
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2Panose
+		## not an int
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2Panose"] = [0, 1, 2, 3, 4, 5, 6, 7, 8, str(9)]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## too few values
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2Panose"] = [0, 1, 2, 3]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2Panose"] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2FamilyClass
+		## not an int
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2FamilyClass"] = [1, str(1)]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## too few values
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2FamilyClass"] = [1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2FamilyClass"] = [1, 1, 1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out of range
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2FamilyClass"] = [1, 201]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2UnicodeRanges
+		## not an int
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2UnicodeRanges"] = ["0"]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out of range
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2UnicodeRanges"] = [-1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2CodePageRanges
+		## not an int
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2CodePageRanges"] = ["0"]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out of range
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2CodePageRanges"] = [-1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2TypoAscender
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2TypoAscender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2TypoDescender
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2TypoDescender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2TypoLineGap
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2TypoLineGap"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2WinAscent
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2WinAscent"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2WinDescent
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2WinDescent"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2Type
+		## not an int
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2Type"] = ["1"]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out of range
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2Type"] = [-1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SubscriptXSize
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2SubscriptXSize"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SubscriptYSize
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2SubscriptYSize"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SubscriptXOffset
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2SubscriptXOffset"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SubscriptYOffset
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2SubscriptYOffset"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SuperscriptXSize
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2SuperscriptXSize"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SuperscriptYSize
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2SuperscriptYSize"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SuperscriptXOffset
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2SuperscriptXOffset"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SuperscriptYOffset
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2SuperscriptYOffset"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2StrikeoutSize
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2StrikeoutSize"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2StrikeoutPosition
+		info = dict(fontInfoVersion2)
+		info["openTypeOS2StrikeoutPosition"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testVheaRead(self):
+		# openTypeVheaVertTypoAscender
+		info = dict(fontInfoVersion2)
+		info["openTypeVheaVertTypoAscender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeVheaVertTypoDescender
+		info = dict(fontInfoVersion2)
+		info["openTypeVheaVertTypoDescender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeVheaVertTypoLineGap
+		info = dict(fontInfoVersion2)
+		info["openTypeVheaVertTypoLineGap"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeVheaCaretSlopeRise
+		info = dict(fontInfoVersion2)
+		info["openTypeVheaCaretSlopeRise"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeVheaCaretSlopeRun
+		info = dict(fontInfoVersion2)
+		info["openTypeVheaCaretSlopeRun"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeVheaCaretOffset
+		info = dict(fontInfoVersion2)
+		info["openTypeVheaCaretOffset"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testFONDRead(self):
+		# macintoshFONDFamilyID
+		info = dict(fontInfoVersion2)
+		info["macintoshFONDFamilyID"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# macintoshFONDName
+		info = dict(fontInfoVersion2)
+		info["macintoshFONDName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testPostscriptRead(self):
+		# postscriptFontName
+		info = dict(fontInfoVersion2)
+		info["postscriptFontName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# postscriptFullName
+		info = dict(fontInfoVersion2)
+		info["postscriptFullName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# postscriptSlantAngle
+		info = dict(fontInfoVersion2)
+		info["postscriptSlantAngle"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# postscriptUniqueID
+		info = dict(fontInfoVersion2)
+		info["postscriptUniqueID"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptUnderlineThickness
+		info = dict(fontInfoVersion2)
+		info["postscriptUnderlineThickness"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptUnderlinePosition
+		info = dict(fontInfoVersion2)
+		info["postscriptUnderlinePosition"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptIsFixedPitch
+		info = dict(fontInfoVersion2)
+		info["postscriptIsFixedPitch"] = 2
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptBlueValues
+		## not a list
+		info = dict(fontInfoVersion2)
+		info["postscriptBlueValues"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## uneven value count
+		info = dict(fontInfoVersion2)
+		info["postscriptBlueValues"] = [500]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion2)
+		info["postscriptBlueValues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptOtherBlues
+		## not a list
+		info = dict(fontInfoVersion2)
+		info["postscriptOtherBlues"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## uneven value count
+		info = dict(fontInfoVersion2)
+		info["postscriptOtherBlues"] = [500]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion2)
+		info["postscriptOtherBlues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptFamilyBlues
+		## not a list
+		info = dict(fontInfoVersion2)
+		info["postscriptFamilyBlues"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## uneven value count
+		info = dict(fontInfoVersion2)
+		info["postscriptFamilyBlues"] = [500]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion2)
+		info["postscriptFamilyBlues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptFamilyOtherBlues
+		## not a list
+		info = dict(fontInfoVersion2)
+		info["postscriptFamilyOtherBlues"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## uneven value count
+		info = dict(fontInfoVersion2)
+		info["postscriptFamilyOtherBlues"] = [500]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion2)
+		info["postscriptFamilyOtherBlues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptStemSnapH
+		## not list
+		info = dict(fontInfoVersion2)
+		info["postscriptStemSnapH"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion2)
+		info["postscriptStemSnapH"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptStemSnapV
+		## not list
+		info = dict(fontInfoVersion2)
+		info["postscriptStemSnapV"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion2)
+		info["postscriptStemSnapV"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptBlueFuzz
+		info = dict(fontInfoVersion2)
+		info["postscriptBlueFuzz"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptBlueShift
+		info = dict(fontInfoVersion2)
+		info["postscriptBlueShift"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptBlueScale
+		info = dict(fontInfoVersion2)
+		info["postscriptBlueScale"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptForceBold
+		info = dict(fontInfoVersion2)
+		info["postscriptForceBold"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptDefaultWidthX
+		info = dict(fontInfoVersion2)
+		info["postscriptDefaultWidthX"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptNominalWidthX
+		info = dict(fontInfoVersion2)
+		info["postscriptNominalWidthX"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptWeightName
+		info = dict(fontInfoVersion2)
+		info["postscriptWeightName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptDefaultCharacter
+		info = dict(fontInfoVersion2)
+		info["postscriptDefaultCharacter"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptWindowsCharacterSet
+		info = dict(fontInfoVersion2)
+		info["postscriptWindowsCharacterSet"] = -1
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# macintoshFONDFamilyID
+		info = dict(fontInfoVersion2)
+		info["macintoshFONDFamilyID"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# macintoshFONDName
+		info = dict(fontInfoVersion2)
+		info["macintoshFONDName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+
+
+class WriteFontInfoVersion2TestCase(unittest.TestCase):
+
+	def setUp(self):
+		self.tempDir = tempfile.mktemp()
+		os.mkdir(self.tempDir)
+		self.dstDir = os.path.join(self.tempDir, "test.ufo")
+
+	def tearDown(self):
+		shutil.rmtree(self.tempDir)
+
+	def makeInfoObject(self):
+		infoObject = TestInfoObject()
+		for attr, value in list(fontInfoVersion2.items()):
+			setattr(infoObject, attr, value)
+		return infoObject
+
+	def readPlist(self):
+		path = os.path.join(self.dstDir, "fontinfo.plist")
+		with open(path, "rb") as f:
+			plist = plistlib.load(f)
+		return plist
+
+	def testWrite(self):
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		writer.writeInfo(infoObject)
+		writtenData = self.readPlist()
+		for attr, originalValue in list(fontInfoVersion2.items()):
+			newValue = writtenData[attr]
+			self.assertEqual(newValue, originalValue)
+
+	def testGenericWrite(self):
+		# familyName
+		infoObject = self.makeInfoObject()
+		infoObject.familyName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# styleName
+		infoObject = self.makeInfoObject()
+		infoObject.styleName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# styleMapFamilyName
+		infoObject = self.makeInfoObject()
+		infoObject.styleMapFamilyName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# styleMapStyleName
+		## not a string
+		infoObject = self.makeInfoObject()
+		infoObject.styleMapStyleName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## out of range
+		infoObject = self.makeInfoObject()
+		infoObject.styleMapStyleName = "REGULAR"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# versionMajor
+		infoObject = self.makeInfoObject()
+		infoObject.versionMajor = "1"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# versionMinor
+		infoObject = self.makeInfoObject()
+		infoObject.versionMinor = "0"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# copyright
+		infoObject = self.makeInfoObject()
+		infoObject.copyright = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# trademark
+		infoObject = self.makeInfoObject()
+		infoObject.trademark = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# unitsPerEm
+		infoObject = self.makeInfoObject()
+		infoObject.unitsPerEm = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# descender
+		infoObject = self.makeInfoObject()
+		infoObject.descender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# xHeight
+		infoObject = self.makeInfoObject()
+		infoObject.xHeight = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# capHeight
+		infoObject = self.makeInfoObject()
+		infoObject.capHeight = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# ascender
+		infoObject = self.makeInfoObject()
+		infoObject.ascender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# italicAngle
+		infoObject = self.makeInfoObject()
+		infoObject.italicAngle = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+
+	def testHeadWrite(self):
+		# openTypeHeadCreated
+		## not a string
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHeadCreated = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## invalid format
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHeadCreated = "2000-Jan-01 00:00:00"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeHeadLowestRecPPEM
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHeadLowestRecPPEM = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeHeadFlags
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHeadFlags = [-1]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+
+	def testHheaWrite(self):
+		# openTypeHheaAscender
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHheaAscender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeHheaDescender
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHheaDescender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeHheaLineGap
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHheaLineGap = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeHheaCaretSlopeRise
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHheaCaretSlopeRise = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeHheaCaretSlopeRun
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHheaCaretSlopeRun = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeHheaCaretOffset
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHheaCaretOffset = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+
+	def testNameWrite(self):
+		# openTypeNameDesigner
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameDesigner = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNameDesignerURL
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameDesignerURL = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNameManufacturer
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameManufacturer = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNameManufacturerURL
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameManufacturerURL = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNameLicense
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameLicense = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNameLicenseURL
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameLicenseURL = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNameVersion
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameVersion = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNameUniqueID
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameUniqueID = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNameDescription
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameDescription = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNamePreferredFamilyName
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNamePreferredFamilyName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNamePreferredSubfamilyName
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNamePreferredSubfamilyName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNameCompatibleFullName
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameCompatibleFullName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNameSampleText
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameSampleText = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNameWWSFamilyName
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameWWSFamilyName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeNameWWSSubfamilyName
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameWWSSubfamilyName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+
+	def testOS2Write(self):
+		# openTypeOS2WidthClass
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2WidthClass = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## out or range
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2WidthClass = 15
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2WeightClass
+		infoObject = self.makeInfoObject()
+		## not an int
+		infoObject.openTypeOS2WeightClass = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## out of range
+		infoObject.openTypeOS2WeightClass = -50
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2Selection
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2Selection = [-1]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2VendorID
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2VendorID = 1234
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2Panose
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2Panose = [0, 1, 2, 3, 4, 5, 6, 7, 8, str(9)]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## too few values
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2Panose = [0, 1, 2, 3]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2Panose = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2FamilyClass
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2FamilyClass = [0, str(1)]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## too few values
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2FamilyClass = [1]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2FamilyClass = [1, 1, 1]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## out of range
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2FamilyClass = [1, 20]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2UnicodeRanges
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2UnicodeRanges = ["0"]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## out of range
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2UnicodeRanges = [-1]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2CodePageRanges
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2CodePageRanges = ["0"]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## out of range
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2CodePageRanges = [-1]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2TypoAscender
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2TypoAscender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2TypoDescender
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2TypoDescender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2TypoLineGap
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2TypoLineGap = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2WinAscent
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2WinAscent = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2WinDescent
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2WinDescent = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2Type
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2Type = ["1"]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## out of range
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2Type = [-1]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2SubscriptXSize
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SubscriptXSize = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2SubscriptYSize
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SubscriptYSize = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2SubscriptXOffset
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SubscriptXOffset = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2SubscriptYOffset
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SubscriptYOffset = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2SuperscriptXSize
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SuperscriptXSize = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2SuperscriptYSize
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SuperscriptYSize = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2SuperscriptXOffset
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SuperscriptXOffset = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2SuperscriptYOffset
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SuperscriptYOffset = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2StrikeoutSize
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2StrikeoutSize = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeOS2StrikeoutPosition
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2StrikeoutPosition = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+
+	def testVheaWrite(self):
+		# openTypeVheaVertTypoAscender
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeVheaVertTypoAscender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeVheaVertTypoDescender
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeVheaVertTypoDescender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeVheaVertTypoLineGap
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeVheaVertTypoLineGap = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeVheaCaretSlopeRise
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeVheaCaretSlopeRise = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeVheaCaretSlopeRun
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeVheaCaretSlopeRun = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# openTypeVheaCaretOffset
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeVheaCaretOffset = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+
+	def testFONDWrite(self):
+		# macintoshFONDFamilyID
+		infoObject = self.makeInfoObject()
+		infoObject.macintoshFONDFamilyID = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# macintoshFONDName
+		infoObject = self.makeInfoObject()
+		infoObject.macintoshFONDName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+
+	def testPostscriptWrite(self):
+		# postscriptFontName
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFontName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptFullName
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFullName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptSlantAngle
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptSlantAngle = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptUniqueID
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptUniqueID = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptUnderlineThickness
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptUnderlineThickness = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptUnderlinePosition
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptUnderlinePosition = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptIsFixedPitch
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptIsFixedPitch = 2
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptBlueValues
+		## not a list
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptBlueValues = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## uneven value count
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptBlueValues = [500]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptBlueValues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptOtherBlues
+		## not a list
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptOtherBlues = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## uneven value count
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptOtherBlues = [500]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptOtherBlues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptFamilyBlues
+		## not a list
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFamilyBlues = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## uneven value count
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFamilyBlues = [500]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFamilyBlues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptFamilyOtherBlues
+		## not a list
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFamilyOtherBlues = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## uneven value count
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFamilyOtherBlues = [500]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFamilyOtherBlues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptStemSnapH
+		## not list
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptStemSnapH = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptStemSnapH = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptStemSnapV
+		## not list
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptStemSnapV = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptStemSnapV = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptBlueFuzz
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptBlueFuzz = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptBlueShift
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptBlueShift = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptBlueScale
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptBlueScale = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptForceBold
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptForceBold = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptDefaultWidthX
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptDefaultWidthX = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptNominalWidthX
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptNominalWidthX = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptWeightName
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptWeightName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptDefaultCharacter
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptDefaultCharacter = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# postscriptWindowsCharacterSet
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptWindowsCharacterSet = -1
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# macintoshFONDFamilyID
+		infoObject = self.makeInfoObject()
+		infoObject.macintoshFONDFamilyID = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		# macintoshFONDName
+		infoObject = self.makeInfoObject()
+		infoObject.macintoshFONDName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
diff --git a/Tests/ufoLib/UFO3_test.py b/Tests/ufoLib/UFO3_test.py
new file mode 100644
index 0000000..c421802
--- /dev/null
+++ b/Tests/ufoLib/UFO3_test.py
@@ -0,0 +1,4713 @@
+import os
+import shutil
+import unittest
+import tempfile
+from io import open
+from fontTools.ufoLib import UFOReader, UFOWriter, UFOLibError
+from fontTools.ufoLib.glifLib import GlifLibError
+from fontTools.misc import plistlib
+from .testSupport import fontInfoVersion3
+
+
+class TestInfoObject: pass
+
+
+# --------------
+# fontinfo.plist
+# --------------
+
+class ReadFontInfoVersion3TestCase(unittest.TestCase):
+
+	def setUp(self):
+		self.dstDir = tempfile.mktemp()
+		os.mkdir(self.dstDir)
+		metaInfo = {
+			"creator": "test",
+			"formatVersion": 3
+		}
+		path = os.path.join(self.dstDir, "metainfo.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(metaInfo, f)
+
+	def tearDown(self):
+		shutil.rmtree(self.dstDir)
+
+	def _writeInfoToPlist(self, info):
+		path = os.path.join(self.dstDir, "fontinfo.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(info, f)
+
+	def testRead(self):
+		originalData = dict(fontInfoVersion3)
+		self._writeInfoToPlist(originalData)
+		infoObject = TestInfoObject()
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(infoObject)
+		readData = {}
+		for attr in list(fontInfoVersion3.keys()):
+			readData[attr] = getattr(infoObject, attr)
+		self.assertEqual(originalData, readData)
+
+	def testGenericRead(self):
+		# familyName
+		info = dict(fontInfoVersion3)
+		info["familyName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# styleName
+		info = dict(fontInfoVersion3)
+		info["styleName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# styleMapFamilyName
+		info = dict(fontInfoVersion3)
+		info["styleMapFamilyName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# styleMapStyleName
+		## not a string
+		info = dict(fontInfoVersion3)
+		info["styleMapStyleName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out of range
+		info = dict(fontInfoVersion3)
+		info["styleMapStyleName"] = "REGULAR"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# versionMajor
+		info = dict(fontInfoVersion3)
+		info["versionMajor"] = "1"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# versionMinor
+		info = dict(fontInfoVersion3)
+		info["versionMinor"] = "0"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["versionMinor"] = -1
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# copyright
+		info = dict(fontInfoVersion3)
+		info["copyright"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# trademark
+		info = dict(fontInfoVersion3)
+		info["trademark"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# unitsPerEm
+		info = dict(fontInfoVersion3)
+		info["unitsPerEm"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["unitsPerEm"] = -1
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["unitsPerEm"] = -1.0
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# descender
+		info = dict(fontInfoVersion3)
+		info["descender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# xHeight
+		info = dict(fontInfoVersion3)
+		info["xHeight"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# capHeight
+		info = dict(fontInfoVersion3)
+		info["capHeight"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# ascender
+		info = dict(fontInfoVersion3)
+		info["ascender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# italicAngle
+		info = dict(fontInfoVersion3)
+		info["italicAngle"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testGaspRead(self):
+		# not a list
+		info = dict(fontInfoVersion3)
+		info["openTypeGaspRangeRecords"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# empty list
+		info = dict(fontInfoVersion3)
+		info["openTypeGaspRangeRecords"] = []
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		# not a dict
+		info = dict(fontInfoVersion3)
+		info["openTypeGaspRangeRecords"] = ["abc"]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# dict not properly formatted
+		info = dict(fontInfoVersion3)
+		info["openTypeGaspRangeRecords"] = [dict(rangeMaxPPEM=0xFFFF, notTheRightKey=1)]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeGaspRangeRecords"] = [dict(notTheRightKey=1, rangeGaspBehavior=[0])]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# not an int for ppem
+		info = dict(fontInfoVersion3)
+		info["openTypeGaspRangeRecords"] = [dict(rangeMaxPPEM="abc", rangeGaspBehavior=[0]), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# not a list for behavior
+		info = dict(fontInfoVersion3)
+		info["openTypeGaspRangeRecords"] = [dict(rangeMaxPPEM=10, rangeGaspBehavior="abc"), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# invalid behavior value
+		info = dict(fontInfoVersion3)
+		info["openTypeGaspRangeRecords"] = [dict(rangeMaxPPEM=10, rangeGaspBehavior=[-1]), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# not sorted
+		info = dict(fontInfoVersion3)
+		info["openTypeGaspRangeRecords"] = [dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0]), dict(rangeMaxPPEM=10, rangeGaspBehavior=[0])]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# no 0xFFFF
+		info = dict(fontInfoVersion3)
+		info["openTypeGaspRangeRecords"] = [dict(rangeMaxPPEM=10, rangeGaspBehavior=[0]), dict(rangeMaxPPEM=20, rangeGaspBehavior=[0])]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+
+	def testHeadRead(self):
+		# openTypeHeadCreated
+		## not a string
+		info = dict(fontInfoVersion3)
+		info["openTypeHeadCreated"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## invalid format
+		info = dict(fontInfoVersion3)
+		info["openTypeHeadCreated"] = "2000-Jan-01 00:00:00"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHeadLowestRecPPEM
+		info = dict(fontInfoVersion3)
+		info["openTypeHeadLowestRecPPEM"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeHeadLowestRecPPEM"] = -1
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHeadFlags
+		info = dict(fontInfoVersion3)
+		info["openTypeHeadFlags"] = [-1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testHheaRead(self):
+		# openTypeHheaAscender
+		info = dict(fontInfoVersion3)
+		info["openTypeHheaAscender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHheaDescender
+		info = dict(fontInfoVersion3)
+		info["openTypeHheaDescender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHheaLineGap
+		info = dict(fontInfoVersion3)
+		info["openTypeHheaLineGap"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHheaCaretSlopeRise
+		info = dict(fontInfoVersion3)
+		info["openTypeHheaCaretSlopeRise"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHheaCaretSlopeRun
+		info = dict(fontInfoVersion3)
+		info["openTypeHheaCaretSlopeRun"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeHheaCaretOffset
+		info = dict(fontInfoVersion3)
+		info["openTypeHheaCaretOffset"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testNameRead(self):
+		# openTypeNameDesigner
+		info = dict(fontInfoVersion3)
+		info["openTypeNameDesigner"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameDesignerURL
+		info = dict(fontInfoVersion3)
+		info["openTypeNameDesignerURL"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameManufacturer
+		info = dict(fontInfoVersion3)
+		info["openTypeNameManufacturer"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameManufacturerURL
+		info = dict(fontInfoVersion3)
+		info["openTypeNameManufacturerURL"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameLicense
+		info = dict(fontInfoVersion3)
+		info["openTypeNameLicense"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameLicenseURL
+		info = dict(fontInfoVersion3)
+		info["openTypeNameLicenseURL"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameVersion
+		info = dict(fontInfoVersion3)
+		info["openTypeNameVersion"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameUniqueID
+		info = dict(fontInfoVersion3)
+		info["openTypeNameUniqueID"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameDescription
+		info = dict(fontInfoVersion3)
+		info["openTypeNameDescription"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNamePreferredFamilyName
+		info = dict(fontInfoVersion3)
+		info["openTypeNamePreferredFamilyName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNamePreferredSubfamilyName
+		info = dict(fontInfoVersion3)
+		info["openTypeNamePreferredSubfamilyName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameCompatibleFullName
+		info = dict(fontInfoVersion3)
+		info["openTypeNameCompatibleFullName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameSampleText
+		info = dict(fontInfoVersion3)
+		info["openTypeNameSampleText"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameWWSFamilyName
+		info = dict(fontInfoVersion3)
+		info["openTypeNameWWSFamilyName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameWWSSubfamilyName
+		info = dict(fontInfoVersion3)
+		info["openTypeNameWWSSubfamilyName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeNameRecords
+		## not a list
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## not a dict
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = ["abc"]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## invalid dict structure
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [dict(foo="bar")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## incorrect keys
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [
+			dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record.", foo="bar")
+		]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [
+			dict(platformID=1, encodingID=1, languageID=1, string="Name Record.")
+		]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [
+			dict(nameID=1, encodingID=1, languageID=1, string="Name Record.")
+		]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [
+			dict(nameID=1, platformID=1, languageID=1, string="Name Record.")
+		]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [
+			dict(nameID=1, platformID=1, encodingID=1, string="Name Record.")
+		]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [
+			dict(nameID=1, platformID=1, encodingID=1, languageID=1)
+		]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## invalid values
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [
+			dict(nameID="1", platformID=1, encodingID=1, languageID=1, string="Name Record.")
+		]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [
+			dict(nameID=1, platformID="1", encodingID=1, languageID=1, string="Name Record.")
+		]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [
+			dict(nameID=1, platformID=1, encodingID="1", languageID=1, string="Name Record.")
+		]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [
+			dict(nameID=1, platformID=1, encodingID=1, languageID="1", string="Name Record.")
+		]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [
+			dict(nameID=1, platformID=1, encodingID=1, languageID=1, string=1)
+		]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## duplicate
+		info = dict(fontInfoVersion3)
+		info["openTypeNameRecords"] = [
+			dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record."),
+			dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record.")
+		]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+
+	def testOS2Read(self):
+		# openTypeOS2WidthClass
+		## not an int
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2WidthClass"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out or range
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2WidthClass"] = 15
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2WeightClass
+		info = dict(fontInfoVersion3)
+		## not an int
+		info["openTypeOS2WeightClass"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out of range
+		info["openTypeOS2WeightClass"] = -50
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2Selection
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2Selection"] = [-1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2VendorID
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2VendorID"] = 1234
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2Panose
+		## not an int
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2Panose"] = [0, 1, 2, 3, 4, 5, 6, 7, 8, str(9)]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## negative
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2Panose"] = [0, 1, 2, 3, 4, 5, 6, 7, 8, -9]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## too few values
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2Panose"] = [0, 1, 2, 3]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2Panose"] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2FamilyClass
+		## not an int
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2FamilyClass"] = [1, str(1)]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## too few values
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2FamilyClass"] = [1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2FamilyClass"] = [1, 1, 1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out of range
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2FamilyClass"] = [1, 201]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2UnicodeRanges
+		## not an int
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2UnicodeRanges"] = ["0"]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out of range
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2UnicodeRanges"] = [-1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2CodePageRanges
+		## not an int
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2CodePageRanges"] = ["0"]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out of range
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2CodePageRanges"] = [-1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2TypoAscender
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2TypoAscender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2TypoDescender
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2TypoDescender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2TypoLineGap
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2TypoLineGap"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2WinAscent
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2WinAscent"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2WinAscent"] = -1
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2WinDescent
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2WinDescent"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2WinDescent"] = -1
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2Type
+		## not an int
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2Type"] = ["1"]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		## out of range
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2Type"] = [-1]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SubscriptXSize
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2SubscriptXSize"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SubscriptYSize
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2SubscriptYSize"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SubscriptXOffset
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2SubscriptXOffset"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SubscriptYOffset
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2SubscriptYOffset"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SuperscriptXSize
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2SuperscriptXSize"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SuperscriptYSize
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2SuperscriptYSize"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SuperscriptXOffset
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2SuperscriptXOffset"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2SuperscriptYOffset
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2SuperscriptYOffset"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2StrikeoutSize
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2StrikeoutSize"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeOS2StrikeoutPosition
+		info = dict(fontInfoVersion3)
+		info["openTypeOS2StrikeoutPosition"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testVheaRead(self):
+		# openTypeVheaVertTypoAscender
+		info = dict(fontInfoVersion3)
+		info["openTypeVheaVertTypoAscender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeVheaVertTypoDescender
+		info = dict(fontInfoVersion3)
+		info["openTypeVheaVertTypoDescender"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeVheaVertTypoLineGap
+		info = dict(fontInfoVersion3)
+		info["openTypeVheaVertTypoLineGap"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeVheaCaretSlopeRise
+		info = dict(fontInfoVersion3)
+		info["openTypeVheaCaretSlopeRise"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeVheaCaretSlopeRun
+		info = dict(fontInfoVersion3)
+		info["openTypeVheaCaretSlopeRun"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# openTypeVheaCaretOffset
+		info = dict(fontInfoVersion3)
+		info["openTypeVheaCaretOffset"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testFONDRead(self):
+		# macintoshFONDFamilyID
+		info = dict(fontInfoVersion3)
+		info["macintoshFONDFamilyID"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# macintoshFONDName
+		info = dict(fontInfoVersion3)
+		info["macintoshFONDName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+
+	def testPostscriptRead(self):
+		# postscriptFontName
+		info = dict(fontInfoVersion3)
+		info["postscriptFontName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# postscriptFullName
+		info = dict(fontInfoVersion3)
+		info["postscriptFullName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# postscriptSlantAngle
+		info = dict(fontInfoVersion3)
+		info["postscriptSlantAngle"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, info=TestInfoObject())
+		# postscriptUniqueID
+		info = dict(fontInfoVersion3)
+		info["postscriptUniqueID"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptUnderlineThickness
+		info = dict(fontInfoVersion3)
+		info["postscriptUnderlineThickness"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptUnderlinePosition
+		info = dict(fontInfoVersion3)
+		info["postscriptUnderlinePosition"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptIsFixedPitch
+		info = dict(fontInfoVersion3)
+		info["postscriptIsFixedPitch"] = 2
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptBlueValues
+		## not a list
+		info = dict(fontInfoVersion3)
+		info["postscriptBlueValues"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## uneven value count
+		info = dict(fontInfoVersion3)
+		info["postscriptBlueValues"] = [500]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion3)
+		info["postscriptBlueValues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptOtherBlues
+		## not a list
+		info = dict(fontInfoVersion3)
+		info["postscriptOtherBlues"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## uneven value count
+		info = dict(fontInfoVersion3)
+		info["postscriptOtherBlues"] = [500]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion3)
+		info["postscriptOtherBlues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptFamilyBlues
+		## not a list
+		info = dict(fontInfoVersion3)
+		info["postscriptFamilyBlues"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## uneven value count
+		info = dict(fontInfoVersion3)
+		info["postscriptFamilyBlues"] = [500]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion3)
+		info["postscriptFamilyBlues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptFamilyOtherBlues
+		## not a list
+		info = dict(fontInfoVersion3)
+		info["postscriptFamilyOtherBlues"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## uneven value count
+		info = dict(fontInfoVersion3)
+		info["postscriptFamilyOtherBlues"] = [500]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion3)
+		info["postscriptFamilyOtherBlues"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptStemSnapH
+		## not list
+		info = dict(fontInfoVersion3)
+		info["postscriptStemSnapH"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion3)
+		info["postscriptStemSnapH"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptStemSnapV
+		## not list
+		info = dict(fontInfoVersion3)
+		info["postscriptStemSnapV"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many values
+		info = dict(fontInfoVersion3)
+		info["postscriptStemSnapV"] = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptBlueFuzz
+		info = dict(fontInfoVersion3)
+		info["postscriptBlueFuzz"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptBlueShift
+		info = dict(fontInfoVersion3)
+		info["postscriptBlueShift"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptBlueScale
+		info = dict(fontInfoVersion3)
+		info["postscriptBlueScale"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptForceBold
+		info = dict(fontInfoVersion3)
+		info["postscriptForceBold"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptDefaultWidthX
+		info = dict(fontInfoVersion3)
+		info["postscriptDefaultWidthX"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptNominalWidthX
+		info = dict(fontInfoVersion3)
+		info["postscriptNominalWidthX"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptWeightName
+		info = dict(fontInfoVersion3)
+		info["postscriptWeightName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptDefaultCharacter
+		info = dict(fontInfoVersion3)
+		info["postscriptDefaultCharacter"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# postscriptWindowsCharacterSet
+		info = dict(fontInfoVersion3)
+		info["postscriptWindowsCharacterSet"] = -1
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# macintoshFONDFamilyID
+		info = dict(fontInfoVersion3)
+		info["macintoshFONDFamilyID"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# macintoshFONDName
+		info = dict(fontInfoVersion3)
+		info["macintoshFONDName"] = 123
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+
+	def testWOFFRead(self):
+		# woffMajorVersion
+		info = dict(fontInfoVersion3)
+		info["woffMajorVersion"] = 1.0
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["woffMajorVersion"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# woffMinorVersion
+		info = dict(fontInfoVersion3)
+		info["woffMinorVersion"] = 1.0
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["woffMinorVersion"] = "abc"
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# woffMetadataUniqueID
+		## none
+		info = dict(fontInfoVersion3)
+		del info["woffMetadataUniqueID"]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		## not a dict
+		info = dict(fontInfoVersion3)
+		info["woffMetadataUniqueID"] = 1
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## unknown key
+		info = dict(fontInfoVersion3)
+		info["woffMetadataUniqueID"] = dict(id="foo", notTheRightKey=1)
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## no id
+		info = dict(fontInfoVersion3)
+		info["woffMetadataUniqueID"] = dict()
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## not a string for id
+		info = dict(fontInfoVersion3)
+		info["woffMetadataUniqueID"] = dict(id=1)
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## empty string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataUniqueID"] = dict(id="")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		# woffMetadataVendor
+		## no name
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = dict(url="foo")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## name not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = dict(name=1, url="foo")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## name an empty string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = dict(name="", url="foo")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		## no URL
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = dict(name="foo")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		## url not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = dict(name="foo", url=1)
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## url empty string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = dict(name="foo", url="")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		## have dir
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = dict(name="foo", url="bar", dir="ltr")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = dict(name="foo", url="bar", dir="rtl")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		## dir not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = dict(name="foo", url="bar", dir=1)
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## dir not ltr or rtl
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = dict(name="foo", url="bar", dir="utd")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## have class
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = {"name"  : "foo", "url" : "bar", "class" : "hello"}
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		## class not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = {"name"  : "foo", "url" : "bar", "class" : 1}
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## class empty string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataVendor"] = {"name"  : "foo", "url" : "bar", "class" : ""}
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		# woffMetadataCredits
+		## no credits attribute
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = {}
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## unknown attribute
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = dict(credits=[dict(name="foo")], notTheRightKey=1)
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## not a list
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = dict(credits="abc")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## no elements in credits
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = dict(credits=[])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## credit not a dict
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = dict(credits=["abc"])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## unknown key
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = dict(credits=[dict(name="foo", notTheRightKey=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## no name
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = dict(credits=[dict(url="foo")])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## name not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = dict(credits=[dict(name=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## url not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = dict(credits=[dict(name="foo", url=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## role not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = dict(credits=[dict(name="foo", role=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## dir not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = dict(credits=[dict(name="foo", dir=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## dir not ltr or rtl
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = dict(credits=[dict(name="foo", dir="utd")])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## class not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCredits"] = dict(credits=[{"name"  : "foo", "class" : 1}])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# woffMetadataDescription
+		## no url
+		info = dict(fontInfoVersion3)
+		info["woffMetadataDescription"] = dict(text=[dict(text="foo")])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		## url not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataDescription"] = dict(text=[dict(text="foo")], url=1)
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## no text
+		info = dict(fontInfoVersion3)
+		info["woffMetadataDescription"] = dict(url="foo")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text not a list
+		info = dict(fontInfoVersion3)
+		info["woffMetadataDescription"] = dict(text="abc")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text item not a dict
+		info = dict(fontInfoVersion3)
+		info["woffMetadataDescription"] = dict(text=["abc"])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text item unknown key
+		info = dict(fontInfoVersion3)
+		info["woffMetadataDescription"] = dict(text=[dict(text="foo", notTheRightKey=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text item missing text
+		info = dict(fontInfoVersion3)
+		info["woffMetadataDescription"] = dict(text=[dict(language="foo")])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataDescription"] = dict(text=[dict(text=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## url not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataDescription"] = dict(text=[dict(text="foo", url=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## language not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataDescription"] = dict(text=[dict(text="foo", language=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## dir not ltr or rtl
+		info = dict(fontInfoVersion3)
+		info["woffMetadataDescription"] = dict(text=[dict(text="foo", dir="utd")])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## class not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataDescription"] = dict(text=[{"text"  : "foo", "class" : 1}])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# woffMetadataLicense
+		## no url
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(text=[dict(text="foo")])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		## url not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(text=[dict(text="foo")], url=1)
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## id not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(text=[dict(text="foo")], id=1)
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## no text
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(url="foo")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		## text not a list
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(text="abc")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text item not a dict
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(text=["abc"])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text item unknown key
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(text=[dict(text="foo", notTheRightKey=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text item missing text
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(text=[dict(language="foo")])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(text=[dict(text=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## url not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(text=[dict(text="foo", url=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## language not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(text=[dict(text="foo", language=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## dir not ltr or rtl
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(text=[dict(text="foo", dir="utd")])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## class not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicense"] = dict(text=[{"text"  : "foo", "class" : 1}])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# woffMetadataCopyright
+		## unknown attribute
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCopyright"] = dict(text=[dict(text="foo")], notTheRightKey=1)
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## no text
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCopyright"] = dict()
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text not a list
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCopyright"] = dict(text="abc")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text item not a dict
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCopyright"] = dict(text=["abc"])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text item unknown key
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCopyright"] = dict(text=[dict(text="foo", notTheRightKey=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text item missing text
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCopyright"] = dict(text=[dict(language="foo")])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCopyright"] = dict(text=[dict(text=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## url not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCopyright"] = dict(text=[dict(text="foo", url=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## language not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCopyright"] = dict(text=[dict(text="foo", language=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## dir not ltr or rtl
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCopyright"] = dict(text=[dict(text="foo", dir="utd")])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## class not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataCopyright"] = dict(text=[{"text"  : "foo", "class" : 1}])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# woffMetadataTrademark
+		## unknown attribute
+		info = dict(fontInfoVersion3)
+		info["woffMetadataTrademark"] = dict(text=[dict(text="foo")], notTheRightKey=1)
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## no text
+		info = dict(fontInfoVersion3)
+		info["woffMetadataTrademark"] = dict()
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text not a list
+		info = dict(fontInfoVersion3)
+		info["woffMetadataTrademark"] = dict(text="abc")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text item not a dict
+		info = dict(fontInfoVersion3)
+		info["woffMetadataTrademark"] = dict(text=["abc"])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text item unknown key
+		info = dict(fontInfoVersion3)
+		info["woffMetadataTrademark"] = dict(text=[dict(text="foo", notTheRightKey=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text item missing text
+		info = dict(fontInfoVersion3)
+		info["woffMetadataTrademark"] = dict(text=[dict(language="foo")])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## text not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataTrademark"] = dict(text=[dict(text=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## url not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataTrademark"] = dict(text=[dict(text="foo", url=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## language not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataTrademark"] = dict(text=[dict(text="foo", language=1)])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## dir not ltr or rtl
+		info = dict(fontInfoVersion3)
+		info["woffMetadataTrademark"] = dict(text=[dict(text="foo", dir="utd")])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## class not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataTrademark"] = dict(text=[{"text"  : "foo", "class" : 1}])
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# woffMetadataLicensee
+		## no name
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicensee"] = dict()
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## unknown attribute
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicensee"] = dict(name="foo", notTheRightKey=1)
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## name not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicensee"] = dict(name=1)
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## dir options
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicensee"] = dict(name="foo", dir="ltr")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicensee"] = dict(name="foo", dir="rtl")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		## dir not ltr or rtl
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicensee"] = dict(name="foo", dir="utd")
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## have class
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicensee"] = {"name" : "foo", "class" : "hello"}
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		reader.readInfo(TestInfoObject())
+		## class not a string
+		info = dict(fontInfoVersion3)
+		info["woffMetadataLicensee"] = {"name" : "foo", "class" : 1}
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+
+	def testGuidelinesRead(self):
+		# x
+		## not an int or float
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x="1")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# y
+		## not an int or float
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(y="1")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# angle
+		## < 0
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, y=0, angle=-1)]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## > 360
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, y=0, angle=361)]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# name
+		## not a string
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, name=1)]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# color
+		## not a string
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color=1)]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## not enough commas
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="1 0, 0, 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="1 0 0, 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="1 0 0 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## not enough parts
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color=", 0, 0, 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="1, , 0, 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="1, 0, , 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="1, 0, 0, ")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color=", , , ")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## not a number in all positions
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="r, 1, 1, 1")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="1, g, 1, 1")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="1, 1, b, 1")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="1, 1, 1, a")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## too many parts
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="1, 0, 0, 0, 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## < 0 in each position
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="-1, 0, 0, 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="0, -1, 0, 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="0, 0, -1, 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="0, 0, 0, -1")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		## > 1 in each position
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="2, 0, 0, 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="0, 2, 0, 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="0, 0, 2, 0")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, color="0, 0, 0, 2")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+		# identifier
+		## duplicate
+		info = dict(fontInfoVersion3)
+		info["guidelines"] = [dict(x=0, identifier="guide1"), dict(y=0, identifier="guide1")]
+		self._writeInfoToPlist(info)
+		reader = UFOReader(self.dstDir, validate=True)
+		self.assertRaises(UFOLibError, reader.readInfo, TestInfoObject())
+
+
+class WriteFontInfoVersion3TestCase(unittest.TestCase):
+
+	def setUp(self):
+		self.tempDir = tempfile.mktemp()
+		os.mkdir(self.tempDir)
+		self.dstDir = os.path.join(self.tempDir, "test.ufo")
+
+	def tearDown(self):
+		shutil.rmtree(self.tempDir)
+
+	def tearDownUFO(self):
+		if os.path.exists(self.dstDir):
+			shutil.rmtree(self.dstDir)
+
+	def makeInfoObject(self):
+		infoObject = TestInfoObject()
+		for attr, value in list(fontInfoVersion3.items()):
+			setattr(infoObject, attr, value)
+		return infoObject
+
+	def readPlist(self):
+		path = os.path.join(self.dstDir, "fontinfo.plist")
+		with open(path, "rb") as f:
+			plist = plistlib.load(f)
+		return plist
+
+	def testWrite(self):
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		writtenData = self.readPlist()
+		for attr, originalValue in list(fontInfoVersion3.items()):
+			newValue = writtenData[attr]
+			self.assertEqual(newValue, originalValue)
+		self.tearDownUFO()
+
+	def testGenericWrite(self):
+		# familyName
+		infoObject = self.makeInfoObject()
+		infoObject.familyName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# styleName
+		infoObject = self.makeInfoObject()
+		infoObject.styleName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# styleMapFamilyName
+		infoObject = self.makeInfoObject()
+		infoObject.styleMapFamilyName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# styleMapStyleName
+		## not a string
+		infoObject = self.makeInfoObject()
+		infoObject.styleMapStyleName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## out of range
+		infoObject = self.makeInfoObject()
+		infoObject.styleMapStyleName = "REGULAR"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# versionMajor
+		infoObject = self.makeInfoObject()
+		infoObject.versionMajor = "1"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# versionMinor
+		infoObject = self.makeInfoObject()
+		infoObject.versionMinor = "0"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# copyright
+		infoObject = self.makeInfoObject()
+		infoObject.copyright = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# trademark
+		infoObject = self.makeInfoObject()
+		infoObject.trademark = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# unitsPerEm
+		infoObject = self.makeInfoObject()
+		infoObject.unitsPerEm = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# descender
+		infoObject = self.makeInfoObject()
+		infoObject.descender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# xHeight
+		infoObject = self.makeInfoObject()
+		infoObject.xHeight = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# capHeight
+		infoObject = self.makeInfoObject()
+		infoObject.capHeight = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# ascender
+		infoObject = self.makeInfoObject()
+		infoObject.ascender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# italicAngle
+		infoObject = self.makeInfoObject()
+		infoObject.italicAngle = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+
+	def testGaspWrite(self):
+		# not a list
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeGaspRangeRecords = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# empty list
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeGaspRangeRecords = []
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		# not a dict
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeGaspRangeRecords = ["abc"]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# dict not properly formatted
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeGaspRangeRecords = [dict(rangeMaxPPEM=0xFFFF, notTheRightKey=1)]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeGaspRangeRecords = [dict(notTheRightKey=1, rangeGaspBehavior=[0])]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# not an int for ppem
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeGaspRangeRecords = [dict(rangeMaxPPEM="abc", rangeGaspBehavior=[0]), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# not a list for behavior
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeGaspRangeRecords = [dict(rangeMaxPPEM=10, rangeGaspBehavior="abc"), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# invalid behavior value
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeGaspRangeRecords = [dict(rangeMaxPPEM=10, rangeGaspBehavior=[-1]), dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# not sorted
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeGaspRangeRecords = [dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0]), dict(rangeMaxPPEM=10, rangeGaspBehavior=[0])]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# no 0xFFFF
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeGaspRangeRecords = [dict(rangeMaxPPEM=10, rangeGaspBehavior=[0]), dict(rangeMaxPPEM=20, rangeGaspBehavior=[0])]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+
+	def testHeadWrite(self):
+		# openTypeHeadCreated
+		## not a string
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHeadCreated = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## invalid format
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHeadCreated = "2000-Jan-01 00:00:00"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeHeadLowestRecPPEM
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHeadLowestRecPPEM = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeHeadFlags
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHeadFlags = [-1]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+
+	def testHheaWrite(self):
+		# openTypeHheaAscender
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHheaAscender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeHheaDescender
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHheaDescender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeHheaLineGap
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHheaLineGap = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeHheaCaretSlopeRise
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHheaCaretSlopeRise = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeHheaCaretSlopeRun
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHheaCaretSlopeRun = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeHheaCaretOffset
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeHheaCaretOffset = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+
+	def testNameWrite(self):
+		# openTypeNameDesigner
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameDesigner = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameDesignerURL
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameDesignerURL = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameManufacturer
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameManufacturer = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameManufacturerURL
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameManufacturerURL = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameLicense
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameLicense = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameLicenseURL
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameLicenseURL = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameVersion
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameVersion = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameUniqueID
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameUniqueID = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameDescription
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameDescription = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNamePreferredFamilyName
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNamePreferredFamilyName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNamePreferredSubfamilyName
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNamePreferredSubfamilyName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameCompatibleFullName
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameCompatibleFullName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameSampleText
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameSampleText = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameWWSFamilyName
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameWWSFamilyName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameWWSSubfamilyName
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameWWSSubfamilyName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeNameRecords
+		## not a list
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## not a dict
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = ["abc"]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## invalid dict structure
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [dict(foo="bar")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## incorrect keys
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [
+			dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record.", foo="bar")
+		]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [
+			dict(platformID=1, encodingID=1, languageID=1, string="Name Record.")
+		]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [
+			dict(nameID=1, encodingID=1, languageID=1, string="Name Record.")
+		]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [
+			dict(nameID=1, platformID=1, languageID=1, string="Name Record.")
+		]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [
+			dict(nameID=1, platformID=1, encodingID=1, string="Name Record.")
+		]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [
+			dict(nameID=1, platformID=1, encodingID=1, languageID=1)
+		]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## invalid values
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [
+			dict(nameID="1", platformID=1, encodingID=1, languageID=1, string="Name Record.")
+		]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [
+			dict(nameID=1, platformID="1", encodingID=1, languageID=1, string="Name Record.")
+		]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [
+			dict(nameID=1, platformID=1, encodingID="1", languageID=1, string="Name Record.")
+		]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [
+			dict(nameID=1, platformID=1, encodingID=1, languageID="1", string="Name Record.")
+		]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [
+			dict(nameID=1, platformID=1, encodingID=1, languageID=1, string=1)
+		]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## duplicate
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeNameRecords = [
+			dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record."),
+			dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record.")
+		]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+
+	def testOS2Write(self):
+		# openTypeOS2WidthClass
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2WidthClass = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## out or range
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2WidthClass = 15
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2WeightClass
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2WeightClass = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## out of range
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2WeightClass = -50
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2Selection
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2Selection = [-1]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2VendorID
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2VendorID = 1234
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2Panose
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2Panose = [0, 1, 2, 3, 4, 5, 6, 7, 8, str(9)]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## too few values
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2Panose = [0, 1, 2, 3]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2Panose = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2FamilyClass
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2FamilyClass = [0, str(1)]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## too few values
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2FamilyClass = [1]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2FamilyClass = [1, 1, 1]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## out of range
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2FamilyClass = [1, 20]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2UnicodeRanges
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2UnicodeRanges = ["0"]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## out of range
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2UnicodeRanges = [-1]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2CodePageRanges
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2CodePageRanges = ["0"]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## out of range
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2CodePageRanges = [-1]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2TypoAscender
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2TypoAscender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2TypoDescender
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2TypoDescender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2TypoLineGap
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2TypoLineGap = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2WinAscent
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2WinAscent = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2WinAscent = -1
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2WinDescent
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2WinDescent = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2WinDescent = -1
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2Type
+		## not an int
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2Type = ["1"]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## out of range
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2Type = [-1]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2SubscriptXSize
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SubscriptXSize = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2SubscriptYSize
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SubscriptYSize = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2SubscriptXOffset
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SubscriptXOffset = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2SubscriptYOffset
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SubscriptYOffset = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2SuperscriptXSize
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SuperscriptXSize = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2SuperscriptYSize
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SuperscriptYSize = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2SuperscriptXOffset
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SuperscriptXOffset = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2SuperscriptYOffset
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2SuperscriptYOffset = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2StrikeoutSize
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2StrikeoutSize = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeOS2StrikeoutPosition
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeOS2StrikeoutPosition = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+
+	def testVheaWrite(self):
+		# openTypeVheaVertTypoAscender
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeVheaVertTypoAscender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeVheaVertTypoDescender
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeVheaVertTypoDescender = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeVheaVertTypoLineGap
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeVheaVertTypoLineGap = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeVheaCaretSlopeRise
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeVheaCaretSlopeRise = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeVheaCaretSlopeRun
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeVheaCaretSlopeRun = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# openTypeVheaCaretOffset
+		infoObject = self.makeInfoObject()
+		infoObject.openTypeVheaCaretOffset = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+
+	def testFONDWrite(self):
+		# macintoshFONDFamilyID
+		infoObject = self.makeInfoObject()
+		infoObject.macintoshFONDFamilyID = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# macintoshFONDName
+		infoObject = self.makeInfoObject()
+		infoObject.macintoshFONDName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+
+	def testPostscriptWrite(self):
+		# postscriptFontName
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFontName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptFullName
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFullName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptSlantAngle
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptSlantAngle = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptUniqueID
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptUniqueID = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptUnderlineThickness
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptUnderlineThickness = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptUnderlinePosition
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptUnderlinePosition = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptIsFixedPitch
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptIsFixedPitch = 2
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptBlueValues
+		## not a list
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptBlueValues = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## uneven value count
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptBlueValues = [500]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptBlueValues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptOtherBlues
+		## not a list
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptOtherBlues = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## uneven value count
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptOtherBlues = [500]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptOtherBlues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptFamilyBlues
+		## not a list
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFamilyBlues = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## uneven value count
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFamilyBlues = [500]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFamilyBlues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptFamilyOtherBlues
+		## not a list
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFamilyOtherBlues = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## uneven value count
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFamilyOtherBlues = [500]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptFamilyOtherBlues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptStemSnapH
+		## not list
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptStemSnapH = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptStemSnapH = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptStemSnapV
+		## not list
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptStemSnapV = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## too many values
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptStemSnapV = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptBlueFuzz
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptBlueFuzz = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptBlueShift
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptBlueShift = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptBlueScale
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptBlueScale = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptForceBold
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptForceBold = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptDefaultWidthX
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptDefaultWidthX = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptNominalWidthX
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptNominalWidthX = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptWeightName
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptWeightName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptDefaultCharacter
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptDefaultCharacter = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# postscriptWindowsCharacterSet
+		infoObject = self.makeInfoObject()
+		infoObject.postscriptWindowsCharacterSet = -1
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# macintoshFONDFamilyID
+		infoObject = self.makeInfoObject()
+		infoObject.macintoshFONDFamilyID = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# macintoshFONDName
+		infoObject = self.makeInfoObject()
+		infoObject.macintoshFONDName = 123
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+
+	def testWOFFWrite(self):
+		# woffMajorVersion
+		infoObject = self.makeInfoObject()
+		infoObject.woffMajorVersion = 1.0
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.woffMajorVersion = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# woffMinorVersion
+		infoObject = self.makeInfoObject()
+		infoObject.woffMinorVersion = 1.0
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.woffMinorVersion = "abc"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# woffMetadataUniqueID
+		## none
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataUniqueID = None
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		## not a dict
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataUniqueID = 1
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## unknown key
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataUniqueID = dict(id="foo", notTheRightKey=1)
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## no id
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataUniqueID = dict()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## not a string for id
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataUniqueID = dict(id=1)
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## empty string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataUniqueID = dict(id="")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		# woffMetadataVendor
+		## no name
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = dict(url="foo")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## name not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = dict(name=1, url="foo")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## name an empty string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = dict(name="", url="foo")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		## no URL
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = dict(name="foo")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		## url not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = dict(name="foo", url=1)
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## url empty string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = dict(name="foo", url="")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		## have dir
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = dict(name="foo", url="bar", dir="ltr")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = dict(name="foo", url="bar", dir="rtl")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		## dir not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = dict(name="foo", url="bar", dir=1)
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## dir not ltr or rtl
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = dict(name="foo", url="bar", dir="utd")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## have class
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = {"name"  : "foo", "url" : "bar", "class" : "hello"}
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		## class not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = {"name"  : "foo", "url" : "bar", "class" : 1}
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## class empty string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataVendor = {"name"  : "foo", "url" : "bar", "class" : ""}
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		# woffMetadataCredits
+		## no credits attribute
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = {}
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## unknown attribute
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = dict(credits=[dict(name="foo")], notTheRightKey=1)
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## not a list
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = dict(credits="abc")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## no elements in credits
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = dict(credits=[])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## credit not a dict
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = dict(credits=["abc"])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## unknown key
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = dict(credits=[dict(name="foo", notTheRightKey=1)])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## no name
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = dict(credits=[dict(url="foo")])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## name not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = dict(credits=[dict(name=1)])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## url not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = dict(credits=[dict(name="foo", url=1)])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## role not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = dict(credits=[dict(name="foo", role=1)])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## dir not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = dict(credits=[dict(name="foo", dir=1)])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## dir not ltr or rtl
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = dict(credits=[dict(name="foo", dir="utd")])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## class not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCredits = dict(credits=[{"name"  : "foo", "class" : 1}])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# woffMetadataDescription
+		## no url
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataDescription = dict(text=[dict(text="foo")])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		## url not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataDescription = dict(text=[dict(text="foo")], url=1)
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## no text
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataDescription = dict(url="foo")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text not a list
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataDescription = dict(text="abc")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text item not a dict
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataDescription = dict(text=["abc"])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text item unknown key
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataDescription = dict(text=[dict(text="foo", notTheRightKey=1)])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text item missing text
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataDescription = dict(text=[dict(language="foo")])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataDescription = dict(text=[dict(text=1)])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## url not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataDescription = dict(text=[dict(text="foo", url=1)])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## language not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataDescription = dict(text=[dict(text="foo", language=1)])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## dir not ltr or rtl
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataDescription = dict(text=[dict(text="foo", dir="utd")])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## class not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataDescription = dict(text=[{"text"  : "foo", "class" : 1}])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# woffMetadataLicense
+		## no url
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataLicense = dict(text=[dict(text="foo")])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		## url not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataLicense = dict(text=[dict(text="foo")], url=1)
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## id not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataLicense = dict(text=[dict(text="foo")], id=1)
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## no text
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataLicense = dict(url="foo")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		## text not a list
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataLicense = dict(text="abc")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text item not a dict
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataLicense = dict(text=["abc"])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text item unknown key
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataLicense = dict(text=[dict(text="foo", notTheRightKey=1)])
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text item missing text
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataLicense = dict(text=[dict(language="foo")])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataLicense = dict(text=[dict(text=1)])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## url not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataLicense = dict(text=[dict(text="foo", url=1)])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## language not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataLicense = dict(text=[dict(text="foo", language=1)])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## dir not ltr or rtl
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataLicense = dict(text=[dict(text="foo", dir="utd")])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## class not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataLicense = dict(text=[{"text"  : "foo", "class" : 1}])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# woffMetadataCopyright
+		## unknown attribute
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataCopyright = dict(text=[dict(text="foo")], notTheRightKey=1)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## no text
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataCopyright = dict()
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text not a list
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataCopyright = dict(text="abc")
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text item not a dict
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataCopyright = dict(text=["abc"])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text item unknown key
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataCopyright = dict(text=[dict(text="foo", notTheRightKey=1)])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text item missing text
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataCopyright = dict(text=[dict(language="foo")])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataCopyright = dict(text=[dict(text=1)])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## url not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataCopyright = dict(text=[dict(text="foo", url=1)])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## language not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataCopyright = dict(text=[dict(text="foo", language=1)])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## dir not ltr or rtl
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataCopyright = dict(text=[dict(text="foo", dir="utd")])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## class not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataCopyright = dict(text=[{"text"  : "foo", "class" : 1}])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# woffMetadataTrademark
+		## unknown attribute
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataTrademark = dict(text=[dict(text="foo")], notTheRightKey=1)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## no text
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataTrademark = dict()
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text not a list
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataTrademark = dict(text="abc")
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text item not a dict
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataTrademark = dict(text=["abc"])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text item unknown key
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataTrademark = dict(text=[dict(text="foo", notTheRightKey=1)])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text item missing text
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataTrademark = dict(text=[dict(language="foo")])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## text not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataTrademark = dict(text=[dict(text=1)])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## url not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataTrademark = dict(text=[dict(text="foo", url=1)])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## language not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataTrademark = dict(text=[dict(text="foo", language=1)])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## dir not ltr or rtl
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataTrademark = dict(text=[dict(text="foo", dir="utd")])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## class not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataTrademark = dict(text=[{"text"  : "foo", "class" : 1}])
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# woffMetadataLicensee
+		## no name
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataLicensee = dict()
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## unknown attribute
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataLicensee = dict(name="foo", notTheRightKey=1)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## name not a string
+		infoObject = self.makeInfoObject()
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		infoObject.woffMetadataLicensee = dict(name=1)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## dir options
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataLicensee = dict(name="foo", dir="ltr")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataLicensee = dict(name="foo", dir="rtl")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		## dir not ltr or rtl
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataLicensee = dict(name="foo", dir="utd")
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## have class
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataLicensee = {"name" : "foo", "class" : "hello"}
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeInfo(infoObject)
+		self.tearDownUFO()
+		## class not a string
+		infoObject = self.makeInfoObject()
+		infoObject.woffMetadataLicensee = {"name" : "foo", "class" : 1}
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+
+	def testGuidelinesWrite(self):
+		# x
+		## not an int or float
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x="1")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# y
+		## not an int or float
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(y="1")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# angle
+		## < 0
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, y=0, angle=-1)]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## > 360
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, y=0, angle=361)]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# name
+		## not a string
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, name=1)]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# color
+		## not a string
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color=1)]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## not enough commas
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="1 0, 0, 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="1 0 0, 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="1 0 0 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## not enough parts
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color=", 0, 0, 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="1, , 0, 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="1, 0, , 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="1, 0, 0, ")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color=", , , ")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## not a number in all positions
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="r, 1, 1, 1")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="1, g, 1, 1")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="1, 1, b, 1")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="1, 1, 1, a")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## too many parts
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="1, 0, 0, 0, 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## < 0 in each position
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="-1, 0, 0, 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="0, -1, 0, 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="0, 0, -1, 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="0, 0, 0, -1")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## > 1 in each position
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="2, 0, 0, 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="0, 2, 0, 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="0, 0, 2, 0")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, color="0, 0, 0, 2")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		# identifier
+		## duplicate
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, identifier="guide1"), dict(y=0, identifier="guide1")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## below min
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, identifier="\0x1F")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+		## above max
+		infoObject = self.makeInfoObject()
+		infoObject.guidelines = [dict(x=0, identifier="\0x7F")]
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		self.assertRaises(UFOLibError, writer.writeInfo, info=infoObject)
+		self.tearDownUFO()
+
+
+# ------
+# layers
+# ------
+
+class UFO3ReadLayersTestCase(unittest.TestCase):
+
+	def setUp(self):
+		self.tempDir = tempfile.mktemp()
+		os.mkdir(self.tempDir)
+		self.ufoPath = os.path.join(self.tempDir, "test.ufo")
+
+	def tearDown(self):
+		shutil.rmtree(self.tempDir)
+
+	def makeUFO(self, metaInfo=None, layerContents=None):
+		self.clearUFO()
+		if not os.path.exists(self.ufoPath):
+			os.mkdir(self.ufoPath)
+		# metainfo.plist
+		if metaInfo is None:
+			metaInfo = dict(creator="test", formatVersion=3)
+		path = os.path.join(self.ufoPath, "metainfo.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(metaInfo, f)
+		# layers
+		if layerContents is None:
+			layerContents = [
+				("public.default", "glyphs"),
+				("layer 1", "glyphs.layer 1"),
+				("layer 2", "glyphs.layer 2"),
+			]
+		if layerContents:
+			path = os.path.join(self.ufoPath, "layercontents.plist")
+			with open(path, "wb") as f:
+				plistlib.dump(layerContents, f)
+		else:
+			layerContents = [("", "glyphs")]
+		for name, directory in layerContents:
+			glyphsPath = os.path.join(self.ufoPath, directory)
+			os.mkdir(glyphsPath)
+			contents = dict(a="a.glif")
+			path = os.path.join(glyphsPath, "contents.plist")
+			with open(path, "wb") as f:
+				plistlib.dump(contents, f)
+			path = os.path.join(glyphsPath, "a.glif")
+			with open(path, "w") as f:
+				f.write(" ")
+
+	def clearUFO(self):
+		if os.path.exists(self.ufoPath):
+			shutil.rmtree(self.ufoPath)
+
+	# valid
+
+	def testValidRead(self):
+		# UFO 1
+		self.makeUFO(
+			metaInfo=dict(creator="test", formatVersion=1),
+			layerContents=dict()
+		)
+		reader = UFOReader(self.ufoPath, validate=True)
+		reader.getGlyphSet()
+		# UFO 2
+		self.makeUFO(
+			metaInfo=dict(creator="test", formatVersion=2),
+			layerContents=dict()
+		)
+		reader = UFOReader(self.ufoPath, validate=True)
+		reader.getGlyphSet()
+		# UFO 3
+		self.makeUFO()
+		reader = UFOReader(self.ufoPath, validate=True)
+		reader.getGlyphSet()
+
+	# missing layer contents
+
+	def testMissingLayerContents(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		reader = UFOReader(self.ufoPath, validate=True)
+		self.assertRaises(UFOLibError, reader.getGlyphSet)
+
+	# layer contents invalid format
+
+	def testInvalidLayerContentsFormat(self):
+		# bogus
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		with open(path, "w") as f:
+			f.write("test")
+		reader = UFOReader(self.ufoPath, validate=True)
+		self.assertRaises(UFOLibError, reader.getGlyphSet)
+		# dict
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = {
+			"public.default" : "glyphs",
+			"layer 1" : "glyphs.layer 1",
+			"layer 2" : "glyphs.layer 2",
+		}
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		self.assertRaises(UFOLibError, reader.getGlyphSet)
+
+	# layer contents invalid name format
+
+	def testInvalidLayerContentsNameFormat(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			(1, "glyphs"),
+			("layer 1", "glyphs.layer 1"),
+			("layer 2", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		self.assertRaises(UFOLibError, reader.getGlyphSet)
+
+	# layer contents invalid directory format
+
+	def testInvalidLayerContentsDirectoryFormat(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("public.foregound", "glyphs"),
+			("layer 1", 1),
+			("layer 2", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		self.assertRaises(UFOLibError, reader.getGlyphSet)
+
+	# directory listed in contents not on disk
+
+	def testLayerContentsHasMissingDirectory(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("public.foregound", "glyphs"),
+			("layer 1", "glyphs.doesnotexist"),
+			("layer 2", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		self.assertRaises(UFOLibError, reader.getGlyphSet)
+
+	# # directory on disk not listed in contents
+	# XXX should this raise an error?
+	#
+	# def testLayerContentsHasMissingDirectory(self):
+	# 	self.makeUFO()
+	# 	path = os.path.join(self.ufoPath, "layercontents.plist")
+	# 	os.remove(path)
+	# 	layerContents = [
+	# 		("public.foregound", "glyphs"),
+	# 		("layer 1", "glyphs.layer 2")
+	# 	]
+	# 	with open(path, "wb") as f:
+	# 		plistlib.dump(layerContents, f)
+	# 	reader = UFOReader(self.ufoPath, validate=True)
+	# 	with self.assertRaises(UFOLibError):
+	# 		reader.getGlyphSet()
+
+	# no default layer on disk
+
+	def testMissingDefaultLayer(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("layer 1", "glyphs.layer 1"),
+			("layer 2", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		self.assertRaises(UFOLibError, reader.getGlyphSet)
+
+	# duplicate layer name
+
+	def testDuplicateLayerName(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("public.foregound", "glyphs"),
+			("layer 1", "glyphs.layer 1"),
+			("layer 1", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		self.assertRaises(UFOLibError, reader.getGlyphSet)
+
+	# directory referenced by two layer names
+
+	def testDuplicateLayerDirectory(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("public.foregound", "glyphs"),
+			("layer 1", "glyphs.layer 1"),
+			("layer 2", "glyphs.layer 1")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		self.assertRaises(UFOLibError, reader.getGlyphSet)
+
+	# default without a name
+
+	def testDefaultLayerNoName(self):
+		# get the glyph set
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("public.foregound", "glyphs"),
+			("layer 1", "glyphs.layer 1"),
+			("layer 2", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		reader.getGlyphSet()
+
+	# default with a name
+
+	def testDefaultLayerName(self):
+		# get the name
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("custom name", "glyphs"),
+			("layer 1", "glyphs.layer 1"),
+			("layer 2", "glyphs.layer 2")
+		]
+		expected = layerContents[0][0]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		result = reader.getDefaultLayerName()
+		self.assertEqual(expected, result)
+		# get the glyph set
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("custom name", "glyphs"),
+			("layer 1", "glyphs.layer 1"),
+			("layer 2", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		reader.getGlyphSet(expected)
+
+	# layer order
+
+	def testLayerOrder(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("public.foregound", "glyphs"),
+			("layer 1", "glyphs.layer 1"),
+			("layer 2", "glyphs.layer 2")
+		]
+		expected = [name for (name, directory) in layerContents]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		result = reader.getLayerNames()
+		self.assertEqual(expected, result)
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("layer 1", "glyphs.layer 1"),
+			("public.foregound", "glyphs"),
+			("layer 2", "glyphs.layer 2")
+		]
+		expected = [name for (name, directory) in layerContents]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		result = reader.getLayerNames()
+		self.assertEqual(expected, result)
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("layer 2", "glyphs.layer 2"),
+			("layer 1", "glyphs.layer 1"),
+			("public.foregound", "glyphs")
+		]
+		expected = [name for (name, directory) in layerContents]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		reader = UFOReader(self.ufoPath, validate=True)
+		result = reader.getLayerNames()
+		self.assertEqual(expected, result)
+
+
+class UFO3WriteLayersTestCase(unittest.TestCase):
+
+	def setUp(self):
+		self.tempDir = tempfile.mktemp()
+		os.mkdir(self.tempDir)
+		self.ufoPath = os.path.join(self.tempDir, "test.ufo")
+
+	def tearDown(self):
+		shutil.rmtree(self.tempDir)
+
+	def makeUFO(self, metaInfo=None, layerContents=None):
+		self.clearUFO()
+		if not os.path.exists(self.ufoPath):
+			os.mkdir(self.ufoPath)
+		# metainfo.plist
+		if metaInfo is None:
+			metaInfo = dict(creator="test", formatVersion=3)
+		path = os.path.join(self.ufoPath, "metainfo.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(metaInfo, f)
+		# layers
+		if layerContents is None:
+			layerContents = [
+				("public.default", "glyphs"),
+				("layer 1", "glyphs.layer 1"),
+				("layer 2", "glyphs.layer 2"),
+			]
+		if layerContents:
+			path = os.path.join(self.ufoPath, "layercontents.plist")
+			with open(path, "wb") as f:
+				plistlib.dump(layerContents, f)
+		else:
+			layerContents = [("", "glyphs")]
+		for name, directory in layerContents:
+			glyphsPath = os.path.join(self.ufoPath, directory)
+			os.mkdir(glyphsPath)
+			contents = dict(a="a.glif")
+			path = os.path.join(glyphsPath, "contents.plist")
+			with open(path, "wb") as f:
+				plistlib.dump(contents, f)
+			path = os.path.join(glyphsPath, "a.glif")
+			with open(path, "w") as f:
+				f.write(" ")
+
+	def clearUFO(self):
+		if os.path.exists(self.ufoPath):
+			shutil.rmtree(self.ufoPath)
+
+	# __init__: missing layer contents
+
+	def testMissingLayerContents(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		self.assertRaises(UFOLibError, UFOWriter, self.ufoPath)
+
+	# __init__: layer contents invalid format
+
+	def testInvalidLayerContentsFormat(self):
+		# bogus
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		with open(path, "w") as f:
+			f.write("test")
+		self.assertRaises(UFOLibError, UFOWriter, self.ufoPath)
+		# dict
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = {
+			"public.default" : "glyphs",
+			"layer 1" : "glyphs.layer 1",
+			"layer 2" : "glyphs.layer 2",
+		}
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		self.assertRaises(UFOLibError, UFOWriter, self.ufoPath)
+
+	# __init__: layer contents invalid name format
+
+	def testInvalidLayerContentsNameFormat(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			(1, "glyphs"),
+			("layer 1", "glyphs.layer 1"),
+			("layer 2", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		self.assertRaises(UFOLibError, UFOWriter, self.ufoPath)
+
+	# __init__: layer contents invalid directory format
+
+	def testInvalidLayerContentsDirectoryFormat(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("public.foregound", "glyphs"),
+			("layer 1", 1),
+			("layer 2", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		self.assertRaises(UFOLibError, UFOWriter, self.ufoPath)
+
+	# __init__: directory listed in contents not on disk
+
+	def testLayerContentsHasMissingDirectory(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("public.foregound", "glyphs"),
+			("layer 1", "glyphs.doesnotexist"),
+			("layer 2", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		self.assertRaises(UFOLibError, UFOWriter, self.ufoPath)
+
+	# __init__: no default layer on disk
+
+	def testMissingDefaultLayer(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("layer 1", "glyphs.layer 1"),
+			("layer 2", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		self.assertRaises(UFOLibError, UFOWriter, self.ufoPath)
+
+	# __init__: duplicate layer name
+
+	def testDuplicateLayerName(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("public.foregound", "glyphs"),
+			("layer 1", "glyphs.layer 1"),
+			("layer 1", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		self.assertRaises(UFOLibError, UFOWriter, self.ufoPath)
+
+	# __init__: directory referenced by two layer names
+
+	def testDuplicateLayerDirectory(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("public.foregound", "glyphs"),
+			("layer 1", "glyphs.layer 1"),
+			("layer 2", "glyphs.layer 1")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		self.assertRaises(UFOLibError, UFOWriter, self.ufoPath)
+
+	# __init__: default without a name
+
+	def testDefaultLayerNoName(self):
+		# get the glyph set
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("public.foregound", "glyphs"),
+			("layer 1", "glyphs.layer 1"),
+			("layer 2", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		writer = UFOWriter(self.ufoPath)
+
+	# __init__: default with a name
+
+	def testDefaultLayerName(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		os.remove(path)
+		layerContents = [
+			("custom name", "glyphs"),
+			("layer 1", "glyphs.layer 1"),
+			("layer 2", "glyphs.layer 2")
+		]
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		writer = UFOWriter(self.ufoPath)
+
+	# __init__: up convert 1 > 3
+
+	def testUpConvert1To3(self):
+		self.makeUFO(
+			metaInfo=dict(creator="test", formatVersion=1),
+			layerContents=dict()
+		)
+		writer = UFOWriter(self.ufoPath)
+		writer.writeLayerContents(["public.default"])
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		with open(path, "rb") as f:
+			result = plistlib.load(f)
+		expected = [["public.default", "glyphs"]]
+		self.assertEqual(expected, result)
+
+	# __init__: up convert 2 > 3
+
+	def testUpConvert2To3(self):
+		self.makeUFO(
+			metaInfo=dict(creator="test", formatVersion=2),
+			layerContents=dict()
+		)
+		writer = UFOWriter(self.ufoPath)
+		writer.writeLayerContents(["public.default"])
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		with open(path, "rb") as f:
+			result = plistlib.load(f)
+		expected = [["public.default", "glyphs"]]
+		self.assertEqual(expected, result)
+
+	# __init__: down convert 3 > 1
+
+	def testDownConvert3To1(self):
+		self.makeUFO()
+		self.assertRaises(UFOLibError, UFOWriter, self.ufoPath, formatVersion=1)
+
+	# __init__: down convert 3 > 2
+
+	def testDownConvert3To2(self):
+		self.makeUFO()
+		self.assertRaises(UFOLibError, UFOWriter, self.ufoPath, formatVersion=2)
+
+	# get glyph sets
+
+	def testGetGlyphSets(self):
+		self.makeUFO()
+		# hack contents.plist
+		path = os.path.join(self.ufoPath, "glyphs.layer 1", "contents.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(dict(b="a.glif"), f)
+		path = os.path.join(self.ufoPath, "glyphs.layer 2", "contents.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(dict(c="a.glif"), f)
+		# now test
+		writer = UFOWriter(self.ufoPath)
+		# default
+		expected = ["a"]
+		result = list(writer.getGlyphSet().keys())
+		self.assertEqual(expected, result)
+		# layer 1
+		expected = ["b"]
+		result = list(writer.getGlyphSet("layer 1", defaultLayer=False).keys())
+		self.assertEqual(expected, result)
+		# layer 2
+		expected = ["c"]
+		result = list(writer.getGlyphSet("layer 2", defaultLayer=False).keys())
+		self.assertEqual(expected, result)
+
+	def testGetGlyphSetNoContents(self):
+		self.makeUFO()
+		os.remove(os.path.join(self.ufoPath, "glyphs.layer 1", "contents.plist"))
+
+		reader = UFOReader(self.ufoPath, validate=True)
+		with self.assertRaises(GlifLibError):
+			reader.getGlyphSet("layer 1")
+
+		writer = UFOWriter(self.ufoPath, validate=True)
+		with self.assertRaises(GlifLibError):
+			writer.getGlyphSet("layer 1", defaultLayer=False, expectContentsFile=True)
+
+		# There's a separate code path for < v3 UFOs.
+		with open(os.path.join(self.ufoPath, "metainfo.plist"), "wb") as f:
+			plistlib.dump(dict(creator="test", formatVersion=2), f)
+		os.remove(os.path.join(self.ufoPath, "glyphs", "contents.plist"))
+		writer = UFOWriter(self.ufoPath, validate=True, formatVersion=2)
+		with self.assertRaises(GlifLibError):
+			writer.getGlyphSet(expectContentsFile=True)
+
+	# make a new font with two layers
+
+	def testNewFontOneLayer(self):
+		self.clearUFO()
+		writer = UFOWriter(self.ufoPath)
+		writer.getGlyphSet()
+		writer.writeLayerContents(["public.default"])
+		# directory
+		path = os.path.join(self.ufoPath, "glyphs")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		# layer contents
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		with open(path, "rb") as f:
+			result = plistlib.load(f)
+		expected = [["public.default", "glyphs"]]
+		self.assertEqual(expected, result)
+
+	def testNewFontThreeLayers(self):
+		self.clearUFO()
+		writer = UFOWriter(self.ufoPath)
+		writer.getGlyphSet("layer 1", defaultLayer=False)
+		writer.getGlyphSet()
+		writer.getGlyphSet("layer 2", defaultLayer=False)
+		writer.writeLayerContents(["layer 1", "public.default", "layer 2"])
+		# directories
+		path = os.path.join(self.ufoPath, "glyphs")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer 1")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer 2")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		# layer contents
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		with open(path, "rb") as f:
+			result = plistlib.load(f)
+		expected = [["layer 1", "glyphs.layer 1"], ["public.default", "glyphs"], ["layer 2", "glyphs.layer 2"]]
+		self.assertEqual(expected, result)
+
+	# add a layer to an existing font
+
+	def testAddLayerToExistingFont(self):
+		self.makeUFO()
+		writer = UFOWriter(self.ufoPath)
+		writer.getGlyphSet("layer 3", defaultLayer=False)
+		writer.writeLayerContents(["public.default", "layer 1", "layer 2", "layer 3"])
+		# directories
+		path = os.path.join(self.ufoPath, "glyphs")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer 1")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer 2")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer 3")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		# layer contents
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		with open(path, "rb") as f:
+			result = plistlib.load(f)
+		expected = [['public.default', 'glyphs'], ['layer 1', 'glyphs.layer 1'], ['layer 2', 'glyphs.layer 2'], ["layer 3", "glyphs.layer 3"]]
+		self.assertEqual(expected, result)
+
+	# rename valid name
+
+	def testRenameLayer(self):
+		self.makeUFO()
+		writer = UFOWriter(self.ufoPath)
+		writer.renameGlyphSet("layer 1", "layer 3")
+		writer.writeLayerContents(["public.default", "layer 3", "layer 2"])
+		# directories
+		path = os.path.join(self.ufoPath, "glyphs")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer 1")
+		exists = os.path.exists(path)
+		self.assertEqual(False, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer 2")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer 3")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		# layer contents
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		with open(path, "rb") as f:
+			result = plistlib.load(f)
+		expected = [['public.default', 'glyphs'], ['layer 3', 'glyphs.layer 3'], ['layer 2', 'glyphs.layer 2']]
+		self.assertEqual(expected, result)
+
+	def testRenameLayerDefault(self):
+		self.makeUFO()
+		writer = UFOWriter(self.ufoPath)
+		writer.renameGlyphSet("public.default", "layer xxx")
+		writer.renameGlyphSet("layer 1", "layer 1", defaultLayer=True)
+		writer.writeLayerContents(["layer xxx", "layer 1", "layer 2"])
+		path = os.path.join(self.ufoPath, "glyphs")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer 1")
+		exists = os.path.exists(path)
+		self.assertEqual(False, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer 2")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer xxx")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		# layer contents
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		with open(path, "rb") as f:
+			result = plistlib.load(f)
+		expected = [['layer xxx', 'glyphs.layer xxx'], ['layer 1', 'glyphs'], ['layer 2', 'glyphs.layer 2']]
+		self.assertEqual(expected, result)
+
+	# rename duplicate name
+
+	def testRenameLayerDuplicateName(self):
+		self.makeUFO()
+		writer = UFOWriter(self.ufoPath)
+		self.assertRaises(UFOLibError, writer.renameGlyphSet, "layer 1", "layer 2")
+
+	# rename unknown layer
+
+	def testRenameLayerUnknownName(self):
+		self.makeUFO()
+		writer = UFOWriter(self.ufoPath)
+		self.assertRaises(UFOLibError, writer.renameGlyphSet, "does not exist", "layer 2")
+
+	# remove valid layer
+
+	def testRemoveLayer(self):
+		self.makeUFO()
+		writer = UFOWriter(self.ufoPath)
+		writer.deleteGlyphSet("layer 1")
+		writer.writeLayerContents(["public.default", "layer 2"])
+		# directories
+		path = os.path.join(self.ufoPath, "glyphs")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer 1")
+		exists = os.path.exists(path)
+		self.assertEqual(False, exists)
+		path = os.path.join(self.ufoPath, "glyphs.layer 2")
+		exists = os.path.exists(path)
+		self.assertEqual(True, exists)
+		# layer contents
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		with open(path, "rb") as f:
+			result = plistlib.load(f)
+		expected = [["public.default", "glyphs"], ["layer 2", "glyphs.layer 2"]]
+		self.assertEqual(expected, result)
+
+	# remove default layer
+
+	def testRemoveDefaultLayer(self):
+		self.makeUFO()
+		writer = UFOWriter(self.ufoPath)
+		writer.deleteGlyphSet("public.default")
+		writer.writeLayerContents(["layer 1", "layer 2"])
+		# directories
+		path = os.path.join(self.ufoPath, "glyphs")
+		self.assertEqual(False, os.path.exists(path))
+		path = os.path.join(self.ufoPath, "glyphs.layer 1")
+		self.assertEqual(True, os.path.exists(path))
+		path = os.path.join(self.ufoPath, "glyphs.layer 2")
+		self.assertEqual(True, os.path.exists(path))
+		# layer contents
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		with open(path, "rb") as f:
+			result = plistlib.load(f)
+		expected = [["layer 1", "glyphs.layer 1"], ["layer 2", "glyphs.layer 2"]]
+		self.assertEqual(expected, result)
+
+	# remove unknown layer
+
+	def testRemoveDefaultLayer2(self):
+		self.makeUFO()
+		writer = UFOWriter(self.ufoPath)
+		self.assertRaises(UFOLibError, writer.deleteGlyphSet, "does not exist")
+
+	def testWriteAsciiLayerOrder(self):
+		self.makeUFO(
+			layerContents=[
+				["public.default", "glyphs"],
+				["layer 1", "glyphs.layer 1"],
+				["layer 2", "glyphs.layer 2"],
+			]
+		)
+		writer = UFOWriter(self.ufoPath)
+		writer.writeLayerContents(["public.default", "layer 2", "layer 1"])
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		with open(path, "rb") as f:
+			result = plistlib.load(f)
+		expected = [
+			["public.default", "glyphs"],
+			["layer 2", "glyphs.layer 2"],
+			["layer 1", "glyphs.layer 1"],
+		]
+		self.assertEqual(expected, result)
+		for layerName, _ in result:
+			assert isinstance(layerName, str)
+
+# -----
+# /data
+# -----
+
+
+class UFO3ReadDataTestCase(unittest.TestCase):
+
+	def getFontPath(self):
+		testdata = os.path.join(os.path.dirname(__file__), "testdata")
+		return os.path.join(testdata, "UFO3-Read Data.ufo")
+
+	def testUFOReaderDataDirectoryListing(self):
+		reader = UFOReader(self.getFontPath())
+		found = reader.getDataDirectoryListing()
+		expected = [
+			'org.unifiedfontobject.directory/bar/lol.txt',
+			'org.unifiedfontobject.directory/foo.txt',
+			'org.unifiedfontobject.file.txt'
+		]
+		self.assertEqual(set(found), set(expected))
+
+	def testUFOReaderBytesFromPath(self):
+		reader = UFOReader(self.getFontPath())
+		found = reader.readBytesFromPath("data/org.unifiedfontobject.file.txt")
+		expected = b"file.txt"
+		self.assertEqual(found, expected)
+		found = reader.readBytesFromPath("data/org.unifiedfontobject.directory/bar/lol.txt")
+		expected = b"lol.txt"
+		self.assertEqual(found, expected)
+		found = reader.readBytesFromPath("data/org.unifiedfontobject.doesNotExist")
+		expected = None
+		self.assertEqual(found, expected)
+
+	def testUFOReaderReadFileFromPath(self):
+		reader = UFOReader(self.getFontPath())
+		fileObject = reader.getReadFileForPath("data/org.unifiedfontobject.file.txt")
+		self.assertNotEqual(fileObject, None)
+		hasRead = hasattr(fileObject, "read")
+		self.assertEqual(hasRead, True)
+		fileObject.close()
+		fileObject = reader.getReadFileForPath("data/org.unifiedfontobject.doesNotExist")
+		self.assertEqual(fileObject, None)
+
+	def testUFOReaderKernGroupDuplicatesRemoved(self):
+		# Non-kerning group duplicates are kept
+		# Kerning group duplicates are removed
+		expected_groups = {
+			"group1" : ["A"],
+			"group2" : ["B", "C", "B"],
+			"public.kern1.A" : ["A"],
+			"public.kern2.B" : ["B", "A", "C"],
+		}
+		reader = UFOReader(self.getFontPath())
+		groups = reader.readGroups()
+		self.assertEqual(expected_groups, groups)
+
+
+class UFO3WriteDataTestCase(unittest.TestCase):
+
+	def setUp(self):
+		self.tempDir = tempfile.mktemp()
+		os.mkdir(self.tempDir)
+		self.dstDir = os.path.join(self.tempDir, "test.ufo")
+
+	def tearDown(self):
+		shutil.rmtree(self.tempDir)
+
+	def tearDownUFO(self):
+		if os.path.exists(self.dstDir):
+			shutil.rmtree(self.dstDir)
+
+	def testUFOWriterWriteBytesToPath(self):
+		# basic file
+		path = "data/org.unifiedfontobject.writebytesbasicfile.txt"
+		testBytes = b"test"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeBytesToPath(path, testBytes)
+		path = os.path.join(self.dstDir, path)
+		self.assertEqual(os.path.exists(path), True)
+		with open(path, "rb") as f:
+			written = f.read()
+		self.assertEqual(testBytes, written)
+		self.tearDownUFO()
+		# basic file with unicode text
+		path = "data/org.unifiedfontobject.writebytesbasicunicodefile.txt"
+		text = b"t\xeb\xdft"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeBytesToPath(path, text)
+		path = os.path.join(self.dstDir, path)
+		self.assertEqual(os.path.exists(path), True)
+		with open(path, "rb") as f:
+			written = f.read()
+		self.assertEqual(text, written)
+		self.tearDownUFO()
+		# basic directory
+		path = "data/org.unifiedfontobject.writebytesdirectory/level1/level2/file.txt"
+		testBytes = b"test"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeBytesToPath(path, testBytes)
+		path = os.path.join(self.dstDir, path)
+		self.assertEqual(os.path.exists(path), True)
+		with open(path, "rb") as f:
+			written = f.read()
+		self.assertEqual(testBytes, written)
+		self.tearDownUFO()
+
+	def testUFOWriterWriteFileToPath(self):
+		# basic file
+		path = "data/org.unifiedfontobject.getwritefile.txt"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		fileObject = writer.getFileObjectForPath(path)
+		self.assertNotEqual(fileObject, None)
+		hasRead = hasattr(fileObject, "read")
+		self.assertEqual(hasRead, True)
+		fileObject.close()
+		self.tearDownUFO()
+
+	def testUFOWriterRemoveFile(self):
+		path1 = "data/org.unifiedfontobject.removefile/level1/level2/file1.txt"
+		path2 = "data/org.unifiedfontobject.removefile/level1/level2/file2.txt"
+		path3 = "data/org.unifiedfontobject.removefile/level1/file3.txt"
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.writeBytesToPath(path1, b"test")
+		writer.writeBytesToPath(path2, b"test")
+		writer.writeBytesToPath(path3, b"test")
+		self.assertEqual(os.path.exists(os.path.join(self.dstDir, path1)), True)
+		self.assertEqual(os.path.exists(os.path.join(self.dstDir, path2)), True)
+		self.assertEqual(os.path.exists(os.path.join(self.dstDir, path3)), True)
+		writer.removeFileForPath(path1)
+		self.assertEqual(os.path.exists(os.path.join(self.dstDir, path1)), False)
+		self.assertEqual(os.path.exists(os.path.dirname(os.path.join(self.dstDir, path1))), True)
+		self.assertEqual(os.path.exists(os.path.join(self.dstDir, path2)), True)
+		self.assertEqual(os.path.exists(os.path.join(self.dstDir, path3)), True)
+		writer.removeFileForPath(path2)
+		self.assertEqual(os.path.exists(os.path.dirname(os.path.join(self.dstDir, path1))), False)
+		self.assertEqual(os.path.exists(os.path.join(self.dstDir, path2)), False)
+		self.assertEqual(os.path.exists(os.path.join(self.dstDir, path3)), True)
+		writer.removeFileForPath(path3)
+		self.assertEqual(os.path.exists(os.path.join(self.dstDir, path3)), False)
+		self.assertEqual(os.path.exists(os.path.dirname(os.path.join(self.dstDir, path2))), False)
+		self.assertEqual(os.path.exists(os.path.join(self.dstDir, "data/org.unifiedfontobject.removefile")), False)
+		self.assertRaises(UFOLibError, writer.removeFileForPath, path="data/org.unifiedfontobject.doesNotExist.txt")
+		self.tearDownUFO()
+
+	def testUFOWriterCopy(self):
+		sourceDir = self.dstDir.replace(".ufo", "") + "-copy source" + ".ufo"
+		dataPath = "data/org.unifiedfontobject.copy/level1/level2/file1.txt"
+		writer = UFOWriter(sourceDir, formatVersion=3)
+		writer.writeBytesToPath(dataPath, b"test")
+		# copy a file
+		reader = UFOReader(sourceDir)
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		writer.copyFromReader(reader, dataPath, dataPath)
+		path = os.path.join(self.dstDir, dataPath)
+		self.assertEqual(os.path.exists(path), True)
+		self.tearDownUFO()
+		# copy a directory
+		reader = UFOReader(sourceDir)
+		writer = UFOWriter(self.dstDir, formatVersion=3)
+		p = "data/org.unifiedfontobject.copy"
+		writer.copyFromReader(reader, p, p)
+		path = os.path.join(self.dstDir, dataPath)
+		self.assertEqual(os.path.exists(path), True)
+		self.tearDownUFO()
+
+# ---------------
+# layerinfo.plist
+# ---------------
+
+class TestLayerInfoObject:
+
+	color = guidelines = lib = None
+
+
+class UFO3ReadLayerInfoTestCase(unittest.TestCase):
+
+	def setUp(self):
+		self.tempDir = tempfile.mktemp()
+		os.mkdir(self.tempDir)
+		self.ufoPath = os.path.join(self.tempDir, "test.ufo")
+
+	def tearDown(self):
+		shutil.rmtree(self.tempDir)
+
+	def makeUFO(self, formatVersion=3, layerInfo=None):
+		self.clearUFO()
+		if not os.path.exists(self.ufoPath):
+			os.mkdir(self.ufoPath)
+		# metainfo.plist
+		metaInfo = dict(creator="test", formatVersion=formatVersion)
+		path = os.path.join(self.ufoPath, "metainfo.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(metaInfo, f)
+		# layercontents.plist
+		layerContents = [("public.default", "glyphs")]
+		path = os.path.join(self.ufoPath, "layercontents.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(layerContents, f)
+		# glyphs
+		glyphsPath = os.path.join(self.ufoPath, "glyphs")
+		os.mkdir(glyphsPath)
+		contents = dict(a="a.glif")
+		path = os.path.join(glyphsPath, "contents.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(contents, f)
+		path = os.path.join(glyphsPath, "a.glif")
+		with open(path, "w") as f:
+			f.write(" ")
+		# layerinfo.plist
+		if layerInfo is None:
+			layerInfo = dict(
+				color="0,0,0,1",
+				lib={"foo" : "bar"}
+			)
+		path = os.path.join(glyphsPath, "layerinfo.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(layerInfo, f)
+
+	def clearUFO(self):
+		if os.path.exists(self.ufoPath):
+			shutil.rmtree(self.ufoPath)
+
+	def testValidLayerInfo(self):
+		self.makeUFO()
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		info = TestLayerInfoObject()
+		glyphSet.readLayerInfo(info)
+		expectedColor = "0,0,0,1"
+		self.assertEqual(expectedColor, info.color)
+		expectedLib = {"foo": "bar"}
+		self.assertEqual(expectedLib, info.lib)
+
+	def testMissingLayerInfo(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "glyphs", "layerinfo.plist")
+		os.remove(path)
+		# read
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		info = TestLayerInfoObject()
+		glyphSet.readLayerInfo(info)
+		self.assertEqual(None, info.color)
+		self.assertEqual(None, info.guidelines)
+		self.assertEqual(None, info.lib)
+
+	def testBogusLayerInfo(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "glyphs", "layerinfo.plist")
+		os.remove(path)
+		with open(path, "w") as f:
+			f.write("test")
+		# read
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		info = TestLayerInfoObject()
+		self.assertRaises(UFOLibError, glyphSet.readLayerInfo, info)
+
+	def testInvalidFormatLayerInfo(self):
+		self.makeUFO()
+		path = os.path.join(self.ufoPath, "glyphs", "layerinfo.plist")
+		info = [("color", "0,0,0,0")]
+		with open(path, "wb") as f:
+			plistlib.dump(info, f)
+		# read
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		info = TestLayerInfoObject()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, info)
+
+	def testColor(self):
+		## not a string
+		info = {}
+		info["color"] = 1
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		## not enough commas
+		info = {}
+		info["color"] = "1 0, 0, 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "1 0 0, 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "1 0 0 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		## not enough parts
+		info = {}
+		info["color"] = ", 0, 0, 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "1, , 0, 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "1, 0, , 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "1, 0, 0, "
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = ", , , "
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		## not a number in all positions
+		info = {}
+		info["color"] = "r, 1, 1, 1"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "1, g, 1, 1"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "1, 1, b, 1"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "1, 1, 1, a"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		## too many parts
+		info = {}
+		info["color"] = "1, 0, 0, 0, 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		## < 0 in each position
+		info = {}
+		info["color"] = "-1, 0, 0, 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "0, -1, 0, 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "0, 0, -1, 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "0, 0, 0, -1"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		## > 1 in each position
+		info = {}
+		info["color"] = "2, 0, 0, 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "0, 2, 0, 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "0, 0, 2, 0"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+		info = {}
+		info["color"] = "0, 0, 0, 2"
+		self.makeUFO(layerInfo=info)
+		reader = UFOReader(self.ufoPath, validate=True)
+		glyphSet = reader.getGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.readLayerInfo, TestLayerInfoObject())
+
+
+class UFO3WriteLayerInfoTestCase(unittest.TestCase):
+
+	def setUp(self):
+		self.tempDir = tempfile.mktemp()
+		os.mkdir(self.tempDir)
+		self.ufoPath = os.path.join(self.tempDir, "test.ufo")
+
+	def tearDown(self):
+		shutil.rmtree(self.tempDir)
+
+	def makeGlyphSet(self):
+		self.clearUFO()
+		writer = UFOWriter(self.ufoPath)
+		return writer.getGlyphSet()
+
+	def clearUFO(self):
+		if os.path.exists(self.ufoPath):
+			shutil.rmtree(self.ufoPath)
+
+	def testValidWrite(self):
+		expected = dict(
+			color="0,0,0,1",
+			lib={"foo" : "bar"}
+		)
+		info = TestLayerInfoObject()
+		info.color = expected["color"]
+		info.lib = expected["lib"]
+		glyphSet = self.makeGlyphSet()
+		glyphSet.writeLayerInfo(info)
+		path = os.path.join(self.ufoPath, "glyphs", "layerinfo.plist")
+		with open(path, "rb") as f:
+			result = plistlib.load(f)
+		self.assertEqual(expected, result)
+
+	def testColor(self):
+		## not a string
+		info = TestLayerInfoObject()
+		info.color = 1
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		## not enough commas
+		info = TestLayerInfoObject()
+		info.color = "1 0, 0, 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "1 0 0, 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "1 0 0 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		## not enough parts
+		info = TestLayerInfoObject()
+		info.color = ", 0, 0, 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "1, , 0, 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "1, 0, , 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "1, 0, 0, "
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = ", , , "
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		## not a number in all positions
+		info = TestLayerInfoObject()
+		info.color = "r, 1, 1, 1"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "1, g, 1, 1"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "1, 1, b, 1"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "1, 1, 1, a"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		## too many parts
+		info = TestLayerInfoObject()
+		info.color = "1, 0, 0, 0, 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		## < 0 in each position
+		info = TestLayerInfoObject()
+		info.color = "-1, 0, 0, 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "0, -1, 0, 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "0, 0, -1, 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "0, 0, 0, -1"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		## > 1 in each position
+		info = TestLayerInfoObject()
+		info.color = "2, 0, 0, 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "0, 2, 0, 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "0, 0, 2, 0"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
+		info = TestLayerInfoObject()
+		info.color = "0, 0, 0, 2"
+		glyphSet = self.makeGlyphSet()
+		self.assertRaises(GlifLibError, glyphSet.writeLayerInfo, info)
diff --git a/Tests/ufoLib/UFOConversion_test.py b/Tests/ufoLib/UFOConversion_test.py
new file mode 100644
index 0000000..98a0812
--- /dev/null
+++ b/Tests/ufoLib/UFOConversion_test.py
@@ -0,0 +1,367 @@
+import os
+import shutil
+import unittest
+import tempfile
+from io import open
+from fontTools.ufoLib import UFOReader, UFOWriter
+from fontTools.ufoLib import plistlib
+from .testSupport import expectedFontInfo1To2Conversion, expectedFontInfo2To1Conversion
+
+
+# the format version 1 lib.plist contains some data
+# that these tests shouldn't be concerned about.
+removeFromFormatVersion1Lib = [
+	"org.robofab.opentype.classes",
+	"org.robofab.opentype.features",
+	"org.robofab.opentype.featureorder",
+	"org.robofab.postScriptHintData"
+]
+
+
+class ConversionFunctionsTestCase(unittest.TestCase):
+
+	def tearDown(self):
+		path = self.getFontPath("TestFont1 (UFO1) converted.ufo")
+		if os.path.exists(path):
+			shutil.rmtree(path)
+		path = self.getFontPath("TestFont1 (UFO2) converted.ufo")
+		if os.path.exists(path):
+			shutil.rmtree(path)
+
+	def getFontPath(self, fileName):
+		testdata = os.path.join(os.path.dirname(__file__), "testdata")
+		return os.path.join(testdata, fileName)
+
+	def compareFileStructures(self, path1, path2, expectedInfoData, testFeatures):
+		# result
+		metainfoPath1 = os.path.join(path1, "metainfo.plist")
+		fontinfoPath1 = os.path.join(path1, "fontinfo.plist")
+		kerningPath1 = os.path.join(path1, "kerning.plist")
+		groupsPath1 = os.path.join(path1, "groups.plist")
+		libPath1 = os.path.join(path1, "lib.plist")
+		featuresPath1 = os.path.join(path1, "features.plist")
+		glyphsPath1 = os.path.join(path1, "glyphs")
+		glyphsPath1_contents = os.path.join(glyphsPath1, "contents.plist")
+		glyphsPath1_A = os.path.join(glyphsPath1, "A_.glif")
+		glyphsPath1_B = os.path.join(glyphsPath1, "B_.glif")
+		# expected result
+		metainfoPath2 = os.path.join(path2, "metainfo.plist")
+		fontinfoPath2 = os.path.join(path2, "fontinfo.plist")
+		kerningPath2 = os.path.join(path2, "kerning.plist")
+		groupsPath2 = os.path.join(path2, "groups.plist")
+		libPath2 = os.path.join(path2, "lib.plist")
+		featuresPath2 = os.path.join(path2, "features.plist")
+		glyphsPath2 = os.path.join(path2, "glyphs")
+		glyphsPath2_contents = os.path.join(glyphsPath2, "contents.plist")
+		glyphsPath2_A = os.path.join(glyphsPath2, "A_.glif")
+		glyphsPath2_B = os.path.join(glyphsPath2, "B_.glif")
+		# look for existence
+		self.assertEqual(os.path.exists(metainfoPath1), True)
+		self.assertEqual(os.path.exists(fontinfoPath1), True)
+		self.assertEqual(os.path.exists(kerningPath1), True)
+		self.assertEqual(os.path.exists(groupsPath1), True)
+		self.assertEqual(os.path.exists(libPath1), True)
+		self.assertEqual(os.path.exists(glyphsPath1), True)
+		self.assertEqual(os.path.exists(glyphsPath1_contents), True)
+		self.assertEqual(os.path.exists(glyphsPath1_A), True)
+		self.assertEqual(os.path.exists(glyphsPath1_B), True)
+		if testFeatures:
+			self.assertEqual(os.path.exists(featuresPath1), True)
+		# look for aggrement
+		with open(metainfoPath1, "rb") as f:
+			data1 = plistlib.load(f)
+		with open(metainfoPath2, "rb") as f:
+			data2 = plistlib.load(f)
+		self.assertEqual(data1, data2)
+		with open(fontinfoPath1, "rb") as f:
+			data1 = plistlib.load(f)
+		self.assertEqual(sorted(data1.items()), sorted(expectedInfoData.items()))
+		with open(kerningPath1, "rb") as f:
+			data1 = plistlib.load(f)
+		with open(kerningPath2, "rb") as f:
+			data2 = plistlib.load(f)
+		self.assertEqual(data1, data2)
+		with open(groupsPath1, "rb") as f:
+			data1 = plistlib.load(f)
+		with open(groupsPath2, "rb") as f:
+			data2 = plistlib.load(f)
+		self.assertEqual(data1, data2)
+		with open(libPath1, "rb") as f:
+			data1 = plistlib.load(f)
+		with open(libPath2, "rb") as f:
+			data2 = plistlib.load(f)
+		if "UFO1" in libPath1:
+			for key in removeFromFormatVersion1Lib:
+				if key in data1:
+					del data1[key]
+		if "UFO1" in libPath2:
+			for key in removeFromFormatVersion1Lib:
+				if key in data2:
+					del data2[key]
+		self.assertEqual(data1, data2)
+		with open(glyphsPath1_contents, "rb") as f:
+			data1 = plistlib.load(f)
+		with open(glyphsPath2_contents, "rb") as f:
+			data2 = plistlib.load(f)
+		self.assertEqual(data1, data2)
+		with open(glyphsPath1_A, "rb") as f:
+			data1 = plistlib.load(f)
+		with open(glyphsPath2_A, "rb") as f:
+			data2 = plistlib.load(f)
+		self.assertEqual(data1, data2)
+		with open(glyphsPath1_B, "rb") as f:
+			data1 = plistlib.load(f)
+		with open(glyphsPath2_B, "rb") as f:
+			data2 = plistlib.load(f)
+		self.assertEqual(data1, data2)
+
+
+# ---------------------
+# kerning up conversion
+# ---------------------
+
+class TestInfoObject: pass
+
+
+class KerningUpConversionTestCase(unittest.TestCase):
+
+	expectedKerning = {
+		("public.kern1.BGroup", "public.kern2.CGroup"): 7,
+		("public.kern1.BGroup", "public.kern2.DGroup"): 8,
+		("public.kern1.BGroup", "A"): 5,
+		("public.kern1.BGroup", "B"): 6,
+		("public.kern1.CGroup", "public.kern2.CGroup"): 11,
+		("public.kern1.CGroup", "public.kern2.DGroup"): 12,
+		("public.kern1.CGroup", "A"): 9,
+		("public.kern1.CGroup", "B"): 10,
+		("A", "public.kern2.CGroup"): 3,
+		("A", "public.kern2.DGroup"): 4,
+		("A", "A"): 1,
+		("A", "B"): 2,
+		("X", "A"): 13,
+		("X", "public.kern2.CGroup"): 14
+	}
+
+	expectedGroups = {
+		"BGroup": ["B"],
+		"CGroup": ["C", "Ccedilla"],
+		"DGroup": ["D"],
+		"public.kern1.BGroup": ["B"],
+		"public.kern1.CGroup": ["C", "Ccedilla"],
+		"public.kern2.CGroup": ["C", "Ccedilla"],
+		"public.kern2.DGroup": ["D"],
+		"Not A Kerning Group" : ["A"],
+		"X": ["X", "X.sc"]
+	}
+
+	def setUp(self):
+		self.tempDir = tempfile.mktemp()
+		os.mkdir(self.tempDir)
+		self.ufoPath = os.path.join(self.tempDir, "test.ufo")
+
+	def tearDown(self):
+		shutil.rmtree(self.tempDir)
+
+	def makeUFO(self, formatVersion):
+		self.clearUFO()
+		if not os.path.exists(self.ufoPath):
+			os.mkdir(self.ufoPath)
+
+		# glyphs
+		glyphsPath = os.path.join(self.ufoPath, "glyphs")
+		if not os.path.exists(glyphsPath):
+			os.mkdir(glyphsPath)
+		glyphFile = "X_.glif"
+		glyphsContents = dict(X=glyphFile)
+		path = os.path.join(glyphsPath, "contents.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(glyphsContents, f)
+		path = os.path.join(glyphsPath, glyphFile)
+		with open(path, "w") as f:
+			f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
+
+		# metainfo.plist
+		metaInfo = dict(creator="test", formatVersion=formatVersion)
+		path = os.path.join(self.ufoPath, "metainfo.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(metaInfo, f)
+		# kerning
+		kerning = {
+			"A" : {
+				"A" : 1,
+				"B" : 2,
+				"CGroup" : 3,
+				"DGroup" : 4
+			},
+			"BGroup" : {
+				"A" : 5,
+				"B" : 6,
+				"CGroup" : 7,
+				"DGroup" : 8
+			},
+			"CGroup" : {
+				"A" : 9,
+				"B" : 10,
+				"CGroup" : 11,
+				"DGroup" : 12
+			},
+			"X": {
+				"A" : 13,
+				"CGroup" : 14
+			}
+		}
+		path = os.path.join(self.ufoPath, "kerning.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(kerning, f)
+		# groups
+		groups = {
+			"BGroup" : ["B"],
+			"CGroup" : ["C", "Ccedilla"],
+			"DGroup" : ["D"],
+			"Not A Kerning Group" : ["A"],
+			"X" : ["X", "X.sc"]  # a group with a name that is also a glyph name
+		}
+		path = os.path.join(self.ufoPath, "groups.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(groups, f)
+		# font info
+		fontInfo = {
+			"familyName" : "Test"
+		}
+		path = os.path.join(self.ufoPath, "fontinfo.plist")
+		with open(path, "wb") as f:
+			plistlib.dump(fontInfo, f)
+
+	def clearUFO(self):
+		if os.path.exists(self.ufoPath):
+			shutil.rmtree(self.ufoPath)
+
+	def testUFO1(self):
+		self.makeUFO(formatVersion=2)
+		reader = UFOReader(self.ufoPath, validate=True)
+		kerning = reader.readKerning()
+		self.assertEqual(self.expectedKerning, kerning)
+		groups = reader.readGroups()
+		self.assertEqual(self.expectedGroups, groups)
+		info = TestInfoObject()
+		reader.readInfo(info)
+
+	def testUFO2(self):
+		self.makeUFO(formatVersion=2)
+		reader = UFOReader(self.ufoPath, validate=True)
+		kerning = reader.readKerning()
+		self.assertEqual(self.expectedKerning, kerning)
+		groups = reader.readGroups()
+		self.assertEqual(self.expectedGroups, groups)
+		info = TestInfoObject()
+		reader.readInfo(info)
+
+
+class KerningDownConversionTestCase(unittest.TestCase):
+
+	expectedKerning = {
+		("public.kern1.BGroup", "public.kern2.CGroup"): 7,
+		("public.kern1.BGroup", "public.kern2.DGroup"): 8,
+		("public.kern1.BGroup", "A"): 5,
+		("public.kern1.BGroup", "B"): 6,
+		("public.kern1.CGroup", "public.kern2.CGroup"): 11,
+		("public.kern1.CGroup", "public.kern2.DGroup"): 12,
+		("public.kern1.CGroup", "A"): 9,
+		("public.kern1.CGroup", "B"): 10,
+		("A", "public.kern2.CGroup"): 3,
+		("A", "public.kern2.DGroup"): 4,
+		("A", "A"): 1,
+		("A", "B"): 2
+	}
+
+	groups = {
+		"BGroup": ["B"],
+		"CGroup": ["C"],
+		"DGroup": ["D"],
+		"public.kern1.BGroup": ["B"],
+		"public.kern1.CGroup": ["C", "Ccedilla"],
+		"public.kern2.CGroup": ["C", "Ccedilla"],
+		"public.kern2.DGroup": ["D"],
+		"Not A Kerning Group" : ["A"]
+	}
+	expectedWrittenGroups = {
+		"BGroup": ["B"],
+		"CGroup": ["C", "Ccedilla"],
+		"DGroup": ["D"],
+		"Not A Kerning Group" : ["A"]
+	}
+
+	kerning = {
+		("public.kern1.BGroup", "public.kern2.CGroup"): 7,
+		("public.kern1.BGroup", "public.kern2.DGroup"): 8,
+		("public.kern1.BGroup", "A"): 5,
+		("public.kern1.BGroup", "B"): 6,
+		("public.kern1.CGroup", "public.kern2.CGroup"): 11,
+		("public.kern1.CGroup", "public.kern2.DGroup"): 12,
+		("public.kern1.CGroup", "A"): 9,
+		("public.kern1.CGroup", "B"): 10,
+		("A", "public.kern2.CGroup"): 3,
+		("A", "public.kern2.DGroup"): 4,
+		("A", "A"): 1,
+		("A", "B"): 2
+	}
+	expectedWrittenKerning = {
+		"BGroup" : {
+			"CGroup" : 7,
+			"DGroup" : 8,
+			"A" : 5,
+			"B" : 6
+		},
+		"CGroup" : {
+			"CGroup" : 11,
+			"DGroup" : 12,
+			"A" : 9,
+			"B" : 10
+		},
+		"A" : {
+			"CGroup" : 3,
+			"DGroup" : 4,
+			"A" : 1,
+			"B" : 2
+		}
+	}
+
+
+	downConversionMapping = {
+		"side1" : {
+			"BGroup" : "public.kern1.BGroup",
+			"CGroup" : "public.kern1.CGroup"
+		},
+		"side2" : {
+			"CGroup" : "public.kern2.CGroup",
+			"DGroup" : "public.kern2.DGroup"
+		}
+	}
+
+	def setUp(self):
+		self.tempDir = tempfile.mktemp()
+		os.mkdir(self.tempDir)
+		self.dstDir = os.path.join(self.tempDir, "test.ufo")
+
+	def tearDown(self):
+		shutil.rmtree(self.tempDir)
+
+	def tearDownUFO(self):
+		shutil.rmtree(self.dstDir)
+
+	def testWrite(self):
+		writer = UFOWriter(self.dstDir, formatVersion=2)
+		writer.setKerningGroupConversionRenameMaps(self.downConversionMapping)
+		writer.writeKerning(self.kerning)
+		writer.writeGroups(self.groups)
+		# test groups
+		path = os.path.join(self.dstDir, "groups.plist")
+		with open(path, "rb") as f:
+			writtenGroups = plistlib.load(f)
+		self.assertEqual(writtenGroups, self.expectedWrittenGroups)
+		# test kerning
+		path = os.path.join(self.dstDir, "kerning.plist")
+		with open(path, "rb") as f:
+			writtenKerning = plistlib.load(f)
+		self.assertEqual(writtenKerning, self.expectedWrittenKerning)
+		self.tearDownUFO()
diff --git a/Tests/ufoLib/UFOZ_test.py b/Tests/ufoLib/UFOZ_test.py
new file mode 100644
index 0000000..5136210
--- /dev/null
+++ b/Tests/ufoLib/UFOZ_test.py
@@ -0,0 +1,97 @@
+from fontTools.misc.py23 import tostr
+from fontTools.ufoLib import UFOReader, UFOWriter, UFOFileStructure
+from fontTools.ufoLib.errors import UFOLibError, GlifLibError
+from fontTools.misc import plistlib
+import sys
+import os
+import fs.osfs
+import fs.tempfs
+import fs.memoryfs
+import fs.copy
+import pytest
+import warnings
+
+
+TESTDATA = fs.osfs.OSFS(
+    os.path.join(os.path.dirname(__file__), "testdata")
+)
+TEST_UFO3 = "TestFont1 (UFO3).ufo"
+TEST_UFOZ = "TestFont1 (UFO3).ufoz"
+
+
+@pytest.fixture(params=[TEST_UFO3, TEST_UFOZ])
+def testufo(request):
+    name = request.param
+    with fs.tempfs.TempFS() as tmp:
+        if TESTDATA.isdir(name):
+            fs.copy.copy_dir(TESTDATA, name, tmp, name)
+        else:
+            fs.copy.copy_file(TESTDATA, name, tmp, name)
+        yield tmp.getsyspath(name)
+
+
+@pytest.fixture
+def testufoz():
+    with fs.tempfs.TempFS() as tmp:
+        fs.copy.copy_file(TESTDATA, TEST_UFOZ, tmp, TEST_UFOZ)
+        yield tmp.getsyspath(TEST_UFOZ)
+
+
+class TestUFOZ:
+
+    def test_read(self, testufoz):
+        with UFOReader(testufoz) as reader:
+            assert reader.fileStructure == UFOFileStructure.ZIP
+            assert reader.formatVersion == 3
+
+    def test_write(self, testufoz):
+        with UFOWriter(testufoz, structure="zip") as writer:
+            writer.writeLib({"hello world": 123})
+        with UFOReader(testufoz) as reader:
+            assert reader.readLib() == {"hello world": 123}
+
+
+def test_pathlike(testufo):
+
+    class PathLike:
+
+        def __init__(self, s):
+            self._path = s
+
+        def __fspath__(self):
+            return tostr(self._path, sys.getfilesystemencoding())
+
+    path = PathLike(testufo)
+
+    with UFOReader(path) as reader:
+        assert reader._path == path.__fspath__()
+
+    with UFOWriter(path) as writer:
+        assert writer._path == path.__fspath__()
+
+
+def test_path_attribute_deprecated(testufo):
+    with UFOWriter(testufo) as writer:
+        with pytest.warns(DeprecationWarning, match="The 'path' attribute"):
+            writer.path
+
+
+@pytest.fixture
+def memufo():
+    m = fs.memoryfs.MemoryFS()
+    fs.copy.copy_dir(TESTDATA, TEST_UFO3, m, "/")
+    return m
+
+
+class TestMemoryFS:
+
+    def test_init_reader(self, memufo):
+        with UFOReader(memufo) as reader:
+            assert reader.formatVersion == 3
+            assert reader.fileStructure == UFOFileStructure.PACKAGE
+
+    def test_init_writer(self):
+        m = fs.memoryfs.MemoryFS()
+        with UFOWriter(m) as writer:
+            assert m.exists("metainfo.plist")
+            assert writer._path == "<memfs>"
diff --git a/Tests/ufoLib/__init__.py b/Tests/ufoLib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/ufoLib/__init__.py
diff --git a/Tests/ufoLib/filenames_test.py b/Tests/ufoLib/filenames_test.py
new file mode 100644
index 0000000..a9415a1
--- /dev/null
+++ b/Tests/ufoLib/filenames_test.py
@@ -0,0 +1,97 @@
+import unittest
+from fontTools.ufoLib.filenames import userNameToFileName, handleClash1, handleClash2
+
+
+class TestFilenames(unittest.TestCase):
+
+    def test_userNameToFileName(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("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_userNameToFileName_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'
+        )
diff --git a/Tests/ufoLib/glifLib_test.py b/Tests/ufoLib/glifLib_test.py
new file mode 100644
index 0000000..3af0256
--- /dev/null
+++ b/Tests/ufoLib/glifLib_test.py
@@ -0,0 +1,314 @@
+import logging
+import os
+import tempfile
+import shutil
+import unittest
+from io import open
+from .testSupport import getDemoFontGlyphSetPath
+from fontTools.ufoLib.glifLib import (
+	GlyphSet, glyphNameToFileName, readGlyphFromString, writeGlyphToString,
+)
+from fontTools.ufoLib.errors import GlifLibError, UnsupportedGLIFFormat, UnsupportedUFOFormat
+from fontTools.misc.etree import XML_DECLARATION
+from fontTools.pens.recordingPen import RecordingPointPen
+import pytest
+
+GLYPHSETDIR = getDemoFontGlyphSetPath()
+
+
+class GlyphSetTests(unittest.TestCase):
+
+	def setUp(self):
+		self.dstDir = tempfile.mktemp()
+		os.mkdir(self.dstDir)
+
+	def tearDown(self):
+		shutil.rmtree(self.dstDir)
+
+	def testRoundTrip(self):
+		import difflib
+		srcDir = GLYPHSETDIR
+		dstDir = self.dstDir
+		src = GlyphSet(srcDir, ufoFormatVersion=2, validateRead=True, validateWrite=True)
+		dst = GlyphSet(dstDir, ufoFormatVersion=2, validateRead=True, validateWrite=True)
+		for glyphName in src.keys():
+			g = src[glyphName]
+			g.drawPoints(None)  # load attrs
+			dst.writeGlyph(glyphName, g, g.drawPoints)
+		# compare raw file data:
+		for glyphName in sorted(src.keys()):
+			fileName = src.contents[glyphName]
+			with open(os.path.join(srcDir, fileName), "r") as f:
+				org = f.read()
+			with open(os.path.join(dstDir, fileName), "r") as f:
+				new = f.read()
+			added = []
+			removed = []
+			for line in difflib.unified_diff(
+					org.split("\n"), new.split("\n")):
+				if line.startswith("+ "):
+					added.append(line[1:])
+				elif line.startswith("- "):
+					removed.append(line[1:])
+			self.assertEqual(
+				added, removed,
+				"%s.glif file differs after round tripping" % glyphName)
+
+	def testContentsExist(self):
+		with self.assertRaises(GlifLibError):
+			GlyphSet(
+				self.dstDir,
+				ufoFormatVersion=2,
+				validateRead=True,
+				validateWrite=True,
+				expectContentsFile=True,
+			)
+
+	def testRebuildContents(self):
+		gset = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True)
+		contents = gset.contents
+		gset.rebuildContents()
+		self.assertEqual(contents, gset.contents)
+
+	def testReverseContents(self):
+		gset = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True)
+		d = {}
+		for k, v in gset.getReverseContents().items():
+			d[v] = k
+		org = {}
+		for k, v in gset.contents.items():
+			org[k] = v.lower()
+		self.assertEqual(d, org)
+
+	def testReverseContents2(self):
+		src = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True)
+		dst = GlyphSet(self.dstDir, validateRead=True, validateWrite=True)
+		dstMap = dst.getReverseContents()
+		self.assertEqual(dstMap, {})
+		for glyphName in src.keys():
+			g = src[glyphName]
+			g.drawPoints(None)  # load attrs
+			dst.writeGlyph(glyphName, g, g.drawPoints)
+		self.assertNotEqual(dstMap, {})
+		srcMap = dict(src.getReverseContents())  # copy
+		self.assertEqual(dstMap, srcMap)
+		del srcMap["a.glif"]
+		dst.deleteGlyph("a")
+		self.assertEqual(dstMap, srcMap)
+
+	def testCustomFileNamingScheme(self):
+		def myGlyphNameToFileName(glyphName, glyphSet):
+			return "prefix" + glyphNameToFileName(glyphName, glyphSet)
+		src = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True)
+		dst = GlyphSet(self.dstDir, myGlyphNameToFileName, validateRead=True, validateWrite=True)
+		for glyphName in src.keys():
+			g = src[glyphName]
+			g.drawPoints(None)  # load attrs
+			dst.writeGlyph(glyphName, g, g.drawPoints)
+		d = {}
+		for k, v in src.contents.items():
+			d[k] = "prefix" + v
+		self.assertEqual(d, dst.contents)
+
+	def testGetUnicodes(self):
+		src = GlyphSet(GLYPHSETDIR, validateRead=True, validateWrite=True)
+		unicodes = src.getUnicodes()
+		for glyphName in src.keys():
+			g = src[glyphName]
+			g.drawPoints(None)  # load attrs
+			if not hasattr(g, "unicodes"):
+				self.assertEqual(unicodes[glyphName], [])
+			else:
+				self.assertEqual(g.unicodes, unicodes[glyphName])
+
+
+class FileNameTest:
+
+	def test_default_file_name_scheme(self):
+		assert glyphNameToFileName("a", None) == "a.glif"
+		assert glyphNameToFileName("A", None) == "A_.glif"
+		assert glyphNameToFileName("Aring", None) == "A_ring.glif"
+		assert glyphNameToFileName("F_A_B", None) == "F__A__B_.glif"
+		assert glyphNameToFileName("A.alt", None) == "A_.alt.glif"
+		assert glyphNameToFileName("A.Alt", None) == "A_.A_lt.glif"
+		assert glyphNameToFileName(".notdef", None) == "_notdef.glif"
+		assert glyphNameToFileName("T_H", None) =="T__H_.glif"
+		assert glyphNameToFileName("T_h", None) =="T__h.glif"
+		assert glyphNameToFileName("t_h", None) =="t_h.glif"
+		assert glyphNameToFileName("F_F_I", None) == "F__F__I_.glif"
+		assert glyphNameToFileName("f_f_i", None) == "f_f_i.glif"
+		assert glyphNameToFileName("AE", None) == "A_E_.glif"
+		assert glyphNameToFileName("Ae", None) == "A_e.glif"
+		assert glyphNameToFileName("ae", None) == "ae.glif"
+		assert glyphNameToFileName("aE", None) == "aE_.glif"
+		assert glyphNameToFileName("a.alt", None) == "a.alt.glif"
+		assert glyphNameToFileName("A.aLt", None) == "A_.aL_t.glif"
+		assert glyphNameToFileName("A.alT", None) == "A_.alT_.glif"
+		assert glyphNameToFileName("Aacute_V.swash", None) == "A_acute_V_.swash.glif"
+		assert glyphNameToFileName(".notdef", None) == "_notdef.glif"
+		assert glyphNameToFileName("con", None) == "_con.glif"
+		assert glyphNameToFileName("CON", None) == "C_O_N_.glif"
+		assert glyphNameToFileName("con.alt", None) == "_con.alt.glif"
+		assert glyphNameToFileName("alt.con", None) == "alt._con.glif"
+
+	def test_conflicting_case_insensitive_file_names(self, tmp_path):
+		src = GlyphSet(GLYPHSETDIR)
+		dst = GlyphSet(tmp_path)
+		glyph = src["a"]
+
+		dst.writeGlyph("a", glyph)
+		dst.writeGlyph("A", glyph)
+		dst.writeGlyph("a_", glyph)
+		dst.writeGlyph("A_", glyph)
+		dst.writeGlyph("i_j", glyph)
+
+		assert dst.contents == {
+			'a': 'a.glif',
+			'A': 'A_.glif',
+			'a_': 'a_000000000000001.glif',
+			'A_': 'A__.glif',
+			'i_j': 'i_j.glif',
+		}
+
+		# make sure filenames are unique even on case-insensitive filesystems
+		assert len({fileName.lower() for fileName in dst.contents.values()}) == 5
+
+
+class _Glyph:
+	pass
+
+
+class ReadWriteFuncTest:
+
+	def test_roundtrip(self):
+		glyph = _Glyph()
+		glyph.name = "a"
+		glyph.unicodes = [0x0061]
+
+		s1 = writeGlyphToString(glyph.name, glyph)
+
+		glyph2 = _Glyph()
+		readGlyphFromString(s1, glyph2)
+		assert glyph.__dict__ == glyph2.__dict__
+
+		s2 = writeGlyphToString(glyph2.name, glyph2)
+		assert s1 == s2
+
+	def test_xml_declaration(self):
+		s = writeGlyphToString("a", _Glyph())
+		assert s.startswith(XML_DECLARATION % "UTF-8")
+
+	def test_parse_xml_remove_comments(self):
+		s = b"""<?xml version='1.0' encoding='UTF-8'?>
+		<!-- a comment -->
+		<glyph name="A" format="2">
+			<advance width="1290"/>
+			<unicode hex="0041"/>
+			<!-- another comment -->
+		</glyph>
+		"""
+
+		g = _Glyph()
+		readGlyphFromString(s, g)
+
+		assert g.name == "A"
+		assert g.width == 1290
+		assert g.unicodes == [0x0041]
+
+	def test_read_unsupported_format_version(self, caplog):
+		s = """<?xml version='1.0' encoding='utf-8'?>
+		<glyph name="A" format="0" formatMinor="0">
+			<advance width="500"/>
+			<unicode hex="0041"/>
+		</glyph>
+		"""
+
+		with pytest.raises(UnsupportedGLIFFormat):
+			readGlyphFromString(s, _Glyph())  # validate=True by default
+
+		with pytest.raises(UnsupportedGLIFFormat):
+			readGlyphFromString(s, _Glyph(), validate=True)
+
+		caplog.clear()
+		with caplog.at_level(logging.WARNING, logger="fontTools.ufoLib.glifLib"):
+			readGlyphFromString(s, _Glyph(), validate=False)
+
+		assert len(caplog.records) == 1
+		assert "Unsupported GLIF format" in caplog.text
+		assert "Assuming the latest supported version" in caplog.text
+
+	def test_read_allow_format_versions(self):
+		s = """<?xml version='1.0' encoding='utf-8'?>
+		<glyph name="A" format="2">
+			<advance width="500"/>
+			<unicode hex="0041"/>
+		</glyph>
+		"""
+
+		# these two calls are are equivalent
+		readGlyphFromString(s, _Glyph(), formatVersions=[1, 2])
+		readGlyphFromString(s, _Glyph(), formatVersions=[(1, 0), (2, 0)])
+
+		# if at least one supported formatVersion, unsupported ones are ignored
+		readGlyphFromString(s, _Glyph(), formatVersions=[(2, 0), (123, 456)])
+
+		with pytest.raises(
+			ValueError,
+			match="None of the requested GLIF formatVersions are supported"
+		):
+			readGlyphFromString(s, _Glyph(), formatVersions=[0, 2001])
+
+		with pytest.raises(GlifLibError, match="Forbidden GLIF format version"):
+			readGlyphFromString(s, _Glyph(), formatVersions=[1])
+
+	def test_read_ensure_x_y(self):
+		"""Ensure that a proper GlifLibError is raised when point coordinates are
+		missing, regardless of validation setting."""
+
+		s = """<?xml version='1.0' encoding='utf-8'?>
+		<glyph name="A" format="2">
+			<outline>
+				<contour>
+					<point x="545" y="0" type="line"/>
+					<point x="638" type="line"/>
+				</contour>
+			</outline>
+		</glyph>
+		"""
+		pen = RecordingPointPen() 
+
+		with pytest.raises(GlifLibError, match="Required y attribute"):
+			readGlyphFromString(s, _Glyph(), pen)
+
+		with pytest.raises(GlifLibError, match="Required y attribute"):
+			readGlyphFromString(s, _Glyph(), pen, validate=False)
+
+def test_GlyphSet_unsupported_ufoFormatVersion(tmp_path, caplog):
+	with pytest.raises(UnsupportedUFOFormat):
+		GlyphSet(tmp_path, ufoFormatVersion=0)
+	with pytest.raises(UnsupportedUFOFormat):
+		GlyphSet(tmp_path, ufoFormatVersion=(0, 1))
+
+
+def test_GlyphSet_writeGlyph_formatVersion(tmp_path):
+	src = GlyphSet(GLYPHSETDIR)
+	dst = GlyphSet(tmp_path, ufoFormatVersion=(2, 0))
+	glyph = src["A"]
+
+	# no explicit formatVersion passed: use the more recent GLIF formatVersion
+	# that is supported by given ufoFormatVersion (GLIF 1 for UFO 2)
+	dst.writeGlyph("A", glyph)
+	glif = dst.getGLIF("A")
+	assert b'format="1"' in glif
+	assert b'formatMinor' not in glif  # omitted when 0
+
+	# explicit, unknown formatVersion
+	with pytest.raises(UnsupportedGLIFFormat):
+		dst.writeGlyph("A", glyph, formatVersion=(0, 0))
+
+	# explicit, known formatVersion but unsupported by given ufoFormatVersion
+	with pytest.raises(
+		UnsupportedGLIFFormat,
+		match="Unsupported GLIF format version .*for UFO format version",
+	):
+		dst.writeGlyph("A", glyph, formatVersion=(2, 0))
diff --git a/Tests/ufoLib/testSupport.py b/Tests/ufoLib/testSupport.py
new file mode 100755
index 0000000..49f6a53
--- /dev/null
+++ b/Tests/ufoLib/testSupport.py
@@ -0,0 +1,667 @@
+"""Miscellaneous helpers for our test suite."""
+
+import os
+from fontTools.ufoLib.utils import numberTypes
+
+
+def getDemoFontPath():
+	"""Return the path to Data/DemoFont.ufo/."""
+	testdata = os.path.join(os.path.dirname(__file__), "testdata")
+	return os.path.join(testdata, "DemoFont.ufo")
+
+
+def getDemoFontGlyphSetPath():
+	"""Return the path to Data/DemoFont.ufo/glyphs/."""
+	return os.path.join(getDemoFontPath(), "glyphs")
+
+
+# GLIF test tools
+
+class Glyph:
+
+	def __init__(self):
+		self.name = None
+		self.width = None
+		self.height = None
+		self.unicodes = None
+		self.note = None
+		self.lib = None
+		self.image = None
+		self.guidelines = None
+		self.anchors = None
+		self.outline = []
+
+	def _writePointPenCommand(self, command, args, kwargs):
+		args = _listToString(args)
+		kwargs = _dictToString(kwargs)
+		if args and kwargs:
+			return f"pointPen.{command}(*{args}, **{kwargs})"
+		elif len(args):
+			return f"pointPen.{command}(*{args})"
+		elif len(kwargs):
+			return f"pointPen.{command}(**{kwargs})"
+		else:
+			return "pointPen.%s()" % command
+
+	def beginPath(self, **kwargs):
+		self.outline.append(self._writePointPenCommand("beginPath", [], kwargs))
+
+	def endPath(self):
+		self.outline.append(self._writePointPenCommand("endPath", [], {}))
+
+	def addPoint(self, *args, **kwargs):
+		self.outline.append(self._writePointPenCommand("addPoint", args, kwargs))
+
+	def addComponent(self, *args, **kwargs):
+		self.outline.append(self._writePointPenCommand("addComponent", args, kwargs))
+
+	def drawPoints(self, pointPen):
+		if self.outline:
+			py = "\n".join(self.outline)
+			exec(py, {"pointPen" : pointPen})
+
+	def py(self):
+		text = []
+		if self.name is not None:
+			text.append("glyph.name = \"%s\"" % self.name)
+		if self.width:
+			text.append("glyph.width = %r" % self.width)
+		if self.height:
+			text.append("glyph.height = %r" % self.height)
+		if self.unicodes is not None:
+			text.append("glyph.unicodes = [%s]" % ", ".join([str(i) for i in self.unicodes]))
+		if self.note is not None:
+			text.append("glyph.note = \"%s\"" % self.note)
+		if self.lib is not None:
+			text.append("glyph.lib = %s" % _dictToString(self.lib))
+		if self.image is not None:
+			text.append("glyph.image = %s" % _dictToString(self.image))
+		if self.guidelines is not None:
+			text.append("glyph.guidelines = %s" % _listToString(self.guidelines))
+		if self.anchors is not None:
+			text.append("glyph.anchors = %s" % _listToString(self.anchors))
+		if self.outline:
+			text += self.outline
+		return "\n".join(text)
+
+def _dictToString(d):
+	text = []
+	for key, value in sorted(d.items()):
+		if value is None:
+			continue
+		key = "\"%s\"" % key
+		if isinstance(value, dict):
+			value = _dictToString(value)
+		elif isinstance(value, list):
+			value = _listToString(value)
+		elif isinstance(value, tuple):
+			value = _tupleToString(value)
+		elif isinstance(value, numberTypes):
+			value = repr(value)
+		elif isinstance(value, str):
+			value = "\"%s\"" % value
+		text.append(f"{key} : {value}")
+	if not text:
+		return ""
+	return "{%s}" % ", ".join(text)
+
+def _listToString(l):
+	text = []
+	for value in l:
+		if isinstance(value, dict):
+			value = _dictToString(value)
+		elif isinstance(value, list):
+			value = _listToString(value)
+		elif isinstance(value, tuple):
+			value = _tupleToString(value)
+		elif isinstance(value, numberTypes):
+			value = repr(value)
+		elif isinstance(value, str):
+			value = "\"%s\"" % value
+		text.append(value)
+	if not text:
+		return ""
+	return "[%s]" % ", ".join(text)
+
+def _tupleToString(t):
+	text = []
+	for value in t:
+		if isinstance(value, dict):
+			value = _dictToString(value)
+		elif isinstance(value, list):
+			value = _listToString(value)
+		elif isinstance(value, tuple):
+			value = _tupleToString(value)
+		elif isinstance(value, numberTypes):
+			value = repr(value)
+		elif isinstance(value, str):
+			value = "\"%s\"" % value
+		text.append(value)
+	if not text:
+		return ""
+	return "(%s)" % ", ".join(text)
+
+def stripText(text):
+	new = []
+	for line in text.strip().splitlines():
+		line = line.strip()
+		if not line:
+			continue
+		new.append(line)
+	return "\n".join(new)
+
+# font info values used by several tests
+
+fontInfoVersion1 = {
+	"familyName"   : "Some Font (Family Name)",
+	"styleName"	   : "Regular (Style Name)",
+	"fullName"	   : "Some Font-Regular (Postscript Full Name)",
+	"fontName"	   : "SomeFont-Regular (Postscript Font Name)",
+	"menuName"	   : "Some Font Regular (Style Map Family Name)",
+	"fontStyle"	   : 64,
+	"note"		   : "A note.",
+	"versionMajor" : 1,
+	"versionMinor" : 0,
+	"year"		   : 2008,
+	"copyright"	   : "Copyright Some Foundry.",
+	"notice"	   : "Some Font by Some Designer for Some Foundry.",
+	"trademark"	   : "Trademark Some Foundry",
+	"license"	   : "License info for Some Foundry.",
+	"licenseURL"   : "http://somefoundry.com/license",
+	"createdBy"	   : "Some Foundry",
+	"designer"	   : "Some Designer",
+	"designerURL"  : "http://somedesigner.com",
+	"vendorURL"	   : "http://somefoundry.com",
+	"unitsPerEm"   : 1000,
+	"ascender"	   : 750,
+	"descender"	   : -250,
+	"capHeight"	   : 750,
+	"xHeight"	   : 500,
+	"defaultWidth" : 400,
+	"slantAngle"   : -12.5,
+	"italicAngle"  : -12.5,
+	"widthName"	   : "Medium (normal)",
+	"weightName"   : "Medium",
+	"weightValue"  : 500,
+	"fondName"	   : "SomeFont Regular (FOND Name)",
+	"otFamilyName" : "Some Font (Preferred Family Name)",
+	"otStyleName"  : "Regular (Preferred Subfamily Name)",
+	"otMacName"	   : "Some Font Regular (Compatible Full Name)",
+	"msCharSet"	   : 0,
+	"fondID"	   : 15000,
+	"uniqueID"	   : 4000000,
+	"ttVendor"	   : "SOME",
+	"ttUniqueID"   : "OpenType name Table Unique ID",
+	"ttVersion"	   : "OpenType name Table Version",
+}
+
+fontInfoVersion2 = {
+	"familyName"						 : "Some Font (Family Name)",
+	"styleName"							 : "Regular (Style Name)",
+	"styleMapFamilyName"				 : "Some Font Regular (Style Map Family Name)",
+	"styleMapStyleName"					 : "regular",
+	"versionMajor"						 : 1,
+	"versionMinor"						 : 0,
+	"year"								 : 2008,
+	"copyright"							 : "Copyright Some Foundry.",
+	"trademark"							 : "Trademark Some Foundry",
+	"unitsPerEm"						 : 1000,
+	"descender"							 : -250,
+	"xHeight"							 : 500,
+	"capHeight"							 : 750,
+	"ascender"							 : 750,
+	"italicAngle"						 : -12.5,
+	"note"								 : "A note.",
+	"openTypeHeadCreated"				 : "2000/01/01 00:00:00",
+	"openTypeHeadLowestRecPPEM"			 : 10,
+	"openTypeHeadFlags"					 : [0, 1],
+	"openTypeHheaAscender"				 : 750,
+	"openTypeHheaDescender"				 : -250,
+	"openTypeHheaLineGap"				 : 200,
+	"openTypeHheaCaretSlopeRise"		 : 1,
+	"openTypeHheaCaretSlopeRun"			 : 0,
+	"openTypeHheaCaretOffset"			 : 0,
+	"openTypeNameDesigner"				 : "Some Designer",
+	"openTypeNameDesignerURL"			 : "http://somedesigner.com",
+	"openTypeNameManufacturer"			 : "Some Foundry",
+	"openTypeNameManufacturerURL"		 : "http://somefoundry.com",
+	"openTypeNameLicense"				 : "License info for Some Foundry.",
+	"openTypeNameLicenseURL"			 : "http://somefoundry.com/license",
+	"openTypeNameVersion"				 : "OpenType name Table Version",
+	"openTypeNameUniqueID"				 : "OpenType name Table Unique ID",
+	"openTypeNameDescription"			 : "Some Font by Some Designer for Some Foundry.",
+	"openTypeNamePreferredFamilyName"	 : "Some Font (Preferred Family Name)",
+	"openTypeNamePreferredSubfamilyName" : "Regular (Preferred Subfamily Name)",
+	"openTypeNameCompatibleFullName"	 : "Some Font Regular (Compatible Full Name)",
+	"openTypeNameSampleText"			 : "Sample Text for Some Font.",
+	"openTypeNameWWSFamilyName"			 : "Some Font (WWS Family Name)",
+	"openTypeNameWWSSubfamilyName"		 : "Regular (WWS Subfamily Name)",
+	"openTypeOS2WidthClass"				 : 5,
+	"openTypeOS2WeightClass"			 : 500,
+	"openTypeOS2Selection"				 : [3],
+	"openTypeOS2VendorID"				 : "SOME",
+	"openTypeOS2Panose"					 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+	"openTypeOS2FamilyClass"			 : [1, 1],
+	"openTypeOS2UnicodeRanges"			 : [0, 1],
+	"openTypeOS2CodePageRanges"			 : [0, 1],
+	"openTypeOS2TypoAscender"			 : 750,
+	"openTypeOS2TypoDescender"			 : -250,
+	"openTypeOS2TypoLineGap"			 : 200,
+	"openTypeOS2WinAscent"				 : 750,
+	"openTypeOS2WinDescent"				 : 250,
+	"openTypeOS2Type"					 : [],
+	"openTypeOS2SubscriptXSize"			 : 200,
+	"openTypeOS2SubscriptYSize"			 : 400,
+	"openTypeOS2SubscriptXOffset"		 : 0,
+	"openTypeOS2SubscriptYOffset"		 : -100,
+	"openTypeOS2SuperscriptXSize"		 : 200,
+	"openTypeOS2SuperscriptYSize"		 : 400,
+	"openTypeOS2SuperscriptXOffset"		 : 0,
+	"openTypeOS2SuperscriptYOffset"		 : 200,
+	"openTypeOS2StrikeoutSize"			 : 20,
+	"openTypeOS2StrikeoutPosition"		 : 300,
+	"openTypeVheaVertTypoAscender"		 : 750,
+	"openTypeVheaVertTypoDescender"		 : -250,
+	"openTypeVheaVertTypoLineGap"		 : 200,
+	"openTypeVheaCaretSlopeRise"		 : 0,
+	"openTypeVheaCaretSlopeRun"			 : 1,
+	"openTypeVheaCaretOffset"			 : 0,
+	"postscriptFontName"				 : "SomeFont-Regular (Postscript Font Name)",
+	"postscriptFullName"				 : "Some Font-Regular (Postscript Full Name)",
+	"postscriptSlantAngle"				 : -12.5,
+	"postscriptUniqueID"				 : 4000000,
+	"postscriptUnderlineThickness"		 : 20,
+	"postscriptUnderlinePosition"		 : -200,
+	"postscriptIsFixedPitch"			 : False,
+	"postscriptBlueValues"				 : [500, 510],
+	"postscriptOtherBlues"				 : [-250, -260],
+	"postscriptFamilyBlues"				 : [500, 510],
+	"postscriptFamilyOtherBlues"		 : [-250, -260],
+	"postscriptStemSnapH"				 : [100, 120],
+	"postscriptStemSnapV"				 : [80, 90],
+	"postscriptBlueFuzz"				 : 1,
+	"postscriptBlueShift"				 : 7,
+	"postscriptBlueScale"				 : 0.039625,
+	"postscriptForceBold"				 : True,
+	"postscriptDefaultWidthX"			 : 400,
+	"postscriptNominalWidthX"			 : 400,
+	"postscriptWeightName"				 : "Medium",
+	"postscriptDefaultCharacter"		 : ".notdef",
+	"postscriptWindowsCharacterSet"		 : 1,
+	"macintoshFONDFamilyID"				 : 15000,
+	"macintoshFONDName"					 : "SomeFont Regular (FOND Name)",
+}
+
+fontInfoVersion3 = {
+	"familyName"						 : "Some Font (Family Name)",
+	"styleName"							 : "Regular (Style Name)",
+	"styleMapFamilyName"				 : "Some Font Regular (Style Map Family Name)",
+	"styleMapStyleName"					 : "regular",
+	"versionMajor"						 : 1,
+	"versionMinor"						 : 0,
+	"year"								 : 2008,
+	"copyright"							 : "Copyright Some Foundry.",
+	"trademark"							 : "Trademark Some Foundry",
+	"unitsPerEm"						 : 1000,
+	"descender"							 : -250,
+	"xHeight"							 : 500,
+	"capHeight"							 : 750,
+	"ascender"							 : 750,
+	"italicAngle"						 : -12.5,
+	"note"								 : "A note.",
+	"openTypeGaspRangeRecords"			 : [
+		dict(rangeMaxPPEM=10, rangeGaspBehavior=[0]),
+		dict(rangeMaxPPEM=20, rangeGaspBehavior=[1]),
+		dict(rangeMaxPPEM=30, rangeGaspBehavior=[2]),
+		dict(rangeMaxPPEM=40, rangeGaspBehavior=[3]),
+		dict(rangeMaxPPEM=50, rangeGaspBehavior=[0, 1, 2, 3]),
+		dict(rangeMaxPPEM=0xFFFF, rangeGaspBehavior=[0])
+	],
+	"openTypeHeadCreated"				 : "2000/01/01 00:00:00",
+	"openTypeHeadLowestRecPPEM"			 : 10,
+	"openTypeHeadFlags"					 : [0, 1],
+	"openTypeHheaAscender"				 : 750,
+	"openTypeHheaDescender"				 : -250,
+	"openTypeHheaLineGap"				 : 200,
+	"openTypeHheaCaretSlopeRise"		 : 1,
+	"openTypeHheaCaretSlopeRun"			 : 0,
+	"openTypeHheaCaretOffset"			 : 0,
+	"openTypeNameDesigner"				 : "Some Designer",
+	"openTypeNameDesignerURL"			 : "http://somedesigner.com",
+	"openTypeNameManufacturer"			 : "Some Foundry",
+	"openTypeNameManufacturerURL"		 : "http://somefoundry.com",
+	"openTypeNameLicense"				 : "License info for Some Foundry.",
+	"openTypeNameLicenseURL"			 : "http://somefoundry.com/license",
+	"openTypeNameVersion"				 : "OpenType name Table Version",
+	"openTypeNameUniqueID"				 : "OpenType name Table Unique ID",
+	"openTypeNameDescription"			 : "Some Font by Some Designer for Some Foundry.",
+	"openTypeNamePreferredFamilyName"	 : "Some Font (Preferred Family Name)",
+	"openTypeNamePreferredSubfamilyName" : "Regular (Preferred Subfamily Name)",
+	"openTypeNameCompatibleFullName"	 : "Some Font Regular (Compatible Full Name)",
+	"openTypeNameSampleText"			 : "Sample Text for Some Font.",
+	"openTypeNameWWSFamilyName"			 : "Some Font (WWS Family Name)",
+	"openTypeNameWWSSubfamilyName"		 : "Regular (WWS Subfamily Name)",
+	"openTypeNameRecords"				 : [
+		dict(nameID=1, platformID=1, encodingID=1, languageID=1, string="Name Record."),
+		dict(nameID=2, platformID=1, encodingID=1, languageID=1, string="Name Record.")
+	],
+	"openTypeOS2WidthClass"				 : 5,
+	"openTypeOS2WeightClass"			 : 500,
+	"openTypeOS2Selection"				 : [3],
+	"openTypeOS2VendorID"				 : "SOME",
+	"openTypeOS2Panose"					 : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+	"openTypeOS2FamilyClass"			 : [1, 1],
+	"openTypeOS2UnicodeRanges"			 : [0, 1],
+	"openTypeOS2CodePageRanges"			 : [0, 1],
+	"openTypeOS2TypoAscender"			 : 750,
+	"openTypeOS2TypoDescender"			 : -250,
+	"openTypeOS2TypoLineGap"			 : 200,
+	"openTypeOS2WinAscent"				 : 750,
+	"openTypeOS2WinDescent"				 : 250,
+	"openTypeOS2Type"					 : [],
+	"openTypeOS2SubscriptXSize"			 : 200,
+	"openTypeOS2SubscriptYSize"			 : 400,
+	"openTypeOS2SubscriptXOffset"		 : 0,
+	"openTypeOS2SubscriptYOffset"		 : -100,
+	"openTypeOS2SuperscriptXSize"		 : 200,
+	"openTypeOS2SuperscriptYSize"		 : 400,
+	"openTypeOS2SuperscriptXOffset"		 : 0,
+	"openTypeOS2SuperscriptYOffset"		 : 200,
+	"openTypeOS2StrikeoutSize"			 : 20,
+	"openTypeOS2StrikeoutPosition"		 : 300,
+	"openTypeVheaVertTypoAscender"		 : 750,
+	"openTypeVheaVertTypoDescender"		 : -250,
+	"openTypeVheaVertTypoLineGap"		 : 200,
+	"openTypeVheaCaretSlopeRise"		 : 0,
+	"openTypeVheaCaretSlopeRun"			 : 1,
+	"openTypeVheaCaretOffset"			 : 0,
+	"postscriptFontName"				 : "SomeFont-Regular (Postscript Font Name)",
+	"postscriptFullName"				 : "Some Font-Regular (Postscript Full Name)",
+	"postscriptSlantAngle"				 : -12.5,
+	"postscriptUniqueID"				 : 4000000,
+	"postscriptUnderlineThickness"		 : 20,
+	"postscriptUnderlinePosition"		 : -200,
+	"postscriptIsFixedPitch"			 : False,
+	"postscriptBlueValues"				 : [500, 510],
+	"postscriptOtherBlues"				 : [-250, -260],
+	"postscriptFamilyBlues"				 : [500, 510],
+	"postscriptFamilyOtherBlues"		 : [-250, -260],
+	"postscriptStemSnapH"				 : [100, 120],
+	"postscriptStemSnapV"				 : [80, 90],
+	"postscriptBlueFuzz"				 : 1,
+	"postscriptBlueShift"				 : 7,
+	"postscriptBlueScale"				 : 0.039625,
+	"postscriptForceBold"				 : True,
+	"postscriptDefaultWidthX"			 : 400,
+	"postscriptNominalWidthX"			 : 400,
+	"postscriptWeightName"				 : "Medium",
+	"postscriptDefaultCharacter"		 : ".notdef",
+	"postscriptWindowsCharacterSet"		 : 1,
+	"macintoshFONDFamilyID"				 : 15000,
+	"macintoshFONDName"					 : "SomeFont Regular (FOND Name)",
+	"woffMajorVersion"					 : 1,
+	"woffMinorVersion"					 : 0,
+	"woffMetadataUniqueID"				 : dict(id="string"),
+	"woffMetadataVendor"				 : dict(name="Some Foundry", url="http://somefoundry.com"),
+	"woffMetadataCredits"				 : dict(
+												credits=[
+													dict(name="Some Designer"),
+													dict(name=""),
+													dict(name="Some Designer", url="http://somedesigner.com"),
+													dict(name="Some Designer", url=""),
+													dict(name="Some Designer", role="Designer"),
+													dict(name="Some Designer", role=""),
+													dict(name="Some Designer", dir="ltr"),
+													dict(name="rengiseD emoS", dir="rtl"),
+													{"name" : "Some Designer", "class" : "hello"},
+													{"name" : "Some Designer", "class" : ""},
+												]
+											),
+	"woffMetadataDescription"			 : dict(
+												url="http://somefoundry.com/foo/description",
+												text=[
+													dict(text="foo"),
+													dict(text=""),
+													dict(text="foo", language="bar"),
+													dict(text="foo", language=""),
+													dict(text="foo", dir="ltr"),
+													dict(text="foo", dir="rtl"),
+													{"text" : "foo", "class" : "foo"},
+													{"text" : "foo", "class" : ""},
+												]
+											),
+	"woffMetadataLicense"				 : dict(
+												url="http://somefoundry.com/foo/license",
+												id="foo",
+												text=[
+													dict(text="foo"),
+													dict(text=""),
+													dict(text="foo", language="bar"),
+													dict(text="foo", language=""),
+													dict(text="foo", dir="ltr"),
+													dict(text="foo", dir="rtl"),
+													{"text" : "foo", "class" : "foo"},
+													{"text" : "foo", "class" : ""},
+												]
+											),
+	"woffMetadataCopyright"				 : dict(
+												text=[
+													dict(text="foo"),
+													dict(text=""),
+													dict(text="foo", language="bar"),
+													dict(text="foo", language=""),
+													dict(text="foo", dir="ltr"),
+													dict(text="foo", dir="rtl"),
+													{"text" : "foo", "class" : "foo"},
+													{"text" : "foo", "class" : ""},
+												]
+											),
+	"woffMetadataTrademark"				 : dict(
+												text=[
+													dict(text="foo"),
+													dict(text=""),
+													dict(text="foo", language="bar"),
+													dict(text="foo", language=""),
+													dict(text="foo", dir="ltr"),
+													dict(text="foo", dir="rtl"),
+													{"text" : "foo", "class" : "foo"},
+													{"text" : "foo", "class" : ""},
+												]
+											),
+	"woffMetadataLicensee"				 : dict(
+												name="Some Licensee"
+											),
+	"woffMetadataExtensions"			 : [
+												dict(
+													# everything
+													names=[
+														dict(text="foo"),
+														dict(text=""),
+														dict(text="foo", language="bar"),
+														dict(text="foo", language=""),
+														dict(text="foo", dir="ltr"),
+														dict(text="foo", dir="rtl"),
+														{"text" : "foo", "class" : "hello"},
+														{"text" : "foo", "class" : ""},
+													],
+													items=[
+														# everything
+														dict(
+															id="foo",
+															names=[
+																dict(text="foo"),
+																dict(text=""),
+																dict(text="foo", language="bar"),
+																dict(text="foo", language=""),
+																dict(text="foo", dir="ltr"),
+																dict(text="foo", dir="rtl"),
+																{"text" : "foo", "class" : "hello"},
+																{"text" : "foo", "class" : ""},
+															],
+															values=[
+																dict(text="foo"),
+																dict(text=""),
+																dict(text="foo", language="bar"),
+																dict(text="foo", language=""),
+																dict(text="foo", dir="ltr"),
+																dict(text="foo", dir="rtl"),
+																{"text" : "foo", "class" : "hello"},
+																{"text" : "foo", "class" : ""},
+															]
+														),
+														# no id
+														dict(
+															names=[
+																dict(text="foo")
+															],
+															values=[
+																dict(text="foo")
+															]
+														)
+													]
+												),
+												# no names
+												dict(
+													items=[
+														dict(
+															id="foo",
+															names=[
+																dict(text="foo")
+															],
+															values=[
+																dict(text="foo")
+															]
+														)
+													]
+												),
+											],
+	"guidelines"						 : [
+											# ints
+											dict(x=100, y=200, angle=45),
+											# floats
+											dict(x=100.5, y=200.5, angle=45.5),
+											# edges
+											dict(x=0, y=0, angle=0),
+											dict(x=0, y=0, angle=360),
+											dict(x=0, y=0, angle=360.0),
+											# no y
+											dict(x=100),
+											# no x
+											dict(y=200),
+											# name
+											dict(x=100, y=200, angle=45, name="foo"),
+											dict(x=100, y=200, angle=45, name=""),
+											# identifier
+											dict(x=100, y=200, angle=45, identifier="guide1"),
+											dict(x=100, y=200, angle=45, identifier="guide2"),
+											dict(x=100, y=200, angle=45, identifier="\x20"),
+											dict(x=100, y=200, angle=45, identifier="\x7E"),
+											# colors
+											dict(x=100, y=200, angle=45, color="0,0,0,0"),
+											dict(x=100, y=200, angle=45, color="1,0,0,0"),
+											dict(x=100, y=200, angle=45, color="1,1,1,1"),
+											dict(x=100, y=200, angle=45, color="0,1,0,0"),
+											dict(x=100, y=200, angle=45, color="0,0,1,0"),
+											dict(x=100, y=200, angle=45, color="0,0,0,1"),
+											dict(x=100, y=200, angle=45, color="1, 0, 0, 0"),
+											dict(x=100, y=200, angle=45, color="0, 1, 0, 0"),
+											dict(x=100, y=200, angle=45, color="0, 0, 1, 0"),
+											dict(x=100, y=200, angle=45, color="0, 0, 0, 1"),
+											dict(x=100, y=200, angle=45, color=".5,0,0,0"),
+											dict(x=100, y=200, angle=45, color="0,.5,0,0"),
+											dict(x=100, y=200, angle=45, color="0,0,.5,0"),
+											dict(x=100, y=200, angle=45, color="0,0,0,.5"),
+											dict(x=100, y=200, angle=45, color=".5,1,1,1"),
+											dict(x=100, y=200, angle=45, color="1,.5,1,1"),
+											dict(x=100, y=200, angle=45, color="1,1,.5,1"),
+											dict(x=100, y=200, angle=45, color="1,1,1,.5"),
+										],
+}
+
+expectedFontInfo1To2Conversion = {
+	"familyName"						: "Some Font (Family Name)",
+	"styleMapFamilyName"				: "Some Font Regular (Style Map Family Name)",
+	"styleMapStyleName"					: "regular",
+	"styleName"							: "Regular (Style Name)",
+	"unitsPerEm"						: 1000,
+	"ascender"							: 750,
+	"capHeight"							: 750,
+	"xHeight"							: 500,
+	"descender"							: -250,
+	"italicAngle"						: -12.5,
+	"versionMajor"						: 1,
+	"versionMinor"						: 0,
+	"year"								: 2008,
+	"copyright"							: "Copyright Some Foundry.",
+	"trademark"							: "Trademark Some Foundry",
+	"note"								: "A note.",
+	"macintoshFONDFamilyID"				: 15000,
+	"macintoshFONDName"					: "SomeFont Regular (FOND Name)",
+	"openTypeNameCompatibleFullName"	: "Some Font Regular (Compatible Full Name)",
+	"openTypeNameDescription"			: "Some Font by Some Designer for Some Foundry.",
+	"openTypeNameDesigner"				: "Some Designer",
+	"openTypeNameDesignerURL"			: "http://somedesigner.com",
+	"openTypeNameLicense"				: "License info for Some Foundry.",
+	"openTypeNameLicenseURL"			: "http://somefoundry.com/license",
+	"openTypeNameManufacturer"			: "Some Foundry",
+	"openTypeNameManufacturerURL"		: "http://somefoundry.com",
+	"openTypeNamePreferredFamilyName"	: "Some Font (Preferred Family Name)",
+	"openTypeNamePreferredSubfamilyName": "Regular (Preferred Subfamily Name)",
+	"openTypeNameCompatibleFullName"	: "Some Font Regular (Compatible Full Name)",
+	"openTypeNameUniqueID"				: "OpenType name Table Unique ID",
+	"openTypeNameVersion"				: "OpenType name Table Version",
+	"openTypeOS2VendorID"				: "SOME",
+	"openTypeOS2WeightClass"			: 500,
+	"openTypeOS2WidthClass"				: 5,
+	"postscriptDefaultWidthX"			: 400,
+	"postscriptFontName"				: "SomeFont-Regular (Postscript Font Name)",
+	"postscriptFullName"				: "Some Font-Regular (Postscript Full Name)",
+	"postscriptSlantAngle"				: -12.5,
+	"postscriptUniqueID"				: 4000000,
+	"postscriptWeightName"				: "Medium",
+	"postscriptWindowsCharacterSet"		: 1
+}
+
+expectedFontInfo2To1Conversion = {
+	"familyName"  	: "Some Font (Family Name)",
+	"menuName"	  	: "Some Font Regular (Style Map Family Name)",
+	"fontStyle"	  	: 64,
+	"styleName"	  	: "Regular (Style Name)",
+	"unitsPerEm"  	: 1000,
+	"ascender"	  	: 750,
+	"capHeight"	  	: 750,
+	"xHeight"	  	: 500,
+	"descender"	  	: -250,
+	"italicAngle" 	: -12.5,
+	"versionMajor"	: 1,
+	"versionMinor"	: 0,
+	"copyright"	  	: "Copyright Some Foundry.",
+	"trademark"	  	: "Trademark Some Foundry",
+	"note"		  	: "A note.",
+	"fondID"	  	: 15000,
+	"fondName"	  	: "SomeFont Regular (FOND Name)",
+	"fullName"	  	: "Some Font Regular (Compatible Full Name)",
+	"notice"	  	: "Some Font by Some Designer for Some Foundry.",
+	"designer"	  	: "Some Designer",
+	"designerURL" 	: "http://somedesigner.com",
+	"license"	  	: "License info for Some Foundry.",
+	"licenseURL"  	: "http://somefoundry.com/license",
+	"createdBy"	  	: "Some Foundry",
+	"vendorURL"	  	: "http://somefoundry.com",
+	"otFamilyName"	: "Some Font (Preferred Family Name)",
+	"otStyleName" 	: "Regular (Preferred Subfamily Name)",
+	"otMacName"	  	: "Some Font Regular (Compatible Full Name)",
+	"ttUniqueID"  	: "OpenType name Table Unique ID",
+	"ttVersion"	  	: "OpenType name Table Version",
+	"ttVendor"	  	: "SOME",
+	"weightValue" 	: 500,
+	"widthName"	  	: "Medium (normal)",
+	"defaultWidth"	: 400,
+	"fontName"	  	: "SomeFont-Regular (Postscript Font Name)",
+	"fullName"	  	: "Some Font-Regular (Postscript Full Name)",
+	"slantAngle"  	: -12.5,
+	"uniqueID"	  	: 4000000,
+	"weightName"  	: "Medium",
+	"msCharSet"	  	: 0,
+	"year"			: 2008
+}
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/fontinfo.plist b/Tests/ufoLib/testdata/DemoFont.ufo/fontinfo.plist
new file mode 100644
index 0000000..501e77f
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/fontinfo.plist
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ascender</key>
+	<integer>750</integer>
+	<key>capHeight</key>
+	<integer>527</integer>
+	<key>copyright</key>
+	<string>TOKEN COPYRIGHT STRING.  COPYRIGHT SAME AS PACKAGE. </string>
+	<key>defaultWidth</key>
+	<integer>500</integer>
+	<key>descender</key>
+	<integer>-170</integer>
+	<key>designer</key>
+	<string>Various</string>
+	<key>designerURL</key>
+	<string></string>
+	<key>familyName</key>
+	<string>UFODEMOFONT</string>
+	<key>fontStyle</key>
+	<integer>64</integer>
+	<key>license</key>
+	<string>LICENSE SAME AS PACKAGE.</string>
+	<key>notice</key>
+	<string>TOKEN DESCRIPTION</string>
+	<key>styleName</key>
+	<string>JUSTADEMO</string>
+	<key>trademark</key>
+	<string>NO TRADEMARKS</string>
+	<key>ttVendor</key>
+	<string>NONE</string>
+	<key>ttVersion</key>
+	<string>Version 1.000;PS development 5;hotconv 1.0.38</string>
+	<key>unitsPerEm</key>
+	<integer>1000</integer>
+	<key>vendorURL</key>
+	<string></string>
+	<key>versionMajor</key>
+	<integer>1</integer>
+	<key>xHeight</key>
+	<integer>456</integer>
+	<key>year</key>
+	<integer>2003</integer>
+</dict>
+</plist>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/A_.glif b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/A_.glif
new file mode 100644
index 0000000..54b23d2
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/A_.glif
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="1">
+  <advance width="487"/>
+  <unicode hex="0041"/>
+  <outline>
+    <contour>
+      <point x="243" y="681" type="move" name="top"/>
+    </contour>
+    <contour>
+      <point x="243" y="739" type="move" name="top"/>
+    </contour>
+    <contour>
+      <point x="243" y="-75" type="move" name="bottom"/>
+    </contour>
+    <contour>
+      <point x="243" y="739" type="move" name="top"/>
+    </contour>
+    <contour>
+      <point x="243" y="-75" type="move" name="bottom"/>
+    </contour>
+    <contour>
+      <point x="460" y="0" type="line"/>
+      <point x="318" y="664" type="line"/>
+      <point x="169" y="664" type="line"/>
+      <point x="27" y="0" type="line"/>
+      <point x="129" y="0" type="line"/>
+      <point x="150" y="94" type="line"/>
+      <point x="328" y="94" type="line"/>
+      <point x="348" y="0" type="line"/>
+    </contour>
+    <contour>
+      <point x="307" y="189" type="line"/>
+      <point x="172" y="189" type="line"/>
+      <point x="214" y="398" type="line"/>
+      <point x="239" y="541" type="line"/>
+      <point x="249" y="541" type="line"/>
+      <point x="264" y="399" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/B_.glif b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/B_.glif
new file mode 100644
index 0000000..97fed73
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/B_.glif
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="B" format="1">
+  <advance width="460"/>
+  <unicode hex="0042"/>
+  <outline>
+    <contour>
+      <point x="201" y="681" type="move" name="top"/>
+    </contour>
+    <contour>
+      <point x="412" y="474" type="curve" smooth="yes"/>
+      <point x="412" y="606"/>
+      <point x="323" y="664"/>
+      <point x="170" y="664" type="curve" smooth="yes"/>
+      <point x="47" y="664" type="line"/>
+      <point x="47" y="0" type="line"/>
+      <point x="170" y="0" type="line" smooth="yes"/>
+      <point x="340" y="0"/>
+      <point x="421" y="70"/>
+      <point x="421" y="189" type="curve" smooth="yes"/>
+      <point x="421" y="265"/>
+      <point x="358" y="330"/>
+      <point x="285" y="330" type="curve"/>
+      <point x="285" y="340" type="line"/>
+      <point x="358" y="340"/>
+      <point x="412" y="392"/>
+    </contour>
+    <contour>
+      <point x="151" y="284" type="line"/>
+      <point x="264" y="284"/>
+      <point x="314" y="253"/>
+      <point x="314" y="189" type="curve" smooth="yes"/>
+      <point x="314" y="130"/>
+      <point x="265" y="95"/>
+      <point x="151" y="95" type="curve"/>
+    </contour>
+    <contour>
+      <point x="151" y="569" type="line"/>
+      <point x="259" y="569"/>
+      <point x="304" y="551"/>
+      <point x="304" y="474" type="curve" smooth="yes"/>
+      <point x="304" y="409"/>
+      <point x="261" y="379"/>
+      <point x="151" y="379" type="curve"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/F_.glif b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/F_.glif
new file mode 100644
index 0000000..4baff85
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/F_.glif
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="F" format="1">
+  <advance width="417"/>
+  <unicode hex="0046"/>
+  <outline>
+    <contour>
+      <point x="213" y="681" type="move" name="top"/>
+    </contour>
+    <contour>
+      <point x="332" y="350" type="line"/>
+      <point x="151" y="350" type="line"/>
+      <point x="151" y="560" type="line"/>
+      <point x="379" y="560" type="line"/>
+      <point x="379" y="664" type="line"/>
+      <point x="47" y="664" type="line"/>
+      <point x="47" y="0" type="line"/>
+      <point x="151" y="0" type="line"/>
+      <point x="151" y="250" type="line"/>
+      <point x="332" y="250" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/F__A__B_.glif b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/F__A__B_.glif
new file mode 100644
index 0000000..f3cbe3f
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/F__A__B_.glif
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="F_A_B" format="1">
+  <advance width="900"/>
+  <outline>
+    <component base="A"/>
+    <component base="B" xOffset="350"/>
+    <component base="F" xScale="0.965925826289" xyScale="-0.258819045103" yxScale="0.258819045103" yScale="0.965925826289" xOffset="-50" yOffset="500"/>
+  </outline>
+</glyph>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/G_.glif b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/G_.glif
new file mode 100644
index 0000000..3531c30
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/G_.glif
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="G" format="1">
+  <advance width="494"/>
+  <unicode hex="0047"/>
+  <outline>
+    <contour>
+      <point x="301" y="681" type="move" name="top"/>
+    </contour>
+    <contour>
+      <point x="272" y="0" type="move" name="bottom"/>
+    </contour>
+    <contour>
+      <point x="446" y="379" type="line"/>
+      <point x="231" y="379" type="line"/>
+      <point x="231" y="284" type="line"/>
+      <point x="350" y="284" type="line"/>
+      <point x="350" y="98" type="line"/>
+      <point x="338" y="95"/>
+      <point x="300" y="94"/>
+      <point x="288" y="94" type="curve" smooth="yes"/>
+      <point x="197" y="94"/>
+      <point x="142" y="130"/>
+      <point x="142" y="322" type="curve" smooth="yes"/>
+      <point x="142" y="514"/>
+      <point x="177" y="569"/>
+      <point x="300" y="569" type="curve" smooth="yes"/>
+      <point x="324" y="569"/>
+      <point x="387" y="567"/>
+      <point x="417" y="563" type="curve"/>
+      <point x="427" y="653" type="line"/>
+      <point x="401" y="663"/>
+      <point x="338" y="674"/>
+      <point x="300" y="674" type="curve" smooth="yes"/>
+      <point x="120" y="674"/>
+      <point x="37" y="570"/>
+      <point x="37" y="322" type="curve" smooth="yes"/>
+      <point x="37" y="71"/>
+      <point x="134" y="-9"/>
+      <point x="272" y="-9" type="curve" smooth="yes"/>
+      <point x="353" y="-9"/>
+      <point x="396" y="-1"/>
+      <point x="446" y="18" type="curve"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/O_.glif b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/O_.glif
new file mode 100644
index 0000000..d0cdba6
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/O_.glif
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="O" format="1">
+  <advance width="513"/>
+  <unicode hex="004F"/>
+  <outline>
+    <contour>
+      <point x="259" y="681" type="move" name="top"/>
+    </contour>
+    <contour>
+      <point x="257" y="0" type="move" name="bottom"/>
+    </contour>
+    <contour>
+      <point x="474" y="332" type="curve" smooth="yes"/>
+      <point x="474" y="579"/>
+      <point x="414" y="674"/>
+      <point x="257" y="674" type="curve" smooth="yes"/>
+      <point x="106" y="674"/>
+      <point x="37" y="567"/>
+      <point x="37" y="332" type="curve" smooth="yes"/>
+      <point x="37" y="85"/>
+      <point x="98" y="-9"/>
+      <point x="256" y="-9" type="curve" smooth="yes"/>
+      <point x="405" y="-9"/>
+      <point x="474" y="98"/>
+    </contour>
+    <contour>
+      <point x="257" y="574" type="curve" smooth="yes"/>
+      <point x="336" y="574"/>
+      <point x="367" y="511"/>
+      <point x="367" y="332" type="curve" smooth="yes"/>
+      <point x="367" y="163"/>
+      <point x="332" y="91"/>
+      <point x="256" y="91" type="curve" smooth="yes"/>
+      <point x="176" y="91"/>
+      <point x="145" y="153"/>
+      <point x="145" y="332" type="curve" smooth="yes"/>
+      <point x="145" y="501"/>
+      <point x="180" y="574"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/R_.glif b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/R_.glif
new file mode 100644
index 0000000..ba45f0e
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/R_.glif
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="R" format="1">
+  <advance width="463"/>
+  <unicode hex="0052"/>
+  <outline>
+    <contour>
+      <point x="208" y="681" type="move" name="top"/>
+    </contour>
+    <contour>
+      <point x="445" y="0" type="line"/>
+      <point x="319" y="249" type="line"/>
+      <point x="380" y="286"/>
+      <point x="417" y="349"/>
+      <point x="417" y="436" type="curve" smooth="yes"/>
+      <point x="417" y="590"/>
+      <point x="315" y="664"/>
+      <point x="151" y="664" type="curve" smooth="yes"/>
+      <point x="47" y="664" type="line"/>
+      <point x="47" y="0" type="line"/>
+      <point x="151" y="0" type="line"/>
+      <point x="151" y="208" type="line"/>
+      <point x="180" y="208"/>
+      <point x="197" y="210"/>
+      <point x="221" y="214" type="curve"/>
+      <point x="331" y="0" type="line"/>
+    </contour>
+    <contour>
+      <point x="313" y="436" type="curve" smooth="yes"/>
+      <point x="313" y="345"/>
+      <point x="250" y="303"/>
+      <point x="151" y="303" type="curve"/>
+      <point x="151" y="569" type="line"/>
+      <point x="251" y="569"/>
+      <point x="313" y="535"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/a.glif b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/a.glif
new file mode 100644
index 0000000..dc1cbdf
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/a.glif
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="1">
+  <unicode hex="0061"/>
+  <outline>
+  </outline>
+</glyph>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/contents.plist b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..8607953
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/contents.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>A</key>
+	<string>A_.glif</string>
+	<key>B</key>
+	<string>B_.glif</string>
+	<key>F</key>
+	<string>F_.glif</string>
+	<key>F_A_B</key>
+	<string>F__A__B_.glif</string>
+	<key>G</key>
+	<string>G_.glif</string>
+	<key>O</key>
+	<string>O_.glif</string>
+	<key>R</key>
+	<string>R_.glif</string>
+	<key>a</key>
+	<string>a.glif</string>
+	<key>testglyph1</key>
+	<string>testglyph1.glif</string>
+	<key>testglyph1.reversed</key>
+	<string>testglyph1.reversed.glif</string>
+</dict>
+</plist>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/testglyph1.glif b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/testglyph1.glif
new file mode 100644
index 0000000..0559cac
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/testglyph1.glif
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="testglyph1" format="1">
+  <advance width="500"/>
+  <outline>
+    <contour>
+      <point x="58" y="443" type="move"/>
+      <point x="84" y="667" type="line"/>
+      <point x="313" y="632" type="line"/>
+      <point x="354" y="380" type="line"/>
+    </contour>
+    <contour>
+      <point x="328" y="238" type="line"/>
+      <point x="328" y="32" type="line"/>
+      <point x="90" y="29" type="line"/>
+      <point x="87" y="235" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/testglyph1.reversed.glif b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/testglyph1.reversed.glif
new file mode 100644
index 0000000..91c3377
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/glyphs/testglyph1.reversed.glif
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="testglyph1.reversed" format="1">
+  <advance width="500"/>
+  <outline>
+    <contour>
+      <point x="354" y="380" type="move"/>
+      <point x="313" y="632" type="line"/>
+      <point x="84" y="667" type="line"/>
+      <point x="58" y="443" type="line"/>
+    </contour>
+    <contour>
+      <point x="328" y="238" type="line"/>
+      <point x="87" y="235" type="line"/>
+      <point x="90" y="29" type="line"/>
+      <point x="328" y="32" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/lib.plist b/Tests/ufoLib/testdata/DemoFont.ufo/lib.plist
new file mode 100644
index 0000000..6056e7d
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/lib.plist
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+</dict>
+</plist>
diff --git a/Tests/ufoLib/testdata/DemoFont.ufo/metainfo.plist b/Tests/ufoLib/testdata/DemoFont.ufo/metainfo.plist
new file mode 100644
index 0000000..c044a5f
--- /dev/null
+++ b/Tests/ufoLib/testdata/DemoFont.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//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>1</integer>
+</dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/fontinfo.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/fontinfo.plist"
new file mode 100644
index 0000000..bab3aa2
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/fontinfo.plist"
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ascender</key>
+	<integer>750</integer>
+	<key>capHeight</key>
+	<integer>750</integer>
+	<key>copyright</key>
+	<string>Copyright Some Foundry.</string>
+	<key>createdBy</key>
+	<string>Some Foundry</string>
+	<key>defaultWidth</key>
+	<integer>400</integer>
+	<key>descender</key>
+	<integer>-250</integer>
+	<key>designer</key>
+	<string>Some Designer</string>
+	<key>designerURL</key>
+	<string>http://somedesigner.com</string>
+	<key>familyName</key>
+	<string>Some Font (Family Name)</string>
+	<key>fondID</key>
+	<integer>15000</integer>
+	<key>fondName</key>
+	<string>SomeFont Regular (FOND Name)</string>
+	<key>fontName</key>
+	<string>SomeFont-Regular (Postscript Font Name)</string>
+	<key>fontStyle</key>
+	<integer>64</integer>
+	<key>fullName</key>
+	<string>Some Font-Regular (Postscript Full Name)</string>
+	<key>italicAngle</key>
+	<real>-12.5</real>
+	<key>license</key>
+	<string>License info for Some Foundry.</string>
+	<key>licenseURL</key>
+	<string>http://somefoundry.com/license</string>
+	<key>menuName</key>
+	<string>Some Font Regular (Style Map Family Name)</string>
+	<key>msCharSet</key>
+	<integer>0</integer>
+	<key>note</key>
+	<string>A note.</string>
+	<key>notice</key>
+	<string>Some Font by Some Designer for Some Foundry.</string>
+	<key>otFamilyName</key>
+	<string>Some Font (Preferred Family Name)</string>
+	<key>otMacName</key>
+	<string>Some Font Regular (Compatible Full Name)</string>
+	<key>otStyleName</key>
+	<string>Regular (Preferred Subfamily Name)</string>
+	<key>slantAngle</key>
+	<real>-12.5</real>
+	<key>styleName</key>
+	<string>Regular (Style Name)</string>
+	<key>trademark</key>
+	<string>Trademark Some Foundry</string>
+	<key>ttUniqueID</key>
+	<string>OpenType name Table Unique ID</string>
+	<key>ttVendor</key>
+	<string>SOME</string>
+	<key>ttVersion</key>
+	<string>OpenType name Table Version</string>
+	<key>uniqueID</key>
+	<integer>4000000</integer>
+	<key>unitsPerEm</key>
+	<integer>1000</integer>
+	<key>vendorURL</key>
+	<string>http://somefoundry.com</string>
+	<key>versionMajor</key>
+	<integer>1</integer>
+	<key>versionMinor</key>
+	<integer>0</integer>
+	<key>weightName</key>
+	<string>Medium</string>
+	<key>weightValue</key>
+	<integer>500</integer>
+	<key>widthName</key>
+	<string>Medium (normal)</string>
+	<key>xHeight</key>
+	<integer>500</integer>
+	<key>year</key>
+	<integer>2008</integer>
+</dict>
+</plist>
+
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/glyphs/A_.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/glyphs/A_.glif"
new file mode 100644
index 0000000..36afacc
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/glyphs/A_.glif"
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="1">
+  <advance width="740"/>
+  <unicode hex="0041"/>
+  <outline>
+    <contour>
+      <point x="20" y="0" type="line"/>
+      <point x="720" y="0" type="line"/>
+      <point x="720" y="700" type="line"/>
+      <point x="20" y="700" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/glyphs/B_.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/glyphs/B_.glif"
new file mode 100644
index 0000000..ddcf3b2
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/glyphs/B_.glif"
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="B" format="1">
+  <advance width="740"/>
+  <unicode hex="0042"/>
+  <outline>
+    <contour>
+      <point x="20" y="350" type="curve" smooth="yes"/>
+      <point x="20" y="157"/>
+      <point x="177" y="0"/>
+      <point x="370" y="0" type="curve" smooth="yes"/>
+      <point x="563" y="0"/>
+      <point x="720" y="157"/>
+      <point x="720" y="350" type="curve" smooth="yes"/>
+      <point x="720" y="543"/>
+      <point x="563" y="700"/>
+      <point x="370" y="700" type="curve" smooth="yes"/>
+      <point x="177" y="700"/>
+      <point x="20" y="543"/>
+    </contour>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/glyphs/contents.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/glyphs/contents.plist"
new file mode 100644
index 0000000..08f7bba
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/glyphs/contents.plist"
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>A</key>
+	<string>A_.glif</string>
+	<key>B</key>
+	<string>B_.glif</string>
+</dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/groups.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/groups.plist"
new file mode 100644
index 0000000..40d17d9
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/groups.plist"
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>group1</key>
+	<array>
+		<string>A</string>
+	</array>
+	<key>group2</key>
+	<array>
+		<string>A</string>
+		<string>B</string>
+	</array>
+</dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/kerning.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/kerning.plist"
new file mode 100644
index 0000000..c07bf22
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/kerning.plist"
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>A</key>
+	<dict>
+		<key>B</key>
+		<integer>100</integer>
+	</dict>
+	<key>B</key>
+	<dict>
+		<key>A</key>
+		<integer>-200</integer>
+	</dict>
+</dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/lib.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/lib.plist"
new file mode 100644
index 0000000..df50b29
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/lib.plist"
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>org.robofab.opentype.classes</key>
+	<string>@myClass = [A B];
+</string>
+	<key>org.robofab.opentype.featureorder</key>
+	<array>
+		<string>liga</string>
+	</array>
+	<key>org.robofab.opentype.features</key>
+	<dict>
+		<key>liga</key>
+		<string>feature liga {
+    sub A A by b;
+} liga;
+</string>
+	</dict>
+	<key>org.robofab.postScriptHintData</key>
+	<dict>
+		<key>blueFuzz</key>
+		<integer>1</integer>
+		<key>blueScale</key>
+		<real>0.039625</real>
+		<key>blueShift</key>
+		<integer>7</integer>
+		<key>blueValues</key>
+		<array>
+			<array>
+				<integer>500</integer>
+				<integer>510</integer>
+			</array>
+		</array>
+		<key>familyBlues</key>
+		<array>
+			<array>
+				<integer>500</integer>
+				<integer>510</integer>
+			</array>
+		</array>
+		<key>familyOtherBlues</key>
+		<array>
+			<array>
+				<integer>-260</integer>
+				<integer>-250</integer>
+			</array>
+		</array>
+		<key>forceBold</key>
+		<true/>
+		<key>hStems</key>
+		<array>
+			<integer>100</integer>
+			<integer>120</integer>
+		</array>
+		<key>otherBlues</key>
+		<array>
+			<array>
+				<integer>-260</integer>
+				<integer>-250</integer>
+			</array>
+		</array>
+		<key>vStems</key>
+		<array>
+			<integer>80</integer>
+			<integer>90</integer>
+		</array>
+	</dict>
+	<key>org.robofab.testFontLibData</key>
+	<string>Foo Bar</string>
+</dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/metainfo.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/metainfo.plist"
new file mode 100644
index 0000000..c044a5f
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO1\051.ufo/metainfo.plist"
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//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>1</integer>
+</dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/features.fea" "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/features.fea"
new file mode 100644
index 0000000..40188d9
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/features.fea"
@@ -0,0 +1,5 @@
+@myClass = [A B];
+
+feature liga {
+    sub A A by b;
+} liga;
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/fontinfo.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/fontinfo.plist"
new file mode 100644
index 0000000..021d46d
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/fontinfo.plist"
@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>ascender</key>
+	<integer>750</integer>
+	<key>capHeight</key>
+	<integer>750</integer>
+	<key>copyright</key>
+	<string>Copyright Some Foundry.</string>
+	<key>descender</key>
+	<integer>-250</integer>
+	<key>familyName</key>
+	<string>Some Font (Family Name)</string>
+	<key>italicAngle</key>
+	<real>-12.5</real>
+	<key>macintoshFONDFamilyID</key>
+	<integer>15000</integer>
+	<key>macintoshFONDName</key>
+	<string>SomeFont Regular (FOND Name)</string>
+	<key>note</key>
+	<string>A note.</string>
+	<key>openTypeHeadCreated</key>
+	<string>2000/01/01 00:00:00</string>
+	<key>openTypeHeadFlags</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+	</array>
+	<key>openTypeHeadLowestRecPPEM</key>
+	<integer>10</integer>
+	<key>openTypeHheaAscender</key>
+	<integer>750</integer>
+	<key>openTypeHheaCaretOffset</key>
+	<integer>0</integer>
+	<key>openTypeHheaCaretSlopeRise</key>
+	<integer>1</integer>
+	<key>openTypeHheaCaretSlopeRun</key>
+	<integer>0</integer>
+	<key>openTypeHheaDescender</key>
+	<integer>-250</integer>
+	<key>openTypeHheaLineGap</key>
+	<integer>200</integer>
+	<key>openTypeNameCompatibleFullName</key>
+	<string>Some Font Regular (Compatible Full Name)</string>
+	<key>openTypeNameDescription</key>
+	<string>Some Font by Some Designer for Some Foundry.</string>
+	<key>openTypeNameDesigner</key>
+	<string>Some Designer</string>
+	<key>openTypeNameDesignerURL</key>
+	<string>http://somedesigner.com</string>
+	<key>openTypeNameLicense</key>
+	<string>License info for Some Foundry.</string>
+	<key>openTypeNameLicenseURL</key>
+	<string>http://somefoundry.com/license</string>
+	<key>openTypeNameManufacturer</key>
+	<string>Some Foundry</string>
+	<key>openTypeNameManufacturerURL</key>
+	<string>http://somefoundry.com</string>
+	<key>openTypeNamePreferredFamilyName</key>
+	<string>Some Font (Preferred Family Name)</string>
+	<key>openTypeNamePreferredSubfamilyName</key>
+	<string>Regular (Preferred Subfamily Name)</string>
+	<key>openTypeNameSampleText</key>
+	<string>Sample Text for Some Font.</string>
+	<key>openTypeNameUniqueID</key>
+	<string>OpenType name Table Unique ID</string>
+	<key>openTypeNameVersion</key>
+	<string>OpenType name Table Version</string>
+	<key>openTypeNameWWSFamilyName</key>
+	<string>Some Font (WWS Family Name)</string>
+	<key>openTypeNameWWSSubfamilyName</key>
+	<string>Regular (WWS Subfamily Name)</string>
+	<key>openTypeOS2CodePageRanges</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+	</array>
+	<key>openTypeOS2Panose</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>8</integer>
+		<integer>9</integer>
+	</array>
+	<key>openTypeOS2FamilyClass</key>
+	<array>
+		<integer>1</integer>
+		<integer>1</integer>
+	</array>
+	<key>openTypeOS2Selection</key>
+	<array>
+		<integer>3</integer>
+	</array>
+	<key>openTypeOS2StrikeoutPosition</key>
+	<integer>300</integer>
+	<key>openTypeOS2StrikeoutSize</key>
+	<integer>20</integer>
+	<key>openTypeOS2SubscriptXOffset</key>
+	<integer>0</integer>
+	<key>openTypeOS2SubscriptXSize</key>
+	<integer>200</integer>
+	<key>openTypeOS2SubscriptYOffset</key>
+	<integer>-100</integer>
+	<key>openTypeOS2SubscriptYSize</key>
+	<integer>400</integer>
+	<key>openTypeOS2SuperscriptXOffset</key>
+	<integer>0</integer>
+	<key>openTypeOS2SuperscriptXSize</key>
+	<integer>200</integer>
+	<key>openTypeOS2SuperscriptYOffset</key>
+	<integer>200</integer>
+	<key>openTypeOS2SuperscriptYSize</key>
+	<integer>400</integer>
+	<key>openTypeOS2Type</key>
+	<array>
+	</array>
+	<key>openTypeOS2TypoAscender</key>
+	<integer>750</integer>
+	<key>openTypeOS2TypoDescender</key>
+	<integer>-250</integer>
+	<key>openTypeOS2TypoLineGap</key>
+	<integer>200</integer>
+	<key>openTypeOS2UnicodeRanges</key>
+	<array>
+		<integer>0</integer>
+		<integer>1</integer>
+	</array>
+	<key>openTypeOS2VendorID</key>
+	<string>SOME</string>
+	<key>openTypeOS2WeightClass</key>
+	<integer>500</integer>
+	<key>openTypeOS2WidthClass</key>
+	<integer>5</integer>
+	<key>openTypeOS2WinAscent</key>
+	<integer>750</integer>
+	<key>openTypeOS2WinDescent</key>
+	<integer>-250</integer>
+	<key>openTypeVheaCaretOffset</key>
+	<integer>0</integer>
+	<key>openTypeVheaCaretSlopeRise</key>
+	<integer>0</integer>
+	<key>openTypeVheaCaretSlopeRun</key>
+	<integer>1</integer>
+	<key>openTypeVheaVertTypoAscender</key>
+	<integer>750</integer>
+	<key>openTypeVheaVertTypoDescender</key>
+	<integer>-250</integer>
+	<key>openTypeVheaVertTypoLineGap</key>
+	<integer>200</integer>
+	<key>postscriptBlueFuzz</key>
+	<integer>1</integer>
+	<key>postscriptBlueScale</key>
+	<real>0.039625</real>
+	<key>postscriptBlueShift</key>
+	<integer>7</integer>
+	<key>postscriptBlueValues</key>
+	<array>
+		<integer>500</integer>
+		<integer>510</integer>
+	</array>
+	<key>postscriptDefaultCharacter</key>
+	<string>.notdef</string>
+	<key>postscriptDefaultWidthX</key>
+	<integer>400</integer>
+	<key>postscriptFamilyBlues</key>
+	<array>
+		<integer>500</integer>
+		<integer>510</integer>
+	</array>
+	<key>postscriptFamilyOtherBlues</key>
+	<array>
+		<integer>-250</integer>
+		<integer>-260</integer>
+	</array>
+	<key>postscriptFontName</key>
+	<string>SomeFont-Regular (Postscript Font Name)</string>
+	<key>postscriptForceBold</key>
+	<true/>
+	<key>postscriptFullName</key>
+	<string>Some Font-Regular (Postscript Full Name)</string>
+	<key>postscriptIsFixedPitch</key>
+	<false/>
+	<key>postscriptNominalWidthX</key>
+	<integer>400</integer>
+	<key>postscriptOtherBlues</key>
+	<array>
+		<integer>-250</integer>
+		<integer>-260</integer>
+	</array>
+	<key>postscriptSlantAngle</key>
+	<real>-12.5</real>
+	<key>postscriptStemSnapH</key>
+	<array>
+		<integer>100</integer>
+		<integer>120</integer>
+	</array>
+	<key>postscriptStemSnapV</key>
+	<array>
+		<integer>80</integer>
+		<integer>90</integer>
+	</array>
+	<key>postscriptUnderlinePosition</key>
+	<integer>-200</integer>
+	<key>postscriptUnderlineThickness</key>
+	<integer>20</integer>
+	<key>postscriptUniqueID</key>
+	<integer>4000000</integer>
+	<key>postscriptWeightName</key>
+	<string>Medium</string>
+	<key>postscriptWindowsCharacterSet</key>
+	<integer>1</integer>
+	<key>styleMapFamilyName</key>
+	<string>Some Font Regular (Style Map Family Name)</string>
+	<key>styleMapStyleName</key>
+	<string>regular</string>
+	<key>styleName</key>
+	<string>Regular (Style Name)</string>
+	<key>trademark</key>
+	<string>Trademark Some Foundry</string>
+	<key>unitsPerEm</key>
+	<integer>1000</integer>
+	<key>versionMajor</key>
+	<integer>1</integer>
+	<key>versionMinor</key>
+	<integer>0</integer>
+	<key>xHeight</key>
+	<integer>500</integer>
+	<key>year</key>
+	<integer>2008</integer>
+</dict>
+</plist>
+
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/glyphs/A_.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/glyphs/A_.glif"
new file mode 100644
index 0000000..36afacc
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/glyphs/A_.glif"
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="A" format="1">
+  <advance width="740"/>
+  <unicode hex="0041"/>
+  <outline>
+    <contour>
+      <point x="20" y="0" type="line"/>
+      <point x="720" y="0" type="line"/>
+      <point x="720" y="700" type="line"/>
+      <point x="20" y="700" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/glyphs/B_.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/glyphs/B_.glif"
new file mode 100644
index 0000000..ddcf3b2
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/glyphs/B_.glif"
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="B" format="1">
+  <advance width="740"/>
+  <unicode hex="0042"/>
+  <outline>
+    <contour>
+      <point x="20" y="350" type="curve" smooth="yes"/>
+      <point x="20" y="157"/>
+      <point x="177" y="0"/>
+      <point x="370" y="0" type="curve" smooth="yes"/>
+      <point x="563" y="0"/>
+      <point x="720" y="157"/>
+      <point x="720" y="350" type="curve" smooth="yes"/>
+      <point x="720" y="543"/>
+      <point x="563" y="700"/>
+      <point x="370" y="700" type="curve" smooth="yes"/>
+      <point x="177" y="700"/>
+      <point x="20" y="543"/>
+    </contour>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/glyphs/contents.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/glyphs/contents.plist"
new file mode 100644
index 0000000..08f7bba
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/glyphs/contents.plist"
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>A</key>
+	<string>A_.glif</string>
+	<key>B</key>
+	<string>B_.glif</string>
+</dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/groups.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/groups.plist"
new file mode 100644
index 0000000..40d17d9
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/groups.plist"
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>group1</key>
+	<array>
+		<string>A</string>
+	</array>
+	<key>group2</key>
+	<array>
+		<string>A</string>
+		<string>B</string>
+	</array>
+</dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/kerning.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/kerning.plist"
new file mode 100644
index 0000000..c07bf22
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/kerning.plist"
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>A</key>
+	<dict>
+		<key>B</key>
+		<integer>100</integer>
+	</dict>
+	<key>B</key>
+	<dict>
+		<key>A</key>
+		<integer>-200</integer>
+	</dict>
+</dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/lib.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/lib.plist"
new file mode 100644
index 0000000..0931165
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/lib.plist"
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>org.robofab.testFontLibData</key>
+	<string>Foo Bar</string>
+</dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/metainfo.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/metainfo.plist"
new file mode 100644
index 0000000..f5ec4e5
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO2\051.ufo/metainfo.plist"
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//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/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/data/com.github.fonttools.ttx/CUST.ttx" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/data/com.github.fonttools.ttx/CUST.ttx"
new file mode 100644
index 0000000..51847fd
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/data/com.github.fonttools.ttx/CUST.ttx"
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.13">
+
+  <CUST raw="True">
+    <hexdata>
+      0001beef
+    </hexdata>
+  </CUST>
+
+</ttFont>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/fontinfo.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/fontinfo.plist"
new file mode 100644
index 0000000..78dd88e
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/fontinfo.plist"
@@ -0,0 +1,338 @@
+<?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>750</integer>
+    <key>capHeight</key>
+    <integer>750</integer>
+    <key>copyright</key>
+    <string>Copyright © Some Foundry.</string>
+    <key>descender</key>
+    <integer>-250</integer>
+    <key>familyName</key>
+    <string>Some Font (Family Name)</string>
+    <key>guidelines</key>
+    <array>
+      <dict>
+        <key>x</key>
+        <integer>250</integer>
+      </dict>
+      <dict>
+        <key>x</key>
+        <integer>-20</integer>
+      </dict>
+      <dict>
+        <key>x</key>
+        <integer>30</integer>
+      </dict>
+      <dict>
+        <key>y</key>
+        <integer>500</integer>
+      </dict>
+      <dict>
+        <key>y</key>
+        <integer>-200</integer>
+      </dict>
+      <dict>
+        <key>y</key>
+        <integer>700</integer>
+      </dict>
+      <dict>
+        <key>angle</key>
+        <integer>135</integer>
+        <key>x</key>
+        <integer>0</integer>
+        <key>y</key>
+        <integer>0</integer>
+      </dict>
+      <dict>
+        <key>angle</key>
+        <integer>45</integer>
+        <key>x</key>
+        <integer>0</integer>
+        <key>y</key>
+        <integer>700</integer>
+      </dict>
+      <dict>
+        <key>angle</key>
+        <integer>135</integer>
+        <key>x</key>
+        <integer>20</integer>
+        <key>y</key>
+        <integer>0</integer>
+      </dict>
+    </array>
+    <key>italicAngle</key>
+    <real>-12.5</real>
+    <key>macintoshFONDFamilyID</key>
+    <integer>15000</integer>
+    <key>macintoshFONDName</key>
+    <string>SomeFont Regular (FOND Name)</string>
+    <key>note</key>
+    <string>A note.</string>
+    <key>openTypeGaspRangeRecords</key>
+    <array>
+      <dict>
+        <key>rangeGaspBehavior</key>
+        <array>
+          <integer>1</integer>
+          <integer>3</integer>
+        </array>
+        <key>rangeMaxPPEM</key>
+        <integer>7</integer>
+      </dict>
+      <dict>
+        <key>rangeGaspBehavior</key>
+        <array>
+          <integer>0</integer>
+          <integer>1</integer>
+          <integer>2</integer>
+          <integer>3</integer>
+        </array>
+        <key>rangeMaxPPEM</key>
+        <integer>65535</integer>
+      </dict>
+    </array>
+    <key>openTypeHeadCreated</key>
+    <string>2000/01/01 00:00:00</string>
+    <key>openTypeHeadFlags</key>
+    <array>
+      <integer>0</integer>
+      <integer>1</integer>
+    </array>
+    <key>openTypeHeadLowestRecPPEM</key>
+    <integer>10</integer>
+    <key>openTypeHheaAscender</key>
+    <integer>750</integer>
+    <key>openTypeHheaCaretOffset</key>
+    <integer>0</integer>
+    <key>openTypeHheaCaretSlopeRise</key>
+    <integer>1</integer>
+    <key>openTypeHheaCaretSlopeRun</key>
+    <integer>0</integer>
+    <key>openTypeHheaDescender</key>
+    <integer>-250</integer>
+    <key>openTypeHheaLineGap</key>
+    <integer>200</integer>
+    <key>openTypeNameCompatibleFullName</key>
+    <string>Some Font Regular (Compatible Full Name)</string>
+    <key>openTypeNameDescription</key>
+    <string>Some Font by Some Designer for Some Foundry.</string>
+    <key>openTypeNameDesigner</key>
+    <string>Some Designer</string>
+    <key>openTypeNameDesignerURL</key>
+    <string>http://somedesigner.com</string>
+    <key>openTypeNameLicense</key>
+    <string>License info for Some Foundry.</string>
+    <key>openTypeNameLicenseURL</key>
+    <string>http://somefoundry.com/license</string>
+    <key>openTypeNameManufacturer</key>
+    <string>Some Foundry</string>
+    <key>openTypeNameManufacturerURL</key>
+    <string>http://somefoundry.com</string>
+    <key>openTypeNamePreferredFamilyName</key>
+    <string>Some Font (Preferred Family Name)</string>
+    <key>openTypeNamePreferredSubfamilyName</key>
+    <string>Regular (Preferred Subfamily Name)</string>
+    <key>openTypeNameRecords</key>
+    <array>
+      <dict>
+        <key>encodingID</key>
+        <integer>0</integer>
+        <key>languageID</key>
+        <integer>0</integer>
+        <key>nameID</key>
+        <integer>3</integer>
+        <key>platformID</key>
+        <integer>1</integer>
+        <key>string</key>
+        <string>Unique Font Identifier</string>
+      </dict>
+      <dict>
+        <key>encodingID</key>
+        <integer>1</integer>
+        <key>languageID</key>
+        <integer>1033</integer>
+        <key>nameID</key>
+        <integer>8</integer>
+        <key>platformID</key>
+        <integer>3</integer>
+        <key>string</key>
+        <string>Some Foundry (Manufacturer Name)</string>
+      </dict>
+    </array>
+    <key>openTypeNameSampleText</key>
+    <string>Sample Text for Some Font.</string>
+    <key>openTypeNameUniqueID</key>
+    <string>OpenType name Table Unique ID</string>
+    <key>openTypeNameVersion</key>
+    <string>OpenType name Table Version</string>
+    <key>openTypeNameWWSFamilyName</key>
+    <string>Some Font (WWS Family Name)</string>
+    <key>openTypeNameWWSSubfamilyName</key>
+    <string>Regular (WWS Subfamily Name)</string>
+    <key>openTypeOS2CodePageRanges</key>
+    <array>
+      <integer>0</integer>
+      <integer>1</integer>
+    </array>
+    <key>openTypeOS2FamilyClass</key>
+    <array>
+      <integer>1</integer>
+      <integer>1</integer>
+    </array>
+    <key>openTypeOS2Panose</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>8</integer>
+      <integer>9</integer>
+    </array>
+    <key>openTypeOS2Selection</key>
+    <array>
+      <integer>3</integer>
+    </array>
+    <key>openTypeOS2StrikeoutPosition</key>
+    <integer>300</integer>
+    <key>openTypeOS2StrikeoutSize</key>
+    <integer>20</integer>
+    <key>openTypeOS2SubscriptXOffset</key>
+    <integer>0</integer>
+    <key>openTypeOS2SubscriptXSize</key>
+    <integer>200</integer>
+    <key>openTypeOS2SubscriptYOffset</key>
+    <integer>-100</integer>
+    <key>openTypeOS2SubscriptYSize</key>
+    <integer>400</integer>
+    <key>openTypeOS2SuperscriptXOffset</key>
+    <integer>0</integer>
+    <key>openTypeOS2SuperscriptXSize</key>
+    <integer>200</integer>
+    <key>openTypeOS2SuperscriptYOffset</key>
+    <integer>200</integer>
+    <key>openTypeOS2SuperscriptYSize</key>
+    <integer>400</integer>
+    <key>openTypeOS2Type</key>
+    <array/>
+    <key>openTypeOS2TypoAscender</key>
+    <integer>750</integer>
+    <key>openTypeOS2TypoDescender</key>
+    <integer>-250</integer>
+    <key>openTypeOS2TypoLineGap</key>
+    <integer>200</integer>
+    <key>openTypeOS2UnicodeRanges</key>
+    <array>
+      <integer>0</integer>
+      <integer>1</integer>
+    </array>
+    <key>openTypeOS2VendorID</key>
+    <string>SOME</string>
+    <key>openTypeOS2WeightClass</key>
+    <integer>500</integer>
+    <key>openTypeOS2WidthClass</key>
+    <integer>5</integer>
+    <key>openTypeOS2WinAscent</key>
+    <integer>750</integer>
+    <key>openTypeOS2WinDescent</key>
+    <integer>250</integer>
+    <key>openTypeVheaCaretOffset</key>
+    <integer>0</integer>
+    <key>openTypeVheaCaretSlopeRise</key>
+    <integer>0</integer>
+    <key>openTypeVheaCaretSlopeRun</key>
+    <integer>1</integer>
+    <key>openTypeVheaVertTypoAscender</key>
+    <integer>750</integer>
+    <key>openTypeVheaVertTypoDescender</key>
+    <integer>-250</integer>
+    <key>openTypeVheaVertTypoLineGap</key>
+    <integer>200</integer>
+    <key>postscriptBlueFuzz</key>
+    <integer>1</integer>
+    <key>postscriptBlueScale</key>
+    <real>0.039625</real>
+    <key>postscriptBlueShift</key>
+    <integer>7</integer>
+    <key>postscriptBlueValues</key>
+    <array>
+      <integer>500</integer>
+      <integer>510</integer>
+    </array>
+    <key>postscriptDefaultCharacter</key>
+    <string>.notdef</string>
+    <key>postscriptDefaultWidthX</key>
+    <integer>400</integer>
+    <key>postscriptFamilyBlues</key>
+    <array>
+      <integer>500</integer>
+      <integer>510</integer>
+    </array>
+    <key>postscriptFamilyOtherBlues</key>
+    <array>
+      <integer>-250</integer>
+      <integer>-260</integer>
+    </array>
+    <key>postscriptFontName</key>
+    <string>SomeFont-Regular (Postscript Font Name)</string>
+    <key>postscriptForceBold</key>
+    <true/>
+    <key>postscriptFullName</key>
+    <string>Some Font-Regular (Postscript Full Name)</string>
+    <key>postscriptIsFixedPitch</key>
+    <false/>
+    <key>postscriptNominalWidthX</key>
+    <integer>400</integer>
+    <key>postscriptOtherBlues</key>
+    <array>
+      <integer>-250</integer>
+      <integer>-260</integer>
+    </array>
+    <key>postscriptSlantAngle</key>
+    <real>-12.5</real>
+    <key>postscriptStemSnapH</key>
+    <array>
+      <integer>100</integer>
+      <integer>120</integer>
+    </array>
+    <key>postscriptStemSnapV</key>
+    <array>
+      <integer>80</integer>
+      <integer>90</integer>
+    </array>
+    <key>postscriptUnderlinePosition</key>
+    <integer>-200</integer>
+    <key>postscriptUnderlineThickness</key>
+    <integer>20</integer>
+    <key>postscriptUniqueID</key>
+    <integer>4000000</integer>
+    <key>postscriptWeightName</key>
+    <string>Medium</string>
+    <key>postscriptWindowsCharacterSet</key>
+    <integer>1</integer>
+    <key>styleMapFamilyName</key>
+    <string>Some Font Regular (Style Map Family Name)</string>
+    <key>styleMapStyleName</key>
+    <string>regular</string>
+    <key>styleName</key>
+    <string>Regular (Style Name)</string>
+    <key>trademark</key>
+    <string>Trademark Some Foundry</string>
+    <key>unitsPerEm</key>
+    <integer>1000</integer>
+    <key>versionMajor</key>
+    <integer>1</integer>
+    <key>versionMinor</key>
+    <integer>0</integer>
+    <key>xHeight</key>
+    <integer>500</integer>
+    <key>year</key>
+    <integer>2008</integer>
+  </dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/_notdef.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/_notdef.glif"
new file mode 100644
index 0000000..630ec6b
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/_notdef.glif"
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name=".notdef" format="2">
+  <advance height="1000" width="500"/>
+  <outline>
+    <contour>
+      <point x="450" y="0" type="line"/>
+      <point x="450" y="750" type="line"/>
+      <point x="50" y="750" type="line"/>
+      <point x="50" y="0" type="line"/>
+    </contour>
+    <contour>
+      <point x="400" y="50" type="line"/>
+      <point x="100" y="50" type="line"/>
+      <point x="100" y="700" type="line"/>
+      <point x="400" y="700" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/a.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/a.glif"
new file mode 100644
index 0000000..a751d87
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/a.glif"
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="a" format="2">
+  <advance height="750" width="388"/>
+  <unicode hex="0061"/>
+  <outline>
+    <contour>
+      <point x="66" y="0" type="line"/>
+      <point x="322" y="0" type="line"/>
+      <point x="194" y="510" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/b.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/b.glif"
new file mode 100644
index 0000000..54066a2
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/b.glif"
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="b" format="2">
+  <advance height="750" width="410"/>
+  <unicode hex="0062"/>
+  <outline>
+    <contour>
+      <point x="100" y="505" type="line"/>
+      <point x="100" y="-5" type="line"/>
+      <point x="310" y="-5" type="line"/>
+      <point x="310" y="505" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/c.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/c.glif"
new file mode 100644
index 0000000..7abf451
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/c.glif"
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="c" format="2">
+  <advance height="750" width="374"/>
+  <unicode hex="0063"/>
+  <outline>
+    <contour>
+      <point x="300" y="-10" type="curve"/>
+      <point x="300" y="500" type="line"/>
+      <point x="150" y="500"/>
+      <point x="100" y="450"/>
+      <point x="100" y="245" type="curve"/>
+      <point x="100" y="40"/>
+      <point x="150" y="-10"/>
+    </contour>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/contents.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/contents.plist"
new file mode 100644
index 0000000..f730b93
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/contents.plist"
@@ -0,0 +1,34 @@
+<?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>b</key>
+    <string>b.glif</string>
+    <key>c</key>
+    <string>c.glif</string>
+    <key>d</key>
+    <string>d.glif</string>
+    <key>e</key>
+    <string>e.glif</string>
+    <key>f</key>
+    <string>f.glif</string>
+    <key>g</key>
+    <string>g.glif</string>
+    <key>h</key>
+    <string>h.glif</string>
+    <key>i</key>
+    <string>i.glif</string>
+    <key>j</key>
+    <string>j.glif</string>
+    <key>k</key>
+    <string>k.glif</string>
+    <key>l</key>
+    <string>l.glif</string>
+    <key>space</key>
+    <string>space.glif</string>
+  </dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/d.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/d.glif"
new file mode 100644
index 0000000..09b619b
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/d.glif"
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="d" format="2">
+  <advance height="750" width="374"/>
+  <unicode hex="0064"/>
+  <outline>
+    <contour>
+      <point x="150.66" y="197.32" type="curve"/>
+      <point x="117" y="197.32"/>
+      <point x="90.33" y="170.33"/>
+      <point x="90.33" y="137" type="curve"/>
+      <point x="90.33" y="103.67"/>
+      <point x="117" y="77.01"/>
+      <point x="150.66" y="77.01" type="curve"/>
+      <point x="183.99" y="77.01"/>
+      <point x="210.65" y="103.67"/>
+      <point x="210.65" y="137" type="curve"/>
+      <point x="210.65" y="170.33"/>
+      <point x="183.99" y="197.32"/>
+    </contour>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/e.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/e.glif"
new file mode 100644
index 0000000..52abd0a
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/e.glif"
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="e" format="2">
+  <advance height="750" width="388"/>
+  <unicode hex="0065"/>
+  <outline>
+    <contour>
+      <point x="66" y="510" type="line"/>
+      <point x="194" y="75" type="line"/>
+      <point x="322" y="510" type="line"/>
+    </contour>
+    <contour>
+      <point x="-55" y="23" type="curve"/>
+      <point x="454" y="23" type="line"/>
+      <point x="454" y="173"/>
+      <point x="404" y="223"/>
+      <point x="199" y="223" type="curve"/>
+      <point x="-5" y="223"/>
+      <point x="-55" y="173"/>
+    </contour>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/f.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/f.glif"
new file mode 100644
index 0000000..2c13b95
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/f.glif"
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="f" format="2">
+  <advance height="750" width="410"/>
+  <unicode hex="0066"/>
+  <outline>
+    <contour>
+      <point x="66" y="510" type="line"/>
+      <point x="322" y="510" type="line"/>
+      <point x="194" y="75" type="line"/>
+    </contour>
+    <contour>
+      <point x="-55" y="23" type="curve"/>
+      <point x="454" y="23" type="line"/>
+      <point x="454" y="173"/>
+      <point x="404" y="223"/>
+      <point x="199" y="223" type="curve"/>
+      <point x="-5" y="223"/>
+      <point x="-55" y="173"/>
+    </contour>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/g.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/g.glif"
new file mode 100644
index 0000000..fdbe8ac
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/g.glif"
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="g" format="2">
+  <advance height="750" width="388"/>
+  <unicode hex="0067"/>
+  <outline>
+    <component base="a"/>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/h.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/h.glif"
new file mode 100644
index 0000000..561563f
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/h.glif"
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="h" format="2">
+  <advance height="1000" width="410"/>
+  <unicode hex="0068"/>
+  <outline>
+    <component base="d" xOffset="60" yOffset="460"/>
+    <component base="b"/>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/i.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/i.glif"
new file mode 100644
index 0000000..84ef89b
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/i.glif"
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="i" format="2">
+  <advance height="750" width="600"/>
+  <unicode hex="0069"/>
+  <outline>
+    <contour>
+      <point x="-55" y="-80" type="curve"/>
+      <point x="454" y="-80" type="line"/>
+      <point x="454" y="69"/>
+      <point x="404" y="119"/>
+      <point x="199" y="119" type="curve"/>
+      <point x="-5" y="119"/>
+      <point x="-55" y="69"/>
+    </contour>
+    <component base="a"/>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/j.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/j.glif"
new file mode 100644
index 0000000..550ee9b
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/j.glif"
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="j" format="2">
+  <advance height="1000" width="600"/>
+  <unicode hex="006A"/>
+  <outline>
+    <contour>
+      <point x="-55" y="-80" type="curve"/>
+      <point x="454" y="-80" type="line"/>
+      <point x="454" y="69"/>
+      <point x="404" y="119"/>
+      <point x="199" y="119" type="curve"/>
+      <point x="-5" y="119"/>
+      <point x="-55" y="69"/>
+    </contour>
+    <component base="a" yScale="-1" yOffset="230"/>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/k.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/k.glif"
new file mode 100644
index 0000000..c09ac3a
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/k.glif"
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="k" format="2">
+  <advance height="1000" width="600"/>
+  <unicode hex="006B"/>
+  <outline>
+    <component base="a"/>
+    <component base="a" xOffset="100"/>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/l.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/l.glif"
new file mode 100644
index 0000000..f71c34f
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/l.glif"
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="l" format="2">
+  <advance height="1000" width="600"/>
+  <unicode hex="006C"/>
+  <outline>
+    <component base="a" xScale="-1" xOffset="400"/>
+    <component base="a" xOffset="100"/>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/space.glif" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/space.glif"
new file mode 100644
index 0000000..eaa0d16
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/glyphs/space.glif"
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<glyph name="space" format="2">
+  <advance height="250" width="250"/>
+  <unicode hex="0020"/>
+  <outline>
+  </outline>
+</glyph>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/kerning.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/kerning.plist"
new file mode 100644
index 0000000..1dd116e
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/kerning.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>a</key>
+    <dict>
+      <key>a</key>
+      <integer>5</integer>
+      <key>b</key>
+      <integer>-10</integer>
+      <key>space</key>
+      <integer>1</integer>
+    </dict>
+    <key>b</key>
+    <dict>
+      <key>a</key>
+      <integer>-7</integer>
+    </dict>
+  </dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/layercontents.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/layercontents.plist"
new file mode 100644
index 0000000..963df40
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.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/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/lib.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/lib.plist"
new file mode 100644
index 0000000..f257b3a
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/lib.plist"
@@ -0,0 +1,25 @@
+<?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>glyph1</string>
+      <string>glyph2</string>
+      <string>space</string>
+      <string>a</string>
+      <string>b</string>
+      <string>c</string>
+      <string>d</string>
+      <string>e</string>
+      <string>f</string>
+      <string>g</string>
+      <string>h</string>
+      <string>i</string>
+      <string>j</string>
+      <string>k</string>
+      <string>l</string>
+    </array>
+  </dict>
+</plist>
diff --git "a/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/metainfo.plist" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufo/metainfo.plist"
new file mode 100644
index 0000000..8e836fb
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.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/ufoLib/testdata/TestFont1 \050UFO3\051.ufoz" "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufoz"
new file mode 100644
index 0000000..d78d494
--- /dev/null
+++ "b/Tests/ufoLib/testdata/TestFont1 \050UFO3\051.ufoz"
Binary files differ
diff --git a/Tests/ufoLib/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/bar/lol.txt b/Tests/ufoLib/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/bar/lol.txt
new file mode 100644
index 0000000..8449c6b
--- /dev/null
+++ b/Tests/ufoLib/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/bar/lol.txt
@@ -0,0 +1 @@
+lol.txt
\ No newline at end of file
diff --git a/Tests/ufoLib/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/foo.txt b/Tests/ufoLib/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/foo.txt
new file mode 100644
index 0000000..996f178
--- /dev/null
+++ b/Tests/ufoLib/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/foo.txt
@@ -0,0 +1 @@
+foo.txt
\ No newline at end of file
diff --git a/Tests/ufoLib/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.file.txt b/Tests/ufoLib/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.file.txt
new file mode 100644
index 0000000..4c33073
--- /dev/null
+++ b/Tests/ufoLib/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.file.txt
@@ -0,0 +1 @@
+file.txt
\ No newline at end of file
diff --git a/Tests/ufoLib/testdata/UFO3-Read Data.ufo/groups.plist b/Tests/ufoLib/testdata/UFO3-Read Data.ufo/groups.plist
new file mode 100644
index 0000000..d61c281
--- /dev/null
+++ b/Tests/ufoLib/testdata/UFO3-Read Data.ufo/groups.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>group1</key>
+	<array>
+		<string>A</string>
+	</array>
+	<key>group2</key>
+	<array>
+		<string>B</string>
+		<string>C</string>
+		<string>B</string>
+	</array>
+	<key>public.kern1.A</key>
+	<array>
+		<string>A</string>
+	</array>
+	<key>public.kern2.B</key>
+	<array>
+		<string>B</string>
+		<string>A</string>
+		<string>B</string>
+		<string>A</string>
+		<string>C</string>
+	</array>
+</dict>
+</plist>
diff --git a/Tests/ufoLib/testdata/UFO3-Read Data.ufo/metainfo.plist b/Tests/ufoLib/testdata/UFO3-Read Data.ufo/metainfo.plist
new file mode 100644
index 0000000..b584ee2
--- /dev/null
+++ b/Tests/ufoLib/testdata/UFO3-Read Data.ufo/metainfo.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//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/ufoLib/ufoLib_test.py b/Tests/ufoLib/ufoLib_test.py
new file mode 100644
index 0000000..430e7a7
--- /dev/null
+++ b/Tests/ufoLib/ufoLib_test.py
@@ -0,0 +1,98 @@
+import logging
+import shutil
+
+from fontTools.misc import plistlib
+from fontTools.ufoLib import UFOReader, UFOWriter, UFOFormatVersion
+from fontTools.ufoLib.errors import UFOLibError, UnsupportedUFOFormat
+import pytest
+
+
+@pytest.fixture
+def ufo_path(tmp_path):
+    ufodir = tmp_path / "TestFont.ufo"
+    ufodir.mkdir()
+    with (ufodir / "metainfo.plist").open("wb") as f:
+        plistlib.dump({"creator": "pytest", "formatVersion": 3}, f)
+    (ufodir / "glyphs").mkdir()
+    with (ufodir / "layercontents.plist").open("wb") as f:
+        plistlib.dump([("public.default", "glyphs")], f)
+    return ufodir
+
+
+def test_formatVersion_deprecated(ufo_path):
+    reader = UFOReader(ufo_path)
+
+    with pytest.warns(DeprecationWarning) as warnings:
+        assert reader.formatVersion == 3
+
+    assert len(warnings) == 1
+    assert "is deprecated; use the 'formatVersionTuple'" in warnings[0].message.args[0]
+
+
+def test_formatVersionTuple(ufo_path):
+    reader = UFOReader(ufo_path)
+
+    assert reader.formatVersionTuple == (3, 0)
+    assert reader.formatVersionTuple.major == 3
+    assert reader.formatVersionTuple.minor == 0
+    assert str(reader.formatVersionTuple) == "3.0"
+
+
+def test_readMetaInfo_errors(ufo_path):
+    (ufo_path / "metainfo.plist").unlink()
+    with pytest.raises(UFOLibError, match="'metainfo.plist' is missing"):
+        UFOReader(ufo_path)
+
+    (ufo_path / "metainfo.plist").write_bytes(plistlib.dumps({}))
+    with pytest.raises(UFOLibError, match="Missing required formatVersion"):
+        UFOReader(ufo_path)
+
+    (ufo_path / "metainfo.plist").write_bytes(plistlib.dumps([]))
+    with pytest.raises(UFOLibError, match="metainfo.plist is not properly formatted"):
+        UFOReader(ufo_path)
+
+
+def test_readMetaInfo_unsupported_format_version(ufo_path, caplog):
+    metainfo = {"formatVersion": 10, "formatVersionMinor": 15}
+    (ufo_path / "metainfo.plist").write_bytes(plistlib.dumps(metainfo))
+
+    with pytest.raises(UnsupportedUFOFormat):
+        UFOReader(ufo_path)  # validate=True by default
+
+    with pytest.raises(UnsupportedUFOFormat):
+        UFOReader(ufo_path, validate=True)
+
+    caplog.clear()
+    with caplog.at_level(logging.WARNING, logger="fontTools.ufoLib"):
+        UFOReader(ufo_path, validate=False)
+
+    assert len(caplog.records) == 1
+    assert "Unsupported UFO format" in caplog.text
+    assert "Assuming the latest supported version" in caplog.text
+
+
+def test_UFOWriter_formatVersion(tmp_path):
+    ufo_path = tmp_path / "TestFont.ufo"
+    with UFOWriter(ufo_path, formatVersion=3) as writer:
+        assert writer.formatVersionTuple == (3, 0)
+
+    shutil.rmtree(str(ufo_path))
+    with UFOWriter(ufo_path, formatVersion=(2, 0)) as writer:
+        assert writer.formatVersionTuple == (2, 0)
+
+
+def test_UFOWriter_formatVersion_default_latest(tmp_path):
+    writer = UFOWriter(tmp_path / "TestFont.ufo")
+    assert writer.formatVersionTuple == UFOFormatVersion.default()
+
+
+def test_UFOWriter_unsupported_format_version(tmp_path):
+    with pytest.raises(UnsupportedUFOFormat):
+        UFOWriter(tmp_path, formatVersion=(123, 456))
+
+
+def test_UFOWriter_previous_higher_format_version(ufo_path):
+    with pytest.raises(
+        UnsupportedUFOFormat, match="UFO located at this path is a higher version"
+    ):
+        UFOWriter(ufo_path, formatVersion=(2, 0))
diff --git a/Tests/unicodedata_test.py b/Tests/unicodedata_test.py
index dba02e7..05f7de6 100644
--- a/Tests/unicodedata_test.py
+++ b/Tests/unicodedata_test.py
@@ -1,7 +1,3 @@
-from __future__ import (
-    print_function, division, absolute_import, unicode_literals)
-from fontTools.misc.py23 import *
-
 from fontTools import unicodedata
 
 import pytest
@@ -9,164 +5,165 @@
 
 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"
+    assert unicodedata.script(chr(0)) == "Zyyy"
+    assert unicodedata.script(chr(0x0378)) == "Zzzz"
+    assert unicodedata.script(chr(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'
+    assert unicodedata.script(chr(0x1E918)) == 'Adlm'
+    assert unicodedata.script(chr(0x1170D)) == 'Ahom'
+    assert unicodedata.script(chr(0x145A0)) == 'Hluw'
+    assert unicodedata.script(chr(0x0607)) == 'Arab'
+    assert unicodedata.script(chr(0x056C)) == 'Armn'
+    assert unicodedata.script(chr(0x10B27)) == 'Avst'
+    assert unicodedata.script(chr(0x1B41)) == 'Bali'
+    assert unicodedata.script(chr(0x168AD)) == 'Bamu'
+    assert unicodedata.script(chr(0x16ADD)) == 'Bass'
+    assert unicodedata.script(chr(0x1BE5)) == 'Batk'
+    assert unicodedata.script(chr(0x09F3)) == 'Beng'
+    assert unicodedata.script(chr(0x11C5B)) == 'Bhks'
+    assert unicodedata.script(chr(0x3126)) == 'Bopo'
+    assert unicodedata.script(chr(0x1103B)) == 'Brah'
+    assert unicodedata.script(chr(0x2849)) == 'Brai'
+    assert unicodedata.script(chr(0x1A0A)) == 'Bugi'
+    assert unicodedata.script(chr(0x174E)) == 'Buhd'
+    assert unicodedata.script(chr(0x18EE)) == 'Cans'
+    assert unicodedata.script(chr(0x102B7)) == 'Cari'
+    assert unicodedata.script(chr(0x1053D)) == 'Aghb'
+    assert unicodedata.script(chr(0x11123)) == 'Cakm'
+    assert unicodedata.script(chr(0xAA1F)) == 'Cham'
+    assert unicodedata.script(chr(0xAB95)) == 'Cher'
+    assert unicodedata.script(chr(0x1F0C7)) == 'Zyyy'
+    assert unicodedata.script(chr(0x2C85)) == 'Copt'
+    assert unicodedata.script(chr(0x12014)) == 'Xsux'
+    assert unicodedata.script(chr(0x1082E)) == 'Cprt'
+    assert unicodedata.script(chr(0xA686)) == 'Cyrl'
+    assert unicodedata.script(chr(0x10417)) == 'Dsrt'
+    assert unicodedata.script(chr(0x093E)) == 'Deva'
+    assert unicodedata.script(chr(0x1BC4B)) == 'Dupl'
+    assert unicodedata.script(chr(0x1310C)) == 'Egyp'
+    assert unicodedata.script(chr(0x1051C)) == 'Elba'
+    assert unicodedata.script(chr(0x2DA6)) == 'Ethi'
+    assert unicodedata.script(chr(0x10AD)) == 'Geor'
+    assert unicodedata.script(chr(0x2C52)) == 'Glag'
+    assert unicodedata.script(chr(0x10343)) == 'Goth'
+    assert unicodedata.script(chr(0x11371)) == 'Gran'
+    assert unicodedata.script(chr(0x03D0)) == 'Grek'
+    assert unicodedata.script(chr(0x0AAA)) == 'Gujr'
+    assert unicodedata.script(chr(0x0A4C)) == 'Guru'
+    assert unicodedata.script(chr(0x23C9F)) == 'Hani'
+    assert unicodedata.script(chr(0xC259)) == 'Hang'
+    assert unicodedata.script(chr(0x1722)) == 'Hano'
+    assert unicodedata.script(chr(0x108F5)) == 'Hatr'
+    assert unicodedata.script(chr(0x05C2)) == 'Hebr'
+    assert unicodedata.script(chr(0x1B072)) == 'Hira'
+    assert unicodedata.script(chr(0x10847)) == 'Armi'
+    assert unicodedata.script(chr(0x033A)) == 'Zinh'
+    assert unicodedata.script(chr(0x10B66)) == 'Phli'
+    assert unicodedata.script(chr(0x10B4B)) == 'Prti'
+    assert unicodedata.script(chr(0xA98A)) == 'Java'
+    assert unicodedata.script(chr(0x110B2)) == 'Kthi'
+    assert unicodedata.script(chr(0x0CC6)) == 'Knda'
+    assert unicodedata.script(chr(0x3337)) == 'Kana'
+    assert unicodedata.script(chr(0xA915)) == 'Kali'
+    assert unicodedata.script(chr(0x10A2E)) == 'Khar'
+    assert unicodedata.script(chr(0x17AA)) == 'Khmr'
+    assert unicodedata.script(chr(0x11225)) == 'Khoj'
+    assert unicodedata.script(chr(0x112B6)) == 'Sind'
+    assert unicodedata.script(chr(0x0ED7)) == 'Laoo'
+    assert unicodedata.script(chr(0xAB3C)) == 'Latn'
+    assert unicodedata.script(chr(0x1C48)) == 'Lepc'
+    assert unicodedata.script(chr(0x1923)) == 'Limb'
+    assert unicodedata.script(chr(0x1071D)) == 'Lina'
+    assert unicodedata.script(chr(0x100EC)) == 'Linb'
+    assert unicodedata.script(chr(0xA4E9)) == 'Lisu'
+    assert unicodedata.script(chr(0x10284)) == 'Lyci'
+    assert unicodedata.script(chr(0x10926)) == 'Lydi'
+    assert unicodedata.script(chr(0x11161)) == 'Mahj'
+    assert unicodedata.script(chr(0x0D56)) == 'Mlym'
+    assert unicodedata.script(chr(0x0856)) == 'Mand'
+    assert unicodedata.script(chr(0x10AF0)) == 'Mani'
+    assert unicodedata.script(chr(0x11CB0)) == 'Marc'
+    assert unicodedata.script(chr(0x11D28)) == 'Gonm'
+    assert unicodedata.script(chr(0xABDD)) == 'Mtei'
+    assert unicodedata.script(chr(0x1E897)) == 'Mend'
+    assert unicodedata.script(chr(0x109B0)) == 'Merc'
+    assert unicodedata.script(chr(0x10993)) == 'Mero'
+    assert unicodedata.script(chr(0x16F5D)) == 'Plrd'
+    assert unicodedata.script(chr(0x1160B)) == 'Modi'
+    assert unicodedata.script(chr(0x18A8)) == 'Mong'
+    assert unicodedata.script(chr(0x16A48)) == 'Mroo'
+    assert unicodedata.script(chr(0x1128C)) == 'Mult'
+    assert unicodedata.script(chr(0x105B)) == 'Mymr'
+    assert unicodedata.script(chr(0x108AF)) == 'Nbat'
+    assert unicodedata.script(chr(0x19B3)) == 'Talu'
+    assert unicodedata.script(chr(0x1143D)) == 'Newa'
+    assert unicodedata.script(chr(0x07F4)) == 'Nkoo'
+    assert unicodedata.script(chr(0x1B192)) == 'Nshu'
+    assert unicodedata.script(chr(0x169C)) == 'Ogam'
+    assert unicodedata.script(chr(0x1C56)) == 'Olck'
+    assert unicodedata.script(chr(0x10CE9)) == 'Hung'
+    assert unicodedata.script(chr(0x10316)) == 'Ital'
+    assert unicodedata.script(chr(0x10A93)) == 'Narb'
+    assert unicodedata.script(chr(0x1035A)) == 'Perm'
+    assert unicodedata.script(chr(0x103D5)) == 'Xpeo'
+    assert unicodedata.script(chr(0x10A65)) == 'Sarb'
+    assert unicodedata.script(chr(0x10C09)) == 'Orkh'
+    assert unicodedata.script(chr(0x0B60)) == 'Orya'
+    assert unicodedata.script(chr(0x104CF)) == 'Osge'
+    assert unicodedata.script(chr(0x104A8)) == 'Osma'
+    assert unicodedata.script(chr(0x16B12)) == 'Hmng'
+    assert unicodedata.script(chr(0x10879)) == 'Palm'
+    assert unicodedata.script(chr(0x11AF1)) == 'Pauc'
+    assert unicodedata.script(chr(0xA869)) == 'Phag'
+    assert unicodedata.script(chr(0x10909)) == 'Phnx'
+    assert unicodedata.script(chr(0x10B81)) == 'Phlp'
+    assert unicodedata.script(chr(0xA941)) == 'Rjng'
+    assert unicodedata.script(chr(0x16C3)) == 'Runr'
+    assert unicodedata.script(chr(0x0814)) == 'Samr'
+    assert unicodedata.script(chr(0xA88C)) == 'Saur'
+    assert unicodedata.script(chr(0x111C8)) == 'Shrd'
+    assert unicodedata.script(chr(0x1045F)) == 'Shaw'
+    assert unicodedata.script(chr(0x115AD)) == 'Sidd'
+    assert unicodedata.script(chr(0x1D8C0)) == 'Sgnw'
+    assert unicodedata.script(chr(0x0DB9)) == 'Sinh'
+    assert unicodedata.script(chr(0x110F9)) == 'Sora'
+    assert unicodedata.script(chr(0x11A60)) == 'Soyo'
+    assert unicodedata.script(chr(0x1B94)) == 'Sund'
+    assert unicodedata.script(chr(0xA81F)) == 'Sylo'
+    assert unicodedata.script(chr(0x0740)) == 'Syrc'
+    assert unicodedata.script(chr(0x1714)) == 'Tglg'
+    assert unicodedata.script(chr(0x1761)) == 'Tagb'
+    assert unicodedata.script(chr(0x1965)) == 'Tale'
+    assert unicodedata.script(chr(0x1A32)) == 'Lana'
+    assert unicodedata.script(chr(0xAA86)) == 'Tavt'
+    assert unicodedata.script(chr(0x116A5)) == 'Takr'
+    assert unicodedata.script(chr(0x0B8E)) == 'Taml'
+    assert unicodedata.script(chr(0x1754D)) == 'Tang'
+    assert unicodedata.script(chr(0x0C40)) == 'Telu'
+    assert unicodedata.script(chr(0x07A4)) == 'Thaa'
+    assert unicodedata.script(chr(0x0E42)) == 'Thai'
+    assert unicodedata.script(chr(0x0F09)) == 'Tibt'
+    assert unicodedata.script(chr(0x2D3A)) == 'Tfng'
+    assert unicodedata.script(chr(0x114B0)) == 'Tirh'
+    assert unicodedata.script(chr(0x1038B)) == 'Ugar'
+    assert unicodedata.script(chr(0xA585)) == 'Vaii'
+    assert unicodedata.script(chr(0x118CF)) == 'Wara'
+    assert unicodedata.script(chr(0xA066)) == 'Yiii'
+    assert unicodedata.script(chr(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(chr(0)) == {"Zyyy"}
+    assert unicodedata.script_extension(chr(0x0378)) == {"Zzzz"}
+    assert unicodedata.script_extension(chr(0x10FFFF)) == {"Zzzz"}
 
-    assert unicodedata.script_extension("\u0660") == {'Arab', 'Thaa'}
+    assert unicodedata.script_extension("\u0660") == {'Arab', 'Thaa', 'Yezi'}
     assert unicodedata.script_extension("\u0964") == {
-        'Beng', 'Deva', 'Gran', 'Gujr', 'Guru', 'Knda', 'Mahj', 'Mlym',
-        'Orya', 'Sind', 'Sinh', 'Sylo', 'Takr', 'Taml', 'Telu', 'Tirh'}
+        'Beng', 'Deva', 'Dogr', 'Gong', 'Gonm', 'Gran', 'Gujr', 'Guru', 'Knda',
+        'Mahj', 'Mlym', 'Nand', 'Orya', 'Sind', 'Sinh', 'Sylo', 'Takr', 'Taml',
+        'Telu', 'Tirh'}
 
 
 def test_script_name():
@@ -200,7 +197,54 @@
     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"
+    assert unicodedata.block("\u1c90") == "Georgian Extended"
+    assert unicodedata.block("\u0870") == "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__":
diff --git a/Tests/varLib/builder_test.py b/Tests/varLib/builder_test.py
index 499265a..6cad103 100644
--- a/Tests/varLib/builder_test.py
+++ b/Tests/varLib/builder_test.py
@@ -1,4 +1,3 @@
-from __future__ import print_function, division, absolute_import
 from fontTools.varLib.builder import buildVarData
 import pytest
 
@@ -9,12 +8,20 @@
     ([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),
+    ([0], [[32768]], 0x8001),
+    ([0, 1, 2], [[32768, 1, 2], [3, -129, 5], [6, 7, 8]], 0x8001),
+    ([0, 1, 2], [[32768, 1, 2], [3, -32769, 5], [6, 7, 8]], 0x8002),
+    ([0, 1, 2], [[0, 32768, 2], [3, 4, 5], [6, 7, -32769]], 0x8003),
 ], ids=[
     "0_regions_0_deltas",
     "1_region_1_uint8",
     "1_region_1_short",
     "3_regions_2_shorts_ordered",
     "3_regions_2_shorts_unordered",
+    "1_region_1_long",
+    "3_regions_1_long_ordered",
+    "3_regions_2_longs_ordered",
+    "3_regions_2_longs_unordered",
 ])
 def test_buildVarData_no_optimize(region_indices, items, expected_num_shorts):
     data = buildVarData(region_indices, items, optimize=False)
@@ -40,12 +47,30 @@
      [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]]),
+    ([0, 1, 2], [[0, 32768, 2], [3, 4, 5], [6, 7, 8]], 0x8001,
+     [1, 0, 2], [[32768, 0, 2], [4, 3, 5], [7, 6, 8]]),
+    ([0, 1, 2], [[0, 1, 32768], [3, 4, 5], [6, -32769, 8]], 0x8002,
+     [1, 2, 0], [[1, 32768, 0], [4, 5, 3], [-32769, 8, 6]]),
+    ([0, 1, 2], [[32768, 1, -32769], [3, 4, 5], [6, 7, 8]], 0x8002,
+     [0, 2, 1], [[32768, -32769, 1], [3, 5, 4], [6, 8, 7]]),
+    ([0, 1, 2], [[0, 1, 32768], [3, -32769, 5], [65536, 7, 8]], 0x8003,
+     [0, 1, 2], [[0, 1, 32768], [3, -32769, 5], [65536, 7, 8]]),
+    ([0, 1, 2], [[0, 32768, 2], [0, 4, 5], [0, 7, 8]], 0x8001,
+     [1, 2], [[32768, 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",
+    "1/3_longs_reorder",
+    "2/3_longs_reorder",
+    "2/3_longs_same_row_reorder",
+    "3/3_longs_no_reorder",
+    "1/3_longs_1/3_zeroes",
 ])
 def test_buildVarData_optimize(
         region_indices, items, expected_num_shorts, expected_regions,
@@ -54,7 +79,7 @@
 
     assert data.ItemCount == len(items)
     assert data.NumShorts == expected_num_shorts
-    assert data.VarRegionCount == len(region_indices)
+    assert data.VarRegionCount == len(expected_regions)
     assert data.VarRegionIndex == expected_regions
     assert data.Item == expected_items
 
diff --git a/Tests/varLib/data/Build.designspace b/Tests/varLib/data/Build.designspace
index e0bf58d..29a43ce 100644
--- a/Tests/varLib/data/Build.designspace
+++ b/Tests/varLib/data/Build.designspace
@@ -4,6 +4,8 @@
         <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>
+            <labelname xml:lang="de">Kontrast</labelname>
+            <labelname xml:lang="fa">کنتراست</labelname>
         </axis>
     </axes>
     <sources>
diff --git a/Tests/varLib/data/BuildGvarCompositeExplicitDelta.designspace b/Tests/varLib/data/BuildGvarCompositeExplicitDelta.designspace
new file mode 100644
index 0000000..b47227f
--- /dev/null
+++ b/Tests/varLib/data/BuildGvarCompositeExplicitDelta.designspace
@@ -0,0 +1,22 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<designspace format="4.0">
+  <axes>
+    <axis tag="slnt" name="Slant" minimum="-15" maximum="0" default="0"/>
+  </axes>
+  <sources>
+    <source filename="master_ufo/TestFamily4-Regular.ufo" name="master_0" familyname="Test Family 4" stylename="Regular">
+      <lib copy="1"/>
+      <groups copy="1"/>
+      <features copy="1"/>
+      <info copy="1"/>
+      <location>
+        <dimension name="Slant" xvalue="0"/>
+      </location>
+    </source>
+    <source filename="master_ufo/TestFamily4-Italic15.ufo" name="master_1" familyname="Test Family 4" stylename="Italic">
+      <location>
+        <dimension name="Slant" xvalue="-15"/>
+      </location>
+    </source>
+  </sources>
+</designspace>
diff --git a/Tests/varLib/data/Designspace.designspace b/Tests/varLib/data/Designspace.designspace
deleted file mode 100644
index df1036e..0000000
--- a/Tests/varLib/data/Designspace.designspace
+++ /dev/null
@@ -1,39 +0,0 @@
-<?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
deleted file mode 100644
index ac7d403..0000000
--- a/Tests/varLib/data/Designspace2.designspace
+++ /dev/null
@@ -1,8 +0,0 @@
-<?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/FeatureVars.designspace b/Tests/varLib/data/FeatureVars.designspace
new file mode 100644
index 0000000..902fade
--- /dev/null
+++ b/Tests/varLib/data/FeatureVars.designspace
@@ -0,0 +1,71 @@
+<?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>
+    <rules processing="last">
+        <rule name="dollar-stroke">
+            <conditionset>
+                <condition name="weight" minimum="500" /> <!-- intentionally omitted maximum -->
+            </conditionset>
+            <sub name="uni0024" with="uni0024.nostroke" />
+        </rule>
+        <rule name="to-lowercase">
+            <conditionset>
+                <condition name="contrast" minimum="75" maximum="100" />
+            </conditionset>
+            <sub name="uni0041" with="uni0061" />
+        </rule>
+        <rule name="to-uppercase">
+            <conditionset>
+                <condition name="weight" minimum="0" maximum="200" />
+                <condition name="contrast" minimum="0" maximum="25" />
+            </conditionset>
+            <sub name="uni0061" with="uni0041" />
+        </rule>
+    </rules>
+    <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>
+</designspace>
diff --git a/Tests/varLib/data/FeatureVarsCustomTag.designspace b/Tests/varLib/data/FeatureVarsCustomTag.designspace
new file mode 100644
index 0000000..45b06f3
--- /dev/null
+++ b/Tests/varLib/data/FeatureVarsCustomTag.designspace
@@ -0,0 +1,77 @@
+<?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>
+    <rules processing="last">
+        <rule name="dollar-stroke">
+            <conditionset>
+                <condition name="weight" minimum="500" /> <!-- intentionally omitted maximum -->
+            </conditionset>
+            <sub name="uni0024" with="uni0024.nostroke" />
+        </rule>
+        <rule name="to-lowercase">
+            <conditionset>
+                <condition name="contrast" minimum="75" maximum="100" />
+            </conditionset>
+            <sub name="uni0041" with="uni0061" />
+        </rule>
+        <rule name="to-uppercase">
+            <conditionset>
+                <condition name="weight" minimum="0" maximum="200" />
+                <condition name="contrast" minimum="0" maximum="25" />
+            </conditionset>
+            <sub name="uni0061" with="uni0041" />
+        </rule>
+    </rules>
+    <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>
+    <lib>
+        <dict>
+            <key>com.github.fonttools.varLib.featureVarsFeatureTag</key>
+            <string>calt</string>
+        </dict>
+    </lib>
+</designspace>
diff --git a/Tests/varLib/data/FeatureVarsWholeRange.designspace b/Tests/varLib/data/FeatureVarsWholeRange.designspace
new file mode 100644
index 0000000..2d8802c
--- /dev/null
+++ b/Tests/varLib/data/FeatureVarsWholeRange.designspace
@@ -0,0 +1,34 @@
+<?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" />
+    </axes>
+    <rules processing="last">
+        <rule name="always">
+            <conditionset>
+                <condition name="weight" minimum="0" maximum="1000" />
+            </conditionset>
+            <sub name="uni0024" with="uni0024.nostroke" />
+        </rule>
+    </rules>
+    <sources>
+        <source familyname="Test Family" filename="master_ufo/TestFamily-Master0.ufo" name="master_0" stylename="Master0">
+            <location>
+                <dimension name="weight" 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" />
+            </location>
+        </source>
+        <source familyname="Test Family" filename="master_ufo/TestFamily-Master3.ufo" name="master_3" stylename="Master3">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+        </source>
+    </sources>
+</designspace>
diff --git a/Tests/varLib/data/FeatureVarsWholeRangeEmpty.designspace b/Tests/varLib/data/FeatureVarsWholeRangeEmpty.designspace
new file mode 100644
index 0000000..a692daa
--- /dev/null
+++ b/Tests/varLib/data/FeatureVarsWholeRangeEmpty.designspace
@@ -0,0 +1,33 @@
+<?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" />
+    </axes>
+    <rules processing="last">
+        <rule name="always">
+            <conditionset>
+            </conditionset>
+            <sub name="uni0024" with="uni0024.nostroke" />
+        </rule>
+    </rules>
+    <sources>
+        <source familyname="Test Family" filename="master_ufo/TestFamily-Master0.ufo" name="master_0" stylename="Master0">
+            <location>
+                <dimension name="weight" 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" />
+            </location>
+        </source>
+        <source familyname="Test Family" filename="master_ufo/TestFamily-Master3.ufo" name="master_3" stylename="Master3">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+        </source>
+    </sources>
+</designspace>
diff --git a/Tests/varLib/data/IncompatibleArrays.designspace b/Tests/varLib/data/IncompatibleArrays.designspace
new file mode 100644
index 0000000..399810e
--- /dev/null
+++ b/Tests/varLib/data/IncompatibleArrays.designspace
@@ -0,0 +1,22 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<designspace format="4.1">
+  <axes>
+    <axis tag="wght" name="Weight" minimum="200" maximum="700" default="200"/>
+  </axes>
+  <sources>
+    <source filename="master_incompatible_arrays/IncompatibleArrays-Regular.ttx" name="Simple Two Axis Regular" familyname="Simple Two Axis" stylename="Regular">
+      <lib copy="1"/>
+      <groups copy="1"/>
+      <features copy="1"/>
+      <info copy="1"/>
+      <location>
+        <dimension name="Weight" xvalue="200"/>
+      </location>
+    </source>
+    <source filename="master_incompatible_arrays/IncompatibleArrays-Bold.ttx" name="Simple Two Axis Bold" familyname="Simple Two Axis" stylename="Bold">
+      <location>
+        <dimension name="Weight" xvalue="700"/>
+      </location>
+    </source>
+  </sources>
+</designspace>
diff --git a/Tests/varLib/data/IncompatibleFeatures.designspace b/Tests/varLib/data/IncompatibleFeatures.designspace
new file mode 100644
index 0000000..ab27516
--- /dev/null
+++ b/Tests/varLib/data/IncompatibleFeatures.designspace
@@ -0,0 +1,22 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<designspace format="4.1">
+  <axes>
+    <axis tag="wght" name="Weight" minimum="200" maximum="700" default="200"/>
+  </axes>
+  <sources>
+    <source filename="master_incompatible_features/IncompatibleFeatures-Regular.ttx" name="Simple Two Axis Regular" familyname="Simple Two Axis" stylename="Regular">
+      <lib copy="1"/>
+      <groups copy="1"/>
+      <features copy="1"/>
+      <info copy="1"/>
+      <location>
+        <dimension name="Weight" xvalue="200"/>
+      </location>
+    </source>
+    <source filename="master_incompatible_features/IncompatibleFeatures-Bold.ttx" name="Simple Two Axis Bold" familyname="Simple Two Axis" stylename="Bold">
+      <location>
+        <dimension name="Weight" xvalue="700"/>
+      </location>
+    </source>
+  </sources>
+</designspace>
diff --git a/Tests/varLib/data/IncompatibleLookupTypes.designspace b/Tests/varLib/data/IncompatibleLookupTypes.designspace
new file mode 100644
index 0000000..c7d3575
--- /dev/null
+++ b/Tests/varLib/data/IncompatibleLookupTypes.designspace
@@ -0,0 +1,22 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<designspace format="4.1">
+  <axes>
+    <axis tag="wght" name="Weight" minimum="200" maximum="700" default="200"/>
+  </axes>
+  <sources>
+    <source filename="master_incompatible_lookup_types/IncompatibleLookupTypes-Regular.ttx" name="Simple Two Axis Regular" familyname="Simple Two Axis" stylename="Regular">
+      <lib copy="1"/>
+      <groups copy="1"/>
+      <features copy="1"/>
+      <info copy="1"/>
+      <location>
+        <dimension name="Weight" xvalue="200"/>
+      </location>
+    </source>
+    <source filename="master_incompatible_lookup_types/IncompatibleLookupTypes-Bold.ttx" name="Simple Two Axis Bold" familyname="Simple Two Axis" stylename="Bold">
+      <location>
+        <dimension name="Weight" xvalue="700"/>
+      </location>
+    </source>
+  </sources>
+</designspace>
diff --git a/Tests/varLib/data/KerningMerging.designspace b/Tests/varLib/data/KerningMerging.designspace
new file mode 100644
index 0000000..f7ce7ef
--- /dev/null
+++ b/Tests/varLib/data/KerningMerging.designspace
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<designspace format="4.0">
+  <axes>
+    <axis tag="wght" name="Weight" minimum="100" maximum="900" default="400"/>
+  </axes>
+  <sources>
+    <source filename="master_kerning_merging/0.ttf">
+      <location>
+        <dimension name="Weight" xvalue="100"/>
+      </location>
+    </source>
+    <source filename="master_kerning_merging/1.ttf">
+      <location>
+        <dimension name="Weight" xvalue="400"/>
+      </location>
+    </source>
+    <source filename="master_kerning_merging/2.ttf">
+      <location>
+        <dimension name="Weight" xvalue="900"/>
+      </location>
+    </source>
+  </sources>
+</designspace>
diff --git a/Tests/varLib/data/SingleMaster.designspace b/Tests/varLib/data/SingleMaster.designspace
new file mode 100644
index 0000000..53bdeab
--- /dev/null
+++ b/Tests/varLib/data/SingleMaster.designspace
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='utf-8'?>
+<designspace format="3">
+    <axes>
+        <axis default="400" maximum="400" minimum="400" name="weight" tag="wght" />
+    </axes>
+    <sources>
+        <source familyname="Test Family" filename="master_ufo/TestFamily-Master0.ufo" name="master_0">
+            <location>
+                <dimension name="weight" xvalue="400" />
+            </location>
+        </source>
+    </sources>
+</designspace>
diff --git a/Tests/varLib/data/SparseMasters.designspace b/Tests/varLib/data/SparseMasters.designspace
new file mode 100644
index 0000000..327fb43
--- /dev/null
+++ b/Tests/varLib/data/SparseMasters.designspace
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<designspace format="4.0">
+  <axes>
+    <axis tag="wght" name="Weight" minimum="350" maximum="625" default="350"/>
+  </axes>
+  <sources>
+      <source filename="master_ttx_interpolatable_ttf/SparseMasters-Regular.ttx" name="Sparse Masters Regular">
+      <location>
+        <dimension name="Weight" xvalue="350"/>
+      </location>
+    </source>
+    <source filename="master_ttx_interpolatable_ttf/SparseMasters-Medium.ttx" name="Sparse Masters Medium">
+      <location>
+        <dimension name="Weight" xvalue="450"/>
+      </location>
+    </source>
+    <source filename="master_ttx_interpolatable_ttf/SparseMasters-Bold.ttx" name="Sparse Masters Bold">
+      <location>
+        <dimension name="Weight" xvalue="625"/>
+      </location>
+    </source>
+  </sources>
+</designspace>
diff --git a/Tests/varLib/data/TestBASE.designspace b/Tests/varLib/data/TestBASE.designspace
new file mode 100644
index 0000000..6a584a5
--- /dev/null
+++ b/Tests/varLib/data/TestBASE.designspace
@@ -0,0 +1,36 @@
+<?xml version='1.0' encoding='utf-8'?>
+<designspace format="3">
+	<axes>
+		<axis default="0" maximum="900" minimum="0" name="weight" tag="wght" />
+	</axes>
+	<sources>
+		<source filename="master_base_test/TestBASE.0.ufo" stylename="w0.00">
+			<info copy="1" />
+			<location>
+				<dimension name="weight" xvalue="0.00" />
+			</location>
+		</source>
+		<source filename="master_base_test/TestBASE.900.ufo" stylename="w900.00">
+			
+			<location>
+				<dimension name="weight" xvalue="900.00" />
+			</location>
+		</source>
+	</sources>
+	<instances>
+		<instance familyname="TestBASE" filename="instances/TestBASE-Thin.otf" postscriptfontname="TestBASE-Thin" stylename="ExtraLight">
+			<location>
+				<dimension name="weight" xvalue="0" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+		<instance familyname="TestBASE" filename="instances/TestBase-Black.otf" postscriptfontname="TestBASE-Black" stylename="Heavy">
+			<location>
+				<dimension name="weight" xvalue="900" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+	</instances>
+</designspace>
diff --git a/Tests/varLib/data/TestCFF2.designspace b/Tests/varLib/data/TestCFF2.designspace
new file mode 100644
index 0000000..92c45ba
--- /dev/null
+++ b/Tests/varLib/data/TestCFF2.designspace
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<designspace format="3">
+    <axes>
+        <axis default="400.0" maximum="900.0" minimum="200.0" name="weight" tag="wght">
+            <map input="200" output="0" />   <!-- ExtraLight -->
+            <map input="300" output="100" /> <!-- Light -->
+            <map input="400" output="368" /> <!-- Regular -->
+            <map input="500" output="486" /> <!-- Medium -->
+            <map input="600" output="600" /> <!-- Semibold -->
+            <map input="700" output="824" /> <!-- Bold -->
+            <map input="900" output="1000" /><!-- Black -->
+        </axis>
+    </axes>
+    <rules>
+        <rule name="named.rule.1">
+            <conditionset>
+                <condition maximum="600" minimum="0" name="weight" />
+            </conditionset>
+            <sub name="dollar" with="dollar.a" />
+        </rule>
+    </rules>
+    <sources>
+        <source filename="master_cff2/TestCFF2_ExtraLight.ufo" name="master_0">
+            <lib copy="1" />
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+        </source>
+        <source filename="master_cff2/TestCFF2_Regular.ufo" name="master_1">
+            <glyph mute="1" name="T" />
+            <info copy="1" />
+            <location>
+                <dimension name="weight" xvalue="368" />
+            </location>
+        </source>
+        <source filename="master_cff2/TestCFF2_Black.ufo" name="master_2">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-ExtraLight" stylename="ExtraLight">
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Light" stylename="Light">
+            <location>
+                <dimension name="weight" xvalue="100" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Regular" stylename="Regular">
+            <location>
+                <dimension name="weight" xvalue="368" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Medium" stylename="Medium">
+            <location>
+                <dimension name="weight" xvalue="486" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Semibold" stylename="Semibold">
+            <location>
+                <dimension name="weight" xvalue="600" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Bold" stylename="Bold">
+            <location>
+                <dimension name="weight" xvalue="824" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Black" stylename="Black">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/TestCFF2Input.designspace b/Tests/varLib/data/TestCFF2Input.designspace
new file mode 100644
index 0000000..ebbf14a
--- /dev/null
+++ b/Tests/varLib/data/TestCFF2Input.designspace
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<designspace format="3">
+    <axes>
+        <axis default="400.0" maximum="900.0" minimum="200.0" name="weight" tag="wght">
+            <map input="200" output="0" />   <!-- ExtraLight -->
+            <map input="300" output="100" /> <!-- Light -->
+            <map input="400" output="368" /> <!-- Regular -->
+            <map input="500" output="486" /> <!-- Medium -->
+            <map input="600" output="600" /> <!-- Semibold -->
+            <map input="700" output="824" /> <!-- Bold -->
+            <map input="900" output="1000" /><!-- Black -->
+        </axis>
+    </axes>
+    <rules>
+        <rule name="named.rule.1">
+            <conditionset>
+                <condition maximum="600" minimum="0" name="weight" />
+            </conditionset>
+            <sub name="dollar" with="dollar.a" />
+        </rule>
+    </rules>
+    <sources>
+        <source filename="master_cff2_input/TestCFF2_ExtraLight.ufo" name="master_0">
+            <lib copy="1" />
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+        </source>
+        <source filename="master_cff2_input/TestCFF2_Regular.ufo" name="master_1">
+            <glyph mute="1" name="T" />
+            <info copy="1" />
+            <location>
+                <dimension name="weight" xvalue="368" />
+            </location>
+        </source>
+        <source filename="master_cff2_input/TestCFF2_Black.ufo" name="master_2">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-ExtraLight" stylename="ExtraLight">
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Light" stylename="Light">
+            <location>
+                <dimension name="weight" xvalue="100" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Regular" stylename="Regular">
+            <location>
+                <dimension name="weight" xvalue="368" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Medium" stylename="Medium">
+            <location>
+                <dimension name="weight" xvalue="486" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Semibold" stylename="Semibold">
+            <location>
+                <dimension name="weight" xvalue="600" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Bold" stylename="Bold">
+            <location>
+                <dimension name="weight" xvalue="824" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test CFF2 Roman" postscriptfontname="TestCFF2Roman-Black" stylename="Black">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/TestNonMarkingCFF2.designspace b/Tests/varLib/data/TestNonMarkingCFF2.designspace
new file mode 100644
index 0000000..23c7797
--- /dev/null
+++ b/Tests/varLib/data/TestNonMarkingCFF2.designspace
@@ -0,0 +1,35 @@
+<?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 filename="master_non_marking_cff2/TestNonMarkingCFF2_ExtraLight.ufo" name="master_0">
+            <lib copy="1" />
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+        </source>
+        <source filename="master_non_marking_cff2/TestNonMarkingCFF2_Regular.ufo" name="master_1">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+        <instance familyname="Test Non Marking CFF2 Roman" postscriptfontname="TestNonMarkingCFF2-ExtraLight" stylename="ExtraLight">
+            <location>
+                <dimension name="weight" xvalue="0" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+        <instance familyname="Test Non Marking CFF2 Roman" postscriptfontname="TestNonMarkingCFF2-Regular" stylename="Regular">
+            <location>
+                <dimension name="weight" xvalue="1000" />
+            </location>
+            <kerning />
+            <info />
+        </instance>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/TestSparseCFF2VF.designspace b/Tests/varLib/data/TestSparseCFF2VF.designspace
new file mode 100644
index 0000000..d8165cf
--- /dev/null
+++ b/Tests/varLib/data/TestSparseCFF2VF.designspace
@@ -0,0 +1,229 @@
+<?xml version='1.0' encoding='utf-8'?>
+<designspace format="3">
+	<axes>
+		<axis default="200" maximum="900" minimum="200" name="weight" tag="wght">
+			<map input="200" output="0" />   <!-- ExtraLight -->
+			<map input="300" output="160" /> <!-- Light -->
+			<map input="350" output="320" /> <!-- Normal -->
+			<map input="400" output="390" /> <!-- Regular -->
+			<map input="500" output="560" /> <!-- Medium -->
+			<map input="700" output="780" /> <!-- Bold -->
+			<map input="900" output="1000" /><!-- Heavy -->
+		</axis>
+	</axes>
+	<sources>
+		<source filename="master_sparse_cff2/MasterSet_Kanji-w0.00.ufo" stylename="w0.00">
+			<info copy="1" />
+			<location>
+				<dimension name="weight" xvalue="0.00" />
+			</location>
+		</source>
+		<source filename="master_sparse_cff2/MasterSet_Kanji-w439.00.ufo" stylename="w439.00">
+			
+			<location>
+				<dimension name="weight" xvalue="439.00" />
+			</location>
+		</source>
+		<source filename="master_sparse_cff2/MasterSet_Kanji-w440.00.ufo" stylename="w440.00">
+			
+			<location>
+				<dimension name="weight" xvalue="440.00" />
+			</location>
+		</source>
+		<source filename="master_sparse_cff2/MasterSet_Kanji-w599.00.ufo" stylename="w599.00">
+			
+			<location>
+				<dimension name="weight" xvalue="599.00" />
+			</location>
+		</source>
+		<source filename="master_sparse_cff2/MasterSet_Kanji-w600.00.ufo" stylename="w600.00">
+			
+			<location>
+				<dimension name="weight" xvalue="600.00" />
+			</location>
+		</source>
+		<source filename="master_sparse_cff2/MasterSet_Kanji-w669.00.ufo" stylename="w669.00">
+			
+			<location>
+				<dimension name="weight" xvalue="669.00" />
+			</location>
+		</source>
+		<source filename="master_sparse_cff2/MasterSet_Kanji-w670.00.ufo" stylename="w670.00">
+			
+			<location>
+				<dimension name="weight" xvalue="670.00" />
+			</location>
+		</source>
+		<source filename="master_sparse_cff2/MasterSet_Kanji-w799.00.ufo" stylename="w799.00">
+			
+			<location>
+				<dimension name="weight" xvalue="799.00" />
+			</location>
+		</source>
+		<source filename="master_sparse_cff2/MasterSet_Kanji-w800.00.ufo" stylename="w800.00">
+			
+			<location>
+				<dimension name="weight" xvalue="800.00" />
+			</location>
+		</source>
+		<source filename="master_sparse_cff2/MasterSet_Kanji-w889.00.ufo" stylename="w889.00">
+			
+			<location>
+				<dimension name="weight" xvalue="889.00" />
+			</location>
+		</source>
+		<source filename="master_sparse_cff2/MasterSet_Kanji-w890.00.ufo" stylename="w890.00">
+			
+			<location>
+				<dimension name="weight" xvalue="890.00" />
+			</location>
+		</source>
+		<source filename="master_sparse_cff2/MasterSet_Kanji-w1000.00.ufo" stylename="w1000.00">
+			
+			<location>
+				<dimension name="weight" xvalue="1000.00" />
+			</location>
+		</source>
+	</sources>
+	<instances>
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w0.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w0.00" stylename="Kanji-w0.00">
+			<location>
+				<dimension name="weight" xvalue="0" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w239.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w239.00" stylename="Kanji-w239.00">
+			<location>
+				<dimension name="weight" xvalue="239" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w240.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w240.00" stylename="Kanji-w240.00">
+			<location>
+				<dimension name="weight" xvalue="240" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w439.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w439.00" stylename="Kanji-w439.00">
+			<location>
+				<dimension name="weight" xvalue="439" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w440.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w440.00" stylename="Kanji-w440.00">
+			<location>
+				<dimension name="weight" xvalue="440" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+
+
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w499.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w499.00" stylename="Kanji-w499.00">
+			<location>
+				<dimension name="weight" xvalue="499" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w500.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w500.00" stylename="Kanji-w500.00">
+			<location>
+				<dimension name="weight" xvalue="500" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+
+
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w599.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w599.00" stylename="Kanji-w599.00">
+			<location>
+				<dimension name="weight" xvalue="599" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w600.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w600.00" stylename="Kanji-w600.00">
+			<location>
+				<dimension name="weight" xvalue="600" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+
+
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w669.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w669.00" stylename="Kanji-w669.00">
+			<location>
+				<dimension name="weight" xvalue="669" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w670.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w670.00" stylename="Kanji-w670.00">
+			<location>
+				<dimension name="weight" xvalue="670" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+
+
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w699.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w699.00" stylename="Kanji-w699.00">
+			<location>
+				<dimension name="weight" xvalue="699" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w700.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w700.00" stylename="Kanji-w700.00">
+			<location>
+				<dimension name="weight" xvalue="700" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+
+
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-799.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-799.00" stylename="Kanji-799.00">
+			<location>
+				<dimension name="weight" xvalue="799" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w800.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w800.00" stylename="Kanji-w800.00">
+			<location>
+				<dimension name="weight" xvalue="800" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+
+
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w889.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w889.00" stylename="Kanji-w889.00">
+			<location>
+				<dimension name="weight" xvalue="889" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w890.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w890.00" stylename="Kanji-w890.00">
+			<location>
+				<dimension name="weight" xvalue="890" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+
+
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Kanji-w1000.00.otf" postscriptfontname="SHSansJPVFTest-Kanji-w1000.00" stylename="Kanji-w1000.00">
+			<location>
+				<dimension name="weight" xvalue="1000" />
+			</location>
+		</instance>
+	</instances>
+</designspace>
diff --git a/Tests/varLib/data/TestVVAR.designspace b/Tests/varLib/data/TestVVAR.designspace
new file mode 100644
index 0000000..162f738
--- /dev/null
+++ b/Tests/varLib/data/TestVVAR.designspace
@@ -0,0 +1,36 @@
+<?xml version='1.0' encoding='utf-8'?>
+<designspace format="3">
+	<axes>
+		<axis default="0" maximum="1000" minimum="0" name="weight" tag="wght" />
+	</axes>
+	<sources>
+		<source filename="master_vvar_cff2/TestVVAR.0.ufo" stylename="w0.00">
+			<info copy="1" />
+			<location>
+				<dimension name="weight" xvalue="0.00" />
+			</location>
+		</source>
+		<source filename="master_vvar_cff2/TestVVAR.1.ufo" stylename="w1000.00">
+			
+			<location>
+				<dimension name="weight" xvalue="1000.00" />
+			</location>
+		</source>
+	</sources>
+	<instances>
+		<instance familyname="TestVVAR" filename="instances/TestVVAR-ExtraLight.otf" postscriptfontname="TestVVAR-ExtraLight" stylename="ExtraLight">
+			<location>
+				<dimension name="weight" xvalue="0" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+		<instance familyname="TestVVAR" filename="instances/TestVVAR-Heavy.otf" postscriptfontname="TestVVAR-Heavy" stylename="Heavy">
+			<location>
+				<dimension name="weight" xvalue="1000" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+	</instances>
+</designspace>
diff --git a/Tests/varLib/data/VarLibLocationTest.designspace b/Tests/varLib/data/VarLibLocationTest.designspace
new file mode 100644
index 0000000..b73e1b5
--- /dev/null
+++ b/Tests/varLib/data/VarLibLocationTest.designspace
@@ -0,0 +1,26 @@
+<?xml version='1.0' encoding='utf-8'?>
+<designspace format="4.0">
+    <axes>
+        <axis default="100" maximum="900" minimum="100" name="weight" tag="wght">
+            <map input="500" output="105"/>
+            <map input="300" output="57"/>
+            <map input="900" output="158"/>
+            <map input="100" output="0"/>
+        </axis>
+        <axis default="50" maximum="100" minimum="0" name="width" tag="wdth" />
+    </axes>
+    <sources>
+        <source filename="A.ufo">
+            <location>
+                <dimension name="weight" xvalue="0" />
+                <dimension name="width" xvalue="50" />
+            </location>
+        </source>
+    </sources>
+    <instances>
+        <instance filename="C.ufo" familyname="C" stylename="CCC">
+            <location>
+            </location>
+        </instance>
+    </instances>
+</designspace>
diff --git a/Tests/varLib/data/master_base_test/TestBASE.0.ttx b/Tests/varLib/data/master_base_test/TestBASE.0.ttx
new file mode 100644
index 0000000..07f827f
--- /dev/null
+++ b/Tests/varLib/data/master_base_test/TestBASE.0.ttx
@@ -0,0 +1,700 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.4">
+
+  <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="uni5B89"/>
+    <GlyphID id="4" name="uni304B"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xf10ba20c"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 19 17:38:24 2020"/>
+    <modified value="Thu Mar 19 12:05:00 2020"/>
+    <xMin value="-2"/>
+    <yMin value="-200"/>
+    <xMax value="801"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1096"/>
+    <descent value="-161"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="-2"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="801"/>
+    <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="5"/>
+    <maxPoints value="73"/>
+    <maxContours value="10"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="2"/>
+    <maxStorage value="30"/>
+    <maxFunctionDefs value="6"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="100"/>
+    <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="740"/>
+    <usWeightClass value="100"/>
+    <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="2"/>
+      <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="32"/>
+    <usLastCharIndex value="23433"/>
+    <sTypoAscender value="1096"/>
+    <sTypoDescender value="-161"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1096"/>
+    <usWinDescent value="161"/>
+    <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="93"/>
+    <mtx name="A" width="600" lsb="-2"/>
+    <mtx name="space" width="600" lsb="0"/>
+    <mtx name="uni304B" width="1000" lsb="199"/>
+    <mtx name="uni5B89" width="1000" lsb="198"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x304b" name="uni304B"/><!-- HIRAGANA LETTER KA -->
+      <map code="0x5b89" name="uni5B89"/><!-- CJK UNIFIED IDEOGRAPH-5B89 -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x304b" name="uni304B"/><!-- HIRAGANA LETTER KA -->
+      <map code="0x5b89" name="uni5B89"/><!-- CJK UNIFIED IDEOGRAPH-5B89 -->
+    </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="93" yMin="-200" xMax="410" yMax="800">
+      <contour>
+        <pt x="410" y="800" on="1"/>
+        <pt x="410" y="-200" on="1"/>
+        <pt x="93" y="-200" on="1"/>
+        <pt x="93" y="800" on="1"/>
+      </contour>
+      <contour>
+        <pt x="333" y="733" on="1"/>
+        <pt x="168" y="733" on="1"/>
+        <pt x="168" y="700" on="1"/>
+        <pt x="233" y="700" on="1"/>
+        <pt x="233" y="663" on="1"/>
+        <pt x="167" y="663" on="1"/>
+        <pt x="167" y="630" on="1"/>
+        <pt x="333" y="630" on="1"/>
+        <pt x="333" y="663" on="1"/>
+        <pt x="267" y="663" on="1"/>
+        <pt x="267" y="700" on="1"/>
+        <pt x="333" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="267" y="604" on="1"/>
+        <pt x="167" y="604" on="1"/>
+        <pt x="167" y="500" on="1"/>
+        <pt x="333" y="500" on="1"/>
+        <pt x="333" y="534" on="1"/>
+        <pt x="267" y="534" on="1"/>
+      </contour>
+      <contour>
+        <pt x="233" y="570" on="1"/>
+        <pt x="233" y="534" on="1"/>
+        <pt x="200" y="534" on="1"/>
+        <pt x="200" y="570" on="1"/>
+      </contour>
+      <contour>
+        <pt x="333" y="473" on="1"/>
+        <pt x="167" y="473" on="1"/>
+        <pt x="167" y="440" on="1"/>
+        <pt x="233" y="440" on="1"/>
+        <pt x="233" y="403" on="1"/>
+        <pt x="167" y="403" on="1"/>
+        <pt x="167" y="370" on="1"/>
+        <pt x="267" y="370" on="1"/>
+        <pt x="267" y="440" on="1"/>
+        <pt x="333" y="440" on="1"/>
+      </contour>
+      <contour>
+        <pt x="333" y="413" on="1"/>
+        <pt x="300" y="413" on="1"/>
+        <pt x="300" y="347" on="1"/>
+        <pt x="167" y="347" on="1"/>
+        <pt x="167" y="313" on="1"/>
+        <pt x="333" y="313" on="1"/>
+      </contour>
+      <contour>
+        <pt x="333" y="291" on="1"/>
+        <pt x="233" y="291" on="1"/>
+        <pt x="233" y="235" on="1"/>
+        <pt x="267" y="235" on="1"/>
+        <pt x="267" y="258" on="1"/>
+        <pt x="300" y="258" on="1"/>
+        <pt x="300" y="211" on="1"/>
+        <pt x="200" y="211" on="1"/>
+        <pt x="200" y="291" on="1"/>
+        <pt x="167" y="291" on="1"/>
+        <pt x="167" y="178" on="1"/>
+        <pt x="333" y="178" on="1"/>
+      </contour>
+      <contour>
+        <pt x="333" y="118" on="1"/>
+        <pt x="167" y="118" on="1"/>
+        <pt x="167" y="5" on="1"/>
+        <pt x="333" y="5" on="1"/>
+      </contour>
+      <contour>
+        <pt x="300" y="85" on="1"/>
+        <pt x="300" y="38" on="1"/>
+        <pt x="200" y="38" on="1"/>
+        <pt x="200" y="85" on="1"/>
+      </contour>
+      <contour>
+        <pt x="333" y="-18" on="1"/>
+        <pt x="167" y="-18" on="1"/>
+        <pt x="167" y="-51" on="1"/>
+        <pt x="237" y="-51" on="1"/>
+        <pt x="167" y="-98" on="1"/>
+        <pt x="167" y="-131" on="1"/>
+        <pt x="333" y="-131" on="1"/>
+        <pt x="333" y="-98" on="1"/>
+        <pt x="231" y="-98" on="1"/>
+        <pt x="301" y="-51" on="1"/>
+        <pt x="333" y="-51" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="-2" yMin="0" xMax="600" yMax="700">
+      <contour>
+        <pt x="-2" y="700" on="1"/>
+        <pt x="600" y="700" on="1"/>
+        <pt x="600" y="0" on="1"/>
+        <pt x="-2" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="uni304B" xMin="199" yMin="-20" xMax="801" yMax="630">
+      <contour>
+        <pt x="199" y="630" on="1"/>
+        <pt x="801" y="630" on="1"/>
+        <pt x="801" y="-20" on="1"/>
+        <pt x="199" y="-20" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni5B89" xMin="198" yMin="-51" xMax="800" yMax="649">
+      <contour>
+        <pt x="198" y="649" on="1"/>
+        <pt x="800" y="649" on="1"/>
+        <pt x="800" y="-51" on="1"/>
+        <pt x="198" y="-51" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestBASE
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Thin
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      1.000;UKWN;TestBASE-Thin
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestBASE Thin
+    </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">
+      TestBASE-Thin
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      TestBASE Thin
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;UKWN;TestBASE-Thin
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      TestBASE Thin
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestBASE-Thin
+    </namerecord>
+    <namerecord nameID="16" platformID="3" platEncID="1" langID="0x409">
+      TestBASE
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Thin
+    </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="uni5B89"/>
+      <psName name="uni304B"/>
+    </extraNames>
+  </post>
+
+  <BASE>
+    <Version value="0x00010000"/>
+    <HorizAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=5 -->
+        <BaselineTag index="0" value="icfb"/>
+        <BaselineTag index="1" value="icft"/>
+        <BaselineTag index="2" value="ideo"/>
+        <BaselineTag index="3" value="idtp"/>
+        <BaselineTag index="4" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=6 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-75"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="835"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-75"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="835"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-75"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="835"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="hani"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-75"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="835"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="4">
+          <BaseScriptTag value="kana"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-75"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="835"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="5">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-75"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="835"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </HorizAxis>
+    <VertAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=5 -->
+        <BaselineTag index="0" value="icfb"/>
+        <BaselineTag index="1" value="icft"/>
+        <BaselineTag index="2" value="ideo"/>
+        <BaselineTag index="3" value="idtp"/>
+        <BaselineTag index="4" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=6 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="45"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="955"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="45"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="955"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="45"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="955"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="hani"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="45"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="955"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="4">
+          <BaseScriptTag value="kana"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="45"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="955"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="5">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="45"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="955"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </VertAxis>
+  </BASE>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+  </GSUB>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_base_test/TestBASE.900.ttx b/Tests/varLib/data/master_base_test/TestBASE.900.ttx
new file mode 100644
index 0000000..8b27fb6
--- /dev/null
+++ b/Tests/varLib/data/master_base_test/TestBASE.900.ttx
@@ -0,0 +1,700 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.4">
+
+  <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="uni5B89"/>
+    <GlyphID id="4" name="uni304B"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xc8faf2b8"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Mar 19 17:38:24 2020"/>
+    <modified value="Thu Mar 19 12:04:56 2020"/>
+    <xMin value="-2"/>
+    <yMin value="-200"/>
+    <xMax value="801"/>
+    <yMax value="800"/>
+    <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="1000"/>
+    <minLeftSideBearing value="-2"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="801"/>
+    <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="5"/>
+    <maxPoints value="73"/>
+    <maxContours value="10"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="2"/>
+    <maxStorage value="30"/>
+    <maxFunctionDefs value="6"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="100"/>
+    <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="740"/>
+    <usWeightClass value="900"/>
+    <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="10"/>
+      <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="32"/>
+    <usLastCharIndex value="23433"/>
+    <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="500" lsb="93"/>
+    <mtx name="A" width="600" lsb="-2"/>
+    <mtx name="space" width="600" lsb="0"/>
+    <mtx name="uni304B" width="1000" lsb="199"/>
+    <mtx name="uni5B89" width="1000" lsb="198"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x304b" name="uni304B"/><!-- HIRAGANA LETTER KA -->
+      <map code="0x5b89" name="uni5B89"/><!-- CJK UNIFIED IDEOGRAPH-5B89 -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x304b" name="uni304B"/><!-- HIRAGANA LETTER KA -->
+      <map code="0x5b89" name="uni5B89"/><!-- CJK UNIFIED IDEOGRAPH-5B89 -->
+    </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="93" yMin="-200" xMax="410" yMax="800">
+      <contour>
+        <pt x="410" y="800" on="1"/>
+        <pt x="410" y="-200" on="1"/>
+        <pt x="93" y="-200" on="1"/>
+        <pt x="93" y="800" on="1"/>
+      </contour>
+      <contour>
+        <pt x="333" y="733" on="1"/>
+        <pt x="168" y="733" on="1"/>
+        <pt x="168" y="700" on="1"/>
+        <pt x="233" y="700" on="1"/>
+        <pt x="233" y="663" on="1"/>
+        <pt x="167" y="663" on="1"/>
+        <pt x="167" y="630" on="1"/>
+        <pt x="333" y="630" on="1"/>
+        <pt x="333" y="663" on="1"/>
+        <pt x="267" y="663" on="1"/>
+        <pt x="267" y="700" on="1"/>
+        <pt x="333" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="267" y="604" on="1"/>
+        <pt x="167" y="604" on="1"/>
+        <pt x="167" y="500" on="1"/>
+        <pt x="333" y="500" on="1"/>
+        <pt x="333" y="534" on="1"/>
+        <pt x="267" y="534" on="1"/>
+      </contour>
+      <contour>
+        <pt x="233" y="570" on="1"/>
+        <pt x="233" y="534" on="1"/>
+        <pt x="200" y="534" on="1"/>
+        <pt x="200" y="570" on="1"/>
+      </contour>
+      <contour>
+        <pt x="333" y="473" on="1"/>
+        <pt x="167" y="473" on="1"/>
+        <pt x="167" y="440" on="1"/>
+        <pt x="233" y="440" on="1"/>
+        <pt x="233" y="403" on="1"/>
+        <pt x="167" y="403" on="1"/>
+        <pt x="167" y="370" on="1"/>
+        <pt x="267" y="370" on="1"/>
+        <pt x="267" y="440" on="1"/>
+        <pt x="333" y="440" on="1"/>
+      </contour>
+      <contour>
+        <pt x="333" y="413" on="1"/>
+        <pt x="300" y="413" on="1"/>
+        <pt x="300" y="347" on="1"/>
+        <pt x="167" y="347" on="1"/>
+        <pt x="167" y="313" on="1"/>
+        <pt x="333" y="313" on="1"/>
+      </contour>
+      <contour>
+        <pt x="333" y="291" on="1"/>
+        <pt x="233" y="291" on="1"/>
+        <pt x="233" y="235" on="1"/>
+        <pt x="267" y="235" on="1"/>
+        <pt x="267" y="258" on="1"/>
+        <pt x="300" y="258" on="1"/>
+        <pt x="300" y="211" on="1"/>
+        <pt x="200" y="211" on="1"/>
+        <pt x="200" y="291" on="1"/>
+        <pt x="167" y="291" on="1"/>
+        <pt x="167" y="178" on="1"/>
+        <pt x="333" y="178" on="1"/>
+      </contour>
+      <contour>
+        <pt x="333" y="118" on="1"/>
+        <pt x="167" y="118" on="1"/>
+        <pt x="167" y="5" on="1"/>
+        <pt x="333" y="5" on="1"/>
+      </contour>
+      <contour>
+        <pt x="300" y="85" on="1"/>
+        <pt x="300" y="38" on="1"/>
+        <pt x="200" y="38" on="1"/>
+        <pt x="200" y="85" on="1"/>
+      </contour>
+      <contour>
+        <pt x="333" y="-18" on="1"/>
+        <pt x="167" y="-18" on="1"/>
+        <pt x="167" y="-51" on="1"/>
+        <pt x="237" y="-51" on="1"/>
+        <pt x="167" y="-98" on="1"/>
+        <pt x="167" y="-131" on="1"/>
+        <pt x="333" y="-131" on="1"/>
+        <pt x="333" y="-98" on="1"/>
+        <pt x="231" y="-98" on="1"/>
+        <pt x="301" y="-51" on="1"/>
+        <pt x="333" y="-51" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="-2" yMin="0" xMax="600" yMax="700">
+      <contour>
+        <pt x="-2" y="700" on="1"/>
+        <pt x="600" y="700" on="1"/>
+        <pt x="600" y="0" on="1"/>
+        <pt x="-2" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+    <TTGlyph name="uni304B" xMin="199" yMin="-20" xMax="801" yMax="630">
+      <contour>
+        <pt x="199" y="630" on="1"/>
+        <pt x="801" y="630" on="1"/>
+        <pt x="801" y="-20" on="1"/>
+        <pt x="199" y="-20" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="uni5B89" xMin="198" yMin="-51" xMax="800" yMax="649">
+      <contour>
+        <pt x="198" y="649" on="1"/>
+        <pt x="800" y="649" on="1"/>
+        <pt x="800" y="-51" on="1"/>
+        <pt x="198" y="-51" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestBASE
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Black
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      1.000;UKWN;TestBASE-Black
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestBASE Black
+    </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">
+      TestBASE-Black
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      TestBASE Black
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;UKWN;TestBASE-Black
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      TestBASE Black
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestBASE-Black
+    </namerecord>
+    <namerecord nameID="16" platformID="3" platEncID="1" langID="0x409">
+      TestBASE
+    </namerecord>
+    <namerecord nameID="17" 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 -->
+      <psName name="uni5B89"/>
+      <psName name="uni304B"/>
+    </extraNames>
+  </post>
+
+  <BASE>
+    <Version value="0x00010000"/>
+    <HorizAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=5 -->
+        <BaselineTag index="0" value="icfb"/>
+        <BaselineTag index="1" value="icft"/>
+        <BaselineTag index="2" value="ideo"/>
+        <BaselineTag index="3" value="idtp"/>
+        <BaselineTag index="4" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=6 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-92"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="852"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-92"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="852"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-92"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="852"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="hani"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-92"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="852"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="4">
+          <BaseScriptTag value="kana"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-92"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="852"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="5">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="-92"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="852"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </HorizAxis>
+    <VertAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=5 -->
+        <BaselineTag index="0" value="icfb"/>
+        <BaselineTag index="1" value="icft"/>
+        <BaselineTag index="2" value="ideo"/>
+        <BaselineTag index="3" value="idtp"/>
+        <BaselineTag index="4" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=6 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="28"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="972"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="28"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="972"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="28"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="972"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="hani"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="28"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="972"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="4">
+          <BaseScriptTag value="kana"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="28"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="972"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="5">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="1">
+                <Coordinate value="28"/>
+              </BaseCoord>
+              <BaseCoord index="1" Format="1">
+                <Coordinate value="972"/>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </VertAxis>
+  </BASE>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+  </GSUB>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_cff2/TestCFF2_Black.ttx b/Tests/varLib/data/master_cff2/TestCFF2_Black.ttx
new file mode 100644
index 0000000..8b633f1
--- /dev/null
+++ b/Tests/varLib/data/master_cff2/TestCFF2_Black.ttx
@@ -0,0 +1,518 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <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="T"/>
+    <GlyphID id="3" name="dollar.a"/>
+    <GlyphID id="4" name="dollar"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0x26378952"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Nov 29 14:52:09 2018"/>
+    <modified value="Thu Nov 29 14:52:09 2018"/>
+    <xMin value="0"/>
+    <yMin value="-116"/>
+    <xMax value="600"/>
+    <yMax value="750"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <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="600"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="600"/>
+    <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>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="5"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="592"/>
+    <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="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </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="ADBO"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="36"/>
+    <usLastCharIndex value="84"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </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.010;ADBO;SourceCode_Black
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceCode_Black
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Roman Master 2
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCode_Black
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCode_Black
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Roman Master 2
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x24" name="dollar"/>
+      <map code="0x41" name="A"/>
+      <map code="0x54" name="T"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="SourceCode_Black">
+      <version value="1.0"/>
+      <Notice value="Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries."/>
+      <Copyright value="Copyright 2010 - 2012 Adobe Systems Incorporated. All Rights Reserved."/>
+      <FamilyName value="Source Code"/>
+      <isFixedPitch value="1"/>
+      <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 -116 600 750"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-12 0 500 512 580 592 634 646 650 662 696 708"/>
+        <OtherBlues value="-188 -176"/>
+        <FamilyBlues value="-12 0 486 498 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"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="600"/>
+        <nominalWidthX value="667"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          24 0 rmoveto
+          552 0 rlineto
+          0 660 rlineto
+          -552 0 rlineto
+          0 -660 rlineto
+          212 104 rmoveto
+          26 56 rlineto
+          36 96 rlineto
+          4 0 rlineto
+          36 -96 rlineto
+          26 -56 rlineto
+          -128 0 rlineto
+          -100 68 rmoveto
+          0 336 rlineto
+          82 -168 rlineto
+          -82 -168 rlineto
+          162 252 rmoveto
+          -40 96 rlineto
+          -18 36 rlineto
+          120 0 rlineto
+          -18 -36 rlineto
+          -40 -96 rlineto
+          -4 0 rlineto
+          84 -84 rmoveto
+          82 168 rlineto
+          0 -336 rlineto
+          -82 168 rlineto
+          endchar
+        </CharString>
+        <CharString name="A">
+          0 0 rmoveto
+          176 0 rlineto
+          73 316 rlineto
+          14 62 17 78 14 66 rrcurveto
+          4 0 rlineto
+          14 -66 19 -78 14 -62 rrcurveto
+          73 -316 rlineto
+          182 0 rlineto
+          -196 650 rlineto
+          -208 0 rlineto
+          -196 -650 rlineto
+          141 138 rmoveto
+          316 0 rlineto
+          0 133 rlineto
+          -316 0 rlineto
+          0 -133 rlineto
+          endchar
+        </CharString>
+        <CharString name="T">
+          214 0 rmoveto
+          172 0 rlineto
+          0 506 rlineto
+          187 0 rlineto
+          0 144 rlineto
+          -546 0 rlineto
+          0 -144 rlineto
+          187 0 rlineto
+          0 -506 rlineto
+          endchar
+        </CharString>
+        <CharString name="dollar">
+          -107 260 39 rmoveto
+          -65 0 -28 11 -49 24 rrcurveto
+          89 -53 rlineto
+          -15 89 rlineto
+          -9 52 -22 18 -43 0 rrcurveto
+          -26 0 -27 -14 -14 -38 rrcurveto
+          0 -90 71 -54 139 0 rrcurveto
+          163 0 99 84 0 117 rrcurveto
+          0 98 -58 68 -142 45 rrcurveto
+          -33 10 rlineto
+          -72 22 -24 24 0 49 rrcurveto
+          0 61 47 23 67 0 rrcurveto
+          42 0 27 -4 52 -24 rrcurveto
+          -85 47 rlineto
+          10 -67 rlineto
+          11 -75 37 -14 39 0 rrcurveto
+          26 0 29 15 5 41 rrcurveto
+          -8 88 -76 48 -121 0 rrcurveto
+          -158 0 -85 -80 0 -115 rrcurveto
+          0 -93 66 -69 121 -39 rrcurveto
+          32 -11 rlineto
+          80 -28 23 -19 0 -53 rrcurveto
+          0 -55 -43 -39 -72 0 rrcurveto
+          64 275 rmoveto
+          0 417 rlineto
+          -71 0 rlineto
+          0 -417 rlineto
+          71 0 rlineto
+          -79 -429 rmoveto
+          71 0 rlineto
+          0 429 rlineto
+          -71 0 rlineto
+          0 -429 rlineto
+          endchar
+        </CharString>
+        <CharString name="dollar.a">
+          292 34 rmoveto
+          163 0 83 80 0 100 rrcurveto
+          0 182 -302 -4 0 56 rrcurveto
+          0 21 18 11 36 0 rrcurveto
+          55 0 39 -16 52 -32 rrcurveto
+          84 98 rlineto
+          -53 52 -69 36 -97 0 rrcurveto
+          -141 0 -88 -68 0 -104 rrcurveto
+          0 -188 302 12 0 -68 rrcurveto
+          0 -20 -19 -10 -37 0 rrcurveto
+          -61 0 -55 20 -72 40 rrcurveto
+          -74 -116 rlineto
+          65 -54 101 -28 70 0 rrcurveto
+          -19 -150 rmoveto
+          160 854 rlineto
+          -100 12 rlineto
+          -160 -854 rlineto
+          100 -12 rlineto
+          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>
+
+  <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>
+
+  <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="dollar" out="dollar.a"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="24"/>
+    <mtx name="A" width="600" lsb="0"/>
+    <mtx name="T" width="600" lsb="27"/>
+    <mtx name="dollar" width="560" lsb="51"/>
+    <mtx name="dollar.a" width="600" lsb="56"/>
+  </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/varLib/data/master_cff2/TestCFF2_ExtraLight.ttx b/Tests/varLib/data/master_cff2/TestCFF2_ExtraLight.ttx
new file mode 100644
index 0000000..aae43aa
--- /dev/null
+++ b/Tests/varLib/data/master_cff2/TestCFF2_ExtraLight.ttx
@@ -0,0 +1,518 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <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="T"/>
+    <GlyphID id="3" name="dollar.a"/>
+    <GlyphID id="4" name="dollar"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0xeb345d38"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Nov 29 14:52:09 2018"/>
+    <modified value="Thu Nov 29 14:52:09 2018"/>
+    <xMin value="50"/>
+    <yMin value="-115"/>
+    <xMax value="550"/>
+    <yMax value="762"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <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="600"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <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="5"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="5"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="578"/>
+    <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="286"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="3"/>
+      <bProportion value="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </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="ADBO"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="36"/>
+    <usLastCharIndex value="84"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="478"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </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.010;ADBO;SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Roman Master 0
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Roman Master 0
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x24" name="dollar"/>
+      <map code="0x41" name="A"/>
+      <map code="0x54" name="T"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="SourceCode_ExtraLight">
+      <version value="1.0"/>
+      <Notice value="Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries."/>
+      <Copyright value="Copyright 2010 - 2012 Adobe Systems Incorporated. All Rights Reserved."/>
+      <FamilyName value="Source Code"/>
+      <isFixedPitch value="1"/>
+      <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="50 -115 550 762"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-12 0 478 490 570 582 640 652 660 672 722 734"/>
+        <OtherBlues value="-234 -222"/>
+        <FamilyBlues value="-12 0 486 498 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="34"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="600"/>
+        <nominalWidthX value="597"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          84 0 rmoveto
+          432 0 rlineto
+          0 660 rlineto
+          -432 0 rlineto
+          0 -660 rlineto
+          48 32 rmoveto
+          102 176 rlineto
+          64 106 rlineto
+          4 0 rlineto
+          62 -106 rlineto
+          100 -176 rlineto
+          -332 0 rlineto
+          -10 42 rmoveto
+          0 536 rlineto
+          154 -270 rlineto
+          -154 -266 rlineto
+          176 292 rmoveto
+          -56 92 rlineto
+          -94 168 rlineto
+          302 0 rlineto
+          -94 -168 rlineto
+          -54 -92 rlineto
+          -4 0 rlineto
+          26 -26 rmoveto
+          152 270 rlineto
+          0 -536 rlineto
+          -152 266 rlineto
+          endchar
+        </CharString>
+        <CharString name="A">
+          50 0 rmoveto
+          32 0 rlineto
+          140 396 rlineto
+          28 80 24 68 24 82 rrcurveto
+          4 0 rlineto
+          24 -82 24 -68 28 -80 rrcurveto
+          138 -396 rlineto
+          34 0 rlineto
+          -236 660 rlineto
+          -28 0 rlineto
+          -236 -660 rlineto
+          102 236 rmoveto
+          293 0 rlineto
+          0 28 rlineto
+          -293 0 rlineto
+          0 -28 rlineto
+          endchar
+        </CharString>
+        <CharString name="T">
+          284 0 rmoveto
+          32 0 rlineto
+          0 632 rlineto
+          234 0 rlineto
+          0 28 rlineto
+          -500 0 rlineto
+          0 -28 rlineto
+          234 0 rlineto
+          0 -632 rlineto
+          endchar
+        </CharString>
+        <CharString name="dollar">
+          -107 245 7 rmoveto
+          -65 0 -39 15 -46 50 rrcurveto
+          36 -48 rlineto
+          -28 100 rlineto
+          -4 16 -12 4 -11 0 rrcurveto
+          -14 0 -8 -7 -1 -14 rrcurveto
+          24 -85 61 -51 107 0 rrcurveto
+          91 0 90 53 0 111 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 -92 -65 -54 -90 0 rrcurveto
+          18 327 rmoveto
+          0 428 rlineto
+          -22 0 rlineto
+          0 -428 rlineto
+          22 0 rlineto
+          -22 -449 rmoveto
+          22 0 rlineto
+          0 449 rlineto
+          -22 0 rlineto
+          0 -449 rlineto
+          endchar
+        </CharString>
+        <CharString name="dollar.a">
+          311 34 rmoveto
+          103 0 88 56 0 94 rrcurveto
+          0 184 -338 -32 0 142 rrcurveto
+          0 68 57 44 85 0 rrcurveto
+          76 0 34 -24 44 -38 rrcurveto
+          20 20 rlineto
+          -41 38 -45 32 -85 0 rrcurveto
+          -99 0 -78 -54 0 -88 rrcurveto
+          0 -166 338 28 0 -156 rrcurveto
+          0 -70 -56 -50 -103 0 rrcurveto
+          -85 0 -66 38 -40 34 rrcurveto
+          -18 -22 rlineto
+          45 -38 73 -40 91 0 rrcurveto
+          -70 -146 rmoveto
+          158 860 rlineto
+          -30 4 rlineto
+          -158 -860 rlineto
+          30 -4 rlineto
+          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>
+
+  <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>
+
+  <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="dollar" out="dollar.a"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="84"/>
+    <mtx name="A" width="600" lsb="50"/>
+    <mtx name="T" width="600" lsb="50"/>
+    <mtx name="dollar" width="490" lsb="53"/>
+    <mtx name="dollar.a" width="600" lsb="102"/>
+  </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/varLib/data/master_cff2/TestCFF2_Regular.ttx b/Tests/varLib/data/master_cff2/TestCFF2_Regular.ttx
new file mode 100644
index 0000000..471eb24
--- /dev/null
+++ b/Tests/varLib/data/master_cff2/TestCFF2_Regular.ttx
@@ -0,0 +1,516 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <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="T"/>
+    <GlyphID id="3" name="dollar.a"/>
+    <GlyphID id="4" name="dollar"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0x60d07155"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Nov 29 14:52:09 2018"/>
+    <modified value="Thu Nov 29 14:52:09 2018"/>
+    <xMin value="31"/>
+    <yMin value="-115"/>
+    <xMax value="569"/>
+    <yMax value="751"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <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="600"/>
+    <minLeftSideBearing value="31"/>
+    <minRightSideBearing value="31"/>
+    <xMaxExtent value="569"/>
+    <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>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="5"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="579"/>
+    <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="291"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="5"/>
+      <bProportion value="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </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="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="36"/>
+    <usLastCharIndex value="84"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="486"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </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.010;ADBO;SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Roman
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Roman
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x24" name="dollar"/>
+      <map code="0x41" name="A"/>
+      <map code="0x54" name="T"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="SourceCodeVariable-Roman">
+      <version value="1.0"/>
+      <Notice value="Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries."/>
+      <Copyright value="Copyright 2010 - 2012 Adobe Systems Incorporated. All Rights Reserved."/>
+      <FamilyName value="Source Code"/>
+      <isFixedPitch value="1"/>
+      <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="31 -115 569 751"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-12 0 486 498 574 586 638 650 656 668 712 724"/>
+        <OtherBlues value="-217 -205"/>
+        <BlueScale value="0.0625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="0"/>
+        <StdHW value="67"/>
+        <StdVW value="85"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="600"/>
+        <nominalWidthX value="604"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          62 0 rmoveto
+          476 0 rlineto
+          0 660 rlineto
+          -476 0 rlineto
+          0 -660 rlineto
+          109 59 rmoveto
+          73 131 rlineto
+          54 102 rlineto
+          4 0 rlineto
+          52 -102 rlineto
+          73 -131 rlineto
+          -256 0 rlineto
+          -44 52 rmoveto
+          0 461 rlineto
+          127 -232 rlineto
+          -127 -229 rlineto
+          171 277 rmoveto
+          -50 93 rlineto
+          -66 119 rlineto
+          234 0 rlineto
+          -65 -119 rlineto
+          -49 -93 rlineto
+          -4 0 rlineto
+          48 -48 rmoveto
+          126 232 rlineto
+          0 -461 rlineto
+          -126 229 rlineto
+          endchar
+        </CharString>
+        <CharString name="A">
+          31 0 rmoveto
+          86 0 rlineto
+          115 366 rlineto
+          23 73 21 72 21 76 rrcurveto
+          4 0 rlineto
+          20 -76 22 -72 23 -73 rrcurveto
+          113 -366 rlineto
+          90 0 rlineto
+          -221 656 rlineto
+          -96 0 rlineto
+          -221 -656 rlineto
+          117 199 rmoveto
+          301 0 rlineto
+          0 68 rlineto
+          -301 0 rlineto
+          0 -68 rlineto
+          endchar
+        </CharString>
+        <CharString name="T">
+          258 0 rmoveto
+          84 0 rlineto
+          0 585 rlineto
+          217 0 rlineto
+          0 71 rlineto
+          -518 0 rlineto
+          0 -71 rlineto
+          217 0 rlineto
+          0 -585 rlineto
+          endchar
+        </CharString>
+        <CharString name="dollar">
+          -107 248 35 rmoveto
+          -39 0 -45 5 -46 18 rrcurveto
+          53 -36 rlineto
+          -17 76 rlineto
+          -12 53 -22 13 -24 0 rrcurveto
+          -22 0 -14 -11 -9 -20 rrcurveto
+          4 -87 81 -59 107 0 rrcurveto
+          136 0 82 76 0 107 rrcurveto
+          0 82 -41 65 -135 47 rrcurveto
+          -38 13 rlineto
+          -71 23 -40 35 0 64 rrcurveto
+          0 75 57 37 74 0 rrcurveto
+          30 0 36 -5 42 -17 rrcurveto
+          -52 36 rlineto
+          17 -76 rlineto
+          12 -52 25 -14 22 0 rrcurveto
+          19 0 17 10 8 21 rrcurveto
+          -6 86 -80 60 -101 0 rrcurveto
+          -115 0 -83 -80 0 -102 rrcurveto
+          0 -100 62 -54 105 -37 rrcurveto
+          37 -13 rlineto
+          85 -30 36 -30 0 -63 rrcurveto
+          0 -74 -53 -42 -82 0 rrcurveto
+          31 287 rmoveto
+          0 428 rlineto
+          -40 0 rlineto
+          0 -428 rlineto
+          40 0 rlineto
+          -41 -437 rmoveto
+          40 0 rlineto
+          0 437 rlineto
+          -40 0 rlineto
+          0 -437 rlineto
+          endchar
+        </CharString>
+        <CharString name="dollar.a">
+          304 34 rmoveto
+          125 0 86 65 0 96 rrcurveto
+          0 183 -324 -21 0 110 rrcurveto
+          0 50 42 32 67 0 rrcurveto
+          68 0 36 -21 47 -36 rrcurveto
+          44 49 rlineto
+          -46 44 -54 33 -89 0 rrcurveto
+          -115 0 -81 -59 0 -94 rrcurveto
+          0 -174 324 22 0 -124 rrcurveto
+          0 -51 -42 -35 -78 0 rrcurveto
+          -76 0 -62 31 -52 37 rrcurveto
+          -39 -58 rlineto
+          52 -43 84 -36 83 0 rrcurveto
+          -51 -147 rmoveto
+          159 857 rlineto
+          -56 7 rlineto
+          -159 -858 rlineto
+          56 -6 rlineto
+          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>
+
+  <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>
+
+  <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="dollar" out="dollar.a"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="62"/>
+    <mtx name="A" width="600" lsb="31"/>
+    <mtx name="T" width="600" lsb="41"/>
+    <mtx name="dollar" width="497" lsb="51"/>
+    <mtx name="dollar.a" width="600" lsb="85"/>
+  </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/varLib/data/master_cff2_input/TestCFF2_Black.ttx b/Tests/varLib/data/master_cff2_input/TestCFF2_Black.ttx
new file mode 100644
index 0000000..3280eea
--- /dev/null
+++ b/Tests/varLib/data/master_cff2_input/TestCFF2_Black.ttx
@@ -0,0 +1,510 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.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="T"/>
+    <GlyphID id="3" name="dollar.a"/>
+    <GlyphID id="4" name="dollar"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0x26378952"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Nov 29 14:52:09 2018"/>
+    <modified value="Thu Nov 29 14:52:09 2018"/>
+    <xMin value="0"/>
+    <yMin value="-116"/>
+    <xMax value="600"/>
+    <yMax value="750"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <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="600"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="600"/>
+    <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>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="5"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="592"/>
+    <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="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </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="ADBO"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="36"/>
+    <usLastCharIndex value="84"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </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.010;ADBO;SourceCode_Black
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceCode_Black
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Roman Master 2
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCode_Black
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCode_Black
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Roman Master 2
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x24" name="dollar"/>
+      <map code="0x41" name="A"/>
+      <map code="0x54" name="T"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <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>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.a"/>
+    </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 value="-12 0 500 512 580 592 634 646 650 662 696 708"/>
+            <OtherBlues value="-188 -176"/>
+            <FamilyBlues value="-12 0 486 498 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"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+          24 0 rmoveto
+          552 0 rlineto
+          0 660 rlineto
+          -552 0 rlineto
+          0 -660 rlineto
+          212 104 rmoveto
+          26 56 rlineto
+          36 96 rlineto
+          4 0 rlineto
+          36 -96 rlineto
+          26 -56 rlineto
+          -128 0 rlineto
+          -100 68 rmoveto
+          0 336 rlineto
+          82 -168 rlineto
+          -82 -168 rlineto
+          162 252 rmoveto
+          -40 96 rlineto
+          -18 36 rlineto
+          120 0 rlineto
+          -18 -36 rlineto
+          -40 -96 rlineto
+          -4 0 rlineto
+          84 -84 rmoveto
+          82 168 rlineto
+          0 -336 rlineto
+          -82 168 rlineto
+        </CharString>
+        <CharString name="A">
+          0 0 rmoveto
+          176 0 rlineto
+          73 316 rlineto
+          14 62 17 78 14 66 rrcurveto
+          4 0 rlineto
+          14 -66 19 -78 14 -62 rrcurveto
+          73 -316 rlineto
+          182 0 rlineto
+          -196 650 rlineto
+          -208 0 rlineto
+          -196 -650 rlineto
+          141 138 rmoveto
+          316 0 rlineto
+          0 133 rlineto
+          -316 0 rlineto
+          0 -133 rlineto
+        </CharString>
+        <CharString name="T">
+          214 0 rmoveto
+          172 0 rlineto
+          0 506 rlineto
+          187 0 rlineto
+          0 144 rlineto
+          -546 0 rlineto
+          0 -144 rlineto
+          187 0 rlineto
+          0 -506 rlineto
+        </CharString>
+        <CharString name="dollar">
+          -107 260 39 rmoveto
+          -65 0 -28 11 -49 24 rrcurveto
+          89 -53 rlineto
+          -15 89 rlineto
+          -9 52 -22 18 -43 0 rrcurveto
+          -26 0 -27 -14 -14 -38 rrcurveto
+          0 -90 71 -54 139 0 rrcurveto
+          163 0 99 84 0 117 rrcurveto
+          0 98 -58 68 -142 45 rrcurveto
+          -33 10 rlineto
+          -72 22 -24 24 0 49 rrcurveto
+          0 61 47 23 67 0 rrcurveto
+          42 0 27 -4 52 -24 rrcurveto
+          -85 47 rlineto
+          10 -67 rlineto
+          11 -75 37 -14 39 0 rrcurveto
+          26 0 29 15 5 41 rrcurveto
+          -8 88 -76 48 -121 0 rrcurveto
+          -158 0 -85 -80 0 -115 rrcurveto
+          0 -93 66 -69 121 -39 rrcurveto
+          32 -11 rlineto
+          80 -28 23 -19 0 -53 rrcurveto
+          0 -55 -43 -39 -72 0 rrcurveto
+          64 275 rmoveto
+          0 417 rlineto
+          -71 0 rlineto
+          0 -417 rlineto
+          71 0 rlineto
+          -79 -429 rmoveto
+          71 0 rlineto
+          0 429 rlineto
+          -71 0 rlineto
+          0 -429 rlineto
+        </CharString>
+        <CharString name="dollar.a">
+          292 34 rmoveto
+          163 0 83 80 0 100 rrcurveto
+          0 182 -302 -4 0 56 rrcurveto
+          0 21 18 11 36 0 rrcurveto
+          55 0 39 -16 52 -32 rrcurveto
+          84 98 rlineto
+          -53 52 -69 36 -97 0 rrcurveto
+          -141 0 -88 -68 0 -104 rrcurveto
+          0 -188 302 12 0 -68 rrcurveto
+          0 -20 -19 -10 -37 0 rrcurveto
+          -61 0 -55 20 -72 40 rrcurveto
+          -74 -116 rlineto
+          65 -54 101 -28 70 0 rrcurveto
+          -19 -150 rmoveto
+          160 854 rlineto
+          -100 12 rlineto
+          -160 -854 rlineto
+          100 -12 rlineto
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+  <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>
+
+  <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>
+
+  <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="dollar" out="dollar.a"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="24"/>
+    <mtx name="A" width="600" lsb="0"/>
+    <mtx name="T" width="600" lsb="27"/>
+    <mtx name="dollar" width="560" lsb="51"/>
+    <mtx name="dollar.a" width="600" lsb="56"/>
+  </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/varLib/data/master_cff2_input/TestCFF2_ExtraLight.ttx b/Tests/varLib/data/master_cff2_input/TestCFF2_ExtraLight.ttx
new file mode 100644
index 0000000..fbcf91a
--- /dev/null
+++ b/Tests/varLib/data/master_cff2_input/TestCFF2_ExtraLight.ttx
@@ -0,0 +1,510 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.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="T"/>
+    <GlyphID id="3" name="dollar.a"/>
+    <GlyphID id="4" name="dollar"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0xeb345d38"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Nov 29 14:52:09 2018"/>
+    <modified value="Thu Nov 29 14:52:09 2018"/>
+    <xMin value="50"/>
+    <yMin value="-115"/>
+    <xMax value="550"/>
+    <yMax value="762"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <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="600"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <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="5"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="5"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="578"/>
+    <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="286"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="3"/>
+      <bProportion value="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </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="ADBO"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="36"/>
+    <usLastCharIndex value="84"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="478"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </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.010;ADBO;SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Roman Master 0
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Roman Master 0
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x24" name="dollar"/>
+      <map code="0x41" name="A"/>
+      <map code="0x54" name="T"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <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>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.a"/>
+    </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 value="-12 0 478 490 570 582 640 652 660 672 722 734"/>
+            <OtherBlues value="-234 -222"/>
+            <FamilyBlues value="-12 0 486 498 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="34"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+          84 0 rmoveto
+          432 0 rlineto
+          0 660 rlineto
+          -432 0 rlineto
+          0 -660 rlineto
+          48 32 rmoveto
+          102 176 rlineto
+          64 106 rlineto
+          4 0 rlineto
+          62 -106 rlineto
+          100 -176 rlineto
+          -332 0 rlineto
+          -10 42 rmoveto
+          0 536 rlineto
+          154 -270 rlineto
+          -154 -266 rlineto
+          176 292 rmoveto
+          -56 92 rlineto
+          -94 168 rlineto
+          302 0 rlineto
+          -94 -168 rlineto
+          -54 -92 rlineto
+          -4 0 rlineto
+          26 -26 rmoveto
+          152 270 rlineto
+          0 -536 rlineto
+          -152 266 rlineto
+        </CharString>
+        <CharString name="A">
+          50 0 rmoveto
+          32 0 rlineto
+          140 396 rlineto
+          28 80 24 68 24 82 rrcurveto
+          4 0 rlineto
+          24 -82 24 -68 28 -80 rrcurveto
+          138 -396 rlineto
+          34 0 rlineto
+          -236 660 rlineto
+          -28 0 rlineto
+          -236 -660 rlineto
+          102 236 rmoveto
+          293 0 rlineto
+          0 28 rlineto
+          -293 0 rlineto
+          0 -28 rlineto
+        </CharString>
+        <CharString name="T">
+          284 0 rmoveto
+          32 0 rlineto
+          0 632 rlineto
+          234 0 rlineto
+          0 28 rlineto
+          -500 0 rlineto
+          0 -28 rlineto
+          234 0 rlineto
+          0 -632 rlineto
+        </CharString>
+        <CharString name="dollar">
+          -107 245 7 rmoveto
+          -65 0 -39 15 -46 50 rrcurveto
+          36 -48 rlineto
+          -28 100 rlineto
+          -4 16 -12 4 -11 0 rrcurveto
+          -14 0 -8 -7 -1 -14 rrcurveto
+          24 -85 61 -51 107 0 rrcurveto
+          91 0 90 53 0 111 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 -92 -65 -54 -90 0 rrcurveto
+          18 327 rmoveto
+          0 428 rlineto
+          -22 0 rlineto
+          0 -428 rlineto
+          22 0 rlineto
+          -22 -449 rmoveto
+          22 0 rlineto
+          0 449 rlineto
+          -22 0 rlineto
+          0 -449 rlineto
+        </CharString>
+        <CharString name="dollar.a">
+          311 34 rmoveto
+          103 0 88 56 0 94 rrcurveto
+          0 184 -338 -32 0 142 rrcurveto
+          0 68 57 44 85 0 rrcurveto
+          76 0 34 -24 44 -38 rrcurveto
+          20 20 rlineto
+          -41 38 -45 32 -85 0 rrcurveto
+          -99 0 -78 -54 0 -88 rrcurveto
+          0 -166 338 28 0 -156 rrcurveto
+          0 -70 -56 -50 -103 0 rrcurveto
+          -85 0 -66 38 -40 34 rrcurveto
+          -18 -22 rlineto
+          45 -38 73 -40 91 0 rrcurveto
+          -70 -146 rmoveto
+          158 860 rlineto
+          -30 4 rlineto
+          -158 -860 rlineto
+          30 -4 rlineto
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+  <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>
+
+  <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>
+
+  <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="dollar" out="dollar.a"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="84"/>
+    <mtx name="A" width="600" lsb="50"/>
+    <mtx name="T" width="600" lsb="50"/>
+    <mtx name="dollar" width="490" lsb="53"/>
+    <mtx name="dollar.a" width="600" lsb="102"/>
+  </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/varLib/data/master_cff2_input/TestCFF2_Regular.ttx b/Tests/varLib/data/master_cff2_input/TestCFF2_Regular.ttx
new file mode 100644
index 0000000..757e5b8
--- /dev/null
+++ b/Tests/varLib/data/master_cff2_input/TestCFF2_Regular.ttx
@@ -0,0 +1,508 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.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="T"/>
+    <GlyphID id="3" name="dollar.a"/>
+    <GlyphID id="4" name="dollar"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0x60d07155"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Nov 29 14:52:09 2018"/>
+    <modified value="Thu Nov 29 14:52:09 2018"/>
+    <xMin value="31"/>
+    <yMin value="-115"/>
+    <xMax value="569"/>
+    <yMax value="751"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <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="600"/>
+    <minLeftSideBearing value="31"/>
+    <minRightSideBearing value="31"/>
+    <xMaxExtent value="569"/>
+    <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>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="5"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="579"/>
+    <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="291"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="5"/>
+      <bProportion value="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </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="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="36"/>
+    <usLastCharIndex value="84"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="486"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </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.010;ADBO;SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Roman
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Roman
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x24" name="dollar"/>
+      <map code="0x41" name="A"/>
+      <map code="0x54" name="T"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <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>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dollar.a"/>
+    </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 value="-12 0 486 498 574 586 638 650 656 668 712 724"/>
+            <OtherBlues value="-217 -205"/>
+            <BlueScale value="0.0625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="67"/>
+            <StdVW value="85"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+          62 0 rmoveto
+          476 0 rlineto
+          0 660 rlineto
+          -476 0 rlineto
+          0 -660 rlineto
+          109 59 rmoveto
+          73 131 rlineto
+          54 102 rlineto
+          4 0 rlineto
+          52 -102 rlineto
+          73 -131 rlineto
+          -256 0 rlineto
+          -44 52 rmoveto
+          0 461 rlineto
+          127 -232 rlineto
+          -127 -229 rlineto
+          171 277 rmoveto
+          -50 93 rlineto
+          -66 119 rlineto
+          234 0 rlineto
+          -65 -119 rlineto
+          -49 -93 rlineto
+          -4 0 rlineto
+          48 -48 rmoveto
+          126 232 rlineto
+          0 -461 rlineto
+          -126 229 rlineto
+        </CharString>
+        <CharString name="A">
+          31 0 rmoveto
+          86 0 rlineto
+          115 366 rlineto
+          23 73 21 72 21 76 rrcurveto
+          4 0 rlineto
+          20 -76 22 -72 23 -73 rrcurveto
+          113 -366 rlineto
+          90 0 rlineto
+          -221 656 rlineto
+          -96 0 rlineto
+          -221 -656 rlineto
+          117 199 rmoveto
+          301 0 rlineto
+          0 68 rlineto
+          -301 0 rlineto
+          0 -68 rlineto
+        </CharString>
+        <CharString name="T">
+          258 0 rmoveto
+          84 0 rlineto
+          0 585 rlineto
+          217 0 rlineto
+          0 71 rlineto
+          -518 0 rlineto
+          0 -71 rlineto
+          217 0 rlineto
+          0 -585 rlineto
+        </CharString>
+        <CharString name="dollar">
+          -107 248 35 rmoveto
+          -39 0 -45 5 -46 18 rrcurveto
+          53 -36 rlineto
+          -17 76 rlineto
+          -12 53 -22 13 -24 0 rrcurveto
+          -22 0 -14 -11 -9 -20 rrcurveto
+          4 -87 81 -59 107 0 rrcurveto
+          136 0 82 76 0 107 rrcurveto
+          0 82 -41 65 -135 47 rrcurveto
+          -38 13 rlineto
+          -71 23 -40 35 0 64 rrcurveto
+          0 75 57 37 74 0 rrcurveto
+          30 0 36 -5 42 -17 rrcurveto
+          -52 36 rlineto
+          17 -76 rlineto
+          12 -52 25 -14 22 0 rrcurveto
+          19 0 17 10 8 21 rrcurveto
+          -6 86 -80 60 -101 0 rrcurveto
+          -115 0 -83 -80 0 -102 rrcurveto
+          0 -100 62 -54 105 -37 rrcurveto
+          37 -13 rlineto
+          85 -30 36 -30 0 -63 rrcurveto
+          0 -74 -53 -42 -82 0 rrcurveto
+          31 287 rmoveto
+          0 428 rlineto
+          -40 0 rlineto
+          0 -428 rlineto
+          40 0 rlineto
+          -41 -437 rmoveto
+          40 0 rlineto
+          0 437 rlineto
+          -40 0 rlineto
+          0 -437 rlineto
+        </CharString>
+        <CharString name="dollar.a">
+          304 34 rmoveto
+          125 0 86 65 0 96 rrcurveto
+          0 183 -324 -21 0 110 rrcurveto
+          0 50 42 32 67 0 rrcurveto
+          68 0 36 -21 47 -36 rrcurveto
+          44 49 rlineto
+          -46 44 -54 33 -89 0 rrcurveto
+          -115 0 -81 -59 0 -94 rrcurveto
+          0 -174 324 22 0 -124 rrcurveto
+          0 -51 -42 -35 -78 0 rrcurveto
+          -76 0 -62 31 -52 37 rrcurveto
+          -39 -58 rlineto
+          52 -43 84 -36 83 0 rrcurveto
+          -51 -147 rmoveto
+          159 857 rlineto
+          -56 7 rlineto
+          -159 -858 rlineto
+          56 -6 rlineto
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+  <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>
+
+  <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>
+
+  <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="dollar" out="dollar.a"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="62"/>
+    <mtx name="A" width="600" lsb="31"/>
+    <mtx name="T" width="600" lsb="41"/>
+    <mtx name="dollar" width="497" lsb="51"/>
+    <mtx name="dollar.a" width="600" lsb="85"/>
+  </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/varLib/data/master_incompatible_arrays/IncompatibleArrays-Bold.ttx b/Tests/varLib/data/master_incompatible_arrays/IncompatibleArrays-Bold.ttx
new file mode 100644
index 0000000..c47ecba
--- /dev/null
+++ b/Tests/varLib/data/master_incompatible_arrays/IncompatibleArrays-Bold.ttx
@@ -0,0 +1,612 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.20">
+
+  <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="Aacute"/>
+    <GlyphID id="3" name="O"/>
+    <GlyphID id="4" name="V"/>
+    <GlyphID id="5" name="space"/>
+    <GlyphID id="6" name="dollar"/>
+    <GlyphID id="7" name="dollar.bold"/>
+    <GlyphID id="8" name="acutecomb"/>
+    <GlyphID id="9" name="dollar.BRACKET.500"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x10cb3f3"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Jan 15 14:37:13 2021"/>
+    <modified value="Mon Mar 15 12:57:03 2021"/>
+    <xMin value="-141"/>
+    <yMin value="-200"/>
+    <xMax value="906"/>
+    <yMax value="949"/>
+    <macStyle value="00000000 00000001"/>
+    <lowestRecPPEM value="6"/>
+    <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="911"/>
+    <minLeftSideBearing value="-141"/>
+    <minRightSideBearing value="-125"/>
+    <xMaxExtent value="906"/>
+    <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="10"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="10"/>
+    <maxPoints value="52"/>
+    <maxContours value="3"/>
+    <maxCompositePoints value="16"/>
+    <maxCompositeContours value="4"/>
+    <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="672"/>
+    <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="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 01000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 00100000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="769"/>
+    <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="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="A" width="911" lsb="5"/>
+    <mtx name="Aacute" width="911" lsb="5"/>
+    <mtx name="O" width="715" lsb="15"/>
+    <mtx name="V" width="911" lsb="5"/>
+    <mtx name="acutecomb" width="0" lsb="-141"/>
+    <mtx name="dollar" width="600" lsb="1"/>
+    <mtx name="dollar.BRACKET.500" width="600" lsb="1"/>
+    <mtx name="dollar.bold" width="600" lsb="1"/>
+    <mtx name="space" width="300" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0xc1" name="Aacute"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0x301" name="acutecomb"/><!-- COMBINING ACUTE ACCENT -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0xc1" name="Aacute"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0x301" name="acutecomb"/><!-- COMBINING ACUTE ACCENT -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="5" yMin="0" xMax="906" yMax="700">
+      <contour>
+        <pt x="705" y="0" on="1"/>
+        <pt x="906" y="0" on="1"/>
+        <pt x="556" y="700" on="1"/>
+        <pt x="355" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="5" y="0" on="1"/>
+        <pt x="206" y="0" on="1"/>
+        <pt x="556" y="700" on="1"/>
+        <pt x="355" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="640" y="311" on="1"/>
+        <pt x="190" y="311" on="1"/>
+        <pt x="190" y="191" on="1"/>
+        <pt x="640" y="191" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Aacute" xMin="5" yMin="0" xMax="906" yMax="949">
+      <component glyphName="A" x="0" y="0" flags="0x204"/>
+      <component glyphName="acutecomb" x="479" y="124" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="O" xMin="15" yMin="-10" xMax="670" yMax="710">
+      <contour>
+        <pt x="342" y="-10" on="1"/>
+        <pt x="172" y="-10" on="0"/>
+        <pt x="15" y="163" on="0"/>
+        <pt x="15" y="350" on="1"/>
+        <pt x="15" y="538" on="0"/>
+        <pt x="172" y="710" on="0"/>
+        <pt x="342" y="710" on="1"/>
+        <pt x="513" y="710" on="0"/>
+        <pt x="670" y="538" on="0"/>
+        <pt x="670" y="350" on="1"/>
+        <pt x="670" y="163" on="0"/>
+        <pt x="513" y="-10" on="0"/>
+      </contour>
+      <contour>
+        <pt x="342" y="153" on="1"/>
+        <pt x="419" y="153" on="0"/>
+        <pt x="490" y="247" on="0"/>
+        <pt x="490" y="350" on="1"/>
+        <pt x="490" y="453" on="0"/>
+        <pt x="419" y="547" on="0"/>
+        <pt x="342" y="547" on="1"/>
+        <pt x="266" y="547" on="0"/>
+        <pt x="195" y="453" on="0"/>
+        <pt x="195" y="350" on="1"/>
+        <pt x="195" y="247" on="0"/>
+        <pt x="266" y="153" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="V" xMin="5" yMin="0" xMax="906" yMax="700">
+      <contour>
+        <pt x="355" y="0" on="1"/>
+        <pt x="705" y="700" on="1"/>
+        <pt x="906" y="700" on="1"/>
+        <pt x="556" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="355" y="0" on="1"/>
+        <pt x="5" y="700" on="1"/>
+        <pt x="206" y="700" on="1"/>
+        <pt x="556" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="acutecomb" xMin="-141" yMin="630" xMax="125" yMax="825">
+      <contour>
+        <pt x="-118" y="756" on="1"/>
+        <pt x="-141" y="630" on="1"/>
+        <pt x="102" y="699" on="1"/>
+        <pt x="125" y="825" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar" xMin="1" yMin="-98" xMax="595" yMax="789">
+      <contour>
+        <pt x="249" y="789" on="1"/>
+        <pt x="369" y="789" on="1"/>
+        <pt x="369" y="743" on="1"/>
+        <pt x="427" y="735" on="0"/>
+        <pt x="537" y="681" on="0"/>
+        <pt x="590" y="623" on="1"/>
+        <pt x="510" y="515" on="1"/>
+        <pt x="479" y="549" on="0"/>
+        <pt x="411" y="588" on="0"/>
+        <pt x="369" y="595" on="1"/>
+        <pt x="369" y="400" on="1"/>
+        <pt x="476" y="378" on="0"/>
+        <pt x="595" y="278" on="0"/>
+        <pt x="595" y="184" on="1"/>
+        <pt x="595" y="93" on="0"/>
+        <pt x="474" y="-32" on="0"/>
+        <pt x="369" y="-46" on="1"/>
+        <pt x="369" y="-98" on="1"/>
+        <pt x="249" y="-98" on="1"/>
+        <pt x="249" y="-47" on="1"/>
+        <pt x="176" y="-39" on="0"/>
+        <pt x="52" y="17" on="0"/>
+        <pt x="1" y="69" on="1"/>
+        <pt x="80" y="179" on="1"/>
+        <pt x="118" y="144" on="0"/>
+        <pt x="195" y="106" on="0"/>
+        <pt x="249" y="100" on="1"/>
+        <pt x="249" y="273" on="1"/>
+        <pt x="246" y="274" on="1"/>
+        <pt x="144" y="294" on="0"/>
+        <pt x="28" y="405" on="0"/>
+        <pt x="28" y="502" on="1"/>
+        <pt x="28" y="567" on="0"/>
+        <pt x="84" y="667" on="0"/>
+        <pt x="184" y="732" on="0"/>
+        <pt x="249" y="742" on="1"/>
+      </contour>
+      <contour>
+        <pt x="152" y="502" on="1"/>
+        <pt x="152" y="480" on="0"/>
+        <pt x="166" y="453" on="0"/>
+        <pt x="208" y="434" on="0"/>
+        <pt x="249" y="424" on="1"/>
+        <pt x="249" y="595" on="1"/>
+        <pt x="199" y="587" on="0"/>
+        <pt x="152" y="538" on="0"/>
+      </contour>
+      <contour>
+        <pt x="369" y="100" on="1"/>
+        <pt x="426" y="107" on="0"/>
+        <pt x="471" y="150" on="0"/>
+        <pt x="471" y="183" on="1"/>
+        <pt x="471" y="201" on="0"/>
+        <pt x="456" y="225" on="0"/>
+        <pt x="412" y="243" on="0"/>
+        <pt x="369" y="252" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar.BRACKET.500" xMin="1" yMin="-98" xMax="595" yMax="789">
+      <contour>
+        <pt x="249" y="789" on="1"/>
+        <pt x="369" y="789" on="1"/>
+        <pt x="369" y="743" on="1"/>
+        <pt x="427" y="735" on="0"/>
+        <pt x="537" y="681" on="0"/>
+        <pt x="590" y="623" on="1"/>
+        <pt x="510" y="515" on="1"/>
+        <pt x="468" y="560" on="0"/>
+        <pt x="374" y="600" on="0"/>
+        <pt x="308" y="600" on="1"/>
+        <pt x="227" y="600" on="0"/>
+        <pt x="152" y="548" on="0"/>
+        <pt x="152" y="502" on="1"/>
+        <pt x="152" y="479" on="0"/>
+        <pt x="168" y="450" on="0"/>
+        <pt x="217" y="431" on="0"/>
+        <pt x="264" y="421" on="1"/>
+        <pt x="363" y="401" on="1"/>
+        <pt x="473" y="379" on="0"/>
+        <pt x="595" y="279" on="0"/>
+        <pt x="595" y="184" on="1"/>
+        <pt x="595" y="93" on="0"/>
+        <pt x="474" y="-32" on="0"/>
+        <pt x="369" y="-46" on="1"/>
+        <pt x="369" y="-98" on="1"/>
+        <pt x="249" y="-98" on="1"/>
+        <pt x="249" y="-47" on="1"/>
+        <pt x="176" y="-39" on="0"/>
+        <pt x="52" y="17" on="0"/>
+        <pt x="1" y="69" on="1"/>
+        <pt x="80" y="179" on="1"/>
+        <pt x="112" y="150" on="0"/>
+        <pt x="176" y="114" on="0"/>
+        <pt x="256" y="97" on="0"/>
+        <pt x="310" y="97" on="1"/>
+        <pt x="402" y="97" on="0"/>
+        <pt x="471" y="143" on="0"/>
+        <pt x="471" y="183" on="1"/>
+        <pt x="471" y="203" on="0"/>
+        <pt x="453" y="228" on="0"/>
+        <pt x="399" y="247" on="0"/>
+        <pt x="345" y="256" on="1"/>
+        <pt x="246" y="274" on="1"/>
+        <pt x="144" y="293" on="0"/>
+        <pt x="28" y="405" on="0"/>
+        <pt x="28" y="502" on="1"/>
+        <pt x="28" y="567" on="0"/>
+        <pt x="84" y="667" on="0"/>
+        <pt x="184" y="732" on="0"/>
+        <pt x="249" y="742" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar.bold" xMin="1" yMin="-98" xMax="595" yMax="789">
+      <contour>
+        <pt x="249" y="789" on="1"/>
+        <pt x="369" y="789" on="1"/>
+        <pt x="369" y="743" on="1"/>
+        <pt x="427" y="735" on="0"/>
+        <pt x="537" y="681" on="0"/>
+        <pt x="590" y="623" on="1"/>
+        <pt x="510" y="515" on="1"/>
+        <pt x="468" y="560" on="0"/>
+        <pt x="374" y="600" on="0"/>
+        <pt x="308" y="600" on="1"/>
+        <pt x="227" y="600" on="0"/>
+        <pt x="152" y="548" on="0"/>
+        <pt x="152" y="502" on="1"/>
+        <pt x="152" y="479" on="0"/>
+        <pt x="168" y="450" on="0"/>
+        <pt x="217" y="431" on="0"/>
+        <pt x="264" y="421" on="1"/>
+        <pt x="363" y="401" on="1"/>
+        <pt x="473" y="379" on="0"/>
+        <pt x="595" y="279" on="0"/>
+        <pt x="595" y="184" on="1"/>
+        <pt x="595" y="93" on="0"/>
+        <pt x="474" y="-32" on="0"/>
+        <pt x="369" y="-46" on="1"/>
+        <pt x="369" y="-98" on="1"/>
+        <pt x="249" y="-98" on="1"/>
+        <pt x="249" y="-47" on="1"/>
+        <pt x="176" y="-39" on="0"/>
+        <pt x="52" y="17" on="0"/>
+        <pt x="1" y="69" on="1"/>
+        <pt x="80" y="179" on="1"/>
+        <pt x="112" y="150" on="0"/>
+        <pt x="176" y="114" on="0"/>
+        <pt x="256" y="97" on="0"/>
+        <pt x="310" y="97" on="1"/>
+        <pt x="402" y="97" on="0"/>
+        <pt x="471" y="143" on="0"/>
+        <pt x="471" y="183" on="1"/>
+        <pt x="471" y="203" on="0"/>
+        <pt x="453" y="228" on="0"/>
+        <pt x="399" y="247" on="0"/>
+        <pt x="345" y="256" on="1"/>
+        <pt x="246" y="274" on="1"/>
+        <pt x="144" y="293" on="0"/>
+        <pt x="28" y="405" on="0"/>
+        <pt x="28" y="502" on="1"/>
+        <pt x="28" y="567" on="0"/>
+        <pt x="84" y="667" on="0"/>
+        <pt x="184" y="732" on="0"/>
+        <pt x="249" y="742" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Simple Two Axis
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;SimpleTwoAxis-Bold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Simple Two Axis Bold
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SimpleTwoAxis-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 -->
+      <psName name="dollar.bold"/>
+      <psName name="acutecomb"/>
+      <psName name="dollar.BRACKET.500"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Aacute" class="1"/>
+      <ClassDef glyph="acutecomb" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+    </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="mark"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="Aacute"/>
+            <Glyph value="V"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=3 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="2">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="O"/>
+              <Value1 XAdvance="-20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="acutecomb"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="A"/>
+            <Glyph value="Aacute"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="4"/>
+                <YCoordinate value="623"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=2 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="406"/>
+                <YCoordinate value="753"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="1">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="406"/>
+                <YCoordinate value="753"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_incompatible_arrays/IncompatibleArrays-Regular.ttx b/Tests/varLib/data/master_incompatible_arrays/IncompatibleArrays-Regular.ttx
new file mode 100644
index 0000000..cabb69a
--- /dev/null
+++ b/Tests/varLib/data/master_incompatible_arrays/IncompatibleArrays-Regular.ttx
@@ -0,0 +1,626 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.20">
+
+  <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="Aacute"/>
+    <GlyphID id="3" name="O"/>
+    <GlyphID id="4" name="V"/>
+    <GlyphID id="5" name="space"/>
+    <GlyphID id="6" name="dollar"/>
+    <GlyphID id="7" name="dollar.bold"/>
+    <GlyphID id="8" name="acutecomb"/>
+    <GlyphID id="9" name="dollar.BRACKET.500"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x3c7bc79b"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Jan 15 14:37:13 2021"/>
+    <modified value="Mon Mar 15 12:57:03 2021"/>
+    <xMin value="-141"/>
+    <yMin value="-200"/>
+    <xMax value="751"/>
+    <yMax value="915"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="756"/>
+    <minLeftSideBearing value="-141"/>
+    <minRightSideBearing value="-125"/>
+    <xMaxExtent value="751"/>
+    <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="10"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="10"/>
+    <maxPoints value="52"/>
+    <maxContours value="3"/>
+    <maxCompositePoints value="16"/>
+    <maxCompositeContours value="4"/>
+    <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="604"/>
+    <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="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 01000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="769"/>
+    <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="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="A" width="756" lsb="5"/>
+    <mtx name="Aacute" width="756" lsb="5"/>
+    <mtx name="O" width="664" lsb="30"/>
+    <mtx name="V" width="756" lsb="5"/>
+    <mtx name="acutecomb" width="0" lsb="-141"/>
+    <mtx name="dollar" width="600" lsb="29"/>
+    <mtx name="dollar.BRACKET.500" width="600" lsb="29"/>
+    <mtx name="dollar.bold" width="600" lsb="29"/>
+    <mtx name="space" width="200" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0xc1" name="Aacute"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0x301" name="acutecomb"/><!-- COMBINING ACUTE ACCENT -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0xc1" name="Aacute"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0x301" name="acutecomb"/><!-- COMBINING ACUTE ACCENT -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="5" yMin="0" xMax="751" yMax="700">
+      <contour>
+        <pt x="641" y="0" on="1"/>
+        <pt x="751" y="0" on="1"/>
+        <pt x="433" y="700" on="1"/>
+        <pt x="323" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="5" y="0" on="1"/>
+        <pt x="115" y="0" on="1"/>
+        <pt x="433" y="700" on="1"/>
+        <pt x="323" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="567" y="284" on="1"/>
+        <pt x="152" y="284" on="1"/>
+        <pt x="152" y="204" on="1"/>
+        <pt x="567" y="204" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Aacute" xMin="5" yMin="0" xMax="751" yMax="915">
+      <component glyphName="A" x="0" y="0" flags="0x204"/>
+      <component glyphName="acutecomb" x="402" y="130" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="O" xMin="30" yMin="-10" xMax="634" yMax="710">
+      <contour>
+        <pt x="332" y="-10" on="1"/>
+        <pt x="181" y="-10" on="0"/>
+        <pt x="30" y="169" on="0"/>
+        <pt x="30" y="350" on="1"/>
+        <pt x="30" y="531" on="0"/>
+        <pt x="181" y="710" on="0"/>
+        <pt x="332" y="710" on="1"/>
+        <pt x="484" y="710" on="0"/>
+        <pt x="634" y="531" on="0"/>
+        <pt x="634" y="350" on="1"/>
+        <pt x="634" y="169" on="0"/>
+        <pt x="484" y="-10" on="0"/>
+      </contour>
+      <contour>
+        <pt x="332" y="74" on="1"/>
+        <pt x="438" y="74" on="0"/>
+        <pt x="544" y="212" on="0"/>
+        <pt x="544" y="350" on="1"/>
+        <pt x="544" y="488" on="0"/>
+        <pt x="438" y="626" on="0"/>
+        <pt x="332" y="626" on="1"/>
+        <pt x="226" y="626" on="0"/>
+        <pt x="120" y="488" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="212" on="0"/>
+        <pt x="226" y="74" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="V" xMin="5" yMin="0" xMax="751" yMax="700">
+      <contour>
+        <pt x="323" y="0" on="1"/>
+        <pt x="641" y="700" on="1"/>
+        <pt x="751" y="700" on="1"/>
+        <pt x="433" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="323" y="0" on="1"/>
+        <pt x="5" y="700" on="1"/>
+        <pt x="115" y="700" on="1"/>
+        <pt x="433" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="acutecomb" xMin="-141" yMin="630" xMax="125" yMax="785">
+      <contour>
+        <pt x="-118" y="716" on="1"/>
+        <pt x="-141" y="630" on="1"/>
+        <pt x="102" y="699" on="1"/>
+        <pt x="125" y="785" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar" xMin="29" yMin="-68" xMax="580" yMax="759">
+      <contour>
+        <pt x="264" y="759" on="1"/>
+        <pt x="354" y="759" on="1"/>
+        <pt x="354" y="715" on="1"/>
+        <pt x="415" y="709" on="0"/>
+        <pt x="519" y="662" on="0"/>
+        <pt x="562" y="620" on="1"/>
+        <pt x="509" y="548" on="1"/>
+        <pt x="473" y="584" on="0"/>
+        <pt x="398" y="621" on="0"/>
+        <pt x="354" y="627" on="1"/>
+        <pt x="354" y="373" on="1"/>
+        <pt x="467" y="351" on="0"/>
+        <pt x="580" y="263" on="0"/>
+        <pt x="580" y="184" on="1"/>
+        <pt x="580" y="102" on="0"/>
+        <pt x="459" y="-8" on="0"/>
+        <pt x="354" y="-18" on="1"/>
+        <pt x="354" y="-68" on="1"/>
+        <pt x="264" y="-68" on="1"/>
+        <pt x="264" y="-18" on="1"/>
+        <pt x="192" y="-12" on="0"/>
+        <pt x="72" y="34" on="0"/>
+        <pt x="29" y="74" on="1"/>
+        <pt x="81" y="146" on="1"/>
+        <pt x="123" y="110" on="0"/>
+        <pt x="207" y="73" on="0"/>
+        <pt x="264" y="69" on="1"/>
+        <pt x="264" y="301" on="1"/>
+        <pt x="249" y="304" on="1"/>
+        <pt x="148" y="323" on="0"/>
+        <pt x="43" y="420" on="0"/>
+        <pt x="43" y="502" on="1"/>
+        <pt x="43" y="559" on="0"/>
+        <pt x="99" y="650" on="0"/>
+        <pt x="199" y="707" on="0"/>
+        <pt x="264" y="715" on="1"/>
+      </contour>
+      <contour>
+        <pt x="137" y="502" on="1"/>
+        <pt x="137" y="470" on="0"/>
+        <pt x="160" y="428" on="0"/>
+        <pt x="214" y="402" on="0"/>
+        <pt x="261" y="392" on="1"/>
+        <pt x="264" y="627" on="1"/>
+        <pt x="203" y="618" on="0"/>
+        <pt x="137" y="553" on="0"/>
+      </contour>
+      <contour>
+        <pt x="354" y="69" on="1"/>
+        <pt x="423" y="76" on="0"/>
+        <pt x="486" y="135" on="0"/>
+        <pt x="486" y="183" on="1"/>
+        <pt x="486" y="211" on="0"/>
+        <pt x="462" y="250" on="0"/>
+        <pt x="405" y="275" on="0"/>
+        <pt x="354" y="285" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar.BRACKET.500" xMin="29" yMin="-76" xMax="580" yMax="759">
+      <contour>
+        <pt x="264" y="759" on="1"/>
+        <pt x="354" y="759" on="1"/>
+        <pt x="354" y="715" on="1"/>
+        <pt x="415" y="709" on="0"/>
+        <pt x="519" y="662" on="0"/>
+        <pt x="562" y="620" on="1"/>
+        <pt x="509" y="548" on="1"/>
+        <pt x="464" y="592" on="0"/>
+        <pt x="370" y="630" on="0"/>
+        <pt x="308" y="630" on="1"/>
+        <pt x="226" y="630" on="0"/>
+        <pt x="137" y="562" on="0"/>
+        <pt x="137" y="502" on="1"/>
+        <pt x="137" y="470" on="0"/>
+        <pt x="160" y="428" on="0"/>
+        <pt x="214" y="402" on="0"/>
+        <pt x="261" y="392" on="1"/>
+        <pt x="360" y="372" on="1"/>
+        <pt x="469" y="350" on="0"/>
+        <pt x="580" y="263" on="0"/>
+        <pt x="580" y="184" on="1"/>
+        <pt x="580" y="102" on="0"/>
+        <pt x="459" y="-8" on="0"/>
+        <pt x="354" y="-18" on="1"/>
+        <pt x="354" y="-76" on="1"/>
+        <pt x="264" y="-76" on="1"/>
+        <pt x="264" y="-18" on="1"/>
+        <pt x="192" y="-12" on="0"/>
+        <pt x="72" y="34" on="0"/>
+        <pt x="29" y="74" on="1"/>
+        <pt x="81" y="146" on="1"/>
+        <pt x="115" y="118" on="0"/>
+        <pt x="180" y="83" on="0"/>
+        <pt x="259" y="67" on="0"/>
+        <pt x="310" y="67" on="1"/>
+        <pt x="403" y="67" on="0"/>
+        <pt x="486" y="128" on="0"/>
+        <pt x="486" y="183" on="1"/>
+        <pt x="486" y="212" on="0"/>
+        <pt x="461" y="251" on="0"/>
+        <pt x="401" y="277" on="0"/>
+        <pt x="348" y="286" on="1"/>
+        <pt x="249" y="304" on="1"/>
+        <pt x="148" y="323" on="0"/>
+        <pt x="43" y="420" on="0"/>
+        <pt x="43" y="502" on="1"/>
+        <pt x="43" y="559" on="0"/>
+        <pt x="99" y="650" on="0"/>
+        <pt x="199" y="707" on="0"/>
+        <pt x="264" y="715" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar.bold" xMin="29" yMin="-76" xMax="580" yMax="759">
+      <contour>
+        <pt x="264" y="759" on="1"/>
+        <pt x="354" y="759" on="1"/>
+        <pt x="354" y="715" on="1"/>
+        <pt x="415" y="709" on="0"/>
+        <pt x="519" y="662" on="0"/>
+        <pt x="562" y="620" on="1"/>
+        <pt x="509" y="548" on="1"/>
+        <pt x="464" y="592" on="0"/>
+        <pt x="370" y="630" on="0"/>
+        <pt x="308" y="630" on="1"/>
+        <pt x="226" y="630" on="0"/>
+        <pt x="137" y="562" on="0"/>
+        <pt x="137" y="502" on="1"/>
+        <pt x="137" y="470" on="0"/>
+        <pt x="160" y="428" on="0"/>
+        <pt x="214" y="402" on="0"/>
+        <pt x="261" y="392" on="1"/>
+        <pt x="360" y="372" on="1"/>
+        <pt x="469" y="350" on="0"/>
+        <pt x="580" y="263" on="0"/>
+        <pt x="580" y="184" on="1"/>
+        <pt x="580" y="102" on="0"/>
+        <pt x="459" y="-8" on="0"/>
+        <pt x="354" y="-18" on="1"/>
+        <pt x="354" y="-76" on="1"/>
+        <pt x="264" y="-76" on="1"/>
+        <pt x="264" y="-18" on="1"/>
+        <pt x="192" y="-12" on="0"/>
+        <pt x="72" y="34" on="0"/>
+        <pt x="29" y="74" on="1"/>
+        <pt x="81" y="146" on="1"/>
+        <pt x="115" y="118" on="0"/>
+        <pt x="180" y="83" on="0"/>
+        <pt x="259" y="67" on="0"/>
+        <pt x="310" y="67" on="1"/>
+        <pt x="403" y="67" on="0"/>
+        <pt x="486" y="128" on="0"/>
+        <pt x="486" y="183" on="1"/>
+        <pt x="486" y="212" on="0"/>
+        <pt x="461" y="251" on="0"/>
+        <pt x="401" y="277" on="0"/>
+        <pt x="348" y="286" on="1"/>
+        <pt x="249" y="304" on="1"/>
+        <pt x="148" y="323" on="0"/>
+        <pt x="43" y="420" on="0"/>
+        <pt x="43" y="502" on="1"/>
+        <pt x="43" y="559" on="0"/>
+        <pt x="99" y="650" on="0"/>
+        <pt x="199" y="707" on="0"/>
+        <pt x="264" y="715" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Simple Two Axis
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;SimpleTwoAxis-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Simple Two Axis Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SimpleTwoAxis-Regular
+    </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 -->
+      <psName name="dollar.bold"/>
+      <psName name="acutecomb"/>
+      <psName name="dollar.BRACKET.500"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Aacute" class="1"/>
+      <ClassDef glyph="acutecomb" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <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="mark"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="Aacute"/>
+            <Glyph value="V"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=3 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="2">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="O"/>
+              <Value1 XAdvance="-20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="acutecomb"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="A"/>
+            <Glyph value="Aacute"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="4"/>
+                <YCoordinate value="623"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=2 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="406"/>
+                <YCoordinate value="753"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="1">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="406"/>
+                <YCoordinate value="753"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_incompatible_features/IncompatibleFeatures-Bold.ttx b/Tests/varLib/data/master_incompatible_features/IncompatibleFeatures-Bold.ttx
new file mode 100644
index 0000000..18aee9f
--- /dev/null
+++ b/Tests/varLib/data/master_incompatible_features/IncompatibleFeatures-Bold.ttx
@@ -0,0 +1,578 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.20">
+
+  <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="Aacute"/>
+    <GlyphID id="3" name="O"/>
+    <GlyphID id="4" name="V"/>
+    <GlyphID id="5" name="space"/>
+    <GlyphID id="6" name="dollar"/>
+    <GlyphID id="7" name="dollar.bold"/>
+    <GlyphID id="8" name="acutecomb"/>
+    <GlyphID id="9" name="dollar.BRACKET.500"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x10cb3f3"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Jan 15 14:37:13 2021"/>
+    <modified value="Mon Mar 15 12:57:03 2021"/>
+    <xMin value="-141"/>
+    <yMin value="-200"/>
+    <xMax value="906"/>
+    <yMax value="949"/>
+    <macStyle value="00000000 00000001"/>
+    <lowestRecPPEM value="6"/>
+    <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="911"/>
+    <minLeftSideBearing value="-141"/>
+    <minRightSideBearing value="-125"/>
+    <xMaxExtent value="906"/>
+    <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="10"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="10"/>
+    <maxPoints value="52"/>
+    <maxContours value="3"/>
+    <maxCompositePoints value="16"/>
+    <maxCompositeContours value="4"/>
+    <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="672"/>
+    <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="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 01000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 00100000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="769"/>
+    <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="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="A" width="911" lsb="5"/>
+    <mtx name="Aacute" width="911" lsb="5"/>
+    <mtx name="O" width="715" lsb="15"/>
+    <mtx name="V" width="911" lsb="5"/>
+    <mtx name="acutecomb" width="0" lsb="-141"/>
+    <mtx name="dollar" width="600" lsb="1"/>
+    <mtx name="dollar.BRACKET.500" width="600" lsb="1"/>
+    <mtx name="dollar.bold" width="600" lsb="1"/>
+    <mtx name="space" width="300" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0xc1" name="Aacute"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0x301" name="acutecomb"/><!-- COMBINING ACUTE ACCENT -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0xc1" name="Aacute"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0x301" name="acutecomb"/><!-- COMBINING ACUTE ACCENT -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="5" yMin="0" xMax="906" yMax="700">
+      <contour>
+        <pt x="705" y="0" on="1"/>
+        <pt x="906" y="0" on="1"/>
+        <pt x="556" y="700" on="1"/>
+        <pt x="355" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="5" y="0" on="1"/>
+        <pt x="206" y="0" on="1"/>
+        <pt x="556" y="700" on="1"/>
+        <pt x="355" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="640" y="311" on="1"/>
+        <pt x="190" y="311" on="1"/>
+        <pt x="190" y="191" on="1"/>
+        <pt x="640" y="191" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Aacute" xMin="5" yMin="0" xMax="906" yMax="949">
+      <component glyphName="A" x="0" y="0" flags="0x204"/>
+      <component glyphName="acutecomb" x="479" y="124" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="O" xMin="15" yMin="-10" xMax="670" yMax="710">
+      <contour>
+        <pt x="342" y="-10" on="1"/>
+        <pt x="172" y="-10" on="0"/>
+        <pt x="15" y="163" on="0"/>
+        <pt x="15" y="350" on="1"/>
+        <pt x="15" y="538" on="0"/>
+        <pt x="172" y="710" on="0"/>
+        <pt x="342" y="710" on="1"/>
+        <pt x="513" y="710" on="0"/>
+        <pt x="670" y="538" on="0"/>
+        <pt x="670" y="350" on="1"/>
+        <pt x="670" y="163" on="0"/>
+        <pt x="513" y="-10" on="0"/>
+      </contour>
+      <contour>
+        <pt x="342" y="153" on="1"/>
+        <pt x="419" y="153" on="0"/>
+        <pt x="490" y="247" on="0"/>
+        <pt x="490" y="350" on="1"/>
+        <pt x="490" y="453" on="0"/>
+        <pt x="419" y="547" on="0"/>
+        <pt x="342" y="547" on="1"/>
+        <pt x="266" y="547" on="0"/>
+        <pt x="195" y="453" on="0"/>
+        <pt x="195" y="350" on="1"/>
+        <pt x="195" y="247" on="0"/>
+        <pt x="266" y="153" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="V" xMin="5" yMin="0" xMax="906" yMax="700">
+      <contour>
+        <pt x="355" y="0" on="1"/>
+        <pt x="705" y="700" on="1"/>
+        <pt x="906" y="700" on="1"/>
+        <pt x="556" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="355" y="0" on="1"/>
+        <pt x="5" y="700" on="1"/>
+        <pt x="206" y="700" on="1"/>
+        <pt x="556" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="acutecomb" xMin="-141" yMin="630" xMax="125" yMax="825">
+      <contour>
+        <pt x="-118" y="756" on="1"/>
+        <pt x="-141" y="630" on="1"/>
+        <pt x="102" y="699" on="1"/>
+        <pt x="125" y="825" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar" xMin="1" yMin="-98" xMax="595" yMax="789">
+      <contour>
+        <pt x="249" y="789" on="1"/>
+        <pt x="369" y="789" on="1"/>
+        <pt x="369" y="743" on="1"/>
+        <pt x="427" y="735" on="0"/>
+        <pt x="537" y="681" on="0"/>
+        <pt x="590" y="623" on="1"/>
+        <pt x="510" y="515" on="1"/>
+        <pt x="479" y="549" on="0"/>
+        <pt x="411" y="588" on="0"/>
+        <pt x="369" y="595" on="1"/>
+        <pt x="369" y="400" on="1"/>
+        <pt x="476" y="378" on="0"/>
+        <pt x="595" y="278" on="0"/>
+        <pt x="595" y="184" on="1"/>
+        <pt x="595" y="93" on="0"/>
+        <pt x="474" y="-32" on="0"/>
+        <pt x="369" y="-46" on="1"/>
+        <pt x="369" y="-98" on="1"/>
+        <pt x="249" y="-98" on="1"/>
+        <pt x="249" y="-47" on="1"/>
+        <pt x="176" y="-39" on="0"/>
+        <pt x="52" y="17" on="0"/>
+        <pt x="1" y="69" on="1"/>
+        <pt x="80" y="179" on="1"/>
+        <pt x="118" y="144" on="0"/>
+        <pt x="195" y="106" on="0"/>
+        <pt x="249" y="100" on="1"/>
+        <pt x="249" y="273" on="1"/>
+        <pt x="246" y="274" on="1"/>
+        <pt x="144" y="294" on="0"/>
+        <pt x="28" y="405" on="0"/>
+        <pt x="28" y="502" on="1"/>
+        <pt x="28" y="567" on="0"/>
+        <pt x="84" y="667" on="0"/>
+        <pt x="184" y="732" on="0"/>
+        <pt x="249" y="742" on="1"/>
+      </contour>
+      <contour>
+        <pt x="152" y="502" on="1"/>
+        <pt x="152" y="480" on="0"/>
+        <pt x="166" y="453" on="0"/>
+        <pt x="208" y="434" on="0"/>
+        <pt x="249" y="424" on="1"/>
+        <pt x="249" y="595" on="1"/>
+        <pt x="199" y="587" on="0"/>
+        <pt x="152" y="538" on="0"/>
+      </contour>
+      <contour>
+        <pt x="369" y="100" on="1"/>
+        <pt x="426" y="107" on="0"/>
+        <pt x="471" y="150" on="0"/>
+        <pt x="471" y="183" on="1"/>
+        <pt x="471" y="201" on="0"/>
+        <pt x="456" y="225" on="0"/>
+        <pt x="412" y="243" on="0"/>
+        <pt x="369" y="252" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar.BRACKET.500" xMin="1" yMin="-98" xMax="595" yMax="789">
+      <contour>
+        <pt x="249" y="789" on="1"/>
+        <pt x="369" y="789" on="1"/>
+        <pt x="369" y="743" on="1"/>
+        <pt x="427" y="735" on="0"/>
+        <pt x="537" y="681" on="0"/>
+        <pt x="590" y="623" on="1"/>
+        <pt x="510" y="515" on="1"/>
+        <pt x="468" y="560" on="0"/>
+        <pt x="374" y="600" on="0"/>
+        <pt x="308" y="600" on="1"/>
+        <pt x="227" y="600" on="0"/>
+        <pt x="152" y="548" on="0"/>
+        <pt x="152" y="502" on="1"/>
+        <pt x="152" y="479" on="0"/>
+        <pt x="168" y="450" on="0"/>
+        <pt x="217" y="431" on="0"/>
+        <pt x="264" y="421" on="1"/>
+        <pt x="363" y="401" on="1"/>
+        <pt x="473" y="379" on="0"/>
+        <pt x="595" y="279" on="0"/>
+        <pt x="595" y="184" on="1"/>
+        <pt x="595" y="93" on="0"/>
+        <pt x="474" y="-32" on="0"/>
+        <pt x="369" y="-46" on="1"/>
+        <pt x="369" y="-98" on="1"/>
+        <pt x="249" y="-98" on="1"/>
+        <pt x="249" y="-47" on="1"/>
+        <pt x="176" y="-39" on="0"/>
+        <pt x="52" y="17" on="0"/>
+        <pt x="1" y="69" on="1"/>
+        <pt x="80" y="179" on="1"/>
+        <pt x="112" y="150" on="0"/>
+        <pt x="176" y="114" on="0"/>
+        <pt x="256" y="97" on="0"/>
+        <pt x="310" y="97" on="1"/>
+        <pt x="402" y="97" on="0"/>
+        <pt x="471" y="143" on="0"/>
+        <pt x="471" y="183" on="1"/>
+        <pt x="471" y="203" on="0"/>
+        <pt x="453" y="228" on="0"/>
+        <pt x="399" y="247" on="0"/>
+        <pt x="345" y="256" on="1"/>
+        <pt x="246" y="274" on="1"/>
+        <pt x="144" y="293" on="0"/>
+        <pt x="28" y="405" on="0"/>
+        <pt x="28" y="502" on="1"/>
+        <pt x="28" y="567" on="0"/>
+        <pt x="84" y="667" on="0"/>
+        <pt x="184" y="732" on="0"/>
+        <pt x="249" y="742" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar.bold" xMin="1" yMin="-98" xMax="595" yMax="789">
+      <contour>
+        <pt x="249" y="789" on="1"/>
+        <pt x="369" y="789" on="1"/>
+        <pt x="369" y="743" on="1"/>
+        <pt x="427" y="735" on="0"/>
+        <pt x="537" y="681" on="0"/>
+        <pt x="590" y="623" on="1"/>
+        <pt x="510" y="515" on="1"/>
+        <pt x="468" y="560" on="0"/>
+        <pt x="374" y="600" on="0"/>
+        <pt x="308" y="600" on="1"/>
+        <pt x="227" y="600" on="0"/>
+        <pt x="152" y="548" on="0"/>
+        <pt x="152" y="502" on="1"/>
+        <pt x="152" y="479" on="0"/>
+        <pt x="168" y="450" on="0"/>
+        <pt x="217" y="431" on="0"/>
+        <pt x="264" y="421" on="1"/>
+        <pt x="363" y="401" on="1"/>
+        <pt x="473" y="379" on="0"/>
+        <pt x="595" y="279" on="0"/>
+        <pt x="595" y="184" on="1"/>
+        <pt x="595" y="93" on="0"/>
+        <pt x="474" y="-32" on="0"/>
+        <pt x="369" y="-46" on="1"/>
+        <pt x="369" y="-98" on="1"/>
+        <pt x="249" y="-98" on="1"/>
+        <pt x="249" y="-47" on="1"/>
+        <pt x="176" y="-39" on="0"/>
+        <pt x="52" y="17" on="0"/>
+        <pt x="1" y="69" on="1"/>
+        <pt x="80" y="179" on="1"/>
+        <pt x="112" y="150" on="0"/>
+        <pt x="176" y="114" on="0"/>
+        <pt x="256" y="97" on="0"/>
+        <pt x="310" y="97" on="1"/>
+        <pt x="402" y="97" on="0"/>
+        <pt x="471" y="143" on="0"/>
+        <pt x="471" y="183" on="1"/>
+        <pt x="471" y="203" on="0"/>
+        <pt x="453" y="228" on="0"/>
+        <pt x="399" y="247" on="0"/>
+        <pt x="345" y="256" on="1"/>
+        <pt x="246" y="274" on="1"/>
+        <pt x="144" y="293" on="0"/>
+        <pt x="28" y="405" on="0"/>
+        <pt x="28" y="502" on="1"/>
+        <pt x="28" y="567" on="0"/>
+        <pt x="84" y="667" on="0"/>
+        <pt x="184" y="732" on="0"/>
+        <pt x="249" y="742" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Simple Two Axis
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;SimpleTwoAxis-Bold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Simple Two Axis Bold
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SimpleTwoAxis-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 -->
+      <psName name="dollar.bold"/>
+      <psName name="acutecomb"/>
+      <psName name="dollar.BRACKET.500"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Aacute" class="1"/>
+      <ClassDef glyph="acutecomb" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <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"/>
+          </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>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="Aacute"/>
+            <Glyph value="V"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=3 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-120"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-120"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="2">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="O"/>
+              <Value1 XAdvance="-20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_incompatible_features/IncompatibleFeatures-Regular.ttx b/Tests/varLib/data/master_incompatible_features/IncompatibleFeatures-Regular.ttx
new file mode 100644
index 0000000..cabb69a
--- /dev/null
+++ b/Tests/varLib/data/master_incompatible_features/IncompatibleFeatures-Regular.ttx
@@ -0,0 +1,626 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.20">
+
+  <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="Aacute"/>
+    <GlyphID id="3" name="O"/>
+    <GlyphID id="4" name="V"/>
+    <GlyphID id="5" name="space"/>
+    <GlyphID id="6" name="dollar"/>
+    <GlyphID id="7" name="dollar.bold"/>
+    <GlyphID id="8" name="acutecomb"/>
+    <GlyphID id="9" name="dollar.BRACKET.500"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x3c7bc79b"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Jan 15 14:37:13 2021"/>
+    <modified value="Mon Mar 15 12:57:03 2021"/>
+    <xMin value="-141"/>
+    <yMin value="-200"/>
+    <xMax value="751"/>
+    <yMax value="915"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="756"/>
+    <minLeftSideBearing value="-141"/>
+    <minRightSideBearing value="-125"/>
+    <xMaxExtent value="751"/>
+    <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="10"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="10"/>
+    <maxPoints value="52"/>
+    <maxContours value="3"/>
+    <maxCompositePoints value="16"/>
+    <maxCompositeContours value="4"/>
+    <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="604"/>
+    <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="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 01000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="769"/>
+    <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="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="A" width="756" lsb="5"/>
+    <mtx name="Aacute" width="756" lsb="5"/>
+    <mtx name="O" width="664" lsb="30"/>
+    <mtx name="V" width="756" lsb="5"/>
+    <mtx name="acutecomb" width="0" lsb="-141"/>
+    <mtx name="dollar" width="600" lsb="29"/>
+    <mtx name="dollar.BRACKET.500" width="600" lsb="29"/>
+    <mtx name="dollar.bold" width="600" lsb="29"/>
+    <mtx name="space" width="200" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0xc1" name="Aacute"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0x301" name="acutecomb"/><!-- COMBINING ACUTE ACCENT -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0xc1" name="Aacute"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0x301" name="acutecomb"/><!-- COMBINING ACUTE ACCENT -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="5" yMin="0" xMax="751" yMax="700">
+      <contour>
+        <pt x="641" y="0" on="1"/>
+        <pt x="751" y="0" on="1"/>
+        <pt x="433" y="700" on="1"/>
+        <pt x="323" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="5" y="0" on="1"/>
+        <pt x="115" y="0" on="1"/>
+        <pt x="433" y="700" on="1"/>
+        <pt x="323" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="567" y="284" on="1"/>
+        <pt x="152" y="284" on="1"/>
+        <pt x="152" y="204" on="1"/>
+        <pt x="567" y="204" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Aacute" xMin="5" yMin="0" xMax="751" yMax="915">
+      <component glyphName="A" x="0" y="0" flags="0x204"/>
+      <component glyphName="acutecomb" x="402" y="130" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="O" xMin="30" yMin="-10" xMax="634" yMax="710">
+      <contour>
+        <pt x="332" y="-10" on="1"/>
+        <pt x="181" y="-10" on="0"/>
+        <pt x="30" y="169" on="0"/>
+        <pt x="30" y="350" on="1"/>
+        <pt x="30" y="531" on="0"/>
+        <pt x="181" y="710" on="0"/>
+        <pt x="332" y="710" on="1"/>
+        <pt x="484" y="710" on="0"/>
+        <pt x="634" y="531" on="0"/>
+        <pt x="634" y="350" on="1"/>
+        <pt x="634" y="169" on="0"/>
+        <pt x="484" y="-10" on="0"/>
+      </contour>
+      <contour>
+        <pt x="332" y="74" on="1"/>
+        <pt x="438" y="74" on="0"/>
+        <pt x="544" y="212" on="0"/>
+        <pt x="544" y="350" on="1"/>
+        <pt x="544" y="488" on="0"/>
+        <pt x="438" y="626" on="0"/>
+        <pt x="332" y="626" on="1"/>
+        <pt x="226" y="626" on="0"/>
+        <pt x="120" y="488" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="212" on="0"/>
+        <pt x="226" y="74" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="V" xMin="5" yMin="0" xMax="751" yMax="700">
+      <contour>
+        <pt x="323" y="0" on="1"/>
+        <pt x="641" y="700" on="1"/>
+        <pt x="751" y="700" on="1"/>
+        <pt x="433" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="323" y="0" on="1"/>
+        <pt x="5" y="700" on="1"/>
+        <pt x="115" y="700" on="1"/>
+        <pt x="433" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="acutecomb" xMin="-141" yMin="630" xMax="125" yMax="785">
+      <contour>
+        <pt x="-118" y="716" on="1"/>
+        <pt x="-141" y="630" on="1"/>
+        <pt x="102" y="699" on="1"/>
+        <pt x="125" y="785" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar" xMin="29" yMin="-68" xMax="580" yMax="759">
+      <contour>
+        <pt x="264" y="759" on="1"/>
+        <pt x="354" y="759" on="1"/>
+        <pt x="354" y="715" on="1"/>
+        <pt x="415" y="709" on="0"/>
+        <pt x="519" y="662" on="0"/>
+        <pt x="562" y="620" on="1"/>
+        <pt x="509" y="548" on="1"/>
+        <pt x="473" y="584" on="0"/>
+        <pt x="398" y="621" on="0"/>
+        <pt x="354" y="627" on="1"/>
+        <pt x="354" y="373" on="1"/>
+        <pt x="467" y="351" on="0"/>
+        <pt x="580" y="263" on="0"/>
+        <pt x="580" y="184" on="1"/>
+        <pt x="580" y="102" on="0"/>
+        <pt x="459" y="-8" on="0"/>
+        <pt x="354" y="-18" on="1"/>
+        <pt x="354" y="-68" on="1"/>
+        <pt x="264" y="-68" on="1"/>
+        <pt x="264" y="-18" on="1"/>
+        <pt x="192" y="-12" on="0"/>
+        <pt x="72" y="34" on="0"/>
+        <pt x="29" y="74" on="1"/>
+        <pt x="81" y="146" on="1"/>
+        <pt x="123" y="110" on="0"/>
+        <pt x="207" y="73" on="0"/>
+        <pt x="264" y="69" on="1"/>
+        <pt x="264" y="301" on="1"/>
+        <pt x="249" y="304" on="1"/>
+        <pt x="148" y="323" on="0"/>
+        <pt x="43" y="420" on="0"/>
+        <pt x="43" y="502" on="1"/>
+        <pt x="43" y="559" on="0"/>
+        <pt x="99" y="650" on="0"/>
+        <pt x="199" y="707" on="0"/>
+        <pt x="264" y="715" on="1"/>
+      </contour>
+      <contour>
+        <pt x="137" y="502" on="1"/>
+        <pt x="137" y="470" on="0"/>
+        <pt x="160" y="428" on="0"/>
+        <pt x="214" y="402" on="0"/>
+        <pt x="261" y="392" on="1"/>
+        <pt x="264" y="627" on="1"/>
+        <pt x="203" y="618" on="0"/>
+        <pt x="137" y="553" on="0"/>
+      </contour>
+      <contour>
+        <pt x="354" y="69" on="1"/>
+        <pt x="423" y="76" on="0"/>
+        <pt x="486" y="135" on="0"/>
+        <pt x="486" y="183" on="1"/>
+        <pt x="486" y="211" on="0"/>
+        <pt x="462" y="250" on="0"/>
+        <pt x="405" y="275" on="0"/>
+        <pt x="354" y="285" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar.BRACKET.500" xMin="29" yMin="-76" xMax="580" yMax="759">
+      <contour>
+        <pt x="264" y="759" on="1"/>
+        <pt x="354" y="759" on="1"/>
+        <pt x="354" y="715" on="1"/>
+        <pt x="415" y="709" on="0"/>
+        <pt x="519" y="662" on="0"/>
+        <pt x="562" y="620" on="1"/>
+        <pt x="509" y="548" on="1"/>
+        <pt x="464" y="592" on="0"/>
+        <pt x="370" y="630" on="0"/>
+        <pt x="308" y="630" on="1"/>
+        <pt x="226" y="630" on="0"/>
+        <pt x="137" y="562" on="0"/>
+        <pt x="137" y="502" on="1"/>
+        <pt x="137" y="470" on="0"/>
+        <pt x="160" y="428" on="0"/>
+        <pt x="214" y="402" on="0"/>
+        <pt x="261" y="392" on="1"/>
+        <pt x="360" y="372" on="1"/>
+        <pt x="469" y="350" on="0"/>
+        <pt x="580" y="263" on="0"/>
+        <pt x="580" y="184" on="1"/>
+        <pt x="580" y="102" on="0"/>
+        <pt x="459" y="-8" on="0"/>
+        <pt x="354" y="-18" on="1"/>
+        <pt x="354" y="-76" on="1"/>
+        <pt x="264" y="-76" on="1"/>
+        <pt x="264" y="-18" on="1"/>
+        <pt x="192" y="-12" on="0"/>
+        <pt x="72" y="34" on="0"/>
+        <pt x="29" y="74" on="1"/>
+        <pt x="81" y="146" on="1"/>
+        <pt x="115" y="118" on="0"/>
+        <pt x="180" y="83" on="0"/>
+        <pt x="259" y="67" on="0"/>
+        <pt x="310" y="67" on="1"/>
+        <pt x="403" y="67" on="0"/>
+        <pt x="486" y="128" on="0"/>
+        <pt x="486" y="183" on="1"/>
+        <pt x="486" y="212" on="0"/>
+        <pt x="461" y="251" on="0"/>
+        <pt x="401" y="277" on="0"/>
+        <pt x="348" y="286" on="1"/>
+        <pt x="249" y="304" on="1"/>
+        <pt x="148" y="323" on="0"/>
+        <pt x="43" y="420" on="0"/>
+        <pt x="43" y="502" on="1"/>
+        <pt x="43" y="559" on="0"/>
+        <pt x="99" y="650" on="0"/>
+        <pt x="199" y="707" on="0"/>
+        <pt x="264" y="715" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar.bold" xMin="29" yMin="-76" xMax="580" yMax="759">
+      <contour>
+        <pt x="264" y="759" on="1"/>
+        <pt x="354" y="759" on="1"/>
+        <pt x="354" y="715" on="1"/>
+        <pt x="415" y="709" on="0"/>
+        <pt x="519" y="662" on="0"/>
+        <pt x="562" y="620" on="1"/>
+        <pt x="509" y="548" on="1"/>
+        <pt x="464" y="592" on="0"/>
+        <pt x="370" y="630" on="0"/>
+        <pt x="308" y="630" on="1"/>
+        <pt x="226" y="630" on="0"/>
+        <pt x="137" y="562" on="0"/>
+        <pt x="137" y="502" on="1"/>
+        <pt x="137" y="470" on="0"/>
+        <pt x="160" y="428" on="0"/>
+        <pt x="214" y="402" on="0"/>
+        <pt x="261" y="392" on="1"/>
+        <pt x="360" y="372" on="1"/>
+        <pt x="469" y="350" on="0"/>
+        <pt x="580" y="263" on="0"/>
+        <pt x="580" y="184" on="1"/>
+        <pt x="580" y="102" on="0"/>
+        <pt x="459" y="-8" on="0"/>
+        <pt x="354" y="-18" on="1"/>
+        <pt x="354" y="-76" on="1"/>
+        <pt x="264" y="-76" on="1"/>
+        <pt x="264" y="-18" on="1"/>
+        <pt x="192" y="-12" on="0"/>
+        <pt x="72" y="34" on="0"/>
+        <pt x="29" y="74" on="1"/>
+        <pt x="81" y="146" on="1"/>
+        <pt x="115" y="118" on="0"/>
+        <pt x="180" y="83" on="0"/>
+        <pt x="259" y="67" on="0"/>
+        <pt x="310" y="67" on="1"/>
+        <pt x="403" y="67" on="0"/>
+        <pt x="486" y="128" on="0"/>
+        <pt x="486" y="183" on="1"/>
+        <pt x="486" y="212" on="0"/>
+        <pt x="461" y="251" on="0"/>
+        <pt x="401" y="277" on="0"/>
+        <pt x="348" y="286" on="1"/>
+        <pt x="249" y="304" on="1"/>
+        <pt x="148" y="323" on="0"/>
+        <pt x="43" y="420" on="0"/>
+        <pt x="43" y="502" on="1"/>
+        <pt x="43" y="559" on="0"/>
+        <pt x="99" y="650" on="0"/>
+        <pt x="199" y="707" on="0"/>
+        <pt x="264" y="715" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Simple Two Axis
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;SimpleTwoAxis-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Simple Two Axis Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SimpleTwoAxis-Regular
+    </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 -->
+      <psName name="dollar.bold"/>
+      <psName name="acutecomb"/>
+      <psName name="dollar.BRACKET.500"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Aacute" class="1"/>
+      <ClassDef glyph="acutecomb" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <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="mark"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="Aacute"/>
+            <Glyph value="V"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=3 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="2">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="O"/>
+              <Value1 XAdvance="-20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="acutecomb"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="A"/>
+            <Glyph value="Aacute"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="4"/>
+                <YCoordinate value="623"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=2 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="406"/>
+                <YCoordinate value="753"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="1">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="406"/>
+                <YCoordinate value="753"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_incompatible_lookup_types/IncompatibleLookupTypes-Bold.ttx b/Tests/varLib/data/master_incompatible_lookup_types/IncompatibleLookupTypes-Bold.ttx
new file mode 100644
index 0000000..6a28223
--- /dev/null
+++ b/Tests/varLib/data/master_incompatible_lookup_types/IncompatibleLookupTypes-Bold.ttx
@@ -0,0 +1,622 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.20">
+
+  <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="Aacute"/>
+    <GlyphID id="3" name="O"/>
+    <GlyphID id="4" name="V"/>
+    <GlyphID id="5" name="space"/>
+    <GlyphID id="6" name="dollar"/>
+    <GlyphID id="7" name="dollar.bold"/>
+    <GlyphID id="8" name="acutecomb"/>
+    <GlyphID id="9" name="dollar.BRACKET.500"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x10cb3f3"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Jan 15 14:37:13 2021"/>
+    <modified value="Mon Mar 15 12:57:03 2021"/>
+    <xMin value="-141"/>
+    <yMin value="-200"/>
+    <xMax value="906"/>
+    <yMax value="949"/>
+    <macStyle value="00000000 00000001"/>
+    <lowestRecPPEM value="6"/>
+    <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="911"/>
+    <minLeftSideBearing value="-141"/>
+    <minRightSideBearing value="-125"/>
+    <xMaxExtent value="906"/>
+    <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="10"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="10"/>
+    <maxPoints value="52"/>
+    <maxContours value="3"/>
+    <maxCompositePoints value="16"/>
+    <maxCompositeContours value="4"/>
+    <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="672"/>
+    <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="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 01000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 00100000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="769"/>
+    <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="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="A" width="911" lsb="5"/>
+    <mtx name="Aacute" width="911" lsb="5"/>
+    <mtx name="O" width="715" lsb="15"/>
+    <mtx name="V" width="911" lsb="5"/>
+    <mtx name="acutecomb" width="0" lsb="-141"/>
+    <mtx name="dollar" width="600" lsb="1"/>
+    <mtx name="dollar.BRACKET.500" width="600" lsb="1"/>
+    <mtx name="dollar.bold" width="600" lsb="1"/>
+    <mtx name="space" width="300" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0xc1" name="Aacute"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0x301" name="acutecomb"/><!-- COMBINING ACUTE ACCENT -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0xc1" name="Aacute"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0x301" name="acutecomb"/><!-- COMBINING ACUTE ACCENT -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="5" yMin="0" xMax="906" yMax="700">
+      <contour>
+        <pt x="705" y="0" on="1"/>
+        <pt x="906" y="0" on="1"/>
+        <pt x="556" y="700" on="1"/>
+        <pt x="355" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="5" y="0" on="1"/>
+        <pt x="206" y="0" on="1"/>
+        <pt x="556" y="700" on="1"/>
+        <pt x="355" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="640" y="311" on="1"/>
+        <pt x="190" y="311" on="1"/>
+        <pt x="190" y="191" on="1"/>
+        <pt x="640" y="191" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Aacute" xMin="5" yMin="0" xMax="906" yMax="949">
+      <component glyphName="A" x="0" y="0" flags="0x204"/>
+      <component glyphName="acutecomb" x="479" y="124" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="O" xMin="15" yMin="-10" xMax="670" yMax="710">
+      <contour>
+        <pt x="342" y="-10" on="1"/>
+        <pt x="172" y="-10" on="0"/>
+        <pt x="15" y="163" on="0"/>
+        <pt x="15" y="350" on="1"/>
+        <pt x="15" y="538" on="0"/>
+        <pt x="172" y="710" on="0"/>
+        <pt x="342" y="710" on="1"/>
+        <pt x="513" y="710" on="0"/>
+        <pt x="670" y="538" on="0"/>
+        <pt x="670" y="350" on="1"/>
+        <pt x="670" y="163" on="0"/>
+        <pt x="513" y="-10" on="0"/>
+      </contour>
+      <contour>
+        <pt x="342" y="153" on="1"/>
+        <pt x="419" y="153" on="0"/>
+        <pt x="490" y="247" on="0"/>
+        <pt x="490" y="350" on="1"/>
+        <pt x="490" y="453" on="0"/>
+        <pt x="419" y="547" on="0"/>
+        <pt x="342" y="547" on="1"/>
+        <pt x="266" y="547" on="0"/>
+        <pt x="195" y="453" on="0"/>
+        <pt x="195" y="350" on="1"/>
+        <pt x="195" y="247" on="0"/>
+        <pt x="266" y="153" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="V" xMin="5" yMin="0" xMax="906" yMax="700">
+      <contour>
+        <pt x="355" y="0" on="1"/>
+        <pt x="705" y="700" on="1"/>
+        <pt x="906" y="700" on="1"/>
+        <pt x="556" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="355" y="0" on="1"/>
+        <pt x="5" y="700" on="1"/>
+        <pt x="206" y="700" on="1"/>
+        <pt x="556" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="acutecomb" xMin="-141" yMin="630" xMax="125" yMax="825">
+      <contour>
+        <pt x="-118" y="756" on="1"/>
+        <pt x="-141" y="630" on="1"/>
+        <pt x="102" y="699" on="1"/>
+        <pt x="125" y="825" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar" xMin="1" yMin="-98" xMax="595" yMax="789">
+      <contour>
+        <pt x="249" y="789" on="1"/>
+        <pt x="369" y="789" on="1"/>
+        <pt x="369" y="743" on="1"/>
+        <pt x="427" y="735" on="0"/>
+        <pt x="537" y="681" on="0"/>
+        <pt x="590" y="623" on="1"/>
+        <pt x="510" y="515" on="1"/>
+        <pt x="479" y="549" on="0"/>
+        <pt x="411" y="588" on="0"/>
+        <pt x="369" y="595" on="1"/>
+        <pt x="369" y="400" on="1"/>
+        <pt x="476" y="378" on="0"/>
+        <pt x="595" y="278" on="0"/>
+        <pt x="595" y="184" on="1"/>
+        <pt x="595" y="93" on="0"/>
+        <pt x="474" y="-32" on="0"/>
+        <pt x="369" y="-46" on="1"/>
+        <pt x="369" y="-98" on="1"/>
+        <pt x="249" y="-98" on="1"/>
+        <pt x="249" y="-47" on="1"/>
+        <pt x="176" y="-39" on="0"/>
+        <pt x="52" y="17" on="0"/>
+        <pt x="1" y="69" on="1"/>
+        <pt x="80" y="179" on="1"/>
+        <pt x="118" y="144" on="0"/>
+        <pt x="195" y="106" on="0"/>
+        <pt x="249" y="100" on="1"/>
+        <pt x="249" y="273" on="1"/>
+        <pt x="246" y="274" on="1"/>
+        <pt x="144" y="294" on="0"/>
+        <pt x="28" y="405" on="0"/>
+        <pt x="28" y="502" on="1"/>
+        <pt x="28" y="567" on="0"/>
+        <pt x="84" y="667" on="0"/>
+        <pt x="184" y="732" on="0"/>
+        <pt x="249" y="742" on="1"/>
+      </contour>
+      <contour>
+        <pt x="152" y="502" on="1"/>
+        <pt x="152" y="480" on="0"/>
+        <pt x="166" y="453" on="0"/>
+        <pt x="208" y="434" on="0"/>
+        <pt x="249" y="424" on="1"/>
+        <pt x="249" y="595" on="1"/>
+        <pt x="199" y="587" on="0"/>
+        <pt x="152" y="538" on="0"/>
+      </contour>
+      <contour>
+        <pt x="369" y="100" on="1"/>
+        <pt x="426" y="107" on="0"/>
+        <pt x="471" y="150" on="0"/>
+        <pt x="471" y="183" on="1"/>
+        <pt x="471" y="201" on="0"/>
+        <pt x="456" y="225" on="0"/>
+        <pt x="412" y="243" on="0"/>
+        <pt x="369" y="252" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar.BRACKET.500" xMin="1" yMin="-98" xMax="595" yMax="789">
+      <contour>
+        <pt x="249" y="789" on="1"/>
+        <pt x="369" y="789" on="1"/>
+        <pt x="369" y="743" on="1"/>
+        <pt x="427" y="735" on="0"/>
+        <pt x="537" y="681" on="0"/>
+        <pt x="590" y="623" on="1"/>
+        <pt x="510" y="515" on="1"/>
+        <pt x="468" y="560" on="0"/>
+        <pt x="374" y="600" on="0"/>
+        <pt x="308" y="600" on="1"/>
+        <pt x="227" y="600" on="0"/>
+        <pt x="152" y="548" on="0"/>
+        <pt x="152" y="502" on="1"/>
+        <pt x="152" y="479" on="0"/>
+        <pt x="168" y="450" on="0"/>
+        <pt x="217" y="431" on="0"/>
+        <pt x="264" y="421" on="1"/>
+        <pt x="363" y="401" on="1"/>
+        <pt x="473" y="379" on="0"/>
+        <pt x="595" y="279" on="0"/>
+        <pt x="595" y="184" on="1"/>
+        <pt x="595" y="93" on="0"/>
+        <pt x="474" y="-32" on="0"/>
+        <pt x="369" y="-46" on="1"/>
+        <pt x="369" y="-98" on="1"/>
+        <pt x="249" y="-98" on="1"/>
+        <pt x="249" y="-47" on="1"/>
+        <pt x="176" y="-39" on="0"/>
+        <pt x="52" y="17" on="0"/>
+        <pt x="1" y="69" on="1"/>
+        <pt x="80" y="179" on="1"/>
+        <pt x="112" y="150" on="0"/>
+        <pt x="176" y="114" on="0"/>
+        <pt x="256" y="97" on="0"/>
+        <pt x="310" y="97" on="1"/>
+        <pt x="402" y="97" on="0"/>
+        <pt x="471" y="143" on="0"/>
+        <pt x="471" y="183" on="1"/>
+        <pt x="471" y="203" on="0"/>
+        <pt x="453" y="228" on="0"/>
+        <pt x="399" y="247" on="0"/>
+        <pt x="345" y="256" on="1"/>
+        <pt x="246" y="274" on="1"/>
+        <pt x="144" y="293" on="0"/>
+        <pt x="28" y="405" on="0"/>
+        <pt x="28" y="502" on="1"/>
+        <pt x="28" y="567" on="0"/>
+        <pt x="84" y="667" on="0"/>
+        <pt x="184" y="732" on="0"/>
+        <pt x="249" y="742" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar.bold" xMin="1" yMin="-98" xMax="595" yMax="789">
+      <contour>
+        <pt x="249" y="789" on="1"/>
+        <pt x="369" y="789" on="1"/>
+        <pt x="369" y="743" on="1"/>
+        <pt x="427" y="735" on="0"/>
+        <pt x="537" y="681" on="0"/>
+        <pt x="590" y="623" on="1"/>
+        <pt x="510" y="515" on="1"/>
+        <pt x="468" y="560" on="0"/>
+        <pt x="374" y="600" on="0"/>
+        <pt x="308" y="600" on="1"/>
+        <pt x="227" y="600" on="0"/>
+        <pt x="152" y="548" on="0"/>
+        <pt x="152" y="502" on="1"/>
+        <pt x="152" y="479" on="0"/>
+        <pt x="168" y="450" on="0"/>
+        <pt x="217" y="431" on="0"/>
+        <pt x="264" y="421" on="1"/>
+        <pt x="363" y="401" on="1"/>
+        <pt x="473" y="379" on="0"/>
+        <pt x="595" y="279" on="0"/>
+        <pt x="595" y="184" on="1"/>
+        <pt x="595" y="93" on="0"/>
+        <pt x="474" y="-32" on="0"/>
+        <pt x="369" y="-46" on="1"/>
+        <pt x="369" y="-98" on="1"/>
+        <pt x="249" y="-98" on="1"/>
+        <pt x="249" y="-47" on="1"/>
+        <pt x="176" y="-39" on="0"/>
+        <pt x="52" y="17" on="0"/>
+        <pt x="1" y="69" on="1"/>
+        <pt x="80" y="179" on="1"/>
+        <pt x="112" y="150" on="0"/>
+        <pt x="176" y="114" on="0"/>
+        <pt x="256" y="97" on="0"/>
+        <pt x="310" y="97" on="1"/>
+        <pt x="402" y="97" on="0"/>
+        <pt x="471" y="143" on="0"/>
+        <pt x="471" y="183" on="1"/>
+        <pt x="471" y="203" on="0"/>
+        <pt x="453" y="228" on="0"/>
+        <pt x="399" y="247" on="0"/>
+        <pt x="345" y="256" on="1"/>
+        <pt x="246" y="274" on="1"/>
+        <pt x="144" y="293" on="0"/>
+        <pt x="28" y="405" on="0"/>
+        <pt x="28" y="502" on="1"/>
+        <pt x="28" y="567" on="0"/>
+        <pt x="84" y="667" on="0"/>
+        <pt x="184" y="732" on="0"/>
+        <pt x="249" y="742" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Simple Two Axis
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;SimpleTwoAxis-Bold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Simple Two Axis Bold
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SimpleTwoAxis-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 -->
+      <psName name="dollar.bold"/>
+      <psName name="acutecomb"/>
+      <psName name="dollar.BRACKET.500"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Aacute" class="1"/>
+      <ClassDef glyph="acutecomb" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <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="mark"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="Aacute"/>
+            <Glyph value="V"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=3 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="2">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="O"/>
+              <Value1 XAdvance="-20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="Aacute"/>
+            <Glyph value="V"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=3 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="2">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="O"/>
+              <Value1 XAdvance="-20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_incompatible_lookup_types/IncompatibleLookupTypes-Regular.ttx b/Tests/varLib/data/master_incompatible_lookup_types/IncompatibleLookupTypes-Regular.ttx
new file mode 100644
index 0000000..dc6eb17
--- /dev/null
+++ b/Tests/varLib/data/master_incompatible_lookup_types/IncompatibleLookupTypes-Regular.ttx
@@ -0,0 +1,626 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.20">
+
+  <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="Aacute"/>
+    <GlyphID id="3" name="O"/>
+    <GlyphID id="4" name="V"/>
+    <GlyphID id="5" name="space"/>
+    <GlyphID id="6" name="dollar"/>
+    <GlyphID id="7" name="dollar.bold"/>
+    <GlyphID id="8" name="acutecomb"/>
+    <GlyphID id="9" name="dollar.BRACKET.500"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x3c7bc79b"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Jan 15 14:37:13 2021"/>
+    <modified value="Mon Mar 15 12:57:03 2021"/>
+    <xMin value="-141"/>
+    <yMin value="-200"/>
+    <xMax value="751"/>
+    <yMax value="915"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="756"/>
+    <minLeftSideBearing value="-141"/>
+    <minRightSideBearing value="-125"/>
+    <xMaxExtent value="751"/>
+    <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="10"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="10"/>
+    <maxPoints value="52"/>
+    <maxContours value="3"/>
+    <maxCompositePoints value="16"/>
+    <maxCompositeContours value="4"/>
+    <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="604"/>
+    <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="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 01000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="769"/>
+    <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="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="A" width="756" lsb="5"/>
+    <mtx name="Aacute" width="756" lsb="5"/>
+    <mtx name="O" width="664" lsb="30"/>
+    <mtx name="V" width="756" lsb="5"/>
+    <mtx name="acutecomb" width="0" lsb="-141"/>
+    <mtx name="dollar" width="600" lsb="29"/>
+    <mtx name="dollar.BRACKET.500" width="600" lsb="29"/>
+    <mtx name="dollar.bold" width="600" lsb="29"/>
+    <mtx name="space" width="200" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0xc1" name="Aacute"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0x301" name="acutecomb"/><!-- COMBINING ACUTE ACCENT -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x56" name="V"/><!-- LATIN CAPITAL LETTER V -->
+      <map code="0xc1" name="Aacute"/><!-- LATIN CAPITAL LETTER A WITH ACUTE -->
+      <map code="0x301" name="acutecomb"/><!-- COMBINING ACUTE ACCENT -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="5" yMin="0" xMax="751" yMax="700">
+      <contour>
+        <pt x="641" y="0" on="1"/>
+        <pt x="751" y="0" on="1"/>
+        <pt x="433" y="700" on="1"/>
+        <pt x="323" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="5" y="0" on="1"/>
+        <pt x="115" y="0" on="1"/>
+        <pt x="433" y="700" on="1"/>
+        <pt x="323" y="700" on="1"/>
+      </contour>
+      <contour>
+        <pt x="567" y="284" on="1"/>
+        <pt x="152" y="284" on="1"/>
+        <pt x="152" y="204" on="1"/>
+        <pt x="567" y="204" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Aacute" xMin="5" yMin="0" xMax="751" yMax="915">
+      <component glyphName="A" x="0" y="0" flags="0x204"/>
+      <component glyphName="acutecomb" x="402" y="130" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="O" xMin="30" yMin="-10" xMax="634" yMax="710">
+      <contour>
+        <pt x="332" y="-10" on="1"/>
+        <pt x="181" y="-10" on="0"/>
+        <pt x="30" y="169" on="0"/>
+        <pt x="30" y="350" on="1"/>
+        <pt x="30" y="531" on="0"/>
+        <pt x="181" y="710" on="0"/>
+        <pt x="332" y="710" on="1"/>
+        <pt x="484" y="710" on="0"/>
+        <pt x="634" y="531" on="0"/>
+        <pt x="634" y="350" on="1"/>
+        <pt x="634" y="169" on="0"/>
+        <pt x="484" y="-10" on="0"/>
+      </contour>
+      <contour>
+        <pt x="332" y="74" on="1"/>
+        <pt x="438" y="74" on="0"/>
+        <pt x="544" y="212" on="0"/>
+        <pt x="544" y="350" on="1"/>
+        <pt x="544" y="488" on="0"/>
+        <pt x="438" y="626" on="0"/>
+        <pt x="332" y="626" on="1"/>
+        <pt x="226" y="626" on="0"/>
+        <pt x="120" y="488" on="0"/>
+        <pt x="120" y="350" on="1"/>
+        <pt x="120" y="212" on="0"/>
+        <pt x="226" y="74" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="V" xMin="5" yMin="0" xMax="751" yMax="700">
+      <contour>
+        <pt x="323" y="0" on="1"/>
+        <pt x="641" y="700" on="1"/>
+        <pt x="751" y="700" on="1"/>
+        <pt x="433" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="323" y="0" on="1"/>
+        <pt x="5" y="700" on="1"/>
+        <pt x="115" y="700" on="1"/>
+        <pt x="433" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="acutecomb" xMin="-141" yMin="630" xMax="125" yMax="785">
+      <contour>
+        <pt x="-118" y="716" on="1"/>
+        <pt x="-141" y="630" on="1"/>
+        <pt x="102" y="699" on="1"/>
+        <pt x="125" y="785" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar" xMin="29" yMin="-68" xMax="580" yMax="759">
+      <contour>
+        <pt x="264" y="759" on="1"/>
+        <pt x="354" y="759" on="1"/>
+        <pt x="354" y="715" on="1"/>
+        <pt x="415" y="709" on="0"/>
+        <pt x="519" y="662" on="0"/>
+        <pt x="562" y="620" on="1"/>
+        <pt x="509" y="548" on="1"/>
+        <pt x="473" y="584" on="0"/>
+        <pt x="398" y="621" on="0"/>
+        <pt x="354" y="627" on="1"/>
+        <pt x="354" y="373" on="1"/>
+        <pt x="467" y="351" on="0"/>
+        <pt x="580" y="263" on="0"/>
+        <pt x="580" y="184" on="1"/>
+        <pt x="580" y="102" on="0"/>
+        <pt x="459" y="-8" on="0"/>
+        <pt x="354" y="-18" on="1"/>
+        <pt x="354" y="-68" on="1"/>
+        <pt x="264" y="-68" on="1"/>
+        <pt x="264" y="-18" on="1"/>
+        <pt x="192" y="-12" on="0"/>
+        <pt x="72" y="34" on="0"/>
+        <pt x="29" y="74" on="1"/>
+        <pt x="81" y="146" on="1"/>
+        <pt x="123" y="110" on="0"/>
+        <pt x="207" y="73" on="0"/>
+        <pt x="264" y="69" on="1"/>
+        <pt x="264" y="301" on="1"/>
+        <pt x="249" y="304" on="1"/>
+        <pt x="148" y="323" on="0"/>
+        <pt x="43" y="420" on="0"/>
+        <pt x="43" y="502" on="1"/>
+        <pt x="43" y="559" on="0"/>
+        <pt x="99" y="650" on="0"/>
+        <pt x="199" y="707" on="0"/>
+        <pt x="264" y="715" on="1"/>
+      </contour>
+      <contour>
+        <pt x="137" y="502" on="1"/>
+        <pt x="137" y="470" on="0"/>
+        <pt x="160" y="428" on="0"/>
+        <pt x="214" y="402" on="0"/>
+        <pt x="261" y="392" on="1"/>
+        <pt x="264" y="627" on="1"/>
+        <pt x="203" y="618" on="0"/>
+        <pt x="137" y="553" on="0"/>
+      </contour>
+      <contour>
+        <pt x="354" y="69" on="1"/>
+        <pt x="423" y="76" on="0"/>
+        <pt x="486" y="135" on="0"/>
+        <pt x="486" y="183" on="1"/>
+        <pt x="486" y="211" on="0"/>
+        <pt x="462" y="250" on="0"/>
+        <pt x="405" y="275" on="0"/>
+        <pt x="354" y="285" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar.BRACKET.500" xMin="29" yMin="-76" xMax="580" yMax="759">
+      <contour>
+        <pt x="264" y="759" on="1"/>
+        <pt x="354" y="759" on="1"/>
+        <pt x="354" y="715" on="1"/>
+        <pt x="415" y="709" on="0"/>
+        <pt x="519" y="662" on="0"/>
+        <pt x="562" y="620" on="1"/>
+        <pt x="509" y="548" on="1"/>
+        <pt x="464" y="592" on="0"/>
+        <pt x="370" y="630" on="0"/>
+        <pt x="308" y="630" on="1"/>
+        <pt x="226" y="630" on="0"/>
+        <pt x="137" y="562" on="0"/>
+        <pt x="137" y="502" on="1"/>
+        <pt x="137" y="470" on="0"/>
+        <pt x="160" y="428" on="0"/>
+        <pt x="214" y="402" on="0"/>
+        <pt x="261" y="392" on="1"/>
+        <pt x="360" y="372" on="1"/>
+        <pt x="469" y="350" on="0"/>
+        <pt x="580" y="263" on="0"/>
+        <pt x="580" y="184" on="1"/>
+        <pt x="580" y="102" on="0"/>
+        <pt x="459" y="-8" on="0"/>
+        <pt x="354" y="-18" on="1"/>
+        <pt x="354" y="-76" on="1"/>
+        <pt x="264" y="-76" on="1"/>
+        <pt x="264" y="-18" on="1"/>
+        <pt x="192" y="-12" on="0"/>
+        <pt x="72" y="34" on="0"/>
+        <pt x="29" y="74" on="1"/>
+        <pt x="81" y="146" on="1"/>
+        <pt x="115" y="118" on="0"/>
+        <pt x="180" y="83" on="0"/>
+        <pt x="259" y="67" on="0"/>
+        <pt x="310" y="67" on="1"/>
+        <pt x="403" y="67" on="0"/>
+        <pt x="486" y="128" on="0"/>
+        <pt x="486" y="183" on="1"/>
+        <pt x="486" y="212" on="0"/>
+        <pt x="461" y="251" on="0"/>
+        <pt x="401" y="277" on="0"/>
+        <pt x="348" y="286" on="1"/>
+        <pt x="249" y="304" on="1"/>
+        <pt x="148" y="323" on="0"/>
+        <pt x="43" y="420" on="0"/>
+        <pt x="43" y="502" on="1"/>
+        <pt x="43" y="559" on="0"/>
+        <pt x="99" y="650" on="0"/>
+        <pt x="199" y="707" on="0"/>
+        <pt x="264" y="715" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dollar.bold" xMin="29" yMin="-76" xMax="580" yMax="759">
+      <contour>
+        <pt x="264" y="759" on="1"/>
+        <pt x="354" y="759" on="1"/>
+        <pt x="354" y="715" on="1"/>
+        <pt x="415" y="709" on="0"/>
+        <pt x="519" y="662" on="0"/>
+        <pt x="562" y="620" on="1"/>
+        <pt x="509" y="548" on="1"/>
+        <pt x="464" y="592" on="0"/>
+        <pt x="370" y="630" on="0"/>
+        <pt x="308" y="630" on="1"/>
+        <pt x="226" y="630" on="0"/>
+        <pt x="137" y="562" on="0"/>
+        <pt x="137" y="502" on="1"/>
+        <pt x="137" y="470" on="0"/>
+        <pt x="160" y="428" on="0"/>
+        <pt x="214" y="402" on="0"/>
+        <pt x="261" y="392" on="1"/>
+        <pt x="360" y="372" on="1"/>
+        <pt x="469" y="350" on="0"/>
+        <pt x="580" y="263" on="0"/>
+        <pt x="580" y="184" on="1"/>
+        <pt x="580" y="102" on="0"/>
+        <pt x="459" y="-8" on="0"/>
+        <pt x="354" y="-18" on="1"/>
+        <pt x="354" y="-76" on="1"/>
+        <pt x="264" y="-76" on="1"/>
+        <pt x="264" y="-18" on="1"/>
+        <pt x="192" y="-12" on="0"/>
+        <pt x="72" y="34" on="0"/>
+        <pt x="29" y="74" on="1"/>
+        <pt x="81" y="146" on="1"/>
+        <pt x="115" y="118" on="0"/>
+        <pt x="180" y="83" on="0"/>
+        <pt x="259" y="67" on="0"/>
+        <pt x="310" y="67" on="1"/>
+        <pt x="403" y="67" on="0"/>
+        <pt x="486" y="128" on="0"/>
+        <pt x="486" y="183" on="1"/>
+        <pt x="486" y="212" on="0"/>
+        <pt x="461" y="251" on="0"/>
+        <pt x="401" y="277" on="0"/>
+        <pt x="348" y="286" on="1"/>
+        <pt x="249" y="304" on="1"/>
+        <pt x="148" y="323" on="0"/>
+        <pt x="43" y="420" on="0"/>
+        <pt x="43" y="502" on="1"/>
+        <pt x="43" y="559" on="0"/>
+        <pt x="99" y="650" on="0"/>
+        <pt x="199" y="707" on="0"/>
+        <pt x="264" y="715" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Simple Two Axis
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;SimpleTwoAxis-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Simple Two Axis Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SimpleTwoAxis-Regular
+    </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 -->
+      <psName name="dollar.bold"/>
+      <psName name="acutecomb"/>
+      <psName name="dollar.BRACKET.500"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Aacute" class="1"/>
+      <ClassDef glyph="acutecomb" class="3"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <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="mark"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="Aacute"/>
+            <Glyph value="V"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=3 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="1">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value1 XAdvance="-80"/>
+            </PairValueRecord>
+          </PairSet>
+          <PairSet index="2">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="O"/>
+              <Value1 XAdvance="-20"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="acutecomb"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="A"/>
+            <Glyph value="Aacute"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="4"/>
+                <YCoordinate value="623"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=2 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="3">
+                <XCoordinate value="406"/>
+                <YCoordinate value="753"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="1">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="406"/>
+                <YCoordinate value="753"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_kerning_merging/0.ttx b/Tests/varLib/data/master_kerning_merging/0.ttx
new file mode 100644
index 0000000..1ca22f6
--- /dev/null
+++ b/Tests/varLib/data/master_kerning_merging/0.ttx
@@ -0,0 +1,304 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.41">
+
+  <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="D"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="0.0"/>
+    <checkSumAdjustment value="0x341296b2"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun 12 15:33:01 2019"/>
+    <modified value="Wed Jun 12 15:33:01 2019"/>
+    <xMin value="50"/>
+    <yMin value="-200"/>
+    <xMax value="450"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="500"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <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="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="5"/>
+    <maxPoints value="8"/>
+    <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="500"/>
+    <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="300"/>
+    <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="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="68"/>
+    <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="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="A" width="500" lsb="0"/>
+    <mtx name="B" width="500" lsb="0"/>
+    <mtx name="C" width="500" lsb="0"/>
+    <mtx name="D" 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 -->
+      <map code="0x44" name="D"/><!-- LATIN CAPITAL LETTER D -->
+    </cmap_format_4>
+    <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 -->
+      <map code="0x44" name="D"/><!-- LATIN CAPITAL LETTER D -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="50" y="800" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="100" y="750" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A"/><!-- contains no outline data -->
+
+    <TTGlyph name="B"/><!-- contains no outline data -->
+
+    <TTGlyph name="C"/><!-- contains no outline data -->
+
+    <TTGlyph name="D"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Thin
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      0.000;NONE;Test-Thin
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Thin
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 0.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      Test-Thin
+    </namerecord>
+    <namerecord nameID="16" platformID="3" platEncID="1" langID="0x409">
+      Test
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Thin
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-150"/>
+    <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"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+            <ClassDef glyph="A" class="1"/>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="C" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=2 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="10"/>
+            </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/master_kerning_merging/1.ttx b/Tests/varLib/data/master_kerning_merging/1.ttx
new file mode 100644
index 0000000..9f6756d
--- /dev/null
+++ b/Tests/varLib/data/master_kerning_merging/1.ttx
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.41">
+
+  <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="D"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="0.0"/>
+    <checkSumAdjustment value="0x340ca516"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun 12 15:33:01 2019"/>
+    <modified value="Wed Jun 12 15:33:01 2019"/>
+    <xMin value="50"/>
+    <yMin value="-200"/>
+    <xMax value="450"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="500"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <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="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="5"/>
+    <maxPoints value="8"/>
+    <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="500"/>
+    <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="300"/>
+    <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="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="68"/>
+    <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="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="A" width="500" lsb="0"/>
+    <mtx name="B" width="500" lsb="0"/>
+    <mtx name="C" width="500" lsb="0"/>
+    <mtx name="D" 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 -->
+      <map code="0x44" name="D"/><!-- LATIN CAPITAL LETTER D -->
+    </cmap_format_4>
+    <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 -->
+      <map code="0x44" name="D"/><!-- LATIN CAPITAL LETTER D -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="50" y="800" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="100" y="750" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A"/><!-- contains no outline data -->
+
+    <TTGlyph name="B"/><!-- contains no outline data -->
+
+    <TTGlyph name="C"/><!-- contains no outline data -->
+
+    <TTGlyph name="D"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      0.000;NONE;Test-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 0.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      Test-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-150"/>
+    <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"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="B" class="2"/>
+            <ClassDef glyph="D" class="1"/>
+          </ClassDef2>
+          <!-- Class1Count=1 -->
+          <!-- Class2Count=3 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-20"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-20"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_kerning_merging/2.ttx b/Tests/varLib/data/master_kerning_merging/2.ttx
new file mode 100644
index 0000000..b8302e8
--- /dev/null
+++ b/Tests/varLib/data/master_kerning_merging/2.ttx
@@ -0,0 +1,304 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.41">
+
+  <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="D"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="0.0"/>
+    <checkSumAdjustment value="0x330c9492"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun 12 15:33:01 2019"/>
+    <modified value="Wed Jun 12 15:33:01 2019"/>
+    <xMin value="50"/>
+    <yMin value="-200"/>
+    <xMax value="450"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="500"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <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="1"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="5"/>
+    <maxPoints value="8"/>
+    <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="500"/>
+    <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="300"/>
+    <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="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="68"/>
+    <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="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="A" width="500" lsb="0"/>
+    <mtx name="B" width="500" lsb="0"/>
+    <mtx name="C" width="500" lsb="0"/>
+    <mtx name="D" 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 -->
+      <map code="0x44" name="D"/><!-- LATIN CAPITAL LETTER D -->
+    </cmap_format_4>
+    <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 -->
+      <map code="0x44" name="D"/><!-- LATIN CAPITAL LETTER D -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="50" y="800" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="100" y="750" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A"/><!-- contains no outline data -->
+
+    <TTGlyph name="B"/><!-- contains no outline data -->
+
+    <TTGlyph name="C"/><!-- contains no outline data -->
+
+    <TTGlyph name="D"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Black
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      0.000;NONE;Test-Black
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Black
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 0.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      Test-Black
+    </namerecord>
+    <namerecord nameID="16" platformID="3" platEncID="1" langID="0x409">
+      Test
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-150"/>
+    <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"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+            <ClassDef glyph="A" class="1"/>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="D" 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="40"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_non_marking_cff2/TestNonMarkingCFF2_ExtraLight.ttx b/Tests/varLib/data/master_non_marking_cff2/TestNonMarkingCFF2_ExtraLight.ttx
new file mode 100644
index 0000000..9d3b267
--- /dev/null
+++ b/Tests/varLib/data/master_non_marking_cff2/TestNonMarkingCFF2_ExtraLight.ttx
@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0xeb345d38"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Nov 29 14:52:09 2018"/>
+    <modified value="Thu Nov 29 14:52:09 2018"/>
+    <xMin value="50"/>
+    <yMin value="-115"/>
+    <xMax value="550"/>
+    <yMax value="762"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <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="600"/>
+    <minLeftSideBearing value="50"/>
+    <minRightSideBearing value="50"/>
+    <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="5"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="5"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="578"/>
+    <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="286"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="3"/>
+      <bProportion value="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </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="ADBO"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="36"/>
+    <usLastCharIndex value="84"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="478"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </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.010;ADBO;SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Roman Master 0
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCode_ExtraLight
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Roman Master 0
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x41" name="A"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="SourceCode_ExtraLight">
+      <version value="1.0"/>
+      <Notice value="Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries."/>
+      <Copyright value="Copyright 2010 - 2012 Adobe Systems Incorporated. All Rights Reserved."/>
+      <FamilyName value="Source Code"/>
+      <isFixedPitch value="1"/>
+      <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="50 -115 550 762"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-12 0 478 490 570 582 640 652 660 672 722 734"/>
+        <OtherBlues value="-234 -222"/>
+        <BlueScale value="0.0625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="0"/>
+        <StdHW value="28"/>
+        <StdVW value="34"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="600"/>
+        <nominalWidthX value="597"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          endchar
+        </CharString>
+        <CharString name="A">
+          endchar
+        </CharString>
+=      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="84"/>
+    <mtx name="A" width="600" lsb="50"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_non_marking_cff2/TestNonMarkingCFF2_Regular.ttx b/Tests/varLib/data/master_non_marking_cff2/TestNonMarkingCFF2_Regular.ttx
new file mode 100644
index 0000000..4e6775d
--- /dev/null
+++ b/Tests/varLib/data/master_non_marking_cff2/TestNonMarkingCFF2_Regular.ttx
@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="A"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0x60d07155"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Nov 29 14:52:09 2018"/>
+    <modified value="Thu Nov 29 14:52:09 2018"/>
+    <xMin value="31"/>
+    <yMin value="-115"/>
+    <xMax value="569"/>
+    <yMax value="751"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <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="600"/>
+    <minLeftSideBearing value="31"/>
+    <minRightSideBearing value="31"/>
+    <xMaxExtent value="569"/>
+    <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>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="5"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="579"/>
+    <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="291"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="5"/>
+      <bProportion value="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </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="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="36"/>
+    <usLastCharIndex value="84"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="486"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </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.010;ADBO;SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Roman
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Roman
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x41" name="A"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+    </cmap_format_4>
+  </cmap>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+  <CFF>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="SourceCodeVariable-Roman">
+      <version value="1.0"/>
+      <Notice value="Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries."/>
+      <Copyright value="Copyright 2010 - 2012 Adobe Systems Incorporated. All Rights Reserved."/>
+      <FamilyName value="Source Code"/>
+      <isFixedPitch value="1"/>
+      <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="31 -115 569 751"/>
+      <StrokeWidth value="0"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <Encoding name="StandardEncoding"/>
+      <Private>
+        <BlueValues value="-12 0 486 498 574 586 638 650 656 668 712 724"/>
+        <OtherBlues value="-217 -205"/>
+        <BlueScale value="0.0625"/>
+        <BlueShift value="7"/>
+        <BlueFuzz value="0"/>
+        <StdHW value="67"/>
+        <StdVW value="85"/>
+        <ForceBold value="0"/>
+        <LanguageGroup value="0"/>
+        <ExpansionFactor value="0.06"/>
+        <initialRandomSeed value="0"/>
+        <defaultWidthX value="600"/>
+        <nominalWidthX value="604"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          endchar
+        </CharString>
+        <CharString name="A">
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="62"/>
+    <mtx name="A" width="600" lsb="31"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w0.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w0.00.ttx
new file mode 100644
index 0000000..f6568fd
--- /dev/null
+++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w0.00.ttx
@@ -0,0 +1,1267 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <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="cid01177"/>
+    <GlyphID id="4" name="cid06449"/>
+    <GlyphID id="5" name="cid06821"/>
+    <GlyphID id="6" name="cid07253"/>
+    <GlyphID id="7" name="cid13393"/>
+    <GlyphID id="8" name="cid17290"/>
+    <GlyphID id="9" name="cid17852"/>
+    <GlyphID id="10" name="cid18480"/>
+    <GlyphID id="11" name="cid22370"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0xab835f00"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:06:21 2018"/>
+    <modified value="Tue Apr 16 22:17:21 2019"/>
+    <xMin value="14"/>
+    <yMin value="-120"/>
+    <xMax value="976"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="14"/>
+    <minRightSideBearing value="24"/>
+    <xMaxExtent value="976"/>
+    <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="12"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="977"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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 00000011"/>
+    <ulUnicodeRange2 value="00101000 00000000 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="63964"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w0.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w0.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w0.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w0.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x0" name="cid00001"/><!-- ???? -->
+      <map code="0x1" name="cid00001"/><!-- ???? -->
+      <map code="0x2" name="cid00001"/><!-- ???? -->
+      <map code="0x3" name="cid00001"/><!-- ???? -->
+      <map code="0x4" name="cid00001"/><!-- ???? -->
+      <map code="0x5" name="cid00001"/><!-- ???? -->
+      <map code="0x6" name="cid00001"/><!-- ???? -->
+      <map code="0x7" name="cid00001"/><!-- ???? -->
+      <map code="0x8" name="cid00001"/><!-- ???? -->
+      <map code="0x9" name="cid00001"/><!-- ???? -->
+      <map code="0xa" name="cid00001"/><!-- ???? -->
+      <map code="0xb" name="cid00001"/><!-- ???? -->
+      <map code="0xc" name="cid00001"/><!-- ???? -->
+      <map code="0xd" name="cid00001"/><!-- ???? -->
+      <map code="0xe" name="cid00001"/><!-- ???? -->
+      <map code="0xf" name="cid00001"/><!-- ???? -->
+      <map code="0x10" name="cid00001"/><!-- ???? -->
+      <map code="0x11" name="cid00001"/><!-- ???? -->
+      <map code="0x12" name="cid00001"/><!-- ???? -->
+      <map code="0x13" name="cid00001"/><!-- ???? -->
+      <map code="0x14" name="cid00001"/><!-- ???? -->
+      <map code="0x15" name="cid00001"/><!-- ???? -->
+      <map code="0x16" name="cid00001"/><!-- ???? -->
+      <map code="0x17" name="cid00001"/><!-- ???? -->
+      <map code="0x18" name="cid00001"/><!-- ???? -->
+      <map code="0x19" name="cid00001"/><!-- ???? -->
+      <map code="0x1a" name="cid00001"/><!-- ???? -->
+      <map code="0x1b" name="cid00001"/><!-- ???? -->
+      <map code="0x1c" name="cid00001"/><!-- ???? -->
+      <map code="0x1d" name="cid00001"/><!-- ???? -->
+      <map code="0x1e" name="cid00001"/><!-- ???? -->
+      <map code="0x1f" name="cid00001"/><!-- ???? -->
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+      <map code="0x21" name="cid00002"/><!-- EXCLAMATION MARK -->
+      <map code="0xa0" name="cid00001"/><!-- NO-BREAK SPACE -->
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="532" language="0" nGroups="43">
+      <map code="0x0" name="cid00001"/><!-- ???? -->
+      <map code="0x1" name="cid00001"/><!-- ???? -->
+      <map code="0x2" name="cid00001"/><!-- ???? -->
+      <map code="0x3" name="cid00001"/><!-- ???? -->
+      <map code="0x4" name="cid00001"/><!-- ???? -->
+      <map code="0x5" name="cid00001"/><!-- ???? -->
+      <map code="0x6" name="cid00001"/><!-- ???? -->
+      <map code="0x7" name="cid00001"/><!-- ???? -->
+      <map code="0x8" name="cid00001"/><!-- ???? -->
+      <map code="0x9" name="cid00001"/><!-- ???? -->
+      <map code="0xa" name="cid00001"/><!-- ???? -->
+      <map code="0xb" name="cid00001"/><!-- ???? -->
+      <map code="0xc" name="cid00001"/><!-- ???? -->
+      <map code="0xd" name="cid00001"/><!-- ???? -->
+      <map code="0xe" name="cid00001"/><!-- ???? -->
+      <map code="0xf" name="cid00001"/><!-- ???? -->
+      <map code="0x10" name="cid00001"/><!-- ???? -->
+      <map code="0x11" name="cid00001"/><!-- ???? -->
+      <map code="0x12" name="cid00001"/><!-- ???? -->
+      <map code="0x13" name="cid00001"/><!-- ???? -->
+      <map code="0x14" name="cid00001"/><!-- ???? -->
+      <map code="0x15" name="cid00001"/><!-- ???? -->
+      <map code="0x16" name="cid00001"/><!-- ???? -->
+      <map code="0x17" name="cid00001"/><!-- ???? -->
+      <map code="0x18" name="cid00001"/><!-- ???? -->
+      <map code="0x19" name="cid00001"/><!-- ???? -->
+      <map code="0x1a" name="cid00001"/><!-- ???? -->
+      <map code="0x1b" name="cid00001"/><!-- ???? -->
+      <map code="0x1c" name="cid00001"/><!-- ???? -->
+      <map code="0x1d" name="cid00001"/><!-- ???? -->
+      <map code="0x1e" name="cid00001"/><!-- ???? -->
+      <map code="0x1f" name="cid00001"/><!-- ???? -->
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+      <map code="0x21" name="cid00002"/><!-- EXCLAMATION MARK -->
+      <map code="0xa0" name="cid00001"/><!-- NO-BREAK SPACE -->
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_12>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uv="0x9686" uvs="0xfe00" name="cid13393"/>
+      <map uv="0x9686" uvs="0xe0101" name="cid13393"/>
+    </cmap_format_14>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x0" name="cid00001"/><!-- ???? -->
+      <map code="0x1" name="cid00001"/><!-- ???? -->
+      <map code="0x2" name="cid00001"/><!-- ???? -->
+      <map code="0x3" name="cid00001"/><!-- ???? -->
+      <map code="0x4" name="cid00001"/><!-- ???? -->
+      <map code="0x5" name="cid00001"/><!-- ???? -->
+      <map code="0x6" name="cid00001"/><!-- ???? -->
+      <map code="0x7" name="cid00001"/><!-- ???? -->
+      <map code="0x8" name="cid00001"/><!-- ???? -->
+      <map code="0x9" name="cid00001"/><!-- ???? -->
+      <map code="0xa" name="cid00001"/><!-- ???? -->
+      <map code="0xb" name="cid00001"/><!-- ???? -->
+      <map code="0xc" name="cid00001"/><!-- ???? -->
+      <map code="0xd" name="cid00001"/><!-- ???? -->
+      <map code="0xe" name="cid00001"/><!-- ???? -->
+      <map code="0xf" name="cid00001"/><!-- ???? -->
+      <map code="0x10" name="cid00001"/><!-- ???? -->
+      <map code="0x11" name="cid00001"/><!-- ???? -->
+      <map code="0x12" name="cid00001"/><!-- ???? -->
+      <map code="0x13" name="cid00001"/><!-- ???? -->
+      <map code="0x14" name="cid00001"/><!-- ???? -->
+      <map code="0x15" name="cid00001"/><!-- ???? -->
+      <map code="0x16" name="cid00001"/><!-- ???? -->
+      <map code="0x17" name="cid00001"/><!-- ???? -->
+      <map code="0x18" name="cid00001"/><!-- ???? -->
+      <map code="0x19" name="cid00001"/><!-- ???? -->
+      <map code="0x1a" name="cid00001"/><!-- ???? -->
+      <map code="0x1b" name="cid00001"/><!-- ???? -->
+      <map code="0x1c" name="cid00001"/><!-- ???? -->
+      <map code="0x1d" name="cid00001"/><!-- ???? -->
+      <map code="0x1e" name="cid00001"/><!-- ???? -->
+      <map code="0x1f" name="cid00001"/><!-- ???? -->
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+      <map code="0x21" name="cid00002"/><!-- EXCLAMATION MARK -->
+      <map code="0xa0" name="cid00001"/><!-- NO-BREAK SPACE -->
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="532" language="0" nGroups="43">
+      <map code="0x0" name="cid00001"/><!-- ???? -->
+      <map code="0x1" name="cid00001"/><!-- ???? -->
+      <map code="0x2" name="cid00001"/><!-- ???? -->
+      <map code="0x3" name="cid00001"/><!-- ???? -->
+      <map code="0x4" name="cid00001"/><!-- ???? -->
+      <map code="0x5" name="cid00001"/><!-- ???? -->
+      <map code="0x6" name="cid00001"/><!-- ???? -->
+      <map code="0x7" name="cid00001"/><!-- ???? -->
+      <map code="0x8" name="cid00001"/><!-- ???? -->
+      <map code="0x9" name="cid00001"/><!-- ???? -->
+      <map code="0xa" name="cid00001"/><!-- ???? -->
+      <map code="0xb" name="cid00001"/><!-- ???? -->
+      <map code="0xc" name="cid00001"/><!-- ???? -->
+      <map code="0xd" name="cid00001"/><!-- ???? -->
+      <map code="0xe" name="cid00001"/><!-- ???? -->
+      <map code="0xf" name="cid00001"/><!-- ???? -->
+      <map code="0x10" name="cid00001"/><!-- ???? -->
+      <map code="0x11" name="cid00001"/><!-- ???? -->
+      <map code="0x12" name="cid00001"/><!-- ???? -->
+      <map code="0x13" name="cid00001"/><!-- ???? -->
+      <map code="0x14" name="cid00001"/><!-- ???? -->
+      <map code="0x15" name="cid00001"/><!-- ???? -->
+      <map code="0x16" name="cid00001"/><!-- ???? -->
+      <map code="0x17" name="cid00001"/><!-- ???? -->
+      <map code="0x18" name="cid00001"/><!-- ???? -->
+      <map code="0x19" name="cid00001"/><!-- ???? -->
+      <map code="0x1a" name="cid00001"/><!-- ???? -->
+      <map code="0x1b" name="cid00001"/><!-- ???? -->
+      <map code="0x1c" name="cid00001"/><!-- ???? -->
+      <map code="0x1d" name="cid00001"/><!-- ???? -->
+      <map code="0x1e" name="cid00001"/><!-- ???? -->
+      <map code="0x1f" name="cid00001"/><!-- ???? -->
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+      <map code="0x21" name="cid00002"/><!-- EXCLAMATION MARK -->
+      <map code="0xa0" name="cid00001"/><!-- NO-BREAK SPACE -->
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_Kanji-w0.00">
+      <ROS Registry="Adobe" Order="Japan1" Supplement="6"/>
+      <Notice value="1997-2007, 2012 Adobe Systems Incorporated. All Rights Reserved. Kozuka Mincho is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries.&quot;"/>
+      <FullName value="Master Set Kanji w0.00"/>
+      <FamilyName value="Master Set Kanji"/>
+      <Weight value="ExtraLight"/>
+      <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="14 -120 976 880"/>
+      <StrokeWidth value="0"/>
+      <XUID value="1119273886"/>
+      <CIDFontVersion value="6.004"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="23058"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="MasterSet_Kanji-w0.00-Generic"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <FamilyBlues value="-250 -240 1100 1110"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="607"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="MasterSet_Kanji-w0.00-Kanji"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="0"/>
+          </Private>
+        </FontDict>
+        <FontDict index="2">
+          <FontName value="MasterSet_Kanji-w0.00-Proportional"/>
+          <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="668"/>
+            <nominalWidthX value="504"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          393 -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 hintmask 10111000
+          100 -120 rmoveto
+          800 0 rlineto
+          0 1000 rlineto
+          -800 0 rlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 0 rlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          0 -818 rlineto
+          -668 -41 rmoveto
+          318 409 rlineto
+          318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 rlineto
+          -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid00001" fdSelectIndex="2">
+          -273 endchar
+        </CharString>
+        <CharString name="cid00002" fdSelectIndex="2">
+          -282 -12 83 hstemhm
+          74 73 -57 40 hintmask 10100000
+          134 755 rmoveto
+          -48 0 rlineto
+          8 -565 rlineto
+          32 0 rlineto
+          hintmask 11000000
+          -17 -202 rmoveto
+          24 0 14 18 0 22 rrcurveto
+          0 25 -14 18 -22 0 rrcurveto
+          -23 0 -14 -21 0 -24 rrcurveto
+          0 -18 13 -20 22 0 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="cid01177" fdSelectIndex="1">
+          1000 -72 30 253 30 94 30 92 30 65 30 131 45 -30 112 -99 17 hstemhm
+          193 30 83 30 147 30 173 30 66 30 hintmask 1111100011111000
+          306 142 rmoveto
+          0 -156 rlineto
+          0 -50 22 -8 79 0 rrcurveto
+          17 0 186 0 18 0 rrcurveto
+          70 0 13 25 5 114 rrcurveto
+          -9 3 -12 4 -9 6 rrcurveto
+          -4 -109 -7 -13 -49 0 rrcurveto
+          -38 0 -156 0 -27 0 rrcurveto
+          -59 0 -10 5 0 22 rrcurveto
+          0 157 rlineto
+          63 34 rmoveto
+          65 -30 74 -47 37 -37 rrcurveto
+          20 22 rlineto
+          -37 36 -75 47 -65 28 rrcurveto
+          320 -64 rmoveto
+          76 -49 83 -75 38 -54 rrcurveto
+          23 19 rlineto
+          -38 54 -84 73 -76 49 rrcurveto
+          -557 -5 rmoveto
+          -28 -68 -50 -72 -77 -40 rrcurveto
+          24 -17 rlineto
+          79 42 47 74 31 71 rrcurveto
+          -117 625 rmoveto
+          0 -30 rlineto
+          775 0 rlineto
+          0 30 rlineto
+          -818 -176 rmoveto
+          0 -30 rlineto
+          869 0 rlineto
+          0 30 rlineto
+          hintmask 0000001000100000
+          -455 258 rmoveto
+          hintmask 0000000100100000
+          0 -99 rlineto
+          30 0 rlineto
+          hintmask 0000001000100000
+          0 99 rlineto
+          hintmask 0111010010001000
+          -236 -127 rmoveto
+          26 -40 25 -53 9 -36 rrcurveto
+          29 12 rlineto
+          -10 35 -25 53 -27 39 rrcurveto
+          393 2 rmoveto
+          -16 -38 -31 -57 -23 -35 rrcurveto
+          27 -12 rlineto
+          24 36 26 48 23 46 rrcurveto
+          -504 -378 rmoveto
+          559 0 rlineto
+          0 -94 rlineto
+          -559 0 rlineto
+          0 216 rmoveto
+          559 0 rlineto
+          0 -92 rlineto
+          -559 0 rlineto
+          -30 122 rmoveto
+          0 -276 rlineto
+          619 0 rlineto
+          0 276 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid06449" fdSelectIndex="1">
+          1000 -60 30 203 30 -9 9 67 7 -7 14 -14 30 -20 20 80 30 59 30 121 30 18 93 -30 30 -30 108 hstemhm
+          77 30 42 30 139 30 23 30 71 10 74 30 15 30 16 30 158 30 28 30 -4 29 hintmask 110001011101011101101101
+          53 761 rmoveto
+          0 -30 rlineto
+          896 0 rlineto
+          0 30 rlineto
+          -802 -461 rmoveto
+          0 -30 rlineto
+          703 0 rlineto
+          0 30 rlineto
+          hintmask 000000000000100100000000
+          -532 539 rmoveto
+          hintmask 000000000010000100000000
+          0 -171 rlineto
+          30 0 rlineto
+          hintmask 000000000000100100001000
+          0 171 rlineto
+          299 0 rmoveto
+          hintmask 000000000010000000001000
+          0 -171 rlineto
+          30 0 rlineto
+          hintmask 000000000000100000001000
+          0 171 rlineto
+          hintmask 000000111100011010010100
+          -46 -219 rmoveto
+          204 0 rlineto
+          0 -121 rlineto
+          -204 0 rlineto
+          -230 121 rmoveto
+          200 0 rlineto
+          0 -121 rlineto
+          -200 0 rlineto
+          -222 121 rmoveto
+          192 0 rlineto
+          0 -121 rlineto
+          -192 0 rlineto
+          -30 151 rmoveto
+          0 -181 rlineto
+          716 0 rlineto
+          0 181 rlineto
+          -788 -240 rmoveto
+          0 -130 rlineto
+          30 0 rlineto
+          0 100 rlineto
+          hintmask 000000110000000000000010
+          786 0 rlineto
+          0 -100 rlineto
+          30 0 rlineto
+          0 130 rlineto
+          hintmask 000010000000000100000000
+          -610 -123 rmoveto
+          -50 -62 -93 -73 -118 -54 rrcurveto
+          8 -4 10 -9 6 -7 rrcurveto
+          hintmask 010000000000000001000000
+          121 58 92 75 59 70 rrcurveto
+          124 -78 rmoveto
+          0 -7 rlineto
+          -65 -139 -176 -81 -162 -31 rrcurveto
+          6 -6 8 -12 3 -8 rrcurveto
+          hintmask 001000000000000001000000
+          168 37 178 84 72 154 rrcurveto
+          hintmask 110100000000000001100001
+          -19 11 rlineto
+          -6 -2 rlineto
+          -333 -72 rmoveto
+          65 -25 75 -46 38 -35 rrcurveto
+          26 19 rlineto
+          -39 34 -76 45 -64 25 rrcurveto
+          72 55 rmoveto
+          -30 -30 rlineto
+          269 0 rlineto
+          0 30 rlineto
+          74 74 rmoveto
+          0 -276 rlineto
+          0 -52 21 -9 77 0 rrcurveto
+          16 0 182 0 18 0 rrcurveto
+          62 0 12 21 4 88 rrcurveto
+          -9 2 -12 5 -8 6 rrcurveto
+          -4 -81 -6 -11 -41 0 rrcurveto
+          -37 0 -154 0 -26 0 rrcurveto
+          -56 0 -9 6 0 25 rrcurveto
+          0 276 rlineto
+          278 -62 rmoveto
+          -66 -32 -126 -33 -107 -23 rrcurveto
+          5 -7 5 -10 2 -7 rrcurveto
+          110 22 126 32 81 36 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="cid06821" fdSelectIndex="1">
+          1000 -58 30 100 30 70 22 -22 30 94 30 19 31 -17 28 152 20 -20 30 -12 12 66 30 -30 89 -5 30 -30 121 hstemhm
+          127 30 -18 18 199 30 -20 20 -20 30 -24 14 97 30 -11 11 72 31 202 30 87 29 hintmask 00011000000000000000000100000000
+          193 296 rmoveto
+          625 0 rlineto
+          0 -94 rlineto
+          -625 0 rlineto
+          -30 124 rmoveto
+          0 -154 rlineto
+          685 0 rlineto
+          0 154 rlineto
+          hintmask 00100000000000000000100000000000
+          -365 -132 rmoveto
+          0 -232 rlineto
+          30 -5 rlineto
+          0 237 rlineto
+          hintmask 01000000000010010000010000000000
+          -11 -92 rmoveto
+          0 -30 rlineto
+          397 0 rlineto
+          0 30 rlineto
+          -760 647 rmoveto
+          0 -30 rlineto
+          811 0 rlineto
+          0 30 rlineto
+          hintmask 00000000000010100000000000000000
+          -823 0 rmoveto
+          0 -143 rlineto
+          0 -83 -13 -107 -75 -82 rrcurveto
+          7 -4 11 -9 5 -6 rrcurveto
+          79 85 16 118 0 88 rrcurveto
+          0 143 rlineto
+          hintmask 00000000010100001000000000000000
+          199 -25 rmoveto
+          0 -167 rlineto
+          hintmask 00000000010100000100000000000000
+          30 0 rlineto
+          0 167 rlineto
+          hintmask 00000000101000000001000000000000
+          -14 -59 rmoveto
+          0 -30 rlineto
+          185 0 rlineto
+          0 30 rlineto
+          -365 -96 rmoveto
+          0 -30 rlineto
+          392 0 rlineto
+          0 30 rlineto
+          hintmask 00000011000000000100000000000000
+          -218 -10 rmoveto
+          0 -160 rlineto
+          0 -8 -2 -3 -11 -1 rrcurveto
+          -11 -1 -30 0 -47 1 rrcurveto
+          5 -9 6 -10 2 -9 rrcurveto
+          hintmask 00000011000001000010001000000000
+          50 0 30 0 17 6 rrcurveto
+          17 5 4 9 0 21 rrcurveto
+          0 159 rlineto
+          -132 -50 rmoveto
+          -25 -42 -40 -39 -44 -30 rrcurveto
+          8 -4 13 -10 5 -4 rrcurveto
+          41 30 45 47 26 45 rrcurveto
+          153 -7 rmoveto
+          35 -27 38 -39 18 -28 rrcurveto
+          24 18 rlineto
+          -18 27 -39 39 -34 25 rrcurveto
+          115 330 rmoveto
+          hintmask 10000101000001000000001010000000
+          14 -286 131 -209 160 0 rrcurveto
+          50 1 18 34 6 108 rrcurveto
+          -9 3 -11 5 -9 7 rrcurveto
+          -4 -92 -9 -34 -31 -1 rrcurveto
+          -137 -2 -126 185 -12 281 rrcurveto
+          207 -169 rmoveto
+          -61 -129 -111 -108 -121 -69 rrcurveto
+          7 -5 12 -11 5 -6 rrcurveto
+          119 74 113 110 66 136 rrcurveto
+          -156 153 rmoveto
+          52 -15 63 -26 34 -21 rrcurveto
+          15 27 rlineto
+          -34 20 -64 24 -51 14 rrcurveto
+          -453 -763 rmoveto
+          -25 -16 rlineto
+          89 -100 146 -18 233 0 rrcurveto
+          185 0 rlineto
+          64 0 rlineto
+          2 8 6 14 6 8 rrcurveto
+          -35 0 -207 0 -22 0 rrcurveto
+          -214 0 -150 15 -78 89 rrcurveto
+          5 62 rmoveto
+          -30 -97 -92 -60 -107 -36 rrcurveto
+          8 -6 12 -11 4 -6 rrcurveto
+          105 41 99 65 32 106 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="cid07253" fdSelectIndex="1">
+          1000 -80 27 95 49 -48 48 -45 45 -30 30 -16 16 -13 13 49 30 48 30 47 19 -19 30 53 30 -18 18 51 11 -11 30 -22 22 62 30 60 30 15 81 -30 30 -30 102 hstemhm
+          193 30 -1 30 -15 15 106 29 96 30 142 30 109 30 5 10 hintmask 10000011101100101101000101110000
+          55 767 rmoveto
+          0 -30 rlineto
+          892 0 rlineto
+          0 30 rlineto
+          hintmask 00000000000000000000100000000000
+          -637 72 rmoveto
+          hintmask 00000000000000000010000000000000
+          0 -153 rlineto
+          30 0 rlineto
+          hintmask 00000000000000000000100000100000
+          0 153 rlineto
+          315 0 rmoveto
+          hintmask 00000000000000000010000000100000
+          0 -153 rlineto
+          30 0 rlineto
+          hintmask 00000000000100101100110000110000
+          0 153 rlineto
+          -462 -288 rmoveto
+          571 0 rlineto
+          0 -62 rlineto
+          -571 0 rlineto
+          0 152 rmoveto
+          571 0 rlineto
+          0 -60 rlineto
+          -571 0 rlineto
+          -30 90 rmoveto
+          0 -212 rlineto
+          631 0 rlineto
+          0 212 rlineto
+          -776 -263 rmoveto
+          0 -30 rlineto
+          905 0 rlineto
+          0 30 rlineto
+          hintmask 00000001100000000000000100000000
+          -716 -160 rmoveto
+          0 -30 rlineto
+          554 0 rlineto
+          0 30 rlineto
+          -554 -78 rmoveto
+          0 -30 rlineto
+          563 0 rlineto
+          0 30 rlineto
+          hintmask 00000010000000000000001000000000
+          -578 -79 rmoveto
+          hintmask 00001000000000000000001000001000
+          0 -30 rlineto
+          617 0 rlineto
+          hintmask 00000010000001000000000000001000
+          0 30 rlineto
+          -477 382 rmoveto
+          -46 -92 -113 -104 -167 -65 rrcurveto
+          7 -5 10 -9 5 -8 rrcurveto
+          hintmask 00000100010010010000000001000000
+          172 70 111 106 55 101 rrcurveto
+          298 -65 rmoveto
+          -25 -12 rlineto
+          62 -80 121 -81 100 -38 rrcurveto
+          5 8 9 11 7 6 rrcurveto
+          -101 33 -119 76 -59 77 rrcurveto
+          -211 -88 rmoveto
+          0 -239 rlineto
+          30 0 rlineto
+          0 239 rlineto
+          hintmask 10000010000000000000000000001000
+          316 -223 rmoveto
+          0 -6 rlineto
+          -8 -87 -7 -34 -10 -10 rrcurveto
+          -6 -6 -6 -1 -12 0 rrcurveto
+          -11 0 -31 1 -34 3 rrcurveto
+          5 -8 3 -13 1 -8 rrcurveto
+          28 -2 30 -1 14 1 rrcurveto
+          21 0 10 4 10 9 rrcurveto
+          16 15 7 35 9 89 rrcurveto
+          1 7 1 12 0 0 rrcurveto
+          -660 -34 rmoveto
+          -17 -46 -32 -46 -46 -23 rrcurveto
+          20 -21 rlineto
+          hintmask 10010000000000000000000000000000
+          52 28 31 51 17 46 rrcurveto
+          hintmask 00100000000000000000000010000000
+          110 -3 rmoveto
+          13 -38 10 -49 0 -32 rrcurveto
+          29 6 rlineto
+          -1 31 -10 50 -15 37 rrcurveto
+          hintmask 01000000000000000000000000100000
+          113 -6 rmoveto
+          22 -32 20 -44 7 -30 rrcurveto
+          28 10 rlineto
+          -8 29 -21 44 -23 32 rrcurveto
+          hintmask 00010000001000000000001000000000
+          117 -5 rmoveto
+          25 -23 27 -32 13 -23 rrcurveto
+          21 14 rlineto
+          -12 22 -27 32 -26 22 rrcurveto
+          -381 267 rmoveto
+          -16 -30 rlineto
+          498 0 rlineto
+          0 30 rlineto
+          -516 -23 rmoveto
+          hintmask 00000010000000000000001000000000
+          0 -224 rlineto
+          hintmask 00000010001000000000000100000000
+          30 0 rlineto
+          0 247 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid13393" fdSelectIndex="1">
+          1000 -50 30 -19 19 114 30 44 30 23 30 -30 114 35 30 316 30 -10 10 37 12 hstemhm
+          82 30 197 30 -26 8 317 30 168 13 hintmask 1010101101110110
+          529 746 rmoveto
+          0 -30 rlineto
+          320 0 rlineto
+          0 30 rlineto
+          -397 -495 rmoveto
+          0 -30 rlineto
+          442 0 rlineto
+          0 30 rlineto
+          -420 149 rmoveto
+          0 -30 rlineto
+          374 0 rlineto
+          0 30 rlineto
+          -514 -420 rmoveto
+          0 -30 rlineto
+          626 0 rlineto
+          0 30 rlineto
+          -531 144 rmoveto
+          0 -30 rlineto
+          460 0 rlineto
+          0 30 rlineto
+          -53 622 rmoveto
+          0 -7 rlineto
+          -86 -171 -222 -118 -188 -45 rrcurveto
+          7 -7 8 -11 3 -8 rrcurveto
+          hintmask 0000000010000010
+          192 51 224 119 94 187 rrcurveto
+          hintmask 0100010100000110
+          -19 12 rlineto
+          -6 -2 rlineto
+          -323 -32 rmoveto
+          -25 -11 rlineto
+          83 -154 177 -116 201 -44 rrcurveto
+          4 8 9 12 7 6 rrcurveto
+          -200 39 -177 113 -79 147 rrcurveto
+          59 127 rmoveto
+          -40 -82 -80 -104 -112 -75 rrcurveto
+          8 -4 10 -9 6 -7 rrcurveto
+          115 80 80 106 47 90 rrcurveto
+          -129 -493 rmoveto
+          -27 -73 -43 -71 -51 -50 rrcurveto
+          8 -5 13 -9 5 -5 rrcurveto
+          49 52 47 77 29 77 rrcurveto
+          124 -1 rmoveto
+          0 -374 rlineto
+          30 0 rlineto
+          0 374 rlineto
+          hintmask 0000000000101000
+          -586 460 rmoveto
+          0 -875 rlineto
+          30 0 rlineto
+          0 845 rlineto
+          209 0 rlineto
+          0 30 rlineto
+          -8 0 rmoveto
+          0 -7 rlineto
+          -28 -75 -43 -102 -46 -95 rrcurveto
+          hintmask 0001000000010000
+          89 -91 24 -74 0 -63 rrcurveto
+          0 -33 -6 -35 -19 -13 rrcurveto
+          -10 -6 -12 -3 -14 -1 rrcurveto
+          -20 -2 -26 1 -29 2 rrcurveto
+          7 -9 4 -13 1 -8 rrcurveto
+          22 -2 27 0 22 2 rrcurveto
+          19 2 17 5 12 9 rrcurveto
+          25 17 10 43 0 44 rrcurveto
+          0 67 -22 76 -86 89 rrcurveto
+          hintmask 0000000001001000
+          39 84 42 98 33 81 rrcurveto
+          hintmask 0000000000001000
+          -20 14 rlineto
+          -6 -2 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid17290" fdSelectIndex="1">
+          1000 121 30 -22 22 148 30 -30 136 23 30 129 30 116 30 hstemhm
+          167 30 129 30 -16 16 123 30 48 30 -6 29 -29 111 -30 30 -16 16 201 30 1 29 hintmask 011011111011001010000000
+          326 793 rmoveto
+          0 -280 rlineto
+          0 -47 16 -8 59 0 rrcurveto
+          hintmask 000010000000100000000000
+          13 0 120 0 13 0 rrcurveto
+          49 0 10 20 4 82 rrcurveto
+          hintmask 101010101000010000000000
+          -10 2 -11 5 -8 6 rrcurveto
+          -3 -75 -5 -10 -29 0 rrcurveto
+          -24 0 -102 0 -18 0 rrcurveto
+          -38 0 -6 4 0 21 rrcurveto
+          0 280 rlineto
+          -41 -464 rmoveto
+          0 -30 rlineto
+          617 0 rlineto
+          0 30 rlineto
+          -661 -178 rmoveto
+          0 -30 rlineto
+          689 0 rlineto
+          0 30 rlineto
+          hintmask 010101100111001000000000
+          -481 284 rmoveto
+          0 -306 rlineto
+          30 0 rlineto
+          0 306 rlineto
+          218 0 rmoveto
+          0 -306 rlineto
+          30 0 rlineto
+          0 306 rlineto
+          -417 358 rmoveto
+          0 -30 rlineto
+          217 0 rlineto
+          0 -116 rlineto
+          -217 0 rlineto
+          0 -30 rlineto
+          247 0 rlineto
+          0 176 rlineto
+          75 0 rmoveto
+          hintmask 000010100000001001000000
+          0 -280 rlineto
+          0 -47 17 -8 60 0 rrcurveto
+          12 0 125 0 14 0 rrcurveto
+          49 0 11 20 3 82 rrcurveto
+          -9 2 -12 4 -8 7 rrcurveto
+          -3 -75 -5 -10 -30 0 rrcurveto
+          -25 0 -105 0 -18 0 rrcurveto
+          -40 0 -6 4 0 21 rrcurveto
+          0 280 rlineto
+          hintmask 000001110000000110000000
+          -16 0 rmoveto
+          0 -30 rlineto
+          217 0 rlineto
+          0 -116 rlineto
+          -217 0 rlineto
+          0 -30 rlineto
+          247 0 rlineto
+          0 176 rlineto
+          -424 -714 rmoveto
+          -52 -54 -91 -49 -81 -33 rrcurveto
+          8 -5 11 -13 4 -6 rrcurveto
+          80 36 94 56 56 58 rrcurveto
+          200 -7 rmoveto
+          76 -41 90 -62 46 -42 rrcurveto
+          22 23 rlineto
+          -46 42 -91 60 -75 39 rrcurveto
+          -499 750 rmoveto
+          -54 -167 -87 -164 -96 -108 rrcurveto
+          7 -6 11 -12 4 -6 rrcurveto
+          98 116 88 165 58 175 rrcurveto
+          -113 -214 rmoveto
+          0 -691 rlineto
+          30 0 rlineto
+          0 718 rlineto
+          -1 2 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid17852" fdSelectIndex="1">
+          1000 -67 29 219 30 154 30 -16 16 150 30 -30 122 -85 30 -18 18 87 30 -30 140 -122 12 hstemhm
+          51 188 -30 30 -30 149 21 30 -18 18 -13 13 66 30 -12 12 135 30 41 30 172 30 -6 28 hintmask 000000100001000000000000
+          51 612 rmoveto
+          0 -30 rlineto
+          hintmask 000000100000010000000000
+          307 0 rlineto
+          0 30 rlineto
+          hintmask 000000010010100100000000
+          -149 228 rmoveto
+          0 -918 rlineto
+          30 0 rlineto
+          0 918 rlineto
+          -36 -238 rmoveto
+          -31 -160 -74 -193 -68 -95 rrcurveto
+          7 -5 10 -11 6 -8 rrcurveto
+          70 101 74 203 33 160 rrcurveto
+          4 -143 rmoveto
+          -21 -16 rlineto
+          25 -26 72 -92 21 -33 rrcurveto
+          24 24 rlineto
+          -18 25 -81 96 -22 22 rrcurveto
+          157 278 rmoveto
+          hintmask 000000001000000100000000
+          0 -30 rlineto
+          559 0 rlineto
+          hintmask 010000000010000000100000
+          0 30 rlineto
+          -457 -518 rmoveto
+          0 -30 rlineto
+          176 0 rlineto
+          0 30 rlineto
+          hintmask 000000000100000001010000
+          -194 120 rmoveto
+          0 -365 rlineto
+          30 0 rlineto
+          0 365 rlineto
+          135 508 rmoveto
+          hintmask 000000000010000000010000
+          0 -122 rlineto
+          30 0 rlineto
+          hintmask 000101000100000000010000
+          0 122 rlineto
+          -115 -172 rmoveto
+          0 -288 rlineto
+          30 0 rlineto
+          0 288 rlineto
+          148 0 rmoveto
+          0 -288 rlineto
+          30 0 rlineto
+          0 288 rlineto
+          156 -394 rmoveto
+          -52 -36 -89 -48 -61 -29 rrcurveto
+          15 -21 rlineto
+          62 28 86 41 57 44 rrcurveto
+          hintmask 101010000000000010001100
+          -541 323 rmoveto
+          0 -30 rlineto
+          517 0 rlineto
+          0 -150 rlineto
+          -517 0 rlineto
+          0 -30 rlineto
+          547 0 rlineto
+          0 210 rlineto
+          -232 -242 rmoveto
+          0 -344 rlineto
+          0 -47 15 -9 54 0 rrcurveto
+          hintmask 100000000010001000001010
+          12 0 100 0 12 0 rrcurveto
+          48 0 10 25 3 102 rrcurveto
+          -9 3 -11 4 -8 6 rrcurveto
+          -3 -97 -4 -14 -29 0 rrcurveto
+          -21 0 -84 0 -16 0 rrcurveto
+          -33 0 -6 5 0 22 rrcurveto
+          0 344 rlineto
+          -346 -371 rmoveto
+          10 -31 rlineto
+          77 16 100 22 99 21 rrcurveto
+          -2 29 rlineto
+          -108 -22 -104 -22 -72 -13 rrcurveto
+          -16 767 rmoveto
+          0 -316 rlineto
+          0 -142 -7 -194 -74 -141 rrcurveto
+          8 -3 13 -7 5 -6 rrcurveto
+          75 143 10 205 0 145 rrcurveto
+          0 316 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid18480" fdSelectIndex="1">
+          1000 -71 30 427 30 153 30 33 111 -30 30 -30 126 hstemhm
+          159 30 -19 19 126 30 -6 30 281 30 160 30 18 31 hintmask 1110100101110000
+          58 743 rmoveto
+          0 -30 rlineto
+          887 0 rlineto
+          0 30 rlineto
+          hintmask 0000010010000000
+          -630 96 rmoveto
+          hintmask 0001000010000000
+          0 -207 rlineto
+          30 0 rlineto
+          hintmask 0000010010100000
+          0 207 rlineto
+          305 0 rmoveto
+          hintmask 0001000000100000
+          0 -207 rlineto
+          30 0 rlineto
+          hintmask 0010011000100000
+          0 207 rlineto
+          -521 -240 rmoveto
+          0 -206 rlineto
+          0 -137 -15 -184 -109 -136 rrcurveto
+          7 -3 12 -9 5 -6 rrcurveto
+          hintmask 1110000101010000
+          112 139 18 194 0 141 rrcurveto
+          0 207 rlineto
+          -19 0 rmoveto
+          0 -30 rlineto
+          670 0 rlineto
+          0 -153 rlineto
+          -670 0 rlineto
+          0 -30 rlineto
+          700 0 rlineto
+          0 213 rlineto
+          -531 -249 rmoveto
+          0 -343 rlineto
+          0 -66 31 -12 105 0 rrcurveto
+          23 0 278 0 24 0 rrcurveto
+          hintmask 1000000001001000
+          96 0 15 31 8 123 rrcurveto
+          -9 3 -13 4 -9 7 rrcurveto
+          -6 -117 -11 -21 -69 0 rrcurveto
+          -56 0 -236 0 -41 0 rrcurveto
+          -84 0 -16 11 0 37 rrcurveto
+          0 343 rlineto
+          444 -47 rmoveto
+          -101 -52 -195 -56 -169 -40 rrcurveto
+          4 -7 5 -10 3 -7 rrcurveto
+          172 40 193 54 120 56 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="cid22370" fdSelectIndex="1">
+          1000 64 30 77 30 76 30 74 30 72 30 109 30 25 84 -30 30 -30 108 hstemhm
+          135 30 21 30 102 30 14 30 205 30 17 30 113 30 19 30 hintmask 111111010011001100000000
+          53 761 rmoveto
+          0 -30 rlineto
+          896 0 rlineto
+          0 30 rlineto
+          hintmask 000000001001000000000000
+          -631 78 rmoveto
+          hintmask 000000100001000000000000
+          0 -162 rlineto
+          30 0 rlineto
+          hintmask 000000001001001000000000
+          0 162 rlineto
+          296 0 rmoveto
+          hintmask 000000100000001000000000
+          0 -162 rlineto
+          30 0 rlineto
+          hintmask 000000001000001000000000
+          0 162 rlineto
+          hintmask 000011000100110010000000
+          -47 -217 rmoveto
+          209 0 rlineto
+          0 -109 rlineto
+          -209 0 rlineto
+          -235 109 rmoveto
+          205 0 rlineto
+          0 -109 rlineto
+          -205 0 rlineto
+          -227 109 rmoveto
+          197 0 rlineto
+          0 -109 rlineto
+          -197 0 rlineto
+          -30 139 rmoveto
+          0 -169 rlineto
+          731 0 rlineto
+          0 169 rlineto
+          hintmask 111100000010000100000000
+          -650 -375 rmoveto
+          571 0 rlineto
+          0 -76 rlineto
+          -571 0 rlineto
+          0 -30 rmoveto
+          571 0 rlineto
+          0 -77 rlineto
+          -571 0 rlineto
+          0 287 rmoveto
+          571 0 rlineto
+          0 -74 rlineto
+          -571 0 rlineto
+          -30 104 rmoveto
+          0 -347 rlineto
+          631 0 rlineto
+          0 347 rlineto
+          -216 -389 rmoveto
+          127 -34 121 -39 72 -31 rrcurveto
+          31 22 rlineto
+          -78 32 -126 39 -121 31 rrcurveto
+          -258 -1 rmoveto
+          -81 -39 -128 -36 -107 -23 rrcurveto
+          8 -6 12 -12 5 -6 rrcurveto
+          103 25 130 41 86 43 rrcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <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="231" lsb="0"/>
+    <mtx name="cid00002" width="222" lsb="74"/>
+    <mtx name="cid01177" width="1000" lsb="48"/>
+    <mtx name="cid06449" width="1000" lsb="52"/>
+    <mtx name="cid06821" width="1000" lsb="34"/>
+    <mtx name="cid07253" width="1000" lsb="36"/>
+    <mtx name="cid13393" width="1000" lsb="82"/>
+    <mtx name="cid17290" width="1000" lsb="14"/>
+    <mtx name="cid17852" width="1000" lsb="30"/>
+    <mtx name="cid18480" width="1000" lsb="35"/>
+    <mtx name="cid22370" width="1000" lsb="53"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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"/>
+    <mtx name="cid00002" height="1000" tsb="125"/>
+    <mtx name="cid01177" height="1000" tsb="40"/>
+    <mtx name="cid06449" height="1000" tsb="41"/>
+    <mtx name="cid06821" height="1000" tsb="40"/>
+    <mtx name="cid07253" height="1000" tsb="41"/>
+    <mtx name="cid13393" height="1000" tsb="39"/>
+    <mtx name="cid17290" height="1000" tsb="49"/>
+    <mtx name="cid17852" height="1000" tsb="40"/>
+    <mtx name="cid18480" height="1000" tsb="41"/>
+    <mtx name="cid22370" height="1000" tsb="41"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w1000.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w1000.00.ttx
new file mode 100644
index 0000000..e527d07
--- /dev/null
+++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w1000.00.ttx
@@ -0,0 +1,1266 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <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="cid01177"/>
+    <GlyphID id="4" name="cid06449"/>
+    <GlyphID id="5" name="cid06821"/>
+    <GlyphID id="6" name="cid07253"/>
+    <GlyphID id="7" name="cid13393"/>
+    <GlyphID id="8" name="cid17290"/>
+    <GlyphID id="9" name="cid17852"/>
+    <GlyphID id="10" name="cid18480"/>
+    <GlyphID id="11" name="cid22370"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0xc54fcf7b"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:08:26 2018"/>
+    <modified value="Tue Apr 16 22:17:21 2019"/>
+    <xMin value="8"/>
+    <yMin value="-120"/>
+    <xMax value="995"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="8"/>
+    <minRightSideBearing value="5"/>
+    <xMaxExtent value="995"/>
+    <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="12"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="979"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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 00000011"/>
+    <ulUnicodeRange2 value="00101000 00000000 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="63964"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w1000.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w1000.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w1000.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w1000.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x0" name="cid00001"/><!-- ???? -->
+      <map code="0x1" name="cid00001"/><!-- ???? -->
+      <map code="0x2" name="cid00001"/><!-- ???? -->
+      <map code="0x3" name="cid00001"/><!-- ???? -->
+      <map code="0x4" name="cid00001"/><!-- ???? -->
+      <map code="0x5" name="cid00001"/><!-- ???? -->
+      <map code="0x6" name="cid00001"/><!-- ???? -->
+      <map code="0x7" name="cid00001"/><!-- ???? -->
+      <map code="0x8" name="cid00001"/><!-- ???? -->
+      <map code="0x9" name="cid00001"/><!-- ???? -->
+      <map code="0xa" name="cid00001"/><!-- ???? -->
+      <map code="0xb" name="cid00001"/><!-- ???? -->
+      <map code="0xc" name="cid00001"/><!-- ???? -->
+      <map code="0xd" name="cid00001"/><!-- ???? -->
+      <map code="0xe" name="cid00001"/><!-- ???? -->
+      <map code="0xf" name="cid00001"/><!-- ???? -->
+      <map code="0x10" name="cid00001"/><!-- ???? -->
+      <map code="0x11" name="cid00001"/><!-- ???? -->
+      <map code="0x12" name="cid00001"/><!-- ???? -->
+      <map code="0x13" name="cid00001"/><!-- ???? -->
+      <map code="0x14" name="cid00001"/><!-- ???? -->
+      <map code="0x15" name="cid00001"/><!-- ???? -->
+      <map code="0x16" name="cid00001"/><!-- ???? -->
+      <map code="0x17" name="cid00001"/><!-- ???? -->
+      <map code="0x18" name="cid00001"/><!-- ???? -->
+      <map code="0x19" name="cid00001"/><!-- ???? -->
+      <map code="0x1a" name="cid00001"/><!-- ???? -->
+      <map code="0x1b" name="cid00001"/><!-- ???? -->
+      <map code="0x1c" name="cid00001"/><!-- ???? -->
+      <map code="0x1d" name="cid00001"/><!-- ???? -->
+      <map code="0x1e" name="cid00001"/><!-- ???? -->
+      <map code="0x1f" name="cid00001"/><!-- ???? -->
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+      <map code="0x21" name="cid00002"/><!-- EXCLAMATION MARK -->
+      <map code="0xa0" name="cid00001"/><!-- NO-BREAK SPACE -->
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="532" language="0" nGroups="43">
+      <map code="0x0" name="cid00001"/><!-- ???? -->
+      <map code="0x1" name="cid00001"/><!-- ???? -->
+      <map code="0x2" name="cid00001"/><!-- ???? -->
+      <map code="0x3" name="cid00001"/><!-- ???? -->
+      <map code="0x4" name="cid00001"/><!-- ???? -->
+      <map code="0x5" name="cid00001"/><!-- ???? -->
+      <map code="0x6" name="cid00001"/><!-- ???? -->
+      <map code="0x7" name="cid00001"/><!-- ???? -->
+      <map code="0x8" name="cid00001"/><!-- ???? -->
+      <map code="0x9" name="cid00001"/><!-- ???? -->
+      <map code="0xa" name="cid00001"/><!-- ???? -->
+      <map code="0xb" name="cid00001"/><!-- ???? -->
+      <map code="0xc" name="cid00001"/><!-- ???? -->
+      <map code="0xd" name="cid00001"/><!-- ???? -->
+      <map code="0xe" name="cid00001"/><!-- ???? -->
+      <map code="0xf" name="cid00001"/><!-- ???? -->
+      <map code="0x10" name="cid00001"/><!-- ???? -->
+      <map code="0x11" name="cid00001"/><!-- ???? -->
+      <map code="0x12" name="cid00001"/><!-- ???? -->
+      <map code="0x13" name="cid00001"/><!-- ???? -->
+      <map code="0x14" name="cid00001"/><!-- ???? -->
+      <map code="0x15" name="cid00001"/><!-- ???? -->
+      <map code="0x16" name="cid00001"/><!-- ???? -->
+      <map code="0x17" name="cid00001"/><!-- ???? -->
+      <map code="0x18" name="cid00001"/><!-- ???? -->
+      <map code="0x19" name="cid00001"/><!-- ???? -->
+      <map code="0x1a" name="cid00001"/><!-- ???? -->
+      <map code="0x1b" name="cid00001"/><!-- ???? -->
+      <map code="0x1c" name="cid00001"/><!-- ???? -->
+      <map code="0x1d" name="cid00001"/><!-- ???? -->
+      <map code="0x1e" name="cid00001"/><!-- ???? -->
+      <map code="0x1f" name="cid00001"/><!-- ???? -->
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+      <map code="0x21" name="cid00002"/><!-- EXCLAMATION MARK -->
+      <map code="0xa0" name="cid00001"/><!-- NO-BREAK SPACE -->
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_12>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uv="0x9686" uvs="0xfe00" name="cid13393"/>
+      <map uv="0x9686" uvs="0xe0101" name="cid13393"/>
+    </cmap_format_14>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x0" name="cid00001"/><!-- ???? -->
+      <map code="0x1" name="cid00001"/><!-- ???? -->
+      <map code="0x2" name="cid00001"/><!-- ???? -->
+      <map code="0x3" name="cid00001"/><!-- ???? -->
+      <map code="0x4" name="cid00001"/><!-- ???? -->
+      <map code="0x5" name="cid00001"/><!-- ???? -->
+      <map code="0x6" name="cid00001"/><!-- ???? -->
+      <map code="0x7" name="cid00001"/><!-- ???? -->
+      <map code="0x8" name="cid00001"/><!-- ???? -->
+      <map code="0x9" name="cid00001"/><!-- ???? -->
+      <map code="0xa" name="cid00001"/><!-- ???? -->
+      <map code="0xb" name="cid00001"/><!-- ???? -->
+      <map code="0xc" name="cid00001"/><!-- ???? -->
+      <map code="0xd" name="cid00001"/><!-- ???? -->
+      <map code="0xe" name="cid00001"/><!-- ???? -->
+      <map code="0xf" name="cid00001"/><!-- ???? -->
+      <map code="0x10" name="cid00001"/><!-- ???? -->
+      <map code="0x11" name="cid00001"/><!-- ???? -->
+      <map code="0x12" name="cid00001"/><!-- ???? -->
+      <map code="0x13" name="cid00001"/><!-- ???? -->
+      <map code="0x14" name="cid00001"/><!-- ???? -->
+      <map code="0x15" name="cid00001"/><!-- ???? -->
+      <map code="0x16" name="cid00001"/><!-- ???? -->
+      <map code="0x17" name="cid00001"/><!-- ???? -->
+      <map code="0x18" name="cid00001"/><!-- ???? -->
+      <map code="0x19" name="cid00001"/><!-- ???? -->
+      <map code="0x1a" name="cid00001"/><!-- ???? -->
+      <map code="0x1b" name="cid00001"/><!-- ???? -->
+      <map code="0x1c" name="cid00001"/><!-- ???? -->
+      <map code="0x1d" name="cid00001"/><!-- ???? -->
+      <map code="0x1e" name="cid00001"/><!-- ???? -->
+      <map code="0x1f" name="cid00001"/><!-- ???? -->
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+      <map code="0x21" name="cid00002"/><!-- EXCLAMATION MARK -->
+      <map code="0xa0" name="cid00001"/><!-- NO-BREAK SPACE -->
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="532" language="0" nGroups="43">
+      <map code="0x0" name="cid00001"/><!-- ???? -->
+      <map code="0x1" name="cid00001"/><!-- ???? -->
+      <map code="0x2" name="cid00001"/><!-- ???? -->
+      <map code="0x3" name="cid00001"/><!-- ???? -->
+      <map code="0x4" name="cid00001"/><!-- ???? -->
+      <map code="0x5" name="cid00001"/><!-- ???? -->
+      <map code="0x6" name="cid00001"/><!-- ???? -->
+      <map code="0x7" name="cid00001"/><!-- ???? -->
+      <map code="0x8" name="cid00001"/><!-- ???? -->
+      <map code="0x9" name="cid00001"/><!-- ???? -->
+      <map code="0xa" name="cid00001"/><!-- ???? -->
+      <map code="0xb" name="cid00001"/><!-- ???? -->
+      <map code="0xc" name="cid00001"/><!-- ???? -->
+      <map code="0xd" name="cid00001"/><!-- ???? -->
+      <map code="0xe" name="cid00001"/><!-- ???? -->
+      <map code="0xf" name="cid00001"/><!-- ???? -->
+      <map code="0x10" name="cid00001"/><!-- ???? -->
+      <map code="0x11" name="cid00001"/><!-- ???? -->
+      <map code="0x12" name="cid00001"/><!-- ???? -->
+      <map code="0x13" name="cid00001"/><!-- ???? -->
+      <map code="0x14" name="cid00001"/><!-- ???? -->
+      <map code="0x15" name="cid00001"/><!-- ???? -->
+      <map code="0x16" name="cid00001"/><!-- ???? -->
+      <map code="0x17" name="cid00001"/><!-- ???? -->
+      <map code="0x18" name="cid00001"/><!-- ???? -->
+      <map code="0x19" name="cid00001"/><!-- ???? -->
+      <map code="0x1a" name="cid00001"/><!-- ???? -->
+      <map code="0x1b" name="cid00001"/><!-- ???? -->
+      <map code="0x1c" name="cid00001"/><!-- ???? -->
+      <map code="0x1d" name="cid00001"/><!-- ???? -->
+      <map code="0x1e" name="cid00001"/><!-- ???? -->
+      <map code="0x1f" name="cid00001"/><!-- ???? -->
+      <map code="0x20" name="cid00001"/><!-- SPACE -->
+      <map code="0x21" name="cid00002"/><!-- EXCLAMATION MARK -->
+      <map code="0xa0" name="cid00001"/><!-- NO-BREAK SPACE -->
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_Kanji-w1000.00">
+      <ROS Registry="Adobe" Order="Japan1" Supplement="6"/>
+      <Notice value="1997-2007, 2012 Adobe Systems Incorporated. All Rights Reserved. Kozuka Mincho is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries.&quot;"/>
+      <FullName value="Master Set Kanji w1000.00"/>
+      <FamilyName value="Master Set Kanji"/>
+      <Weight value="ExtraLight"/>
+      <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="8 -120 995 880"/>
+      <StrokeWidth value="0"/>
+      <XUID value="1119273886"/>
+      <CIDFontVersion value="6.004"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="23058"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="MasterSet_Kanji-w1000.00-Generic"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="607"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="MasterSet_Kanji-w1000.00-Kanji"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="0"/>
+          </Private>
+        </FontDict>
+        <FontDict index="2">
+          <FontName value="MasterSet_Kanji-w1000.00-Proportional"/>
+          <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="683"/>
+            <nominalWidthX value="614"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          393 -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 hintmask 10111000
+          100 -120 rmoveto
+          800 0 rlineto
+          0 1000 rlineto
+          -800 0 rlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 0 rlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          0 -818 rlineto
+          -668 -41 rmoveto
+          318 409 rlineto
+          318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 rlineto
+          -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid00001" fdSelectIndex="2">
+          -405 endchar
+        </CharString>
+        <CharString name="cid00002" fdSelectIndex="2">
+          -316 -11 209 hstemhm
+          48 202 -182 161.5 hintmask 10100000
+          241 773 rmoveto
+          -185 0 rlineto
+          24 -515 rlineto
+          138 0 rlineto
+          hintmask 11000000
+          -69 -269 rmoveto
+          62 0 39 44 0 60 rrcurveto
+          0 62 -42 43 -59 0 rrcurveto
+          -57 0 -44 -43 0 -62 rrcurveto
+          0 -58 40 -46 61 0 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="cid01177" fdSelectIndex="1">
+          1000 -84 115 161 86 31 81 30 86 34 111 50 108 -111 157 -126 80 hstemhm
+          165 137 -23 141 6 146 59 122 -49 144 hintmask 1111100011111000
+          279 152 rmoveto
+          0 -92 rlineto
+          0 -109 33 -35 141 0 rrcurveto
+          28 0 89 0 30 0 rrcurveto
+          101 0 37 29 15 113 rrcurveto
+          -37 7 -57 19 -28 19 rrcurveto
+          -5 -62 -6 -10 -34 0 rrcurveto
+          -25 0 -71 0 -19 0 rrcurveto
+          -43 0 -8 3 0 28 rrcurveto
+          0 90 rlineto
+          -43 -2 rmoveto
+          55 -23 70 -40 33 -28 rrcurveto
+          88 89 rlineto
+          -38 28 -72 36 -54 19 rrcurveto
+          251 -110 rmoveto
+          58 -57 64 -81 26 -54 rrcurveto
+          121 69 rlineto
+          -30 57 -68 75 -58 53 rrcurveto
+          -678 -14 rmoveto
+          -22 -67 -45 -63 -59 -39 rrcurveto
+          113 -77 rlineto
+          69 49 39 75 27 78 rrcurveto
+          -153 684 rmoveto
+          0 -111 rlineto
+          770 0 rlineto
+          0 111 rlineto
+          -820 -158 rmoveto
+          0 -111 rlineto
+          874 0 rlineto
+          0 111 rlineto
+          hintmask 0000001000100000
+          -512 204 rmoveto
+          hintmask 0000000100100000
+          0 -126 rlineto
+          146 0 rlineto
+          hintmask 0000001000100000
+          0 126 rlineto
+          hintmask 0111010010001000
+          -322 -154 rmoveto
+          9 -25 10 -34 3 -21 rrcurveto
+          138 30 rlineto
+          -5 20 -11 32 -11 23 rrcurveto
+          232 0 rmoveto
+          -5 -21 -13 -29 -9 -24 rrcurveto
+          128 -26 rlineto
+          12 18 17 23 22 32 rrcurveto
+          -465 -390 rmoveto
+          402 0 rlineto
+          0 -31 rlineto
+          -402 0 rlineto
+          0 142 rmoveto
+          402 0 rlineto
+          0 -30 rlineto
+          -402 0 rlineto
+          -137 116 rmoveto
+          0 -314 rlineto
+          683 0 rlineto
+          0 314 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid06449" fdSelectIndex="1">
+          1000 -86 106 92 77 -23 23 30 20 -20 22.5 -22.5 90 -56 56 21 95 21 90 28 90 -6 148 -116 116 -116 175 hstemhm
+          61 129 -50 128 -7 144 -59 114 -55 40 89 131 -127 114 -60 145 -4 135 -60 136 -95 112 hintmask 110001011101011101101101
+          50 796 rmoveto
+          0 -116 rlineto
+          901 0 rlineto
+          0 116 rlineto
+          -800 -487 rmoveto
+          0 -90 rlineto
+          707 0 rlineto
+          0 90 rlineto
+          hintmask 000000000000100100000000
+          -597 546 rmoveto
+          hintmask 000000000010000100000000
+          0 -207 rlineto
+          144 0 rlineto
+          hintmask 000000000000100100001000
+          0 207 rlineto
+          187 0 rmoveto
+          hintmask 000000000010000000001000
+          0 -207 rlineto
+          145 0 rlineto
+          hintmask 000000000000100000001000
+          0 207 rlineto
+          hintmask 000000111100011010010100
+          -85 -291 rmoveto
+          81 0 rlineto
+          0 -28 rlineto
+          -81 0 rlineto
+          -192 28 rmoveto
+          78 0 rlineto
+          0 -28 rlineto
+          -78 0 rlineto
+          -192 28 rmoveto
+          78 0 rlineto
+          0 -28 rlineto
+          -78 0 rlineto
+          -128 118 rmoveto
+          0 -208 rlineto
+          728 0 rlineto
+          0 208 rlineto
+          -807 -229 rmoveto
+          0 -172 rlineto
+          129 0 rlineto
+          0 77 rlineto
+          hintmask 000000110000000000000010
+          618 0 rlineto
+          0 -77 rlineto
+          136 0 rlineto
+          0 172 rlineto
+          hintmask 000010000000000100000000
+          -673 -173 rmoveto
+          -39 -47 -72 -41 -108 -29 rrcurveto
+          22 -16 31 -39 14 -25 rrcurveto
+          hintmask 010000000000000001000000
+          124 44 81 54 57 78 rrcurveto
+          24 -42 rmoveto
+          0 -13 rlineto
+          -48 -105 -135 -52 -170 -18 rrcurveto
+          20 -24 24 -45 9 -29 rrcurveto
+          hintmask 001000000000000001000000
+          197 32 152 70 67 161 rrcurveto
+          hintmask 110100000000000001100001
+          -72 26 rlineto
+          -21 -3 rlineto
+          -248 -133 rmoveto
+          25 -16 33 -29 17 -20 rrcurveto
+          82 54 rlineto
+          -18 18 -32 25 -28 15 rrcurveto
+          10 86 rmoveto
+          -77 -77 rlineto
+          253 0 rlineto
+          0 77 rlineto
+          89 50 rmoveto
+          0 -186 rlineto
+          0 -106 30 -33 126 0 rrcurveto
+          25 0 81 0 27 0 rrcurveto
+          90 0 34 28 14 99 rrcurveto
+          -35 7 -51 18 -26 17 rrcurveto
+          -4 -53 -7 -10 -29 0 rrcurveto
+          -20 0 -65 0 -16 0 rrcurveto
+          -37 0 -6 4 0 30 rrcurveto
+          0 185 rlineto
+          150 -26 rmoveto
+          -55 -24 -92 -21 -84 -13 rrcurveto
+          13 -23 15 -40 6 -24 rrcurveto
+          93 11 112 20 85 32 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="cid06821" fdSelectIndex="1">
+          1000 -82 111 26 82 31 59 -59 78 31 79 -7 124 -124 84 74 46 -46 69 -47 47 23 56 -56 97 -16 95 -95 173 hstemhm
+          100 131 -63 63 93 115 -91 91 -91 121 -86 56 -11 147 -73 73 -50 130 64 150 14 101 hintmask 00011000000000000000000100000000
+          286 277 rmoveto
+          433 0 rlineto
+          0 -31 rlineto
+          -433 0 rlineto
+          -140 110 rmoveto
+          0 -188 rlineto
+          723 0 rlineto
+          0 188 rlineto
+          hintmask 00100000000000000000100000000000
+          -441 -129 rmoveto
+          0 -248 rlineto
+          147 -32 rlineto
+          0 280 rlineto
+          hintmask 01000000000010010000010000000000
+          -73 -90 rmoveto
+          0 -82 rlineto
+          370 0 rlineto
+          0 82 rlineto
+          -704 638 rmoveto
+          0 -95 rlineto
+          784 0 rlineto
+          0 95 rlineto
+          hintmask 00000000000010100000000000000000
+          -852 0 rmoveto
+          0 -116 rlineto
+          0 -74 -7 -95 -76 -71 rrcurveto
+          28 -16 57 -47 20 -24 rrcurveto
+          90 85 19 137 0 103 rrcurveto
+          0 118 rlineto
+          hintmask 00000000010100001000000000000000
+          93 -79 rmoveto
+          0 -167 rlineto
+          hintmask 00000000010100000100000000000000
+          115 0 rlineto
+          0 167 rlineto
+          hintmask 00000000101000000001000000000000
+          -56 -41 rmoveto
+          0 -56 rlineto
+          176 0 rlineto
+          0 56 rlineto
+          -343 -79 rmoveto
+          0 -69 rlineto
+          383 0 rlineto
+          0 69 rlineto
+          hintmask 00000011000000000100000000000000
+          -251 -23 rmoveto
+          0 -109 rlineto
+          0 -8 -3 -3 -8 0 rrcurveto
+          -7 0 -20 0 -17 1 rrcurveto
+          13 -22 19 -35 8 -28 rrcurveto
+          hintmask 00000011000001000010001000000000
+          39 0 32 0 29 14 rrcurveto
+          29 14 7 17 0 46 rrcurveto
+          0 113 rlineto
+          -220 -49 rmoveto
+          -13 -34 -24 -34 -31 -26 rrcurveto
+          21 -12 35 -24 17 -14 rrcurveto
+          33 30 31 47 18 46 rrcurveto
+          123 -9 rmoveto
+          24 -30 24 -42 9 -28 rrcurveto
+          79 30 rlineto
+          -10 28 -26 41 -25 29 rrcurveto
+          -9 351 rmoveto
+          hintmask 10000101000001000000001010000000
+          22 -272 141 -231 154 -1 rrcurveto
+          83 0 41 26 18 132 rrcurveto
+          -32 10 -43 23 -26 24 rrcurveto
+          -5 -66 -8 -25 -20 0 rrcurveto
+          -70 0 -109 184 -16 196 rrcurveto
+          122 -178 rmoveto
+          -42 -96 -82 -86 -94 -52 rrcurveto
+          27 -19 46 -42 20 -23 rrcurveto
+          96 64 93 105 54 117 rrcurveto
+          -205 150 rmoveto
+          49 -16 64 -28 32 -21 rrcurveto
+          63 74 rlineto
+          -35 21 -65 25 -48 12 rrcurveto
+          -451 -736 rmoveto
+          -96 -33 rlineto
+          77 -146 117 -27 215 0 rrcurveto
+          234 0 rlineto
+          68 0 rlineto
+          5 33 18 52 16 26 rrcurveto
+          -72 -3 -209 0 -54 0 rrcurveto
+          -159 1 -110 10 -50 87 rrcurveto
+          -109 40 rmoveto
+          -18 -82 -58 -57 -91 -34 rrcurveto
+          30 -21 53 -48 22 -25 rrcurveto
+          100 50 72 81 30 120 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="cid07253" fdSelectIndex="1">
+          1000 -94 86 19 87 -85 85 -84 84 -69 69 -35 35 -33 33 22 49 22 49 21 45 -45 63 23 90 -60 60 24 21 -21 67 -56 56 23 64 23 67 -30 147 -92 92 -92 125 hstemhm
+          153 132 -81 136 -44 44 -42 107 37 128 22 143 -26 139 -68 46 hintmask 10000011101100101101000101110000
+          58 822 rmoveto
+          0 -92 rlineto
+          883 0 rlineto
+          0 92 rlineto
+          hintmask 00000000000000000000100000000000
+          -677 33 rmoveto
+          hintmask 00000000000000000010000000000000
+          0 -180 rlineto
+          141 0 rlineto
+          hintmask 00000000000000000000100000100000
+          0 180 rlineto
+          187 0 rmoveto
+          hintmask 00000000000000000010000000100000
+          0 -180 rlineto
+          143 0 rlineto
+          hintmask 00000000000100101100110000110000
+          0 180 rlineto
+          -450 -304 rmoveto
+          424 0 rlineto
+          0 -23 rlineto
+          -424 0 rlineto
+          0 110 rmoveto
+          424 0 rlineto
+          0 -23 rlineto
+          -424 0 rlineto
+          -132 90 rmoveto
+          0 -244 rlineto
+          695 0 rlineto
+          0 244 rlineto
+          -807 -268 rmoveto
+          0 -90 rlineto
+          919 0 rlineto
+          0 90 rlineto
+          hintmask 00000001100000000000000100000000
+          -664 -197 rmoveto
+          0 -49 rlineto
+          469 0 rlineto
+          0 49 rlineto
+          -469 -71 rmoveto
+          0 -49 rlineto
+          482 0 rlineto
+          0 49 rlineto
+          hintmask 00000010000000000000001000000000
+          -574 -71 rmoveto
+          hintmask 00001000000000000000001000001000
+          0 -69 rlineto
+          622 0 rlineto
+          hintmask 00000010000001000000000000001000
+          0 69 rlineto
+          -511 394 rmoveto
+          -38 -100 -88 -88 -172 -56 rrcurveto
+          25 -21 36 -48 13 -31 rrcurveto
+          hintmask 00000100010010010000000001000000
+          192 75 103 107 59 142 rrcurveto
+          263 -61 rmoveto
+          -104 -34 rlineto
+          65 -100 107 -78 129 -36 rrcurveto
+          18 33 37 49 28 25 rrcurveto
+          -120 23 -106 51 -54 67 rrcurveto
+          -266 -105 rmoveto
+          0 -243 rlineto
+          128 0 rlineto
+          0 243 rlineto
+          hintmask 10000010000000000000000000001000
+          210 -208 rmoveto
+          0 -12 rlineto
+          -5 -52 -8 -25 -9 -9 rrcurveto
+          -6 -7 -6 -1 -9 0 rrcurveto
+          -10 0 -16 1 -21 2 rrcurveto
+          13 -24 11 -39 1 -28 rrcurveto
+          37 -1 33 1 19 2 rrcurveto
+          21 2 21 7 17 17 rrcurveto
+          20 19 11 39 9 68 rrcurveto
+          2 15 2 25 0 0 rrcurveto
+          -742 -45 rmoveto
+          -15 -36 -32 -39 -39 -23 rrcurveto
+          101 -56 rlineto
+          hintmask 10010000000000000000000000000000
+          45 29 26 41 18 45 rrcurveto
+          hintmask 00100000000000000000000010000000
+          14 -1 rmoveto
+          9 -33 7 -45 -1 -28 rrcurveto
+          107 17 rlineto
+          -1 27 -9 44 -11 32 rrcurveto
+          hintmask 01000000000000000000000000100000
+          33 -16 rmoveto
+          22 -29 21 -41 8 -27 rrcurveto
+          97 32 rlineto
+          -10 27 -22 38 -24 26 rrcurveto
+          hintmask 00010000001000000000001000000000
+          52 -29 rmoveto
+          24 -21 27 -32 11 -21 rrcurveto
+          84 42 rlineto
+          -13 22 -29 29 -25 20 rrcurveto
+          -325 277 rmoveto
+          -63 -63 rlineto
+          437 0 rlineto
+          0 63 rlineto
+          -485 -44 rmoveto
+          hintmask 00000010000000000000001000000000
+          0 -215 rlineto
+          hintmask 00000010001000000000000100000000
+          136 0 rlineto
+          0 261 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid13393" fdSelectIndex="1">
+          1000 -76 112 -29 29 34 99 -55 124 -35 101 -101 173 -19 98 231 103 -41 41 -26 49 hstemhm
+          66 126 61 127 -122 44 305 135 60 40 hintmask 1010101101110110
+          558 789 rmoveto
+          0 -103 rlineto
+          284 0 rlineto
+          0 103 rlineto
+          -379 -485 rmoveto
+          0 -101 rlineto
+          469 0 rlineto
+          0 101 rlineto
+          -428 151 rmoveto
+          0 -98 rlineto
+          343 0 rlineto
+          0 98 rlineto
+          -472 -419 rmoveto
+          0 -112 rlineto
+          590 0 rlineto
+          0 112 rlineto
+          -512 133 rmoveto
+          0 -99 rlineto
+          455 0 rlineto
+          0 99 rlineto
+          -106 620 rmoveto
+          0 -19 rlineto
+          -68 -125 -188 -94 -197 -38 rrcurveto
+          25 -27 30 -51 14 -32 rrcurveto
+          hintmask 0000000010000010
+          218 54 203 108 96 183 rrcurveto
+          hintmask 0100010100000110
+          -87 46 rlineto
+          -21 -5 rlineto
+          -254 -64 rmoveto
+          -111 -39 rlineto
+          97 -139 155 -90 200 -39 rrcurveto
+          17 34 36 52 28 26 rrcurveto
+          -189 27 -156 68 -77 100 rrcurveto
+          1 133 rmoveto
+          -38 -72 -71 -75 -109 -55 rrcurveto
+          28 -19 42 -46 19 -30 rrcurveto
+          125 75 80 90 60 107 rrcurveto
+          -266 -459 rmoveto
+          -19 -59 -35 -61 -44 -40 rrcurveto
+          28 -15 48 -31 23 -19 rrcurveto
+          45 47 45 76 25 74 rrcurveto
+          47 30 rmoveto
+          0 -369 rlineto
+          135 0 rlineto
+          0 369 rlineto
+          hintmask 0000000000101000
+          -676 436 rmoveto
+          0 -908 rlineto
+          126 0 rlineto
+          0 779 rlineto
+          110 0 rlineto
+          0 129 rlineto
+          -44 0 rmoveto
+          0 -43 rlineto
+          -11 -62 -29 -153 -24 -90 rrcurveto
+          hintmask 0001000000010000
+          49 -63 10 -61 0 -42 rrcurveto
+          0 -29 -5 -17 -10 -8 rrcurveto
+          -7 -5 -10 -3 -9 0 rrcurveto
+          -11 0 -10 0 -15 2 rrcurveto
+          20 -35 10 -54 1 -35 rrcurveto
+          24 -1 23 1 17 2 rrcurveto
+          23 4 20 7 17 13 rrcurveto
+          34 24 15 43 0 71 rrcurveto
+          0 57 -10 68 -56 76 rrcurveto
+          hintmask 0000000001001000
+          26 79 32 115 25 90 rrcurveto
+          hintmask 0000000000001000
+          -95 54 rlineto
+          -20 -5 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid17290" fdSelectIndex="1">
+          1000 92 122 -43 43 65 120 -120 198 -24 99 55 92 32 91 hstemhm
+          118 138 72 115 -64 64 0 133 -67 107 -76 99 -99 222 -115 115 -64 64 69 109 -74 99 hintmask 011011111011001010000000
+          328 822 rmoveto
+          0 -240 rlineto
+          0 -100 26 -29 92 0 rrcurveto
+          hintmask 000010000000100000000000
+          19 0 44 0 20 0 rrcurveto
+          68 0 30 25 12 85 rrcurveto
+          hintmask 101010101000010000000000
+          -31 7 -46 16 -22 17 rrcurveto
+          -3 -43 -4 -8 -17 0 rrcurveto
+          -10 0 -32 0 -8 0 rrcurveto
+          -20 0 -3 3 0 28 rrcurveto
+          0 239 rlineto
+          -115 -423 rmoveto
+          0 -120 rlineto
+          622 0 rlineto
+          0 120 rlineto
+          -649 -185 rmoveto
+          0 -122 rlineto
+          671 0 rlineto
+          0 122 rlineto
+          hintmask 010101100111001000000000
+          -529 263 rmoveto
+          0 -306 rlineto
+          133 0 rlineto
+          0 306 rlineto
+          116 -1 rmoveto
+          0 -306 rlineto
+          134 0 rlineto
+          0 306 rlineto
+          -447 346 rmoveto
+          0 -91 rlineto
+          130 0 rlineto
+          0 -32 rlineto
+          -130 0 rlineto
+          0 -92 rlineto
+          237 0 rlineto
+          0 215 rlineto
+          31 0 rmoveto
+          hintmask 000010100000001001000000
+          0 -240 rlineto
+          0 -100 26 -29 93 0 rrcurveto
+          20 0 47 0 21 0 rrcurveto
+          69 0 30 26 12 88 rrcurveto
+          -31 7 -46 16 -22 17 rrcurveto
+          -3 -46 -5 -9 -17 0 rrcurveto
+          -11 0 -35 0 -9 0 rrcurveto
+          -21 0 -3 3 0 29 rrcurveto
+          0 238 rlineto
+          hintmask 000001110000000110000000
+          -64 0 rmoveto
+          0 -91 rlineto
+          133 0 rlineto
+          0 -32 rlineto
+          -133 0 rlineto
+          0 -92 rlineto
+          242 0 rlineto
+          0 215 rlineto
+          -456 -735 rmoveto
+          -41 -39 -82 -31 -84 -18 rrcurveto
+          30 -23 50 -49 23 -27 rrcurveto
+          87 30 96 52 55 63 rrcurveto
+          46 -15 rmoveto
+          66 -33 83 -52 39 -35 rrcurveto
+          122 71 rlineto
+          -47 37 -86 49 -64 28 rrcurveto
+          -580 760 rmoveto
+          -41 -142 -71 -142 -77 -91 rrcurveto
+          22 -39 35 -85 11 -36 rrcurveto
+          104 122 94 197 57 176 rrcurveto
+          -213 -251 rmoveto
+          0 -658 rlineto
+          138 0 rlineto
+          0 790 rlineto
+          -4 2 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid17852" fdSelectIndex="1">
+          1000 -89 107 113 107 62 111 -17 17 70 111 -111 183 -133 130 -125 125 -5 120 -120 199 -153 74 hstemhm
+          37 238 -133 133 -133 202 14 128 -59 59 -40 40 13 118 -68 68 -11 136 -29 120 7 121 -76 103 hintmask 000000100001000000000000
+          37 661 rmoveto
+          0 -130 rlineto
+          hintmask 000000100000010000000000
+          307 0 rlineto
+          0 130 rlineto
+          hintmask 000000010010100100000000
+          -202 194 rmoveto
+          0 -950 rlineto
+          133 0 rlineto
+          0 950 rlineto
+          -127 -291 rmoveto
+          -21 -118 -53 -156 -61 -93 rrcurveto
+          21 -35 31 -55 13 -40 rrcurveto
+          72 111 54 200 29 158 rrcurveto
+          36 -15 rmoveto
+          -59 -73 rlineto
+          27 -50 51 -103 24 -62 rrcurveto
+          72 129 rlineto
+          -16 23 -78 112 -21 24 rrcurveto
+          158 255 rmoveto
+          hintmask 000000001000000100000000
+          0 -120 rlineto
+          536 0 rlineto
+          hintmask 010000000010000000100000
+          0 120 rlineto
+          -414 -538 rmoveto
+          0 -107 rlineto
+          149 0 rlineto
+          0 107 rlineto
+          hintmask 000000000100000001010000
+          -199 85 rmoveto
+          0 -320 rlineto
+          118 0 rlineto
+          0 320 rlineto
+          -11 532 rmoveto
+          hintmask 000000000010000000010000
+          0 -153 rlineto
+          136 0 rlineto
+          hintmask 000101000100000000010000
+          0 153 rlineto
+          -215 -191 rmoveto
+          0 -270 rlineto
+          113 0 rlineto
+          0 270 rlineto
+          42 0 rmoveto
+          0 -270 rlineto
+          113 0 rlineto
+          0 270 rlineto
+          109 -388 rmoveto
+          -40 -32 -66 -44 -43 -21 rrcurveto
+          57 -87 rlineto
+          45 22 67 36 46 35 rrcurveto
+          hintmask 101010000000000010001100
+          -524 407 rmoveto
+          0 -111 rlineto
+          394 0 rlineto
+          0 -70 rlineto
+          -394 0 rlineto
+          0 -111 rlineto
+          515 0 rlineto
+          0 292 rlineto
+          -248 -269 rmoveto
+          0 -273 rlineto
+          0 -105 19 -34 91 0 rrcurveto
+          hintmask 100000000010001000001010
+          17 0 22 0 18 0 rrcurveto
+          68 0 29 34 11 118 rrcurveto
+          -32 8 -48 19 -23 18 rrcurveto
+          -3 -77 -3 -13 -13 0 rrcurveto
+          -4 0 -13 0 -4 0 rrcurveto
+          -11 0 -1 3 0 29 rrcurveto
+          0 273 rlineto
+          -387 -293 rmoveto
+          15 -121 rlineto
+          74 11 85 12 82 13 rrcurveto
+          -7 115 rlineto
+          -92 -12 -91 -11 -66 -7 rrcurveto
+          -88 746 rmoveto
+          0 -327 rlineto
+          0 -142 -5 -196 -70 -132 rrcurveto
+          29 -14 56 -41 22 -23 rrcurveto
+          82 148 14 240 0 160 rrcurveto
+          0 327 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid18480" fdSelectIndex="1">
+          1000 -84 132 295 117 42 117 20 164 -129 129 -129 181 hstemhm
+          143 144 -61 61 -35 144 -67 141 131 145 6 144 -66 132 hintmask 1110100101110000
+          56 803 rmoveto
+          0 -129 rlineto
+          892 0 rlineto
+          0 129 rlineto
+          hintmask 0000010010000000
+          -696 52 rmoveto
+          hintmask 0001000010000000
+          0 -216 rlineto
+          144 0 rlineto
+          hintmask 0000010010100000
+          0 216 rlineto
+          205 0 rmoveto
+          hintmask 0001000000100000
+          0 -216 rlineto
+          145 0 rlineto
+          hintmask 0010011000100000
+          0 216 rlineto
+          -603 -236 rmoveto
+          0 -216 rlineto
+          0 -126 -9 -174 -110 -117 rrcurveto
+          34 -16 63 -44 26 -25 rrcurveto
+          hintmask 1110000101010000
+          119 131 21 218 0 151 rrcurveto
+          0 218 rlineto
+          -61 0 rmoveto
+          0 -117 rlineto
+          526 0 rlineto
+          0 -42 rlineto
+          -526 0 rlineto
+          0 -117 rlineto
+          670 0 rlineto
+          0 276 rlineto
+          -567 -300 rmoveto
+          0 -231 rlineto
+          0 -132 45 -40 171 0 rrcurveto
+          35 0 144 0 38 0 rrcurveto
+          hintmask 1000000001001000
+          140 0 41 39 19 155 rrcurveto
+          -39 8 -61 21 -32 22 rrcurveto
+          -7 -99 -10 -14 -60 0 rrcurveto
+          -41 0 -124 0 -33 0 rrcurveto
+          -74 0 -11 4 0 39 rrcurveto
+          0 228 rlineto
+          309 12 rmoveto
+          -93 -45 -152 -37 -143 -21 rrcurveto
+          16 -29 20 -51 6 -32 rrcurveto
+          153 19 175 35 131 57 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="cid22370" fdSelectIndex="1">
+          1000 62 77 23 73 22 73 22 77 24 93 28 94 -16 149 -103 103 -103 157 hstemhm
+          114 128 -76 141 -39 141 -75 114 94 114 -69 142 -30 148 -95 135 hintmask 111111010011001100000000
+          50 801 rmoveto
+          0 -103 rlineto
+          901 0 rlineto
+          0 103 rlineto
+          hintmask 000000001001000000000000
+          -683 54 rmoveto
+          hintmask 000000100001000000000000
+          0 -203 rlineto
+          141 0 rlineto
+          hintmask 000000001001001000000000
+          0 203 rlineto
+          178 0 rmoveto
+          hintmask 000000100000001000000000
+          0 -203 rlineto
+          142 0 rlineto
+          hintmask 000000001000001000000000
+          0 203 rlineto
+          hintmask 000011000100110010000000
+          -73 -281 rmoveto
+          96 0 rlineto
+          0 -28 rlineto
+          -96 0 rlineto
+          -208 28 rmoveto
+          94 0 rlineto
+          0 -28 rlineto
+          -94 0 rlineto
+          -206 28 rmoveto
+          92 0 rlineto
+          0 -28 rlineto
+          -92 0 rlineto
+          -128 122 rmoveto
+          0 -215 rlineto
+          773 0 rlineto
+          0 215 rlineto
+          hintmask 111100000010000100000000
+          -580 -411 rmoveto
+          392 0 rlineto
+          0 -22 rlineto
+          -392 0 rlineto
+          0 -73 rmoveto
+          392 0 rlineto
+          0 -23 rlineto
+          -392 0 rlineto
+          0 213 rmoveto
+          392 0 rlineto
+          0 -22 rlineto
+          -392 0 rlineto
+          -141 99 rmoveto
+          0 -367 rlineto
+          681 0 rlineto
+          0 367 rlineto
+          -312 -424 rmoveto
+          108 -32 106 -41 57 -28 rrcurveto
+          184 66 rlineto
+          -77 29 -135 44 -111 31 rrcurveto
+          -333 -2 rmoveto
+          -71 -32 -124 -28 -112 -15 rrcurveto
+          30 -22 48 -48 24 -27 rrcurveto
+          106 24 136 43 87 48 rrcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <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="209" lsb="0"/>
+    <mtx name="cid00002" width="298" lsb="48"/>
+    <mtx name="cid01177" width="1000" lsb="19"/>
+    <mtx name="cid06449" width="1000" lsb="50"/>
+    <mtx name="cid06821" width="1000" lsb="17"/>
+    <mtx name="cid07253" width="1000" lsb="17"/>
+    <mtx name="cid13393" width="1000" lsb="66"/>
+    <mtx name="cid17290" width="1000" lsb="8"/>
+    <mtx name="cid17852" width="1000" lsb="13"/>
+    <mtx name="cid18480" width="1000" lsb="24"/>
+    <mtx name="cid22370" width="1000" lsb="27"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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"/>
+    <mtx name="cid00002" height="1000" tsb="107"/>
+    <mtx name="cid01177" height="1000" tsb="25"/>
+    <mtx name="cid06449" height="1000" tsb="25"/>
+    <mtx name="cid06821" height="1000" tsb="20"/>
+    <mtx name="cid07253" height="1000" tsb="25"/>
+    <mtx name="cid13393" height="1000" tsb="22"/>
+    <mtx name="cid17290" height="1000" tsb="25"/>
+    <mtx name="cid17852" height="1000" tsb="25"/>
+    <mtx name="cid18480" height="1000" tsb="25"/>
+    <mtx name="cid22370" height="1000" tsb="25"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w439.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w439.00.ttx
new file mode 100644
index 0000000..5a0df52
--- /dev/null
+++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w439.00.ttx
@@ -0,0 +1,466 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="cid06821"/>
+    <GlyphID id="2" name="cid18480"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0x3ae58124"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:06:43 2018"/>
+    <modified value="Tue Apr 16 22:17:21 2019"/>
+    <xMin value="29"/>
+    <yMin value="-120"/>
+    <xMax value="973"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="29"/>
+    <minRightSideBearing value="27"/>
+    <xMaxExtent value="973"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="3"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="977"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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="00001000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBE"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="33512"/>
+    <usLastCharIndex value="36441"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w439.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w439.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w439.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w439.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_Kanji-w439.00">
+      <ROS Registry="Adobe" Order="Japan1" Supplement="6"/>
+      <Notice value="1997-2007, 2012 Adobe Systems Incorporated. All Rights Reserved. Kozuka Mincho is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries.&quot;"/>
+      <FullName value="Master Set Kanji w439.00"/>
+      <FamilyName value="Master Set Kanji"/>
+      <Weight value="ExtraLight"/>
+      <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="29 -120 973 880"/>
+      <StrokeWidth value="0"/>
+      <XUID value="1119273886"/>
+      <CIDFontVersion value="6.004"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="23058"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="MasterSet_Kanji-w439.00-Generic"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="607"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="MasterSet_Kanji-w439.00-Kanji"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="0"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          393 -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 hintmask 10111000
+          100 -120 rmoveto
+          800 0 rlineto
+          0 1000 rlineto
+          -800 0 rlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 0 rlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          0 -818 rlineto
+          -668 -41 rmoveto
+          318 409 rlineto
+          318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 rlineto
+          -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid06821" fdSelectIndex="1">
+          1000 -69 66 68 52 53 38 -38 51 67 51 8 72 -64 52 118 31 -31 47 -27 27 47 42 -42 93 -10 58 -58 144 hstemhm
+          115 74 -37 37 153 67 -51 51 -51 70 -51 32 50 81 -38 38 19 74 142 82 55 61 hintmask 00011000000000000000000100000000
+          234 288 rmoveto
+          541 0 rlineto
+          0 -67 rlineto
+          -541 0 rlineto
+          -78 118 rmoveto
+          0 -169 rlineto
+          701 0 rlineto
+          0 169 rlineto
+          hintmask 00100000000000000000100000000000
+          -398 -131 rmoveto
+          0 -239 rlineto
+          81 -16 rlineto
+          0 255 rlineto
+          hintmask 01000000000010010000010000000000
+          -38 -91 rmoveto
+          0 -52 rlineto
+          385 0 rlineto
+          0 52 rlineto
+          -735 643 rmoveto
+          0 -58 rlineto
+          799 0 rlineto
+          0 58 rlineto
+          hintmask 00000000000010100000000000000000
+          -836 0 rmoveto
+          0 -131 rlineto
+          0 -79 -10 -102 -76 -77 rrcurveto
+          17 -9 31 -26 11 -14 rrcurveto
+          84 85 17 127 0 94 rrcurveto
+          0 132 rlineto
+          hintmask 00000000010100001000000000000000
+          153 -48 rmoveto
+          0 -167 rlineto
+          hintmask 00000000010100000100000000000000
+          67 0 rlineto
+          0 167 rlineto
+          hintmask 00000000101000000001000000000000
+          -32 -51 rmoveto
+          0 -42 rlineto
+          181 0 rlineto
+          0 42 rlineto
+          -355 -89 rmoveto
+          0 -47 rlineto
+          388 0 rlineto
+          0 47 rlineto
+          hintmask 00000011000000000100000000000000
+          -233 -16 rmoveto
+          0 -137 rlineto
+          0 -8 -2 -3 -10 -1 rrcurveto
+          -9 0 -26 0 -34 1 rrcurveto
+          9 -15 12 -21 4 -17 rrcurveto
+          hintmask 00000011000001000010001000000000
+          45 0 31 0 22 9 rrcurveto
+          23 9 5 13 0 32 rrcurveto
+          0 138 rlineto
+          -171 -49 rmoveto
+          -19 -39 -33 -36 -39 -29 rrcurveto
+          14 -7 23 -16 10 -9 rrcurveto
+          38 30 38 47 23 46 rrcurveto
+          140 -8 rmoveto
+          27 -24 29 -33 13 -24 rrcurveto
+          47 24 rlineto
+          -14 24 -30 33 -27 21 rrcurveto
+          62 339 rmoveto
+          hintmask 10000101000001000000001010000000
+          17 -280 136 -219 157 0 rrcurveto
+          64 0 29 31 11 118 rrcurveto
+          -19 6 -25 13 -17 15 rrcurveto
+          -4 -81 -9 -30 -26 0 rrcurveto
+          -108 -2 -118 185 -14 244 rrcurveto
+          170 -173 rmoveto
+          -57 -121 -107 -100 -119 -63 rrcurveto
+          15 -11 26 -24 11 -13 rrcurveto
+          119 70 113 109 65 135 rrcurveto
+          -176 151 rmoveto
+          51 -16 63 -26 34 -21 rrcurveto
+          36 47 rlineto
+          -35 21 -64 24 -50 13 rrcurveto
+          -452 -751 rmoveto
+          -56 -23 rlineto
+          84 -121 133 -22 225 0 rrcurveto
+          206 0 rlineto
+          66 0 rlineto
+          4 19 11 31 10 16 rrcurveto
+          -51 -1 -208 0 -36 0 rrcurveto
+          -190 0 -132 13 -66 88 rrcurveto
+          -45 52 rmoveto
+          -25 -90 -77 -59 -100 -35 rrcurveto
+          18 -12 30 -28 12 -14 rrcurveto
+          103 45 87 72 31 112 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="cid18480" fdSelectIndex="1">
+          1000 -77 75 369 68 105 68 29 132 -73 73 -73 150 hstemhm
+          152 80 -37 37 55 80 -32 78 215 81 92 80 -18 75 hintmask 1110100101110000
+          57 769 rmoveto
+          0 -73 rlineto
+          889 0 rlineto
+          0 73 rlineto
+          hintmask 0000010010000000
+          -659 77 rmoveto
+          hintmask 0001000010000000
+          0 -209 rlineto
+          80 0 rlineto
+          hintmask 0000010010100000
+          0 209 rlineto
+          261 0 rmoveto
+          hintmask 0001000000100000
+          0 -209 rlineto
+          81 0 rlineto
+          hintmask 0010011000100000
+          0 209 rlineto
+          -557 -238 rmoveto
+          0 -211 rlineto
+          0 -132 -12 -179 -110 -128 rrcurveto
+          19 -9 34 -24 15 -14 rrcurveto
+          hintmask 1110000101010000
+          115 135 19 205 0 145 rrcurveto
+          0 212 rlineto
+          -37 0 rmoveto
+          0 -68 rlineto
+          606 0 rlineto
+          0 -105 rlineto
+          -606 0 rlineto
+          0 -68 rlineto
+          686 0 rlineto
+          0 241 rlineto
+          -546 -272 rmoveto
+          0 -293 rlineto
+          0 -95 37 -25 134 0 rrcurveto
+          28 0 219 0 30 0 rrcurveto
+          hintmask 1000000001001000
+          116 0 26 35 13 137 rrcurveto
+          -22 5 -34 12 -19 13 rrcurveto
+          -7 -109 -10 -18 -66 0 rrcurveto
+          -49 0 -187 0 -37 0 rrcurveto
+          -80 0 -14 8 0 38 rrcurveto
+          0 292 rlineto
+          385 -21 rmoveto
+          -97 -49 -177 -47 -157 -32 rrcurveto
+          9 -17 12 -28 4 -18 rrcurveto
+          164 31 185 46 125 56 rrcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="cid06821" width="1000" lsb="29"/>
+    <mtx name="cid18480" width="1000" lsb="30"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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="cid06821" height="1000" tsb="32"/>
+    <mtx name="cid18480" height="1000" tsb="34"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w440.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w440.00.ttx
new file mode 100644
index 0000000..b0a8ffa
--- /dev/null
+++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w440.00.ttx
@@ -0,0 +1,466 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="cid06821"/>
+    <GlyphID id="2" name="cid18480"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0xc82be45b"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:06:51 2018"/>
+    <modified value="Tue Apr 16 22:17:21 2019"/>
+    <xMin value="29"/>
+    <yMin value="-120"/>
+    <xMax value="973"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="29"/>
+    <minRightSideBearing value="27"/>
+    <xMaxExtent value="973"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="3"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="977"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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="00001000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBE"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="33512"/>
+    <usLastCharIndex value="36441"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w440.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w440.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w440.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w440.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x82e8" name="cid18480"/><!-- CJK UNIFIED IDEOGRAPH-82E8 -->
+      <map code="0x8e59" name="cid06821"/><!-- CJK UNIFIED IDEOGRAPH-8E59 -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_Kanji-w440.00">
+      <ROS Registry="Adobe" Order="Japan1" Supplement="6"/>
+      <Notice value="1997-2007, 2012 Adobe Systems Incorporated. All Rights Reserved. Kozuka Mincho is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries.&quot;"/>
+      <FullName value="Master Set Kanji w440.00"/>
+      <FamilyName value="Master Set Kanji"/>
+      <Weight value="ExtraLight"/>
+      <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="29 -120 973 880"/>
+      <StrokeWidth value="0"/>
+      <XUID value="1119273886"/>
+      <CIDFontVersion value="6.004"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="23058"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="MasterSet_Kanji-w440.00-Generic"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="607"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="MasterSet_Kanji-w440.00-Kanji"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="0"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          393 -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 hintmask 10111000
+          100 -120 rmoveto
+          800 0 rlineto
+          0 1000 rlineto
+          -800 0 rlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 0 rlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          0 -818 rlineto
+          -668 -41 rmoveto
+          318 409 rlineto
+          318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 rlineto
+          -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid06821" fdSelectIndex="1">
+          1000 -69 66 68 52 53 39 -39 51 67 51 8 72 -64 52 118 31 -31 47 -27 27 47 42 -42 93 -10 58 -58 144 hstemhm
+          115 75 -38 38 152 67 -51 51 -51 70 -51 32 50 81 -38 38 19 74 141 83 55 61 hintmask 00011000000000000000000100000000
+          234 288 rmoveto
+          540 0 rlineto
+          0 -67 rlineto
+          -540 0 rlineto
+          -78 118 rmoveto
+          0 -169 rlineto
+          701 0 rlineto
+          0 169 rlineto
+          hintmask 00100000000000000000100000000000
+          -398 -130 rmoveto
+          0 -240 rlineto
+          81 -16 rlineto
+          0 256 rlineto
+          hintmask 01000000000010010000010000000000
+          -38 -92 rmoveto
+          0 -52 rlineto
+          385 0 rlineto
+          0 52 rlineto
+          -735 643 rmoveto
+          0 -58 rlineto
+          799 0 rlineto
+          0 58 rlineto
+          hintmask 00000000000010100000000000000000
+          -836 0 rmoveto
+          0 -131 rlineto
+          0 -79 -10 -101 -76 -78 rrcurveto
+          17 -9 31 -26 11 -14 rrcurveto
+          84 85 18 127 0 94 rrcurveto
+          0 132 rlineto
+          hintmask 00000000010100001000000000000000
+          152 -48 rmoveto
+          0 -167 rlineto
+          hintmask 00000000010100000100000000000000
+          67 0 rlineto
+          0 167 rlineto
+          hintmask 00000000101000000001000000000000
+          -32 -51 rmoveto
+          0 -42 rlineto
+          181 0 rlineto
+          0 42 rlineto
+          -355 -89 rmoveto
+          0 -47 rlineto
+          388 0 rlineto
+          0 47 rlineto
+          hintmask 00000011000000000100000000000000
+          -233 -16 rmoveto
+          0 -137 rlineto
+          0 -8 -2 -3 -10 -1 rrcurveto
+          -9 0 -26 0 -34 1 rrcurveto
+          9 -15 12 -21 4 -17 rrcurveto
+          hintmask 00000011000001000010001000000000
+          45 0 31 0 23 9 rrcurveto
+          22 9 5 13 0 32 rrcurveto
+          0 138 rlineto
+          -171 -49 rmoveto
+          -19 -39 -33 -36 -39 -29 rrcurveto
+          14 -7 23 -16 10 -9 rrcurveto
+          38 30 38 47 23 46 rrcurveto
+          140 -8 rmoveto
+          30 -29 32 -40 14 -28 rrcurveto
+          48 23 rlineto
+          -15 28 -33 40 -30 26 rrcurveto
+          61 340 rmoveto
+          hintmask 10000101000001000000001010000000
+          17 -280 136 -219 157 0 rrcurveto
+          64 0 29 31 11 118 rrcurveto
+          -19 6 -25 13 -17 15 rrcurveto
+          -4 -81 -9 -30 -26 0 rrcurveto
+          -108 -1 -118 184 -14 244 rrcurveto
+          170 -173 rmoveto
+          -42 -107 -79 -95 -91 -61 rrcurveto
+          16 -11 27 -25 11 -13 rrcurveto
+          91 68 86 106 49 120 rrcurveto
+          -178 151 rmoveto
+          51 -16 63 -26 34 -21 rrcurveto
+          36 47 rlineto
+          -35 21 -64 24 -50 13 rrcurveto
+          -452 -751 rmoveto
+          -56 -23 rlineto
+          84 -121 133 -22 225 0 rrcurveto
+          206 0 rlineto
+          66 0 rlineto
+          3 19 12 31 10 16 rrcurveto
+          -51 -1 -208 0 -36 0 rrcurveto
+          -190 0 -132 13 -66 88 rrcurveto
+          -45 52 rmoveto
+          -25 -90 -77 -59 -100 -35 rrcurveto
+          18 -12 30 -28 12 -14 rrcurveto
+          103 45 87 72 31 112 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="cid18480" fdSelectIndex="1">
+          1000 -77 75 369 68 105 68 27 134 -73 73 -73 150 hstemhm
+          152 80 -37 37 55 80 -32 78 215 81 92 80 -19 76 hintmask 1110100101110000
+          57 769 rmoveto
+          0 -73 rlineto
+          889 0 rlineto
+          0 73 rlineto
+          hintmask 0000010010000000
+          -659 77 rmoveto
+          hintmask 0001000010000000
+          0 -211 rlineto
+          80 0 rlineto
+          hintmask 0000010010100000
+          0 211 rlineto
+          261 0 rmoveto
+          hintmask 0001000000100000
+          0 -211 rlineto
+          81 0 rlineto
+          hintmask 0010011000100000
+          0 211 rlineto
+          -557 -238 rmoveto
+          0 -211 rlineto
+          0 -132 -12 -179 -110 -128 rrcurveto
+          19 -9 34 -24 15 -14 rrcurveto
+          hintmask 1110000101010000
+          115 135 19 205 0 145 rrcurveto
+          0 212 rlineto
+          -37 0 rmoveto
+          0 -68 rlineto
+          606 0 rlineto
+          0 -105 rlineto
+          -606 0 rlineto
+          0 -68 rlineto
+          686 0 rlineto
+          0 241 rlineto
+          -546 -272 rmoveto
+          0 -293 rlineto
+          0 -95 37 -25 134 0 rrcurveto
+          28 0 219 0 30 0 rrcurveto
+          hintmask 1000000001001000
+          116 0 26 35 13 137 rrcurveto
+          -22 5 -34 12 -20 13 rrcurveto
+          -6 -109 -11 -18 -65 0 rrcurveto
+          -49 0 -187 0 -37 0 rrcurveto
+          -80 0 -14 8 0 38 rrcurveto
+          0 292 rlineto
+          385 -21 rmoveto
+          -97 -49 -177 -47 -157 -32 rrcurveto
+          9 -17 12 -28 4 -18 rrcurveto
+          164 31 185 46 125 56 rrcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="cid06821" width="1000" lsb="29"/>
+    <mtx name="cid18480" width="1000" lsb="30"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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="cid06821" height="1000" tsb="32"/>
+    <mtx name="cid18480" height="1000" tsb="34"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w599.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w599.00.ttx
new file mode 100644
index 0000000..65809d4
--- /dev/null
+++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w599.00.ttx
@@ -0,0 +1,487 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="cid17290"/>
+    <GlyphID id="2" name="cid17852"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0x1125c42f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:07:13 2018"/>
+    <modified value="Tue Apr 16 22:17:21 2019"/>
+    <xMin value="10"/>
+    <yMin value="-120"/>
+    <xMax value="979"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="10"/>
+    <minRightSideBearing value="21"/>
+    <xMaxExtent value="979"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="3"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="978"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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="00001000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBE"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="20686"/>
+    <usLastCharIndex value="27162"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w599.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w599.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w599.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w599.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_Kanji-w599.00">
+      <ROS Registry="Adobe" Order="Japan1" Supplement="6"/>
+      <Notice value="1997-2007, 2012 Adobe Systems Incorporated. All Rights Reserved. Kozuka Mincho is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries.&quot;"/>
+      <FullName value="Master Set Kanji w599.00"/>
+      <FamilyName value="Master Set Kanji"/>
+      <Weight value="ExtraLight"/>
+      <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="10 -120 979 880"/>
+      <StrokeWidth value="0"/>
+      <XUID value="1119273886"/>
+      <CIDFontVersion value="6.004"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="23058"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="MasterSet_Kanji-w599.00-Generic"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="607"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="MasterSet_Kanji-w599.00-Kanji"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="0"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          393 -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 hintmask 10111000
+          100 -120 rmoveto
+          800 0 rlineto
+          0 1000 rlineto
+          -800 0 rlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 0 rlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          0 -818 rlineto
+          -668 -41 rmoveto
+          318 409 rlineto
+          318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 rlineto
+          -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid17290" fdSelectIndex="1">
+          1000 100 82 -54 54 94 80 -80 158 21 71 85 67 66 66 hstemhm
+          138 94 95 81 -45 45 51 90 -21 76 -48 71 -71 178 -81 81 -45 45 122 77 -44 71 hintmask 011011111011001010000000
+          327 810 rmoveto
+          0 -256 rlineto
+          0 -78 22 -21 79 0 rrcurveto
+          hintmask 000010000000100000000000
+          17 0 74 0 17 0 rrcurveto
+          61 0 22 23 8 84 rrcurveto
+          hintmask 101010101000010000000000
+          -22 5 -32 11 -17 13 rrcurveto
+          -3 -56 -4 -9 -22 0 rrcurveto
+          -15 0 -60 0 -12 0 rrcurveto
+          -28 0 -4 4 0 25 rrcurveto
+          0 255 rlineto
+          -81 -454 rmoveto
+          0 -80 rlineto
+          612 0 rlineto
+          0 80 rlineto
+          -650 -174 rmoveto
+          0 -82 rlineto
+          678 0 rlineto
+          0 82 rlineto
+          hintmask 010101100111001000000000
+          -508 252 rmoveto
+          0 -306 rlineto
+          90 0 rlineto
+          0 306 rlineto
+          157 -1 rmoveto
+          0 -306 rlineto
+          91 0 rlineto
+          0 306 rlineto
+          -434 377 rmoveto
+          0 -66 rlineto
+          165 0 rlineto
+          0 -66 rlineto
+          -165 0 rlineto
+          0 -67 rlineto
+          241 0 rlineto
+          0 199 rlineto
+          49 0 rmoveto
+          hintmask 000010100000001001000000
+          0 -256 rlineto
+          0 -78 22 -21 80 0 rrcurveto
+          17 0 78 0 18 0 rrcurveto
+          61 0 23 23 8 84 rrcurveto
+          -23 5 -31 11 -17 13 rrcurveto
+          -3 -56 -5 -9 -22 0 rrcurveto
+          -17 0 -63 0 -12 0 rrcurveto
+          -29 0 -4 4 0 25 rrcurveto
+          0 255 rlineto
+          hintmask 000001110000000110000000
+          -45 0 rmoveto
+          0 -66 rlineto
+          167 0 rlineto
+          0 -66 rlineto
+          -167 0 rlineto
+          0 -67 rlineto
+          244 0 rlineto
+          0 199 rlineto
+          -443 -726 rmoveto
+          -45 -45 -86 -38 -83 -24 rrcurveto
+          21 -16 35 -35 15 -18 rrcurveto
+          84 32 96 54 55 61 rrcurveto
+          108 -12 rmoveto
+          70 -36 85 -56 42 -38 rrcurveto
+          82 52 rlineto
+          -46 39 -88 53 -69 32 rrcurveto
+          -547 756 rmoveto
+          -47 -152 -77 -150 -85 -98 rrcurveto
+          16 -26 26 -56 8 -24 rrcurveto
+          102 120 91 184 58 176 rrcurveto
+          -173 -237 rmoveto
+          0 -671 rlineto
+          94 0 rlineto
+          0 761 rlineto
+          -2 2 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid17852" fdSelectIndex="1">
+          1000 -81 75 160 76 101 76 -34 34 96 76 -76 147 -99 90 -82 82 32 84 -84 176 -141 49 hstemhm
+          43 218 -92 92 -92 181 16 89 -42 42 -29 29 34 83 -45 45 48 93 -1 84 73 85 -48 73 hintmask 000000100001000000000000
+          43 641 rmoveto
+          0 -90 rlineto
+          hintmask 000000100000010000000000
+          307 0 rlineto
+          0 90 rlineto
+          hintmask 000000010010100100000000
+          -181 208 rmoveto
+          0 -937 rlineto
+          92 0 rlineto
+          0 937 rlineto
+          -91 -270 rmoveto
+          -25 -135 -61 -170 -64 -94 rrcurveto
+          15 -23 23 -38 10 -27 rrcurveto
+          71 107 62 201 31 159 rrcurveto
+          23 -66 rmoveto
+          -44 -50 rlineto
+          26 -41 60 -98 23 -51 rrcurveto
+          52 87 rlineto
+          -16 24 -80 106 -21 23 rrcurveto
+          158 264 rmoveto
+          hintmask 000000001000000100000000
+          0 -84 rlineto
+          542 0 rlineto
+          hintmask 010000000010000000100000
+          0 84 rlineto
+          -428 -527 rmoveto
+          0 -76 rlineto
+          159 0 rlineto
+          0 76 rlineto
+          hintmask 000000000100000001010000
+          -197 78 rmoveto
+          0 -327 rlineto
+          83 0 rlineto
+          0 327 rlineto
+          48 541 rmoveto
+          hintmask 000000000010000000010000
+          0 -141 rlineto
+          93 0 rlineto
+          hintmask 000101000100000000010000
+          0 141 rlineto
+          -175 -199 rmoveto
+          0 -277 rlineto
+          80 0 rlineto
+          0 277 rlineto
+          86 0 rmoveto
+          0 -277 rlineto
+          80 0 rlineto
+          0 277 rlineto
+          126 -375 rmoveto
+          -45 -34 -75 -45 -50 -25 rrcurveto
+          40 -60 rlineto
+          52 24 74 38 51 39 rrcurveto
+          hintmask 101010000000000010001100
+          -531 367 rmoveto
+          0 -76 rlineto
+          443 0 rlineto
+          0 -96 rlineto
+          -443 0 rlineto
+          0 -76 rlineto
+          528 0 rlineto
+          0 248 rlineto
+          -242 -270 rmoveto
+          0 -286 rlineto
+          0 -80 18 -24 76 0 rrcurveto
+          hintmask 100000000010001000001010
+          15 0 53 0 16 0 rrcurveto
+          60 0 21 29 8 113 rrcurveto
+          -23 6 -34 12 -16 14 rrcurveto
+          -3 -86 -4 -13 -19 0 rrcurveto
+          -11 0 -41 0 -9 0 rrcurveto
+          -20 0 -3 4 0 26 rrcurveto
+          0 285 rlineto
+          -370 -306 rmoveto
+          13 -85 rlineto
+          75 13 91 16 89 16 rrcurveto
+          -5 81 rlineto
+          -99 -16 -96 -16 -68 -9 rrcurveto
+          -60 754 rmoveto
+          0 -322 rlineto
+          0 -142 -5 -196 -72 -135 rrcurveto
+          21 -10 38 -27 16 -16 rrcurveto
+          79 146 12 226 0 154 rrcurveto
+          0 322 rlineto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="cid17290" width="1000" lsb="10"/>
+    <mtx name="cid17852" width="1000" lsb="20"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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="cid17290" height="1000" tsb="35"/>
+    <mtx name="cid17852" height="1000" tsb="31"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w600.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w600.00.ttx
new file mode 100644
index 0000000..e0b217f
--- /dev/null
+++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w600.00.ttx
@@ -0,0 +1,487 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="cid17290"/>
+    <GlyphID id="2" name="cid17852"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0xa6c15fdb"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:07:20 2018"/>
+    <modified value="Tue Apr 16 22:17:21 2019"/>
+    <xMin value="10"/>
+    <yMin value="-120"/>
+    <xMax value="979"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="10"/>
+    <minRightSideBearing value="21"/>
+    <xMaxExtent value="979"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="3"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="978"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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="00001000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBE"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="20686"/>
+    <usLastCharIndex value="27162"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w600.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w600.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w600.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w600.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x50ce" name="cid17290"/><!-- CJK UNIFIED IDEOGRAPH-50CE -->
+      <map code="0x6a1a" name="cid17852"/><!-- CJK UNIFIED IDEOGRAPH-6A1A -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_Kanji-w600.00">
+      <ROS Registry="Adobe" Order="Japan1" Supplement="6"/>
+      <Notice value="1997-2007, 2012 Adobe Systems Incorporated. All Rights Reserved. Kozuka Mincho is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries.&quot;"/>
+      <FullName value="Master Set Kanji w600.00"/>
+      <FamilyName value="Master Set Kanji"/>
+      <Weight value="ExtraLight"/>
+      <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="10 -120 979 880"/>
+      <StrokeWidth value="0"/>
+      <XUID value="1119273886"/>
+      <CIDFontVersion value="6.004"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="23058"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="MasterSet_Kanji-w600.00-Generic"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="607"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="MasterSet_Kanji-w600.00-Kanji"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="0"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          393 -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 hintmask 10111000
+          100 -120 rmoveto
+          800 0 rlineto
+          0 1000 rlineto
+          -800 0 rlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 0 rlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          0 -818 rlineto
+          -668 -41 rmoveto
+          318 409 rlineto
+          318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 rlineto
+          -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid17290" fdSelectIndex="1">
+          1000 104 85 -31 31 103 84 -84 185 -22 71 85 67 66 66 hstemhm
+          138 94 95 81 -45 45 49 92 -21 76 -48 71 -71 178 -81 81 -45 45 122 77 -44 71 hintmask 011011111011001010000000
+          327 810 rmoveto
+          0 -256 rlineto
+          0 -78 22 -21 79 0 rrcurveto
+          hintmask 000010000000100000000000
+          17 0 74 0 17 0 rrcurveto
+          61 0 22 23 8 84 rrcurveto
+          hintmask 101010101000010000000000
+          -22 5 -32 11 -17 13 rrcurveto
+          -3 -56 -4 -9 -22 0 rrcurveto
+          -15 0 -60 0 -12 0 rrcurveto
+          -28 0 -4 4 0 25 rrcurveto
+          0 255 rlineto
+          -89 -434 rmoveto
+          0 -84 rlineto
+          624 0 rlineto
+          0 84 rlineto
+          -654 -187 rmoveto
+          0 -85 rlineto
+          678 0 rlineto
+          0 85 rlineto
+          hintmask 010101100111001000000000
+          -510 288 rmoveto
+          0 -319 rlineto
+          92 0 rlineto
+          0 319 rlineto
+          157 -1 rmoveto
+          0 -319 rlineto
+          92 0 rlineto
+          0 319 rlineto
+          -435 334 rmoveto
+          0 -66 rlineto
+          165 0 rlineto
+          0 -66 rlineto
+          -165 0 rlineto
+          0 -67 rlineto
+          241 0 rlineto
+          0 199 rlineto
+          49 0 rmoveto
+          hintmask 000010100000001001000000
+          0 -256 rlineto
+          0 -78 22 -21 80 0 rrcurveto
+          17 0 78 0 18 0 rrcurveto
+          61 0 23 24 8 85 rrcurveto
+          -22 5 -32 11 -17 13 rrcurveto
+          -3 -57 -5 -10 -22 0 rrcurveto
+          -17 0 -63 0 -12 0 rrcurveto
+          -29 0 -4 4 0 26 rrcurveto
+          0 254 rlineto
+          hintmask 000001110000000110000000
+          -45 0 rmoveto
+          0 -66 rlineto
+          167 0 rlineto
+          0 -66 rlineto
+          -167 0 rlineto
+          0 -67 rlineto
+          244 0 rlineto
+          0 199 rlineto
+          -443 -726 rmoveto
+          -45 -45 -86 -38 -83 -24 rrcurveto
+          21 -16 35 -35 15 -18 rrcurveto
+          84 32 96 54 55 61 rrcurveto
+          108 -12 rmoveto
+          70 -36 85 -56 42 -38 rrcurveto
+          82 52 rlineto
+          -46 39 -88 53 -69 32 rrcurveto
+          -547 756 rmoveto
+          -47 -152 -77 -150 -85 -98 rrcurveto
+          16 -26 26 -56 8 -24 rrcurveto
+          102 120 91 184 58 176 rrcurveto
+          -173 -237 rmoveto
+          0 -671 rlineto
+          94 0 rlineto
+          0 761 rlineto
+          -2 2 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid17852" fdSelectIndex="1">
+          1000 -81 75 157 76 92 78 -14 14 109 78 -78 178 -133 90 -82 82 32 84 -84 176 -141 49 hstemhm
+          43 218 -92 92 -92 181 16 89 -42 42 -29 29 34 83 -45 45 48 93 -1 84 73 85 -48 73 hintmask 000000100001000000000000
+          43 641 rmoveto
+          0 -90 rlineto
+          hintmask 000000100000010000000000
+          307 0 rlineto
+          0 90 rlineto
+          hintmask 000000010010100100000000
+          -181 208 rmoveto
+          0 -937 rlineto
+          92 0 rlineto
+          0 937 rlineto
+          -91 -270 rmoveto
+          -25 -135 -61 -170 -64 -94 rrcurveto
+          15 -23 23 -38 10 -27 rrcurveto
+          71 107 62 201 31 159 rrcurveto
+          23 -66 rmoveto
+          -44 -50 rlineto
+          26 -41 60 -98 23 -51 rrcurveto
+          52 87 rlineto
+          -16 24 -80 106 -21 23 rrcurveto
+          158 264 rmoveto
+          hintmask 000000001000000100000000
+          0 -84 rlineto
+          545 0 rlineto
+          hintmask 010000000010000000100000
+          0 84 rlineto
+          -431 -530 rmoveto
+          0 -76 rlineto
+          159 0 rlineto
+          0 76 rlineto
+          hintmask 000000000100000001010000
+          -197 115 rmoveto
+          0 -356 rlineto
+          83 0 rlineto
+          0 356 rlineto
+          48 507 rmoveto
+          hintmask 000000000010000000010000
+          0 -141 rlineto
+          93 0 rlineto
+          hintmask 000101000100000000010000
+          0 141 rlineto
+          -175 -165 rmoveto
+          0 -301 rlineto
+          80 0 rlineto
+          0 301 rlineto
+          84 0 rmoveto
+          0 -301 rlineto
+          80 0 rlineto
+          0 301 rlineto
+          128 -409 rmoveto
+          -45 -34 -75 -45 -50 -25 rrcurveto
+          40 -60 rlineto
+          52 24 74 38 51 39 rrcurveto
+          hintmask 101010000000000010001100
+          -531 372 rmoveto
+          0 -78 rlineto
+          443 0 rlineto
+          0 -109 rlineto
+          -443 0 rlineto
+          0 -78 rlineto
+          528 0 rlineto
+          0 265 rlineto
+          -242 -241 rmoveto
+          0 -318 rlineto
+          0 -82 18 -24 76 0 rrcurveto
+          hintmask 100000000010001000001010
+          15 0 53 0 16 0 rrcurveto
+          60 0 21 30 8 112 rrcurveto
+          -23 5 -33 13 -17 14 rrcurveto
+          -3 -85 -4 -14 -19 0 rrcurveto
+          -10 0 -42 0 -9 0 rrcurveto
+          -20 0 -3 4 0 26 rrcurveto
+          0 319 rlineto
+          -370 -340 rmoveto
+          13 -85 rlineto
+          75 13 91 16 89 16 rrcurveto
+          -5 81 rlineto
+          -99 -16 -96 -16 -68 -9 rrcurveto
+          -60 754 rmoveto
+          0 -322 rlineto
+          0 -142 -5 -196 -72 -135 rrcurveto
+          21 -10 38 -27 16 -16 rrcurveto
+          79 146 12 226 0 154 rrcurveto
+          0 322 rlineto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="cid17290" width="1000" lsb="10"/>
+    <mtx name="cid17852" width="1000" lsb="20"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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="cid17290" height="1000" tsb="35"/>
+    <mtx name="cid17852" height="1000" tsb="31"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w669.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w669.00.ttx
new file mode 100644
index 0000000..94831b4
--- /dev/null
+++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w669.00.ttx
@@ -0,0 +1,492 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="cid01177"/>
+    <GlyphID id="2" name="cid07253"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0x6e00b443"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:07:27 2018"/>
+    <modified value="Tue Apr 16 22:17:21 2019"/>
+    <xMin value="23"/>
+    <yMin value="-120"/>
+    <xMax value="983"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="23"/>
+    <minRightSideBearing value="17"/>
+    <xMaxExtent value="983"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="3"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="978"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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="00001000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBE"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="24847"/>
+    <usLastCharIndex value="39488"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w669.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w669.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w669.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w669.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_Kanji-w669.00">
+      <ROS Registry="Adobe" Order="Japan1" Supplement="6"/>
+      <Notice value="1997-2007, 2012 Adobe Systems Incorporated. All Rights Reserved. Kozuka Mincho is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries.&quot;"/>
+      <FullName value="Master Set Kanji w669.00"/>
+      <FamilyName value="Master Set Kanji"/>
+      <Weight value="ExtraLight"/>
+      <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="23 -120 983 880"/>
+      <StrokeWidth value="0"/>
+      <XUID value="1119273886"/>
+      <CIDFontVersion value="6.004"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="23058"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="MasterSet_Kanji-w669.00-Generic"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="607"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="MasterSet_Kanji-w669.00-Kanji"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="0"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          393 -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 hintmask 10111000
+          100 -120 rmoveto
+          800 0 rlineto
+          0 1000 rlineto
+          -800 0 rlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 0 rlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          0 -818 rlineto
+          -668 -41 rmoveto
+          318 409 rlineto
+          318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 rlineto
+          -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid01177" fdSelectIndex="1">
+          1000 -80 89 189 69 50 65 49 69 44 86 75 89 -87 143 -117 61 hstemhm
+          173 105 9 108 48 111 94 94 -15 110 hintmask 1111100011111000
+          287 149 rmoveto
+          0 -111 rlineto
+          0 -92 30 -26 122 0 rrcurveto
+          25 0 118 0 26 0 rrcurveto
+          92 0 30 27 12 114 rrcurveto
+          -29 6 -43 14 -22 15 rrcurveto
+          -5 -76 -6 -11 -39 0 rrcurveto
+          -29 0 -96 0 -22 0 rrcurveto
+          -48 0 -8 4 0 26 rrcurveto
+          0 110 rlineto
+          -11 9 rmoveto
+          58 -25 71 -42 34 -31 rrcurveto
+          68 69 rlineto
+          -38 30 -73 39 -57 22 rrcurveto
+          271 -96 rmoveto
+          64 -55 70 -79 29 -54 rrcurveto
+          92 54 rlineto
+          -33 56 -73 75 -63 51 rrcurveto
+          -642 -11 rmoveto
+          -23 -67 -47 -66 -64 -39 rrcurveto
+          86 -59 rlineto
+          72 47 41 75 29 75 rrcurveto
+          -143 667 rmoveto
+          0 -87 rlineto
+          772 0 rlineto
+          0 87 rlineto
+          -819 -164 rmoveto
+          0 -86 rlineto
+          872 0 rlineto
+          0 86 rlineto
+          hintmask 0000001000100000
+          -495 220 rmoveto
+          hintmask 0000000100100000
+          0 -117 rlineto
+          111 0 rlineto
+          hintmask 0000001000100000
+          0 117 rlineto
+          hintmask 0111010010001000
+          -296 -145 rmoveto
+          14 -30 15 -40 5 -25 rrcurveto
+          105 24 rlineto
+          -7 25 -15 38 -16 28 rrcurveto
+          281 1 rmoveto
+          -9 -26 -18 -38 -13 -27 rrcurveto
+          97 -22 rlineto
+          16 23 20 31 22 36 rrcurveto
+          -477 -386 rmoveto
+          449 0 rlineto
+          0 -50 rlineto
+          -449 0 rlineto
+          0 164 rmoveto
+          449 0 rlineto
+          0 -49 rlineto
+          -449 0 rlineto
+          -105 118 rmoveto
+          0 -302 rlineto
+          664 0 rlineto
+          0 302 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid07253" fdSelectIndex="1">
+          1000 -90 68 42 76 -74 74 -72 72 -57 57 -29 29 -27 27 30 43 30 43 29 37 -37 53 32 72 -47 47 32 18 -18 56 -46 46 35 54 34 56 -25 134 -74 74 -74 120 hstemhm
+          165 101 -57 105 -36 36 2 84 54 99 58 109 15 106 -46 35 hintmask 10000011101100101101000101110000
+          57 804 rmoveto
+          0 -74 rlineto
+          886 0 rlineto
+          0 74 rlineto
+          hintmask 00000000000000000000100000000000
+          -665 46 rmoveto
+          hintmask 00000000000000000010000000000000
+          0 -180 rlineto
+          107 0 rlineto
+          hintmask 00000000000000000000100000100000
+          0 180 rlineto
+          226 0 rmoveto
+          hintmask 00000000000000000010000000100000
+          0 -180 rlineto
+          109 0 rlineto
+          hintmask 00000000000100101100110000110000
+          0 180 rlineto
+          -454 -299 rmoveto
+          469 0 rlineto
+          0 -35 rlineto
+          -469 0 rlineto
+          0 123 rmoveto
+          469 0 rlineto
+          0 -34 rlineto
+          -469 0 rlineto
+          -101 90 rmoveto
+          0 -235 rlineto
+          676 0 rlineto
+          0 235 rlineto
+          -798 -267 rmoveto
+          0 -72 rlineto
+          915 0 rlineto
+          0 72 rlineto
+          hintmask 00000001100000000000000100000000
+          -680 -186 rmoveto
+          0 -43 rlineto
+          495 0 rlineto
+          0 43 rlineto
+          -495 -73 rmoveto
+          0 -43 rlineto
+          507 0 rlineto
+          0 43 rlineto
+          hintmask 00000010000000000000001000000000
+          -576 -73 rmoveto
+          hintmask 00001000000000000000001000001000
+          0 -57 rlineto
+          621 0 rlineto
+          hintmask 00000010000001000000000000001000
+          0 57 rlineto
+          -501 390 rmoveto
+          -40 -97 -96 -93 -170 -59 rrcurveto
+          19 -16 28 -36 11 -24 rrcurveto
+          hintmask 00000100010010010000000001000000
+          186 73 105 107 58 129 rrcurveto
+          274 -62 rmoveto
+          -80 -27 rlineto
+          64 -94 111 -79 120 -37 rrcurveto
+          14 26 29 37 21 20 rrcurveto
+          -114 26 -110 58 -55 70 rrcurveto
+          -250 -100 rmoveto
+          0 -241 rlineto
+          99 0 rlineto
+          0 241 rlineto
+          hintmask 10000010000000000000000000001000
+          242 -212 rmoveto
+          0 -10 rlineto
+          -6 -63 -8 -28 -9 -9 rrcurveto
+          -6 -7 -6 -1 -10 0 rrcurveto
+          -10 0 -21 1 -25 3 rrcurveto
+          11 -19 8 -32 1 -22 rrcurveto
+          35 -1 32 1 17 1 rrcurveto
+          21 2 18 6 15 14 rrcurveto
+          19 18 9 38 9 74 rrcurveto
+          2 13 2 21 0 0 rrcurveto
+          -717 -42 rmoveto
+          -16 -39 -32 -41 -41 -23 rrcurveto
+          76 -45 rlineto
+          hintmask 10010000000000000000000000000000
+          48 28 27 44 18 46 rrcurveto
+          hintmask 00100000000000000000000010000000
+          43 -2 rmoveto
+          10 -34 8 -47 -1 -29 rrcurveto
+          84 14 rlineto
+          -1 28 -10 46 -12 33 rrcurveto
+          hintmask 01000000000000000000000000100000
+          57 -13 rmoveto
+          22 -30 21 -41 8 -28 rrcurveto
+          76 25 rlineto
+          -10 28 -21 39 -24 28 rrcurveto
+          hintmask 00010000001000000000001000000000
+          72 -22 rmoveto
+          24 -21 27 -32 11 -22 rrcurveto
+          65 34 rlineto
+          -12 22 -29 30 -25 20 rrcurveto
+          -342 274 rmoveto
+          -49 -53 rlineto
+          456 0 rlineto
+          0 53 rlineto
+          -495 -37 rmoveto
+          hintmask 00000010000000000000001000000000
+          0 -218 rlineto
+          hintmask 00000010001000000000000100000000
+          105 0 rlineto
+          0 257 rlineto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="cid01177" width="1000" lsb="28"/>
+    <mtx name="cid07253" width="1000" lsb="23"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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="cid01177" height="1000" tsb="30"/>
+    <mtx name="cid07253" height="1000" tsb="30"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w670.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w670.00.ttx
new file mode 100644
index 0000000..eff2eec
--- /dev/null
+++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w670.00.ttx
@@ -0,0 +1,492 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="cid01177"/>
+    <GlyphID id="2" name="cid07253"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0x97cdda5"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:07:35 2018"/>
+    <modified value="Tue Apr 16 22:17:21 2019"/>
+    <xMin value="23"/>
+    <yMin value="-120"/>
+    <xMax value="983"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="23"/>
+    <minRightSideBearing value="17"/>
+    <xMaxExtent value="983"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="3"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="978"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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="00001000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBE"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="24847"/>
+    <usLastCharIndex value="39488"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w670.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w670.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w670.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w670.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x610f" name="cid01177"/><!-- CJK UNIFIED IDEOGRAPH-610F -->
+      <map code="0x9a40" name="cid07253"/><!-- CJK UNIFIED IDEOGRAPH-9A40 -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_Kanji-w670.00">
+      <ROS Registry="Adobe" Order="Japan1" Supplement="6"/>
+      <Notice value="1997-2007, 2012 Adobe Systems Incorporated. All Rights Reserved. Kozuka Mincho is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries.&quot;"/>
+      <FullName value="Master Set Kanji w670.00"/>
+      <FamilyName value="Master Set Kanji"/>
+      <Weight value="ExtraLight"/>
+      <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="23 -120 983 880"/>
+      <StrokeWidth value="0"/>
+      <XUID value="1119273886"/>
+      <CIDFontVersion value="6.004"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="23058"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="MasterSet_Kanji-w670.00-Generic"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="607"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="MasterSet_Kanji-w670.00-Kanji"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="0"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          393 -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 hintmask 10111000
+          100 -120 rmoveto
+          800 0 rlineto
+          0 1000 rlineto
+          -800 0 rlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 0 rlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          0 -818 rlineto
+          -668 -41 rmoveto
+          318 409 rlineto
+          318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 rlineto
+          -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid01177" fdSelectIndex="1">
+          1000 -80 87 191 68 52 64 50 68 44 84 77 87 -84 142 -117 59 hstemhm
+          174 102 12 104 53 108 96 92 -11 106 hintmask 1111100011111000
+          288 149 rmoveto
+          0 -113 rlineto
+          0 -90 29 -26 121 0 rrcurveto
+          24 0 121 0 26 0 rrcurveto
+          91 0 29 28 12 113 rrcurveto
+          -28 6 -42 14 -22 14 rrcurveto
+          -5 -77 -6 -11 -39 0 rrcurveto
+          -29 0 -99 0 -22 0 rrcurveto
+          -48 0 -9 4 0 26 rrcurveto
+          0 112 rlineto
+          -8 10 rmoveto
+          59 -26 71 -42 34 -31 rrcurveto
+          66 67 rlineto
+          -38 31 -73 39 -58 22 rrcurveto
+          274 -95 rmoveto
+          64 -54 70 -79 30 -54 rrcurveto
+          89 52 rlineto
+          -33 56 -73 75 -64 51 rrcurveto
+          -638 -11 rmoveto
+          -24 -67 -46 -66 -65 -39 rrcurveto
+          83 -57 rlineto
+          73 46 41 75 28 76 rrcurveto
+          -141 664 rmoveto
+          0 -84 rlineto
+          772 0 rlineto
+          0 84 rlineto
+          -819 -164 rmoveto
+          0 -84 rlineto
+          872 0 rlineto
+          0 84 rlineto
+          hintmask 0000001000100000
+          -493 222 rmoveto
+          hintmask 0000000100100000
+          0 -117 rlineto
+          108 0 rlineto
+          hintmask 0000001000100000
+          0 117 rlineto
+          hintmask 0111010010001000
+          -294 -145 rmoveto
+          15 -30 14 -40 5 -26 rrcurveto
+          102 24 rlineto
+          -6 25 -16 39 -16 28 rrcurveto
+          285 1 rmoveto
+          -9 -27 -19 -38 -13 -28 rrcurveto
+          95 -21 rlineto
+          16 24 19 31 23 37 rrcurveto
+          -478 -386 rmoveto
+          454 0 rlineto
+          0 -52 rlineto
+          -454 0 rlineto
+          0 166 rmoveto
+          454 0 rlineto
+          0 -50 rlineto
+          -454 0 rlineto
+          -102 118 rmoveto
+          0 -302 rlineto
+          662 0 rlineto
+          0 302 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid07253" fdSelectIndex="1">
+          1000 -89 66 44 75 -73 73 -71 71 -56 56 -29 29 -27 27 31 42 31 43 29 37 -37 52 33 70 -46 46 33 18 -18 55 -45 45 36 53 35 55 -24 132 -72 72 -72 120 hstemhm
+          166 99 -55 101 -34 34 7 81 57 95 62 106 18 103 -44 34 hintmask 10000011101100101101000101110000
+          57 802 rmoveto
+          0 -72 rlineto
+          886 0 rlineto
+          0 72 rlineto
+          hintmask 00000000000000000000100000000000
+          -664 48 rmoveto
+          hintmask 00000000000000000010000000000000
+          0 -180 rlineto
+          105 0 rlineto
+          hintmask 00000000000000000000100000100000
+          0 180 rlineto
+          229 0 rmoveto
+          hintmask 00000000000000000010000000100000
+          0 -180 rlineto
+          106 0 rlineto
+          hintmask 00000000000100101100110000110000
+          0 180 rlineto
+          -454 -299 rmoveto
+          472 0 rlineto
+          0 -36 rlineto
+          -472 0 rlineto
+          0 124 rmoveto
+          472 0 rlineto
+          0 -35 rlineto
+          -472 0 rlineto
+          -99 90 rmoveto
+          0 -234 rlineto
+          674 0 rlineto
+          0 234 rlineto
+          -797 -267 rmoveto
+          0 -70 rlineto
+          915 0 rlineto
+          0 70 rlineto
+          hintmask 00000001100000000000000100000000
+          -681 -184 rmoveto
+          0 -43 rlineto
+          497 0 rlineto
+          0 43 rlineto
+          -497 -74 rmoveto
+          0 -42 rlineto
+          508 0 rlineto
+          0 42 rlineto
+          hintmask 00000010000000000000001000000000
+          -575 -73 rmoveto
+          hintmask 00001000000000000000001000001000
+          0 -56 rlineto
+          620 0 rlineto
+          hintmask 00000010000001000000000000001000
+          0 56 rlineto
+          -499 390 rmoveto
+          -41 -98 -96 -93 -171 -59 rrcurveto
+          19 -16 28 -35 10 -23 rrcurveto
+          hintmask 00000100010010010000000001000000
+          186 73 105 107 58 128 rrcurveto
+          274 -62 rmoveto
+          -78 -27 rlineto
+          64 -93 112 -79 120 -37 rrcurveto
+          13 25 28 36 21 19 rrcurveto
+          -114 26 -110 60 -56 70 rrcurveto
+          -247 -99 rmoveto
+          0 -242 rlineto
+          95 0 rlineto
+          0 242 rlineto
+          hintmask 10000010000000000000000000001000
+          245 -213 rmoveto
+          0 -10 rlineto
+          -6 -64 -7 -28 -10 -9 rrcurveto
+          -6 -7 -6 -1 -10 0 rrcurveto
+          -10 0 -21 1 -25 2 rrcurveto
+          10 -18 8 -31 1 -21 rrcurveto
+          34 -1 32 0 18 2 rrcurveto
+          21 1 17 6 15 14 rrcurveto
+          18 18 10 38 9 75 rrcurveto
+          2 12 1 21 0 0 rrcurveto
+          -714 -42 rmoveto
+          -16 -39 -32 -41 -41 -23 rrcurveto
+          74 -45 rlineto
+          hintmask 10010000000000000000000000000000
+          47 29 28 44 17 46 rrcurveto
+          hintmask 00100000000000000000000010000000
+          46 -2 rmoveto
+          10 -35 8 -46 0 -29 rrcurveto
+          81 13 rlineto
+          -1 28 -9 46 -13 34 rrcurveto
+          hintmask 01000000000000000000000000100000
+          60 -13 rmoveto
+          22 -30 20 -42 8 -28 rrcurveto
+          74 25 rlineto
+          -9 28 -22 40 -23 28 rrcurveto
+          hintmask 00010000001000000000001000000000
+          73 -21 rmoveto
+          24 -22 27 -32 12 -22 rrcurveto
+          63 33 rlineto
+          -12 22 -29 30 -25 21 rrcurveto
+          -343 273 rmoveto
+          -48 -52 rlineto
+          457 0 rlineto
+          0 52 rlineto
+          -495 -37 rmoveto
+          hintmask 00000010000000000000001000000000
+          0 -218 rlineto
+          hintmask 00000010001000000000000100000000
+          101 0 rlineto
+          0 257 rlineto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="cid01177" width="1000" lsb="29"/>
+    <mtx name="cid07253" width="1000" lsb="23"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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="cid01177" height="1000" tsb="30"/>
+    <mtx name="cid07253" height="1000" tsb="30"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w799.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w799.00.ttx
new file mode 100644
index 0000000..cfccfa2
--- /dev/null
+++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w799.00.ttx
@@ -0,0 +1,393 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="cid13393"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0xb0503f01"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:07:57 2018"/>
+    <modified value="Tue Apr 16 22:17:21 2019"/>
+    <xMin value="69"/>
+    <yMin value="-120"/>
+    <xMax value="991"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="69"/>
+    <minRightSideBearing value="9"/>
+    <xMaxExtent value="991"/>
+    <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>
+
+  <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="978"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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="00100000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBE"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="63964"/>
+    <usLastCharIndex value="63964"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w799.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w799.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w799.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w799.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="28" language="0" nGroups="1">
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_12>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uv="0x9686" uvs="0xfe00" name="cid13393"/>
+      <map uv="0x9686" uvs="0xe0101" name="cid13393"/>
+    </cmap_format_14>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="28" language="0" nGroups="1">
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_Kanji-w799.00">
+      <ROS Registry="Adobe" Order="Japan1" Supplement="6"/>
+      <Notice value="1997-2007, 2012 Adobe Systems Incorporated. All Rights Reserved. Kozuka Mincho is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries.&quot;"/>
+      <FullName value="Master Set Kanji w799.00"/>
+      <FamilyName value="Master Set Kanji"/>
+      <Weight value="ExtraLight"/>
+      <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="69 -120 991 880"/>
+      <StrokeWidth value="0"/>
+      <XUID value="1119273886"/>
+      <CIDFontVersion value="6.004"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="23058"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="MasterSet_Kanji-w799.00-Generic"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="607"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="MasterSet_Kanji-w799.00-Kanji"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="0"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          393 -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 hintmask 10111000
+          100 -120 rmoveto
+          800 0 rlineto
+          0 1000 rlineto
+          -800 0 rlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 0 rlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          0 -818 rlineto
+          -668 -41 rmoveto
+          318 409 rlineto
+          318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 rlineto
+          -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid13393" fdSelectIndex="1">
+          1000 -71 96 -48 48 50 85 -35 105 -23 86 -86 140 17 84 240 88 -34 34 -9 42 hstemhm
+          69 107 88 108 -103 37 307 114 82 34 hintmask 1010101101110110
+          552 776 rmoveto
+          0 -88 rlineto
+          291 0 rlineto
+          0 88 rlineto
+          -382 -483 rmoveto
+          0 -86 rlineto
+          463 0 rlineto
+          0 86 rlineto
+          -426 155 rmoveto
+          0 -84 rlineto
+          349 0 rlineto
+          0 84 rlineto
+          -480 -423 rmoveto
+          0 -96 rlineto
+          597 0 rlineto
+          0 96 rlineto
+          -516 135 rmoveto
+          0 -85 rlineto
+          456 0 rlineto
+          0 85 rlineto
+          -95 616 rmoveto
+          0 -16 rlineto
+          -72 -134 -195 -99 -195 -40 rrcurveto
+          22 -23 25 -43 12 -27 rrcurveto
+          hintmask 0000000010000010
+          213 54 207 110 96 184 rrcurveto
+          hintmask 0100010100000110
+          -74 39 rlineto
+          -18 -5 rlineto
+          -268 -57 rmoveto
+          -93 -34 rlineto
+          94 -142 159 -95 200 -40 rrcurveto
+          15 29 30 44 24 22 rrcurveto
+          -191 29 -160 77 -78 110 rrcurveto
+          13 136 rmoveto
+          -38 -74 -73 -81 -110 -59 rrcurveto
+          24 -16 36 -39 16 -25 rrcurveto
+          123 76 80 93 58 104 rrcurveto
+          -235 -472 rmoveto
+          -21 -62 -36 -63 -46 -42 rrcurveto
+          24 -13 41 -26 20 -16 rrcurveto
+          46 48 45 76 26 74 rrcurveto
+          58 9 rmoveto
+          0 -370 rlineto
+          114 0 rlineto
+          0 370 rlineto
+          hintmask 0000000000101000
+          -658 462 rmoveto
+          0 -902 rlineto
+          107 0 rlineto
+          0 792 rlineto
+          130 0 rlineto
+          0 110 rlineto
+          -37 0 rmoveto
+          0 -36 rlineto
+          -14 -65 -32 -143 -29 -91 rrcurveto
+          hintmask 0001000000010000
+          57 -68 13 -64 0 -46 rrcurveto
+          0 -30 -5 -20 -12 -9 rrcurveto
+          -7 -6 -11 -3 -10 0 rrcurveto
+          -13 0 -13 0 -18 2 rrcurveto
+          18 -30 9 -46 1 -29 rrcurveto
+          23 -1 24 0 18 2 rrcurveto
+          22 4 20 7 16 12 rrcurveto
+          32 22 14 43 0 66 rrcurveto
+          0 59 -13 70 -62 78 rrcurveto
+          hintmask 0000000001001000
+          29 80 34 112 27 88 rrcurveto
+          hintmask 0000000000001000
+          -80 46 rlineto
+          -18 -4 rlineto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="cid13393" width="1000" lsb="69"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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="cid13393" height="1000" tsb="25"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w800.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w800.00.ttx
new file mode 100644
index 0000000..765a381
--- /dev/null
+++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w800.00.ttx
@@ -0,0 +1,393 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="cid13393"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0xab041b5d"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:08:04 2018"/>
+    <modified value="Tue Apr 16 22:17:21 2019"/>
+    <xMin value="69"/>
+    <yMin value="-120"/>
+    <xMax value="991"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="69"/>
+    <minRightSideBearing value="9"/>
+    <xMaxExtent value="991"/>
+    <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>
+
+  <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="978"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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="00100000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBE"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="63964"/>
+    <usLastCharIndex value="63964"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w800.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w800.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w800.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w800.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="28" language="0" nGroups="1">
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_12>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uv="0x9686" uvs="0xfe00" name="cid13393"/>
+      <map uv="0x9686" uvs="0xe0101" name="cid13393"/>
+    </cmap_format_14>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="28" language="0" nGroups="1">
+      <map code="0xf9dc" name="cid13393"/><!-- CJK COMPATIBILITY IDEOGRAPH-F9DC -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_Kanji-w800.00">
+      <ROS Registry="Adobe" Order="Japan1" Supplement="6"/>
+      <Notice value="1997-2007, 2012 Adobe Systems Incorporated. All Rights Reserved. Kozuka Mincho is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries.&quot;"/>
+      <FullName value="Master Set Kanji w800.00"/>
+      <FamilyName value="Master Set Kanji"/>
+      <Weight value="ExtraLight"/>
+      <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="69 -120 991 880"/>
+      <StrokeWidth value="0"/>
+      <XUID value="1119273886"/>
+      <CIDFontVersion value="6.004"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="23058"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="MasterSet_Kanji-w800.00-Generic"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="607"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="MasterSet_Kanji-w800.00-Kanji"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="0"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          393 -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 hintmask 10111000
+          100 -120 rmoveto
+          800 0 rlineto
+          0 1000 rlineto
+          -800 0 rlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 0 rlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          0 -818 rlineto
+          -668 -41 rmoveto
+          318 409 rlineto
+          318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 rlineto
+          -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid13393" fdSelectIndex="1">
+          1000 -71 96 -27 27 50 85 -35 105 -23 86 -86 161 -8 84 248 88 -34 34 -13 42 hstemhm
+          69 107 88 108 -103 37 307 114 82 34 hintmask 1010101101110110
+          552 780 rmoveto
+          0 -88 rlineto
+          291 0 rlineto
+          0 88 rlineto
+          -382 -487 rmoveto
+          0 -86 rlineto
+          463 0 rlineto
+          0 86 rlineto
+          -426 151 rmoveto
+          0 -84 rlineto
+          349 0 rlineto
+          0 84 rlineto
+          -480 -419 rmoveto
+          0 -96 rlineto
+          597 0 rlineto
+          0 96 rlineto
+          -516 135 rmoveto
+          0 -85 rlineto
+          456 0 rlineto
+          0 85 rlineto
+          -95 620 rmoveto
+          0 -16 rlineto
+          -72 -134 -195 -99 -195 -40 rrcurveto
+          22 -23 25 -43 12 -27 rrcurveto
+          hintmask 0000000010000010
+          213 54 207 110 96 184 rrcurveto
+          hintmask 0100010100000110
+          -74 39 rlineto
+          -18 -5 rlineto
+          -268 -57 rmoveto
+          -93 -34 rlineto
+          94 -142 159 -95 200 -40 rrcurveto
+          15 29 30 44 24 22 rrcurveto
+          -191 29 -160 77 -78 110 rrcurveto
+          13 132 rmoveto
+          -38 -74 -73 -81 -110 -59 rrcurveto
+          24 -16 36 -39 16 -25 rrcurveto
+          123 76 80 93 58 104 rrcurveto
+          -239 -466 rmoveto
+          -21 -62 -36 -63 -46 -42 rrcurveto
+          24 -13 41 -27 20 -16 rrcurveto
+          46 48 45 76 26 75 rrcurveto
+          62 24 rmoveto
+          0 -370 rlineto
+          114 0 rlineto
+          0 370 rlineto
+          hintmask 0000000000101000
+          -658 441 rmoveto
+          0 -902 rlineto
+          107 0 rlineto
+          0 792 rlineto
+          130 0 rlineto
+          0 110 rlineto
+          -37 0 rmoveto
+          0 -36 rlineto
+          -14 -65 -32 -143 -29 -91 rrcurveto
+          hintmask 0001000000010000
+          57 -68 13 -64 0 -46 rrcurveto
+          0 -30 -5 -20 -12 -9 rrcurveto
+          -7 -6 -11 -3 -10 0 rrcurveto
+          -13 0 -13 0 -18 2 rrcurveto
+          18 -30 9 -46 1 -29 rrcurveto
+          23 -1 24 0 18 2 rrcurveto
+          22 4 20 7 16 12 rrcurveto
+          32 22 14 43 0 66 rrcurveto
+          0 59 -13 70 -62 78 rrcurveto
+          hintmask 0000000001001000
+          29 80 34 112 27 88 rrcurveto
+          hintmask 0000000000001000
+          -80 46 rlineto
+          -18 -4 rlineto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="cid13393" width="1000" lsb="69"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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="cid13393" height="1000" tsb="25"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w889.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w889.00.ttx
new file mode 100644
index 0000000..a90da39
--- /dev/null
+++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w889.00.ttx
@@ -0,0 +1,473 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="cid06449"/>
+    <GlyphID id="2" name="cid22370"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0xe198766"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:08:11 2018"/>
+    <modified value="Tue Apr 16 22:17:21 2019"/>
+    <xMin value="30"/>
+    <yMin value="-120"/>
+    <xMax value="986"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="30"/>
+    <minRightSideBearing value="14"/>
+    <xMaxExtent value="986"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="3"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="978"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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="00001000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBE"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="34130"/>
+    <usLastCharIndex value="34216"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w889.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w889.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w889.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w889.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_Kanji-w889.00">
+      <ROS Registry="Adobe" Order="Japan1" Supplement="6"/>
+      <Notice value="1997-2007, 2012 Adobe Systems Incorporated. All Rights Reserved. Kozuka Mincho is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries.&quot;"/>
+      <FullName value="Master Set Kanji w889.00"/>
+      <FamilyName value="Master Set Kanji"/>
+      <Weight value="ExtraLight"/>
+      <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="30 -120 986 880"/>
+      <StrokeWidth value="0"/>
+      <XUID value="1119273886"/>
+      <CIDFontVersion value="6.004"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="23058"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="MasterSet_Kanji-w889.00-Generic"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="607"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="MasterSet_Kanji-w889.00-Kanji"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="0"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          393 -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 hintmask 10111000
+          100 -120 rmoveto
+          800 0 rlineto
+          0 1000 rlineto
+          -800 0 rlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 0 rlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          0 -818 rlineto
+          -668 -41 rmoveto
+          318 409 rlineto
+          318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 rlineto
+          -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid06449" fdSelectIndex="1">
+          1000 -83 97 105 72 -22 22 34 18 -18 21.5 -21.5 83 -52 52 28 87 26 83 38 84 12 126 -106 106 -106 167 hstemhm
+          63 118 -40 117 9 132 -50 104 -41 37 87 120 -111 105 -52 132 14 124 -51 125 -85 103 hintmask 110001011101011101101101
+          50 792 rmoveto
+          0 -106 rlineto
+          901 0 rlineto
+          0 106 rlineto
+          -800 -484 rmoveto
+          0 -83 rlineto
+          706 0 rlineto
+          0 83 rlineto
+          hintmask 000000000000100100000000
+          -590 545 rmoveto
+          hintmask 000000000010000100000000
+          0 -187 rlineto
+          132 0 rlineto
+          hintmask 000000000000100100001000
+          0 187 rlineto
+          199 0 rmoveto
+          hintmask 000000000010000000001000
+          0 -187 rlineto
+          132 0 rlineto
+          hintmask 000000000000100000001000
+          0 187 rlineto
+          hintmask 000000111100011010010100
+          -80 -283 rmoveto
+          94 0 rlineto
+          0 -38 rlineto
+          -94 0 rlineto
+          -197 38 rmoveto
+          92 0 rlineto
+          0 -38 rlineto
+          -92 0 rlineto
+          -195 38 rmoveto
+          91 0 rlineto
+          0 -38 rlineto
+          -91 0 rlineto
+          -117 122 rmoveto
+          0 -205 rlineto
+          727 0 rlineto
+          0 205 rlineto
+          -805 -231 rmoveto
+          0 -167 rlineto
+          118 0 rlineto
+          0 80 rlineto
+          hintmask 000000110000000000000010
+          636 0 rlineto
+          0 -80 rlineto
+          125 0 rlineto
+          0 167 rlineto
+          hintmask 000010000000000100000000
+          -666 -167 rmoveto
+          -41 -49 -74 -44 -109 -32 rrcurveto
+          20 -15 29 -35 13 -23 rrcurveto
+          hintmask 010000000000000001000000
+          124 45 82 56 57 78 rrcurveto
+          35 -46 rmoveto
+          0 -13 rlineto
+          -49 -109 -140 -55 -169 -19 rrcurveto
+          18 -22 23 -42 8 -26 rrcurveto
+          hintmask 001000000000000001000000
+          194 32 155 72 67 160 rrcurveto
+          hintmask 110100000000000001100001
+          -66 24 rlineto
+          -19 -2 rlineto
+          -258 -127 rmoveto
+          30 -17 37 -31 20 -21 rrcurveto
+          75 50 rlineto
+          -20 20 -37 27 -32 16 rrcurveto
+          17 83 rmoveto
+          -72 -72 rlineto
+          255 0 rlineto
+          0 72 rlineto
+          87 52 rmoveto
+          0 -196 rlineto
+          0 -100 29 -30 121 0 rrcurveto
+          24 0 92 0 26 0 rrcurveto
+          87 0 32 27 13 98 rrcurveto
+          -33 6 -46 17 -24 16 rrcurveto
+          -4 -56 -7 -11 -30 0 rrcurveto
+          -22 0 -75 0 -17 0 rrcurveto
+          -39 0 -7 5 0 29 rrcurveto
+          0 195 rlineto
+          164 -30 rmoveto
+          -56 -25 -96 -22 -86 -14 rrcurveto
+          12 -21 14 -37 5 -22 rrcurveto
+          95 12 114 21 84 33 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="cid22370" fdSelectIndex="1">
+          1000 62 72 29 68 28 68 28 72 29 86 37 87 17 114 -95 95 -95 151 hstemhm
+          116 117 -65 129 -23 128 -65 105 106 105 -60 130 -14 135 -83 124 hintmask 111111010011001100000000
+          50 797 rmoveto
+          0 -95 rlineto
+          901 0 rlineto
+          0 95 rlineto
+          hintmask 000000001001000000000000
+          -677 56 rmoveto
+          hintmask 000000100001000000000000
+          0 -170 rlineto
+          128 0 rlineto
+          hintmask 000000001001001000000000
+          0 170 rlineto
+          191 0 rmoveto
+          hintmask 000000100000001000000000
+          0 -170 rlineto
+          130 0 rlineto
+          hintmask 000000001000001000000000
+          0 170 rlineto
+          hintmask 000011000100110010000000
+          -70 -274 rmoveto
+          108 0 rlineto
+          0 -37 rlineto
+          -108 0 rlineto
+          -211 37 rmoveto
+          106 0 rlineto
+          0 -37 rlineto
+          -106 0 rlineto
+          -209 37 rmoveto
+          104 0 rlineto
+          0 -37 rlineto
+          -104 0 rlineto
+          -117 124 rmoveto
+          0 -210 rlineto
+          769 0 rlineto
+          0 210 rlineto
+          hintmask 111100000010000100000000
+          -588 -407 rmoveto
+          412 0 rlineto
+          0 -28 rlineto
+          -412 0 rlineto
+          0 -68 rmoveto
+          412 0 rlineto
+          0 -29 rlineto
+          -412 0 rlineto
+          0 221 rmoveto
+          412 0 rlineto
+          0 -28 rlineto
+          -412 0 rlineto
+          -129 100 rmoveto
+          0 -365 rlineto
+          676 0 rlineto
+          0 365 rlineto
+          -302 -420 rmoveto
+          110 -32 108 -41 59 -28 rrcurveto
+          167 61 rlineto
+          -77 29 -134 43 -112 31 rrcurveto
+          -325 -1 rmoveto
+          -72 -33 -125 -29 -111 -16 rrcurveto
+          27 -20 44 -44 22 -25 rrcurveto
+          106 24 135 43 87 47 rrcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="cid06449" width="1000" lsb="50"/>
+    <mtx name="cid22370" width="1000" lsb="30"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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="cid06449" height="1000" tsb="27"/>
+    <mtx name="cid22370" height="1000" tsb="27"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w890.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w890.00.ttx
new file mode 100644
index 0000000..a0aed4f
--- /dev/null
+++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w890.00.ttx
@@ -0,0 +1,473 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="cid06449"/>
+    <GlyphID id="2" name="cid22370"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.002"/>
+    <checkSumAdjustment value="0x3deef2bc"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Thu Dec 13 12:08:19 2018"/>
+    <modified value="Tue Apr 16 22:17:21 2019"/>
+    <xMin value="30"/>
+    <yMin value="-120"/>
+    <xMax value="986"/>
+    <yMax value="880"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="30"/>
+    <minRightSideBearing value="14"/>
+    <xMaxExtent value="986"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="3"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="978"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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="00001000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="ADBE"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="34130"/>
+    <usLastCharIndex value="34216"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w890.00
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.002;ADBE;MasterSet_Kanji-w890.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      SHSansJPVF w890.00
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.002;hotconv 1.0.109;makeotfexe 2.5.65596 DEVELOPMENT
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_Kanji-w890.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="40" language="0" nGroups="2">
+      <map code="0x8552" name="cid22370"/><!-- CJK UNIFIED IDEOGRAPH-8552 -->
+      <map code="0x85a8" name="cid06449"/><!-- CJK UNIFIED IDEOGRAPH-85A8 -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_Kanji-w890.00">
+      <ROS Registry="Adobe" Order="Japan1" Supplement="6"/>
+      <Notice value="1997-2007, 2012 Adobe Systems Incorporated. All Rights Reserved. Kozuka Mincho is either a registered trademark or trademark of Adobe Systems Incorporated in the United States and/or other countries.&quot;"/>
+      <FullName value="Master Set Kanji w890.00"/>
+      <FamilyName value="Master Set Kanji"/>
+      <Weight value="ExtraLight"/>
+      <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="30 -120 986 880"/>
+      <StrokeWidth value="0"/>
+      <XUID value="1119273886"/>
+      <CIDFontVersion value="6.004"/>
+      <CIDFontRevision value="0"/>
+      <CIDFontType value="0"/>
+      <CIDCount value="23058"/>
+      <!-- charset is dumped separately as the 'GlyphOrder' element -->
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <FontName value="MasterSet_Kanji-w890.00-Generic"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="607"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <FontName value="MasterSet_Kanji-w890.00-Kanji"/>
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <ForceBold value="0"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+            <initialRandomSeed value="0"/>
+            <defaultWidthX value="1000"/>
+            <nominalWidthX value="0"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          393 -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 hintmask 10111000
+          100 -120 rmoveto
+          800 0 rlineto
+          0 1000 rlineto
+          -800 0 rlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 0 rlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          0 -818 rlineto
+          -668 -41 rmoveto
+          318 409 rlineto
+          318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 rlineto
+          -318 -409 rlineto
+          endchar
+        </CharString>
+        <CharString name="cid06449" fdSelectIndex="1">
+          1000 -83 98 104 72 -22 22 34 18 -18 21.5 -21.5 83 -52 52 28 87 26 83 38 84 -7 145 -106 106 -106 167 hstemhm
+          63 118 -40 117 9 132 -50 105 -42 37 87 120 -111 105 -52 132 14 124 -51 125 -85 103 hintmask 110001011101011101101101
+          50 792 rmoveto
+          0 -106 rlineto
+          901 0 rlineto
+          0 106 rlineto
+          -800 -484 rmoveto
+          0 -83 rlineto
+          706 0 rlineto
+          0 83 rlineto
+          hintmask 000000000000100100000000
+          -590 545 rmoveto
+          hintmask 000000000010000100000000
+          0 -206 rlineto
+          132 0 rlineto
+          hintmask 000000000000100100001000
+          0 206 rlineto
+          199 0 rmoveto
+          hintmask 000000000010000000001000
+          0 -206 rlineto
+          132 0 rlineto
+          hintmask 000000000000100000001000
+          0 206 rlineto
+          hintmask 000000111100011010010100
+          -80 -283 rmoveto
+          94 0 rlineto
+          0 -38 rlineto
+          -94 0 rlineto
+          -196 38 rmoveto
+          91 0 rlineto
+          0 -38 rlineto
+          -91 0 rlineto
+          -196 38 rmoveto
+          91 0 rlineto
+          0 -38 rlineto
+          -91 0 rlineto
+          -117 122 rmoveto
+          0 -205 rlineto
+          727 0 rlineto
+          0 205 rlineto
+          -805 -231 rmoveto
+          0 -167 rlineto
+          118 0 rlineto
+          0 80 rlineto
+          hintmask 000000110000000000000010
+          636 0 rlineto
+          0 -80 rlineto
+          125 0 rlineto
+          0 167 rlineto
+          hintmask 000010000000000100000000
+          -666 -167 rmoveto
+          -41 -49 -74 -44 -109 -32 rrcurveto
+          20 -15 29 -35 13 -23 rrcurveto
+          hintmask 010000000000000001000000
+          124 45 82 56 57 78 rrcurveto
+          35 -46 rmoveto
+          0 -13 rlineto
+          -49 -109 -140 -55 -169 -19 rrcurveto
+          18 -22 23 -42 8 -26 rrcurveto
+          hintmask 001000000000000001000000
+          194 32 155 72 67 160 rrcurveto
+          hintmask 110100000000000001100001
+          -66 24 rlineto
+          -19 -2 rlineto
+          -258 -127 rmoveto
+          30 -17 37 -31 20 -21 rrcurveto
+          75 50 rlineto
+          -20 20 -37 27 -32 16 rrcurveto
+          17 83 rmoveto
+          -72 -72 rlineto
+          255 0 rlineto
+          0 72 rlineto
+          87 52 rmoveto
+          0 -196 rlineto
+          0 -100 29 -30 121 0 rrcurveto
+          24 0 92 0 26 0 rrcurveto
+          87 0 32 27 13 98 rrcurveto
+          -33 6 -46 17 -24 16 rrcurveto
+          -4 -56 -7 -10 -31 0 rrcurveto
+          -21 0 -75 0 -17 0 rrcurveto
+          -39 0 -7 4 0 29 rrcurveto
+          0 195 rlineto
+          164 -30 rmoveto
+          -56 -25 -96 -22 -86 -14 rrcurveto
+          12 -21 14 -37 5 -22 rrcurveto
+          95 12 114 21 84 33 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="cid22370" fdSelectIndex="1">
+          1000 62 72 29 68 28 68 28 72 29 86 37 87 -15 146 -95 95 -95 151 hstemhm
+          116 118 -66 129 -23 128 -65 105 106 105 -60 130 -14 135 -83 124 hintmask 111111010011001100000000
+          50 797 rmoveto
+          0 -95 rlineto
+          901 0 rlineto
+          0 95 rlineto
+          hintmask 000000001001000000000000
+          -677 56 rmoveto
+          hintmask 000000100001000000000000
+          0 -202 rlineto
+          128 0 rlineto
+          hintmask 000000001001001000000000
+          0 202 rlineto
+          191 0 rmoveto
+          hintmask 000000100000001000000000
+          0 -202 rlineto
+          130 0 rlineto
+          hintmask 000000001000001000000000
+          0 202 rlineto
+          hintmask 000011000100110010000000
+          -70 -274 rmoveto
+          108 0 rlineto
+          0 -37 rlineto
+          -108 0 rlineto
+          -211 37 rmoveto
+          106 0 rlineto
+          0 -37 rlineto
+          -106 0 rlineto
+          -208 37 rmoveto
+          103 0 rlineto
+          0 -37 rlineto
+          -103 0 rlineto
+          -118 124 rmoveto
+          0 -210 rlineto
+          769 0 rlineto
+          0 210 rlineto
+          hintmask 111100000010000100000000
+          -588 -407 rmoveto
+          412 0 rlineto
+          0 -28 rlineto
+          -412 0 rlineto
+          0 -68 rmoveto
+          412 0 rlineto
+          0 -29 rlineto
+          -412 0 rlineto
+          0 221 rmoveto
+          412 0 rlineto
+          0 -28 rlineto
+          -412 0 rlineto
+          -129 100 rmoveto
+          0 -365 rlineto
+          676 0 rlineto
+          0 365 rlineto
+          -302 -420 rmoveto
+          110 -32 108 -41 59 -28 rrcurveto
+          167 61 rlineto
+          -77 29 -134 43 -112 31 rrcurveto
+          -325 -1 rmoveto
+          -72 -33 -125 -29 -111 -16 rrcurveto
+          27 -20 44 -44 22 -25 rrcurveto
+          106 24 135 43 87 47 rrcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="0"/>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="1000" lsb="100"/>
+    <mtx name="cid06449" width="1000" lsb="50"/>
+    <mtx name="cid22370" width="1000" lsb="30"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="0"/>
+    <minBottomSideBearing value="0"/>
+    <yMaxExtent value="1000"/>
+    <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="cid06449" height="1000" tsb="27"/>
+    <mtx name="cid22370" height="1000" tsb="27"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_getvar_ttf/Mutator_Getvar.ttx b/Tests/varLib/data/master_ttx_getvar_ttf/Mutator_Getvar.ttx
new file mode 100755
index 0000000..5360fe4
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_getvar_ttf/Mutator_Getvar.ttx
@@ -0,0 +1,555 @@
+<?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>
+        <assembly>
+          GETVARIATION[]
+        </assembly>
+    </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_ttx_interpolatable_otf/TestFamily2-Master0.ttx b/Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master0.ttx
index a6a8e00..157043f 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master0.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master0.ttx
@@ -766,7 +766,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="A" out="A.sc"/>
         </SingleSubst>
       </Lookup>
@@ -774,7 +774,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="a" out="a.alt"/>
         </SingleSubst>
       </Lookup>
@@ -782,7 +782,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="ampersand" out="a,n,d"/>
         </MultipleSubst>
       </Lookup>
@@ -790,7 +790,7 @@
         <LookupType value="3"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <AlternateSubst index="0" Format="1">
+        <AlternateSubst index="0">
           <AlternateSet glyph="a">
             <Alternate glyph="a.alt"/>
             <Alternate glyph="A.sc"/>
@@ -801,7 +801,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="f">
             <Ligature components="t" glyph="f_t"/>
           </LigatureSet>
@@ -814,11 +814,11 @@
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=0 -->
           <!-- InputGlyphCount=1 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="a"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=1 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="t"/>
           </LookAheadCoverage>
           <!-- SubstCount=1 -->
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Bold.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Bold.ttx
new file mode 100644
index 0000000..cf4b5da
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Bold.ttx
@@ -0,0 +1,419 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <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="e"/>
+    <GlyphID id="3" name="s"/>
+    <GlyphID id="4" name="dotabovecomb"/>
+    <GlyphID id="5" name="edotabove"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="0.0"/>
+    <checkSumAdjustment value="0x778ee739"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Nov 21 11:49:03 2018"/>
+    <modified value="Tue Jan 15 18:40:09 2019"/>
+    <xMin value="-64"/>
+    <yMin value="-250"/>
+    <xMax value="608"/>
+    <yMax value="812"/>
+    <macStyle value="00000000 00000001"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="950"/>
+    <descent value="-250"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="-64"/>
+    <minRightSideBearing value="-63"/>
+    <xMaxExtent value="608"/>
+    <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="18"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="17"/>
+    <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="2"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="580"/>
+    <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="300"/>
+    <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 01000101"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 00100000"/>
+    <usFirstCharIndex value="97"/>
+    <usLastCharIndex value="775"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="950"/>
+    <usWinDescent value="250"/>
+    <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="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="a" width="600" lsb="9"/>
+    <mtx name="dotabovecomb" width="0" lsb="-64"/>
+    <mtx name="e" width="600" lsb="9"/>
+    <mtx name="edotabove" width="600" lsb="9"/>
+    <mtx name="s" width="600" lsb="7"/>
+  </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="0x65" name="e"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x117" name="edotabove"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x307" name="dotabovecomb"/><!-- COMBINING DOT ABOVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x65" name="e"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x117" name="edotabove"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x307" name="dotabovecomb"/><!-- COMBINING DOT ABOVE -->
+    </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="-250" xMax="450" yMax="750">
+      <contour>
+        <pt x="50" y="-250" on="1"/>
+        <pt x="450" y="-250" on="1"/>
+        <pt x="450" y="750" on="1"/>
+        <pt x="50" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-200" on="1"/>
+        <pt x="100" y="700" on="1"/>
+        <pt x="400" y="700" on="1"/>
+        <pt x="400" y="-200" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="9" yMin="-12" xMax="468" yMax="504">
+      <contour>
+        <pt x="468" y="-1" on="1"/>
+        <pt x="307" y="-1" on="1"/>
+        <pt x="304" y="303" on="1"/>
+        <pt x="208" y="341" on="1"/>
+        <pt x="36" y="281" on="1"/>
+        <pt x="9" y="428" on="1"/>
+        <pt x="214" y="504" on="1"/>
+        <pt x="447" y="434" on="1"/>
+      </contour>
+      <contour>
+        <pt x="378" y="263" on="1"/>
+        <pt x="381" y="184" on="1"/>
+        <pt x="165" y="179" on="1"/>
+        <pt x="163" y="133" on="1"/>
+        <pt x="201" y="102" on="1"/>
+        <pt x="383" y="149" on="1"/>
+        <pt x="389" y="71" on="1"/>
+        <pt x="168" y="-12" on="1"/>
+        <pt x="29" y="22" on="1"/>
+        <pt x="26" y="240" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dotabovecomb" xMin="-64" yMin="483" xMax="63" yMax="625">
+      <contour>
+        <pt x="-29" y="625" on="1"/>
+        <pt x="63" y="605" on="1"/>
+        <pt x="58" y="488" on="1"/>
+        <pt x="-64" y="483" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="e" xMin="9" yMin="-18" xMax="601" yMax="548">
+      <contour>
+        <pt x="197" y="229" on="1"/>
+        <pt x="195" y="299" on="1"/>
+        <pt x="404" y="293" on="1"/>
+        <pt x="301" y="360" on="1"/>
+        <pt x="217" y="264" on="1"/>
+        <pt x="244" y="130" on="1"/>
+        <pt x="524" y="184" on="1"/>
+        <pt x="528" y="0" on="1"/>
+        <pt x="188" y="-18" on="1"/>
+        <pt x="9" y="262" on="1"/>
+        <pt x="314" y="548" on="1"/>
+        <pt x="596" y="304" on="1"/>
+        <pt x="601" y="225" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="edotabove" xMin="9" yMin="-18" xMax="601" yMax="812">
+      <component glyphName="e" x="0" y="0" flags="0x204"/>
+      <component glyphName="dotabovecomb" x="307" y="187" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="7" yMin="-58" xMax="608" yMax="530">
+      <contour>
+        <pt x="559" y="459" on="1"/>
+        <pt x="537" y="336" on="1"/>
+        <pt x="324" y="402" on="1"/>
+        <pt x="268" y="357" on="1"/>
+        <pt x="608" y="141" on="1"/>
+        <pt x="284" y="-58" on="1"/>
+        <pt x="7" y="79" on="1"/>
+        <pt x="26" y="226" on="1"/>
+        <pt x="221" y="119" on="1"/>
+        <pt x="347" y="149" on="1"/>
+        <pt x="16" y="398" on="1"/>
+        <pt x="324" y="530" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Layer Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      0.000;NONE;LayerFont-Bold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Layer Font Bold
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 0.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      LayerFont-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 -->
+      <psName name="dotabovecomb"/>
+      <psName name="edotabove"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="dotabovecomb" 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=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="dotabovecomb"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="e"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="-2"/>
+                <YCoordinate value="465"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="315"/>
+                <YCoordinate value="644"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <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="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="a">
+            <Ligature components="e,s,s" glyph="s"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Medium.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Medium.ttx
new file mode 100644
index 0000000..eb3c745
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Medium.ttx
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="e"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="0.0"/>
+    <checkSumAdjustment value="0x62ddba7b"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Nov 21 11:49:03 2018"/>
+    <modified value="Tue Jan 15 18:40:09 2019"/>
+    <xMin value="40"/>
+    <yMin value="-250"/>
+    <xMax value="576"/>
+    <yMax value="750"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="2"/>
+    <maxPoints value="13"/>
+    <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>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="e" width="600" lsb="40"/>
+  </hmtx>
+
+  <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="-250" xMax="450" yMax="750">
+      <contour>
+        <pt x="50" y="-250" on="1"/>
+        <pt x="450" y="-250" on="1"/>
+        <pt x="450" y="750" on="1"/>
+        <pt x="50" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-200" on="1"/>
+        <pt x="100" y="700" on="1"/>
+        <pt x="400" y="700" on="1"/>
+        <pt x="400" y="-200" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="e" xMin="40" yMin="-18" xMax="576" yMax="513">
+      <contour>
+        <pt x="126" y="203" on="1"/>
+        <pt x="125" y="298" on="1"/>
+        <pt x="396" y="297" on="1"/>
+        <pt x="318" y="387" on="1"/>
+        <pt x="180" y="264" on="1"/>
+        <pt x="264" y="116" on="1"/>
+        <pt x="507" y="157" on="1"/>
+        <pt x="526" y="45" on="1"/>
+        <pt x="188" y="-18" on="1"/>
+        <pt x="40" y="261" on="1"/>
+        <pt x="316" y="513" on="1"/>
+        <pt x="571" y="305" on="1"/>
+        <pt x="576" y="199" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-32768"/>
+    <underlineThickness value="-32768"/>
+    <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>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Regular.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Regular.ttx
new file mode 100644
index 0000000..c93da2a
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Regular.ttx
@@ -0,0 +1,419 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <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="e"/>
+    <GlyphID id="3" name="s"/>
+    <GlyphID id="4" name="dotabovecomb"/>
+    <GlyphID id="5" name="edotabove"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="0.0"/>
+    <checkSumAdjustment value="0x5cdfc4c1"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Nov 21 11:49:03 2018"/>
+    <modified value="Tue Jan 15 18:40:09 2019"/>
+    <xMin value="-37"/>
+    <yMin value="-250"/>
+    <xMax value="582"/>
+    <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="950"/>
+    <descent value="-250"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="-37"/>
+    <minRightSideBearing value="-50"/>
+    <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="6"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="18"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="17"/>
+    <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="2"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="580"/>
+    <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="300"/>
+    <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 01000101"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="97"/>
+    <usLastCharIndex value="775"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="950"/>
+    <usWinDescent value="250"/>
+    <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="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="a" width="600" lsb="9"/>
+    <mtx name="dotabovecomb" width="0" lsb="-37"/>
+    <mtx name="e" width="600" lsb="40"/>
+    <mtx name="edotabove" width="600" lsb="40"/>
+    <mtx name="s" width="600" lsb="25"/>
+  </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="0x65" name="e"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x117" name="edotabove"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x307" name="dotabovecomb"/><!-- COMBINING DOT ABOVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x65" name="e"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x117" name="edotabove"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x307" name="dotabovecomb"/><!-- COMBINING DOT ABOVE -->
+    </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="-250" xMax="450" yMax="750">
+      <contour>
+        <pt x="50" y="-250" on="1"/>
+        <pt x="450" y="-250" on="1"/>
+        <pt x="450" y="750" on="1"/>
+        <pt x="50" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-200" on="1"/>
+        <pt x="100" y="700" on="1"/>
+        <pt x="400" y="700" on="1"/>
+        <pt x="400" y="-200" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="9" yMin="-12" xMax="468" yMax="504">
+      <contour>
+        <pt x="468" y="-1" on="1"/>
+        <pt x="366" y="-3" on="1"/>
+        <pt x="363" y="357" on="1"/>
+        <pt x="208" y="397" on="1"/>
+        <pt x="36" y="337" on="1"/>
+        <pt x="9" y="428" on="1"/>
+        <pt x="214" y="504" on="1"/>
+        <pt x="447" y="434" on="1"/>
+      </contour>
+      <contour>
+        <pt x="378" y="263" on="1"/>
+        <pt x="382" y="207" on="1"/>
+        <pt x="88" y="172" on="1"/>
+        <pt x="86" y="126" on="1"/>
+        <pt x="161" y="74" on="1"/>
+        <pt x="383" y="134" on="1"/>
+        <pt x="389" y="71" on="1"/>
+        <pt x="168" y="-12" on="1"/>
+        <pt x="29" y="22" on="1"/>
+        <pt x="26" y="240" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dotabovecomb" xMin="-37" yMin="501" xMax="50" yMax="597">
+      <contour>
+        <pt x="-21" y="597" on="1"/>
+        <pt x="50" y="589" on="1"/>
+        <pt x="41" y="501" on="1"/>
+        <pt x="-37" y="503" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="e" xMin="40" yMin="-18" xMax="576" yMax="513">
+      <contour>
+        <pt x="127" y="228" on="1"/>
+        <pt x="125" y="298" on="1"/>
+        <pt x="480" y="292" on="1"/>
+        <pt x="317" y="416" on="1"/>
+        <pt x="147" y="263" on="1"/>
+        <pt x="229" y="75" on="1"/>
+        <pt x="509" y="129" on="1"/>
+        <pt x="526" y="45" on="1"/>
+        <pt x="188" y="-18" on="1"/>
+        <pt x="40" y="261" on="1"/>
+        <pt x="316" y="513" on="1"/>
+        <pt x="571" y="305" on="1"/>
+        <pt x="576" y="226" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="edotabove" xMin="40" yMin="-18" xMax="576" yMax="693">
+      <component glyphName="e" x="0" y="0" flags="0x204"/>
+      <component glyphName="dotabovecomb" x="313" y="96" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="25" yMin="-13" xMax="582" yMax="530">
+      <contour>
+        <pt x="559" y="459" on="1"/>
+        <pt x="539" y="376" on="1"/>
+        <pt x="326" y="442" on="1"/>
+        <pt x="213" y="366" on="1"/>
+        <pt x="582" y="174" on="1"/>
+        <pt x="304" y="-13" on="1"/>
+        <pt x="25" y="83" on="1"/>
+        <pt x="53" y="174" on="1"/>
+        <pt x="282" y="76" on="1"/>
+        <pt x="427" y="155" on="1"/>
+        <pt x="38" y="343" on="1"/>
+        <pt x="324" y="530" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Layer Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      0.000;NONE;LayerFont-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Layer Font Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 0.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      LayerFont-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="dotabovecomb"/>
+      <psName name="edotabove"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="dotabovecomb" 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=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="dotabovecomb"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="e"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="-2"/>
+                <YCoordinate value="465"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="314"/>
+                <YCoordinate value="556"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <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="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="a">
+            <Ligature components="e,s,s" glyph="s"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</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
index f14f89d..ca5a2e1 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master0.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master0.ttx
@@ -517,4 +517,14 @@
     </extraNames>
   </post>
 
+  <GDEF>
+    <Version value="0x00010003"/>
+    <GlyphClassDef>
+      <ClassDef glyph="uni0024" class="1"/>
+      <ClassDef glyph="uni0024.nostroke" class="1"/>
+      <ClassDef glyph="uni0041" class="1"/>
+      <ClassDef glyph="uni0061" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
 </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
index 8d5bace..9076cf7 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master1.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master1.ttx
@@ -517,4 +517,14 @@
     </extraNames>
   </post>
 
+  <GDEF>
+    <Version value="0x00010003"/>
+    <GlyphClassDef>
+      <ClassDef glyph="uni0024" class="1"/>
+      <ClassDef glyph="uni0024.nostroke" class="1"/>
+      <ClassDef glyph="uni0041" class="1"/>
+      <ClassDef glyph="uni0061" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
 </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
index 3e20fac..9bec8c0 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master2.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master2.ttx
@@ -501,4 +501,14 @@
     </extraNames>
   </post>
 
+  <GDEF>
+    <Version value="0x00010003"/>
+    <GlyphClassDef>
+      <ClassDef glyph="uni0024" class="1"/>
+      <ClassDef glyph="uni0024.nostroke" class="1"/>
+      <ClassDef glyph="uni0041" class="1"/>
+      <ClassDef glyph="uni0061" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
 </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
index e1d0375..1cfdfd9 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master3.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master3.ttx
@@ -501,4 +501,14 @@
     </extraNames>
   </post>
 
+  <GDEF>
+    <Version value="0x00010003"/>
+    <GlyphClassDef>
+      <ClassDef glyph="uni0024" class="1"/>
+      <ClassDef glyph="uni0024.nostroke" class="1"/>
+      <ClassDef glyph="uni0041" class="1"/>
+      <ClassDef glyph="uni0061" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
 </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
index c0946fd..1ae5d47 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master4.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master4.ttx
@@ -501,4 +501,14 @@
     </extraNames>
   </post>
 
+  <GDEF>
+    <Version value="0x00010003"/>
+    <GlyphClassDef>
+      <ClassDef glyph="uni0024" class="1"/>
+      <ClassDef glyph="uni0024.nostroke" class="1"/>
+      <ClassDef glyph="uni0041" class="1"/>
+      <ClassDef glyph="uni0061" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
 </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
index 13d48e7..d1a3393 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily2-Master0.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily2-Master0.ttx
@@ -1081,7 +1081,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="A" out="A.sc"/>
         </SingleSubst>
       </Lookup>
@@ -1089,7 +1089,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="a" out="a.alt"/>
         </SingleSubst>
       </Lookup>
@@ -1097,7 +1097,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="ampersand" out="a,n,d"/>
         </MultipleSubst>
       </Lookup>
@@ -1105,7 +1105,7 @@
         <LookupType value="3"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <AlternateSubst index="0" Format="1">
+        <AlternateSubst index="0">
           <AlternateSet glyph="a">
             <Alternate glyph="a.alt"/>
             <Alternate glyph="A.sc"/>
@@ -1116,7 +1116,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="f">
             <Ligature components="t" glyph="f_t"/>
           </LigatureSet>
@@ -1129,11 +1129,11 @@
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=0 -->
           <!-- InputGlyphCount=1 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="a"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=1 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="t"/>
           </LookAheadCoverage>
           <!-- SubstCount=1 -->
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Bold.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Bold.ttx
index a752bc1..0f9e97a 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Bold.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Bold.ttx
@@ -477,17 +477,17 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="T"/>
           </Coverage>
           <ValueFormat1 value="4"/>
           <ValueFormat2 value="0"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
           </ClassDef1>
-          <ClassDef2 Format="1">
+          <ClassDef2>
             <ClassDef glyph="T" class="4"/>
             <ClassDef glyph="n" class="3"/>
             <ClassDef glyph="o" class="2"/>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Condensed.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Condensed.ttx
index db0372a..f8ff987 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Condensed.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Condensed.ttx
@@ -483,17 +483,17 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="T"/>
           </Coverage>
           <ValueFormat1 value="4"/>
           <ValueFormat2 value="0"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
           </ClassDef1>
-          <ClassDef2 Format="1">
+          <ClassDef2>
             <ClassDef glyph="T" class="4"/>
             <ClassDef glyph="n" class="3"/>
             <ClassDef glyph="o" class="2"/>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedBold.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedBold.ttx
index 3313ce6..2b7264d 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedBold.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedBold.ttx
@@ -483,17 +483,17 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="T"/>
           </Coverage>
           <ValueFormat1 value="4"/>
           <ValueFormat2 value="0"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
           </ClassDef1>
-          <ClassDef2 Format="1">
+          <ClassDef2>
             <ClassDef glyph="T" class="4"/>
             <ClassDef glyph="n" class="3"/>
             <ClassDef glyph="o" class="2"/>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedLight.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedLight.ttx
index c83912b..d03abf5 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedLight.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedLight.ttx
@@ -483,17 +483,17 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="T"/>
           </Coverage>
           <ValueFormat1 value="4"/>
           <ValueFormat2 value="0"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
           </ClassDef1>
-          <ClassDef2 Format="1">
+          <ClassDef2>
             <ClassDef glyph="T" class="4"/>
             <ClassDef glyph="n" class="3"/>
             <ClassDef glyph="o" class="2"/>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedSemiBold.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedSemiBold.ttx
index 7b0c88f..1189220 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedSemiBold.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-CondensedSemiBold.ttx
@@ -483,17 +483,17 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="T"/>
           </Coverage>
           <ValueFormat1 value="4"/>
           <ValueFormat2 value="0"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
           </ClassDef1>
-          <ClassDef2 Format="1">
+          <ClassDef2>
             <ClassDef glyph="T" class="4"/>
             <ClassDef glyph="n" class="3"/>
             <ClassDef glyph="o" class="2"/>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Light.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Light.ttx
index 0e84719..3583c0d 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Light.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Light.ttx
@@ -483,17 +483,17 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="T"/>
           </Coverage>
           <ValueFormat1 value="4"/>
           <ValueFormat2 value="0"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
           </ClassDef1>
-          <ClassDef2 Format="1">
+          <ClassDef2>
             <ClassDef glyph="T" class="4"/>
             <ClassDef glyph="n" class="3"/>
             <ClassDef glyph="o" class="2"/>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Regular.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Regular.ttx
index 44848db..bf68b16 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Regular.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Regular.ttx
@@ -477,17 +477,17 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="T"/>
           </Coverage>
           <ValueFormat1 value="4"/>
           <ValueFormat2 value="0"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
           </ClassDef1>
-          <ClassDef2 Format="1">
+          <ClassDef2>
             <ClassDef glyph="T" class="4"/>
             <ClassDef glyph="n" class="3"/>
             <ClassDef glyph="o" class="2"/>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-SemiBold.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-SemiBold.ttx
index 7e6a0b3..96badb3 100644
--- a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-SemiBold.ttx
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-SemiBold.ttx
@@ -483,17 +483,17 @@
       <!-- LookupCount=1 -->
       <Lookup index="0">
         <LookupType value="2"/>
-        <LookupFlag value="8"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="T"/>
           </Coverage>
           <ValueFormat1 value="4"/>
           <ValueFormat2 value="0"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
           </ClassDef1>
-          <ClassDef2 Format="1">
+          <ClassDef2>
             <ClassDef glyph="T" class="4"/>
             <ClassDef glyph="n" class="3"/>
             <ClassDef glyph="o" class="2"/>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily4-Italic15.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily4-Italic15.ttx
new file mode 100644
index 0000000..ab5789e
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily4-Italic15.ttx
@@ -0,0 +1,662 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.34">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="N"/>
+    <GlyphID id="2" name="O"/>
+    <GlyphID id="3" name="Odieresis"/>
+    <GlyphID id="4" name="n"/>
+    <GlyphID id="5" name="o"/>
+    <GlyphID id="6" name="odieresis"/>
+    <GlyphID id="7" name="uni0308"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xc48e411d"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="750"/>
+    <created value="Sat Oct 21 13:31:38 2017"/>
+    <modified value="Mon Dec 17 12:42:07 2018"/>
+    <xMin value="6"/>
+    <yMin value="-150"/>
+    <xMax value="436"/>
+    <yMax value="650"/>
+    <macStyle value="00000000 00000010"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="750"/>
+    <descent value="-150"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="408"/>
+    <minLeftSideBearing value="6"/>
+    <minRightSideBearing value="-333"/>
+    <xMaxExtent value="436"/>
+    <caretSlopeRise value="1000"/>
+    <caretSlopeRun value="268"/>
+    <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="36"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="64"/>
+    <maxCompositeContours value="4"/>
+    <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="375"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="488"/>
+    <ySubscriptYSize value="450"/>
+    <ySubscriptXOffset value="-15"/>
+    <ySubscriptYOffset value="56"/>
+    <ySuperscriptXSize value="488"/>
+    <ySuperscriptYSize value="450"/>
+    <ySuperscriptXOffset value="70"/>
+    <ySuperscriptYOffset value="263"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="210"/>
+    <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 01000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="jens"/>
+    <fsSelection value="00000000 00000001"/>
+    <usFirstCharIndex value="78"/>
+    <usLastCharIndex value="776"/>
+    <sTypoAscender value="600"/>
+    <sTypoDescender value="-150"/>
+    <sTypoLineGap value="150"/>
+    <usWinAscent value="700"/>
+    <usWinDescent value="250"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="350"/>
+    <sCapHeight value="500"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="375" lsb="38"/>
+    <mtx name="N" width="408" lsb="11"/>
+    <mtx name="O" width="404" lsb="33"/>
+    <mtx name="Odieresis" width="404" lsb="33"/>
+    <mtx name="n" width="348" lsb="6"/>
+    <mtx name="o" width="342" lsb="22"/>
+    <mtx name="odieresis" width="342" lsb="22"/>
+    <mtx name="uni0308" width="0" lsb="123"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x4e" name="N"/><!-- LATIN CAPITAL LETTER N -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0xd6" name="Odieresis"/><!-- LATIN CAPITAL LETTER O WITH DIAERESIS -->
+      <map code="0xf6" name="odieresis"/><!-- LATIN SMALL LETTER O WITH DIAERESIS -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x4e" name="N"/><!-- LATIN CAPITAL LETTER N -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0xd6" name="Odieresis"/><!-- LATIN CAPITAL LETTER O WITH DIAERESIS -->
+      <map code="0xf6" name="odieresis"/><!-- LATIN SMALL LETTER O WITH DIAERESIS -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+    </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="38" yMin="-150" xMax="337" yMax="600">
+      <contour>
+        <pt x="38" y="-150" on="1"/>
+        <pt x="337" y="-150" on="1"/>
+        <pt x="337" y="600" on="1"/>
+        <pt x="38" y="600" on="1"/>
+      </contour>
+      <contour>
+        <pt x="76" y="-112" on="1"/>
+        <pt x="76" y="562" on="1"/>
+        <pt x="299" y="562" on="1"/>
+        <pt x="299" y="-112" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="N" xMin="11" yMin="-4" xMax="436" yMax="504">
+      <contour>
+        <pt x="38" y="-4" on="1"/>
+        <pt x="26" y="-4" on="0"/>
+        <pt x="11" y="16" on="0"/>
+        <pt x="14" y="28" on="1"/>
+        <pt x="136" y="486" on="1"/>
+        <pt x="138" y="495" on="0"/>
+        <pt x="152" y="504" on="0"/>
+        <pt x="159" y="504" on="1"/>
+        <pt x="167" y="504" on="0"/>
+        <pt x="182" y="495" on="0"/>
+        <pt x="184" y="486" on="1"/>
+        <pt x="287" y="116" on="1"/>
+        <pt x="386" y="485" on="1"/>
+        <pt x="388" y="493" on="0"/>
+        <pt x="401" y="504" on="0"/>
+        <pt x="410" y="504" on="1"/>
+        <pt x="425" y="504" on="0"/>
+        <pt x="436" y="480" on="0"/>
+        <pt x="434" y="472" on="1"/>
+        <pt x="312" y="14" on="1"/>
+        <pt x="310" y="5" on="0"/>
+        <pt x="296" y="-4" on="0"/>
+        <pt x="288" y="-4" on="1"/>
+        <pt x="281" y="-4" on="0"/>
+        <pt x="266" y="5" on="0"/>
+        <pt x="264" y="14" on="1"/>
+        <pt x="161" y="384" on="1"/>
+        <pt x="62" y="15" on="1"/>
+        <pt x="60" y="7" on="0"/>
+        <pt x="47" y="-4" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="O" xMin="33" yMin="-10" xMax="412" yMax="510">
+      <contour>
+        <pt x="159" y="-10" on="1"/>
+        <pt x="116" y="-10" on="0"/>
+        <pt x="57" y="33" on="0"/>
+        <pt x="33" y="105" on="0"/>
+        <pt x="44" y="146" on="1"/>
+        <pt x="103" y="366" on="1"/>
+        <pt x="114" y="406" on="0"/>
+        <pt x="169" y="471" on="0"/>
+        <pt x="245" y="510" on="0"/>
+        <pt x="285" y="510" on="1"/>
+        <pt x="328" y="510" on="0"/>
+        <pt x="388" y="467" on="0"/>
+        <pt x="412" y="396" on="0"/>
+        <pt x="401" y="354" on="1"/>
+        <pt x="342" y="133" on="1"/>
+        <pt x="332" y="94" on="0"/>
+        <pt x="275" y="29" on="0"/>
+        <pt x="199" y="-10" on="0"/>
+      </contour>
+      <contour>
+        <pt x="159" y="40" on="1"/>
+        <pt x="188" y="40" on="0"/>
+        <pt x="244" y="69" on="0"/>
+        <pt x="286" y="117" on="0"/>
+        <pt x="294" y="146" on="1"/>
+        <pt x="353" y="366" on="1"/>
+        <pt x="360" y="393" on="0"/>
+        <pt x="347" y="435" on="0"/>
+        <pt x="312" y="460" on="0"/>
+        <pt x="285" y="460" on="1"/>
+        <pt x="256" y="460" on="0"/>
+        <pt x="200" y="431" on="0"/>
+        <pt x="159" y="383" on="0"/>
+        <pt x="151" y="354" on="1"/>
+        <pt x="92" y="133" on="1"/>
+        <pt x="85" y="107" on="0"/>
+        <pt x="98" y="65" on="0"/>
+        <pt x="133" y="40" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Odieresis" xMin="33" yMin="-10" xMax="425" yMax="650">
+      <component glyphName="O" x="0" y="0" flags="0x204"/>
+      <component glyphName="uni0308" x="92" y="150" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="n" xMin="6" yMin="-4" xMax="327" yMax="350">
+      <contour>
+        <pt x="34" y="-4" on="1"/>
+        <pt x="22" y="-4" on="0"/>
+        <pt x="6" y="15" on="0"/>
+        <pt x="10" y="28" on="1"/>
+        <pt x="90" y="329" on="1"/>
+        <pt x="96" y="350" on="0"/>
+        <pt x="118" y="350" on="1"/>
+        <pt x="238" y="350" on="1"/>
+        <pt x="269" y="350" on="0"/>
+        <pt x="311" y="321" on="0"/>
+        <pt x="327" y="272" on="0"/>
+        <pt x="319" y="242" on="1"/>
+        <pt x="258" y="15" on="1"/>
+        <pt x="256" y="6" on="0"/>
+        <pt x="243" y="-4" on="0"/>
+        <pt x="234" y="-4" on="1"/>
+        <pt x="222" y="-4" on="0"/>
+        <pt x="206" y="15" on="0"/>
+        <pt x="210" y="28" on="1"/>
+        <pt x="271" y="255" on="1"/>
+        <pt x="276" y="275" on="0"/>
+        <pt x="259" y="300" on="0"/>
+        <pt x="238" y="300" on="1"/>
+        <pt x="134" y="300" on="1"/>
+        <pt x="58" y="15" on="1"/>
+        <pt x="56" y="7" on="0"/>
+        <pt x="43" y="-4" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="o" xMin="22" yMin="-10" xMax="322" yMax="360">
+      <contour>
+        <pt x="129" y="-10" on="1"/>
+        <pt x="91" y="-10" on="0"/>
+        <pt x="40" y="25" on="0"/>
+        <pt x="22" y="86" on="0"/>
+        <pt x="32" y="124" on="1"/>
+        <pt x="64" y="240" on="1"/>
+        <pt x="74" y="276" on="0"/>
+        <pt x="119" y="330" on="0"/>
+        <pt x="180" y="360" on="0"/>
+        <pt x="215" y="360" on="1"/>
+        <pt x="253" y="360" on="0"/>
+        <pt x="304" y="325" on="0"/>
+        <pt x="323" y="265" on="0"/>
+        <pt x="312" y="226" on="1"/>
+        <pt x="280" y="110" on="1"/>
+        <pt x="270" y="75" on="0"/>
+        <pt x="225" y="20" on="0"/>
+        <pt x="164" y="-10" on="0"/>
+      </contour>
+      <contour>
+        <pt x="129" y="40" on="1"/>
+        <pt x="165" y="40" on="0"/>
+        <pt x="222" y="86" on="0"/>
+        <pt x="232" y="124" on="1"/>
+        <pt x="264" y="240" on="1"/>
+        <pt x="273" y="273" on="0"/>
+        <pt x="247" y="310" on="0"/>
+        <pt x="215" y="310" on="1"/>
+        <pt x="179" y="310" on="0"/>
+        <pt x="123" y="264" on="0"/>
+        <pt x="112" y="226" on="1"/>
+        <pt x="80" y="110" on="1"/>
+        <pt x="71" y="77" on="0"/>
+        <pt x="97" y="40" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="odieresis" xMin="22" yMin="-10" xMax="354" yMax="500">
+      <component glyphName="o" x="0" y="0" flags="0x204"/>
+      <component glyphName="uni0308" x="21" y="0" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0308" xMin="123" yMin="425" xMax="333" yMax="500">
+      <contour>
+        <pt x="300" y="425" on="1"/>
+        <pt x="288" y="425" on="0"/>
+        <pt x="273" y="445" on="0"/>
+        <pt x="276" y="456" on="1"/>
+        <pt x="282" y="480" on="1"/>
+        <pt x="284" y="489" on="0"/>
+        <pt x="297" y="500" on="0"/>
+        <pt x="306" y="500" on="1"/>
+        <pt x="318" y="500" on="0"/>
+        <pt x="333" y="480" on="0"/>
+        <pt x="330" y="469" on="1"/>
+        <pt x="324" y="445" on="1"/>
+        <pt x="322" y="436" on="0"/>
+        <pt x="309" y="425" on="0"/>
+      </contour>
+      <contour>
+        <pt x="150" y="425" on="1"/>
+        <pt x="138" y="425" on="0"/>
+        <pt x="123" y="445" on="0"/>
+        <pt x="126" y="456" on="1"/>
+        <pt x="132" y="480" on="1"/>
+        <pt x="134" y="489" on="0"/>
+        <pt x="147" y="500" on="0"/>
+        <pt x="156" y="500" on="1"/>
+        <pt x="168" y="500" on="0"/>
+        <pt x="183" y="480" on="0"/>
+        <pt x="180" y="469" on="1"/>
+        <pt x="174" y="445" on="1"/>
+        <pt x="172" y="436" on="0"/>
+        <pt x="159" y="425" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2017 by Jens Kutilek
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 4 15
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Italic
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;jens;TestFamily4-Italic15
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 4 Italic 15
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily4-Italic15
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Jens Kutilek
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Jens Kutilek after the ISO 3098 standard
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      https://www.kutilek.de/
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      https://www.kutilek.de/
+    </namerecord>
+    <namerecord nameID="16" platformID="3" platEncID="1" langID="0x409">
+      Test Family 4
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Italic 15
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="-15.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 -->
+      <psName name="dieresiscomb"/>
+      <psName name="uni0308"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010002"/>
+    <GlyphClassDef>
+      <ClassDef glyph="N" class="1"/>
+      <ClassDef glyph="O" class="1"/>
+      <ClassDef glyph="Odieresis" class="1"/>
+      <ClassDef glyph="n" class="1"/>
+      <ClassDef glyph="o" class="1"/>
+      <ClassDef glyph="odieresis" class="1"/>
+      <ClassDef glyph="uni0308" class="3"/>
+    </GlyphClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=1 -->
+      <Coverage index="0">
+        <Glyph value="uni0308"/>
+      </Coverage>
+    </MarkGlyphSetsDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <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="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="NLD "/>
+            <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="cpsp"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="mark"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="mkmk"/>
+        <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 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="N"/>
+            <Glyph value="O"/>
+            <Glyph value="Odieresis"/>
+          </Coverage>
+          <ValueFormat value="5"/>
+          <Value XPlacement="25" XAdvance="50"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="uni0308"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="N"/>
+            <Glyph value="O"/>
+            <Glyph value="Odieresis"/>
+            <Glyph value="n"/>
+            <Glyph value="o"/>
+            <Glyph value="odieresis"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="197"/>
+                <YCoordinate value="350"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=6 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="291"/>
+                <YCoordinate value="500"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="1">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="289"/>
+                <YCoordinate value="500"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="2">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="322"/>
+                <YCoordinate value="625"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="3">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="222"/>
+                <YCoordinate value="350"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="4">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="218"/>
+                <YCoordinate value="350"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="5">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="251"/>
+                <YCoordinate value="475"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="6"/>
+        <LookupFlag value="16"/><!-- useMarkFilteringSet -->
+        <!-- SubTableCount=1 -->
+        <MarkMarkPos index="0" Format="1">
+          <Mark1Coverage>
+            <Glyph value="uni0308"/>
+          </Mark1Coverage>
+          <Mark2Coverage>
+            <Glyph value="uni0308"/>
+          </Mark2Coverage>
+          <!-- ClassCount=1 -->
+          <Mark1Array>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="197"/>
+                <YCoordinate value="350"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </Mark1Array>
+          <Mark2Array>
+            <!-- Mark2Count=1 -->
+            <Mark2Record index="0">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="230"/>
+                <YCoordinate value="475"/>
+              </Mark2Anchor>
+            </Mark2Record>
+          </Mark2Array>
+        </MarkMarkPos>
+        <MarkFilteringSet value="0"/>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily4-Regular.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily4-Regular.ttx
new file mode 100644
index 0000000..0b7063f
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily4-Regular.ttx
@@ -0,0 +1,656 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.34">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="N"/>
+    <GlyphID id="2" name="O"/>
+    <GlyphID id="3" name="Odieresis"/>
+    <GlyphID id="4" name="n"/>
+    <GlyphID id="5" name="o"/>
+    <GlyphID id="6" name="odieresis"/>
+    <GlyphID id="7" name="uni0308"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x20783e4b"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="750"/>
+    <created value="Sat Oct 21 13:31:38 2017"/>
+    <modified value="Mon Dec 17 12:42:07 2018"/>
+    <xMin value="38"/>
+    <yMin value="-150"/>
+    <xMax value="354"/>
+    <yMax value="650"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="750"/>
+    <descent value="-150"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="408"/>
+    <minLeftSideBearing value="38"/>
+    <minRightSideBearing value="-250"/>
+    <xMaxExtent value="354"/>
+    <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="36"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="64"/>
+    <maxCompositeContours value="4"/>
+    <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="375"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="488"/>
+    <ySubscriptYSize value="450"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="56"/>
+    <ySuperscriptXSize value="488"/>
+    <ySuperscriptYSize value="450"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="263"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="210"/>
+    <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 01000011"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="jens"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="78"/>
+    <usLastCharIndex value="776"/>
+    <sTypoAscender value="600"/>
+    <sTypoDescender value="-150"/>
+    <sTypoLineGap value="150"/>
+    <usWinAscent value="700"/>
+    <usWinDescent value="250"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="350"/>
+    <sCapHeight value="500"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="375" lsb="38"/>
+    <mtx name="N" width="408" lsb="54"/>
+    <mtx name="O" width="404" lsb="52"/>
+    <mtx name="Odieresis" width="404" lsb="52"/>
+    <mtx name="n" width="348" lsb="50"/>
+    <mtx name="o" width="342" lsb="46"/>
+    <mtx name="odieresis" width="342" lsb="46"/>
+    <mtx name="uni0308" width="0" lsb="50"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x4e" name="N"/><!-- LATIN CAPITAL LETTER N -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0xd6" name="Odieresis"/><!-- LATIN CAPITAL LETTER O WITH DIAERESIS -->
+      <map code="0xf6" name="odieresis"/><!-- LATIN SMALL LETTER O WITH DIAERESIS -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x4e" name="N"/><!-- LATIN CAPITAL LETTER N -->
+      <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+      <map code="0x6e" name="n"/><!-- LATIN SMALL LETTER N -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+      <map code="0xd6" name="Odieresis"/><!-- LATIN CAPITAL LETTER O WITH DIAERESIS -->
+      <map code="0xf6" name="odieresis"/><!-- LATIN SMALL LETTER O WITH DIAERESIS -->
+      <map code="0x308" name="uni0308"/><!-- COMBINING DIAERESIS -->
+    </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="38" yMin="-150" xMax="337" yMax="600">
+      <contour>
+        <pt x="38" y="-150" on="1"/>
+        <pt x="337" y="-150" on="1"/>
+        <pt x="337" y="600" on="1"/>
+        <pt x="38" y="600" on="1"/>
+      </contour>
+      <contour>
+        <pt x="76" y="-112" on="1"/>
+        <pt x="76" y="562" on="1"/>
+        <pt x="299" y="562" on="1"/>
+        <pt x="299" y="-112" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="N" xMin="54" yMin="-4" xMax="354" yMax="504">
+      <contour>
+        <pt x="79" y="-4" on="1"/>
+        <pt x="69" y="-4" on="0"/>
+        <pt x="54" y="11" on="0"/>
+        <pt x="54" y="21" on="1"/>
+        <pt x="54" y="479" on="1"/>
+        <pt x="54" y="490" on="0"/>
+        <pt x="69" y="504" on="0"/>
+        <pt x="79" y="504" on="1"/>
+        <pt x="88" y="504" on="0"/>
+        <pt x="98" y="496" on="0"/>
+        <pt x="101" y="491" on="1"/>
+        <pt x="304" y="119" on="1"/>
+        <pt x="304" y="479" on="1"/>
+        <pt x="304" y="490" on="0"/>
+        <pt x="319" y="504" on="0"/>
+        <pt x="329" y="504" on="1"/>
+        <pt x="340" y="504" on="0"/>
+        <pt x="354" y="490" on="0"/>
+        <pt x="354" y="479" on="1"/>
+        <pt x="354" y="21" on="1"/>
+        <pt x="354" y="11" on="0"/>
+        <pt x="340" y="-4" on="0"/>
+        <pt x="329" y="-4" on="1"/>
+        <pt x="320" y="-4" on="0"/>
+        <pt x="310" y="4" on="0"/>
+        <pt x="307" y="9" on="1"/>
+        <pt x="104" y="381" on="1"/>
+        <pt x="104" y="21" on="1"/>
+        <pt x="104" y="11" on="0"/>
+        <pt x="90" y="-4" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="O" xMin="52" yMin="-10" xMax="352" yMax="510">
+      <contour>
+        <pt x="202" y="-10" on="1"/>
+        <pt x="161" y="-10" on="0"/>
+        <pt x="93" y="31" on="0"/>
+        <pt x="52" y="99" on="0"/>
+        <pt x="52" y="140" on="1"/>
+        <pt x="52" y="360" on="1"/>
+        <pt x="52" y="401" on="0"/>
+        <pt x="93" y="469" on="0"/>
+        <pt x="161" y="510" on="0"/>
+        <pt x="202" y="510" on="1"/>
+        <pt x="243" y="510" on="0"/>
+        <pt x="311" y="469" on="0"/>
+        <pt x="352" y="401" on="0"/>
+        <pt x="352" y="360" on="1"/>
+        <pt x="352" y="140" on="1"/>
+        <pt x="352" y="99" on="0"/>
+        <pt x="311" y="31" on="0"/>
+        <pt x="243" y="-10" on="0"/>
+      </contour>
+      <contour>
+        <pt x="202" y="40" on="1"/>
+        <pt x="230" y="40" on="0"/>
+        <pt x="275" y="67" on="0"/>
+        <pt x="302" y="113" on="0"/>
+        <pt x="302" y="140" on="1"/>
+        <pt x="302" y="360" on="1"/>
+        <pt x="302" y="388" on="0"/>
+        <pt x="275" y="433" on="0"/>
+        <pt x="230" y="460" on="0"/>
+        <pt x="202" y="460" on="1"/>
+        <pt x="175" y="460" on="0"/>
+        <pt x="129" y="433" on="0"/>
+        <pt x="102" y="388" on="0"/>
+        <pt x="102" y="360" on="1"/>
+        <pt x="102" y="140" on="1"/>
+        <pt x="102" y="113" on="0"/>
+        <pt x="129" y="67" on="0"/>
+        <pt x="175" y="40" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Odieresis" xMin="52" yMin="-10" xMax="352" yMax="650">
+      <component glyphName="O" x="0" y="0" flags="0x204"/>
+      <component glyphName="uni0308" x="52" y="150" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="n" xMin="50" yMin="-4" xMax="300" yMax="350">
+      <contour>
+        <pt x="75" y="-4" on="1"/>
+        <pt x="65" y="-4" on="0"/>
+        <pt x="50" y="11" on="0"/>
+        <pt x="50" y="21" on="1"/>
+        <pt x="50" y="325" on="1"/>
+        <pt x="50" y="350" on="0"/>
+        <pt x="75" y="350" on="1"/>
+        <pt x="198" y="350" on="1"/>
+        <pt x="228" y="350" on="0"/>
+        <pt x="274" y="324" on="0"/>
+        <pt x="300" y="278" on="0"/>
+        <pt x="300" y="248" on="1"/>
+        <pt x="300" y="21" on="1"/>
+        <pt x="300" y="11" on="0"/>
+        <pt x="286" y="-4" on="0"/>
+        <pt x="275" y="-4" on="1"/>
+        <pt x="265" y="-4" on="0"/>
+        <pt x="250" y="11" on="0"/>
+        <pt x="250" y="21" on="1"/>
+        <pt x="250" y="248" on="1"/>
+        <pt x="250" y="271" on="0"/>
+        <pt x="221" y="300" on="0"/>
+        <pt x="198" y="300" on="1"/>
+        <pt x="100" y="300" on="1"/>
+        <pt x="100" y="21" on="1"/>
+        <pt x="100" y="11" on="0"/>
+        <pt x="86" y="-4" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="o" xMin="46" yMin="-10" xMax="296" yMax="360">
+      <contour>
+        <pt x="171" y="-10" on="1"/>
+        <pt x="135" y="-10" on="0"/>
+        <pt x="78" y="23" on="0"/>
+        <pt x="46" y="80" on="0"/>
+        <pt x="46" y="117" on="1"/>
+        <pt x="46" y="233" on="1"/>
+        <pt x="46" y="270" on="0"/>
+        <pt x="78" y="327" on="0"/>
+        <pt x="135" y="360" on="0"/>
+        <pt x="171" y="360" on="1"/>
+        <pt x="208" y="360" on="0"/>
+        <pt x="264" y="327" on="0"/>
+        <pt x="296" y="270" on="0"/>
+        <pt x="296" y="233" on="1"/>
+        <pt x="296" y="117" on="1"/>
+        <pt x="296" y="80" on="0"/>
+        <pt x="264" y="23" on="0"/>
+        <pt x="208" y="-10" on="0"/>
+      </contour>
+      <contour>
+        <pt x="171" y="40" on="1"/>
+        <pt x="205" y="40" on="0"/>
+        <pt x="246" y="82" on="0"/>
+        <pt x="246" y="117" on="1"/>
+        <pt x="246" y="233" on="1"/>
+        <pt x="246" y="268" on="0"/>
+        <pt x="205" y="310" on="0"/>
+        <pt x="171" y="310" on="1"/>
+        <pt x="137" y="310" on="0"/>
+        <pt x="96" y="268" on="0"/>
+        <pt x="96" y="233" on="1"/>
+        <pt x="96" y="117" on="1"/>
+        <pt x="96" y="82" on="0"/>
+        <pt x="137" y="40" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="odieresis" xMin="46" yMin="-10" xMax="296" yMax="500">
+      <component glyphName="o" x="0" y="0" flags="0x204"/>
+      <component glyphName="uni0308" x="21" y="0" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="uni0308" xMin="50" yMin="425" xMax="250" yMax="500">
+      <contour>
+        <pt x="225" y="425" on="1"/>
+        <pt x="215" y="425" on="0"/>
+        <pt x="200" y="440" on="0"/>
+        <pt x="200" y="450" on="1"/>
+        <pt x="200" y="475" on="1"/>
+        <pt x="200" y="486" on="0"/>
+        <pt x="215" y="500" on="0"/>
+        <pt x="225" y="500" on="1"/>
+        <pt x="236" y="500" on="0"/>
+        <pt x="250" y="486" on="0"/>
+        <pt x="250" y="475" on="1"/>
+        <pt x="250" y="450" on="1"/>
+        <pt x="250" y="440" on="0"/>
+        <pt x="236" y="425" on="0"/>
+      </contour>
+      <contour>
+        <pt x="75" y="425" on="1"/>
+        <pt x="65" y="425" on="0"/>
+        <pt x="50" y="440" on="0"/>
+        <pt x="50" y="450" on="1"/>
+        <pt x="50" y="475" on="1"/>
+        <pt x="50" y="486" on="0"/>
+        <pt x="65" y="500" on="0"/>
+        <pt x="75" y="500" on="1"/>
+        <pt x="86" y="500" on="0"/>
+        <pt x="100" y="486" on="0"/>
+        <pt x="100" y="475" on="1"/>
+        <pt x="100" y="450" on="1"/>
+        <pt x="100" y="440" on="0"/>
+        <pt x="86" y="425" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright 2017 by Jens Kutilek
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Test Family 4
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;jens;TestFamily4-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Family 4 Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestFamily4-Regular
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Jens Kutilek
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Jens Kutilek after the ISO 3098 standard
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      https://www.kutilek.de/
+    </namerecord>
+    <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
+      https://www.kutilek.de/
+    </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 -->
+      <psName name="dieresiscomb"/>
+      <psName name="uni0308"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010002"/>
+    <GlyphClassDef>
+      <ClassDef glyph="N" class="1"/>
+      <ClassDef glyph="O" class="1"/>
+      <ClassDef glyph="Odieresis" class="1"/>
+      <ClassDef glyph="n" class="1"/>
+      <ClassDef glyph="o" class="1"/>
+      <ClassDef glyph="odieresis" class="1"/>
+      <ClassDef glyph="uni0308" class="3"/>
+    </GlyphClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=1 -->
+      <Coverage index="0">
+        <Glyph value="uni0308"/>
+      </Coverage>
+    </MarkGlyphSetsDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=2 -->
+      <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="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="NLD "/>
+            <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="cpsp"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="mark"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="2">
+        <FeatureTag value="mkmk"/>
+        <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 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="N"/>
+            <Glyph value="O"/>
+            <Glyph value="Odieresis"/>
+          </Coverage>
+          <ValueFormat value="5"/>
+          <Value XPlacement="25" XAdvance="50"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage>
+            <Glyph value="uni0308"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="N"/>
+            <Glyph value="O"/>
+            <Glyph value="Odieresis"/>
+            <Glyph value="n"/>
+            <Glyph value="o"/>
+            <Glyph value="odieresis"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="150"/>
+                <YCoordinate value="350"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=6 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="204"/>
+                <YCoordinate value="500"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="1">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="202"/>
+                <YCoordinate value="500"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="2">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="202"/>
+                <YCoordinate value="625"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="3">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="175"/>
+                <YCoordinate value="350"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="4">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="171"/>
+                <YCoordinate value="350"/>
+              </BaseAnchor>
+            </BaseRecord>
+            <BaseRecord index="5">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="171"/>
+                <YCoordinate value="475"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="6"/>
+        <LookupFlag value="16"/><!-- useMarkFilteringSet -->
+        <!-- SubTableCount=1 -->
+        <MarkMarkPos index="0" Format="1">
+          <Mark1Coverage>
+            <Glyph value="uni0308"/>
+          </Mark1Coverage>
+          <Mark2Coverage>
+            <Glyph value="uni0308"/>
+          </Mark2Coverage>
+          <!-- ClassCount=1 -->
+          <Mark1Array>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="150"/>
+                <YCoordinate value="350"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </Mark1Array>
+          <Mark2Array>
+            <!-- Mark2Count=1 -->
+            <Mark2Record index="0">
+              <Mark2Anchor index="0" Format="1">
+                <XCoordinate value="150"/>
+                <YCoordinate value="475"/>
+              </Mark2Anchor>
+            </Mark2Record>
+          </Mark2Array>
+        </MarkMarkPos>
+        <MarkFilteringSet value="0"/>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_varfont_otf/TestCFF2VF.ttx b/Tests/varLib/data/master_ttx_varfont_otf/TestCFF2VF.ttx
new file mode 100644
index 0000000..29c5bb3
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_varfont_otf/TestCFF2VF.ttx
@@ -0,0 +1,835 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <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="T"/>
+    <GlyphID id="3" name="glyph00003"/>
+    <GlyphID id="4" name="dollar"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.01"/>
+    <checkSumAdjustment value="0xf8d4dd69"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Oct 17 15:44:59 2018"/>
+    <modified value="Tue Nov 13 20:50:41 2018"/>
+    <xMin value="31"/>
+    <yMin value="-113"/>
+    <xMax value="569"/>
+    <yMax value="751"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <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="600"/>
+    <minLeftSideBearing value="31"/>
+    <minRightSideBearing value="31"/>
+    <xMaxExtent value="569"/>
+    <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>
+
+  <maxp>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="5"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="600"/>
+    <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="291"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="2"/>
+      <bSerifStyle value="11"/>
+      <bWeight value="5"/>
+      <bProportion value="9"/>
+      <bContrast value="3"/>
+      <bStrokeVariation value="4"/>
+      <bArmStyle value="3"/>
+      <bLetterForm value="2"/>
+      <bMidline value="2"/>
+      <bXHeight value="4"/>
+    </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="ADBO"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="36"/>
+    <usLastCharIndex value="84"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="984"/>
+    <usWinDescent value="273"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="486"/>
+    <sCapHeight value="660"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="62"/>
+    <mtx name="A" width="600" lsb="31"/>
+    <mtx name="T" width="600" lsb="41"/>
+    <mtx name="dollar" width="600" lsb="85"/>
+    <mtx name="glyph00003" width="600" lsb="85"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+      <map code="0x24" name="dollar"/>
+      <map code="0x41" name="A"/>
+      <map code="0x54" name="T"/>
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x24" name="dollar"/><!-- DOLLAR SIGN -->
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+    </cmap_format_4>
+  </cmap>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </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.010;ADBO;SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Roman
+    </namerecord>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraLight
+    </namerecord>
+    <namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestCFF2Roman-ExtraLight
+    </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">
+      TestCFF2Roman-Light
+    </namerecord>
+    <namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="262" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestCFF2Roman-Regular
+    </namerecord>
+    <namerecord nameID="263" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Medium
+    </namerecord>
+    <namerecord nameID="264" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestCFF2Roman-Medium
+    </namerecord>
+    <namerecord nameID="265" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Semibold
+    </namerecord>
+    <namerecord nameID="266" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestCFF2Roman-Semibold
+    </namerecord>
+    <namerecord nameID="267" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Bold
+    </namerecord>
+    <namerecord nameID="268" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestCFF2Roman-Bold
+    </namerecord>
+    <namerecord nameID="269" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Black
+    </namerecord>
+    <namerecord nameID="270" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestCFF2Roman-Black
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.010;ADBO;SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Code Variable
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.010;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SourceCodeVariable-Roman
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Roman
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      ExtraLight
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      TestCFF2Roman-ExtraLight
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      TestCFF2Roman-Light
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
+      TestCFF2Roman-Regular
+    </namerecord>
+    <namerecord nameID="263" platformID="3" platEncID="1" langID="0x409">
+      Medium
+    </namerecord>
+    <namerecord nameID="264" platformID="3" platEncID="1" langID="0x409">
+      TestCFF2Roman-Medium
+    </namerecord>
+    <namerecord nameID="265" platformID="3" platEncID="1" langID="0x409">
+      Semibold
+    </namerecord>
+    <namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
+      TestCFF2Roman-Semibold
+    </namerecord>
+    <namerecord nameID="267" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="268" platformID="3" platEncID="1" langID="0x409">
+      TestCFF2Roman-Bold
+    </namerecord>
+    <namerecord nameID="269" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+    <namerecord nameID="270" platformID="3" platEncID="1" langID="0x409">
+      TestCFF2Roman-Black
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="1"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </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>
+
+  <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="-12 0 0"/>
+                <blend value="0 0 0"/>
+                <blend value="486 -8 14"/>
+                <blend value="498 0 0"/>
+                <blend value="574 4 -8"/>
+                <blend value="586 0 0"/>
+                <blend value="638 6 -10"/>
+                <blend value="650 0 0"/>
+                <blend value="656 2 -2"/>
+                <blend value="668 0 0"/>
+                <blend value="712 6 -10"/>
+                <blend value="724 0 0"/>
+            </BlueValues>
+            <OtherBlues>
+                <blend value="-217 -17 29"/>
+                <blend value="-205 0 0"/>
+            </OtherBlues>
+            <BlueScale value="0.0625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW>
+                <blend value="67 -39 67"/>
+            </StdHW>
+            <StdVW>
+                <blend value="85 -51 87"/>
+            </StdVW>
+            <Subrs>
+              <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+              <CharString index="0">
+                304 7 -12 1 blend
+                34 rmoveto
+                125 86 65 96 -22 38 2 -3 -9 15 -2 4 4 blend
+                hvcurveto
+                183 -324 -21 110 1 -1 -14 22 -11 17 32 -54 4 blend
+                vvcurveto
+                50 42 32 67 68 36 -21 -36 47 18 -29 15 -24 12 -21 18 -31 8 -13 -2 3 -3 5 -2 4 -3 5 9 blend
+                vhcurveto
+                44 49 -24 40 -29 49 2 blend
+                rlineto
+                44 -46 -54 33 -89 -6 8 5 -7 9 -15 -1 3 4 -8 5 blend
+                hhcurveto
+                -115 -81 -59 -94 16 -26 3 -7 5 -9 6 -10 4 blend
+                hvcurveto
+                -174 324 22 -124 8 -14 14 -22 6 -10 -32 56 4 blend
+                vvcurveto
+                -51 -42 -35 -78 -76 -62 31 37 -52 -19 31 -14 23 -15 25 -25 41 -9 15 -4 7 7 -11 -3 3 12 -20 9 blend
+                vhcurveto
+                -39 -58</CharString>
+              <CharString index="1">
+                1 blend
+                hlineto
+              </CharString>
+              <CharString index="2">
+                2 blend
+                rmoveto
+              </CharString>
+            </Subrs>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+          62 22 -38 1 blend
+          hmoveto
+          476 -44 76 1 blend
+          660 -476 44 -76 -106 callsubr
+          109 -601 -61 103 -27 45 -105 callsubr
+          73 131 54 102 29 -47 45 -75 10 -18 4 -6 4 blend
+          rlineto
+          4 hlineto
+          52 -102 73 -131 10 -16 -4 6 27 -47 -45 75 4 blend
+          rlineto
+          -300 52 -42 72 -10 16 -105 callsubr
+          461 75 -125 1 blend
+          vlineto
+          127 -232 27 -45 -38 64 2 blend
+          rlineto
+          44 48 -22 36 -22 36 -105 callsubr
+          -50 93 -66 119 -6 10 -1 3 -28 48 49 -83 4 blend
+          rlineto
+          234 68 -114 -106 callsubr
+          -65 -119 -49 -93 -29 47 -49 83 -5 9 1 -3 4 blend
+          rlineto
+          44 -48 -22 36 22 -36 -105 callsubr
+          126 232 26 -44 38 -64 2 blend
+          rlineto
+          -461 -75 125 1 blend
+          vlineto
+        </CharString>
+        <CharString name="A">
+          31 19 -31 1 blend
+          hmoveto
+          86 -54 90 -106 callsubr
+          115 366 23 73 21 72 21 76 25 -42 30 -50 5 -9 7 -11 3 -4 -4 6 3 -7 6 -10 8 blend
+          rlinecurve
+          4 hlineto
+          20 -76 22 -72 23 -73 113 -366 4 -6 -6 10 2 -3 4 -6 5 -9 -7 11 25 -40 -30 50 8 blend
+          rcurveline
+          90 -56 92 -106 callsubr
+          -221 656 -15 25 4 -6 2 blend
+          rlineto
+          -96 68 -112 -106 callsubr
+          -104 -457 -30 49 33 -55 -105 callsubr
+          301 68 -301 -8 15 -40 65 8 -15 3 blend
+          hlineto
+        </CharString>
+        <CharString name="T">
+          258 26 -44 1 blend
+          hmoveto
+          84 585 217 71 -518 -71 217 -52 88 47 -79 17 -30 -43 73 18 -28 43 -73 17 -30 7 blend
+          hlineto
+        </CharString>
+        <CharString name="dollar">
+          -107 callsubr
+          73 -26 73 -26 73 -27 21 -35 36 -58 -28 -8 -12 -28 0 28 -14 -2 18 -3 27 27 8 blend
+          rlinecurve
+          -24 566 6 -21 0 -34 -105 callsubr
+          56 -26 56 -106 callsubr
+          50 0 9 1 blend
+          0 50 50 0 9 0 10 2 blend
+          vvcurveto
+          -56 26 -56 -106 callsubr
+          -50 0 -9 1 blend
+          0 -50 -50 0 -9 0 -10 2 blend
+          vvcurveto
+          -562 0 102 1 blend
+          vmoveto
+          -148 56 148 0 -68 -26 56 0 68 3 blend
+          vlineto
+        </CharString>
+        <CharString name="glyph00003">
+          -107 callsubr
+          21 -35 36 -58 2 blend
+          rlineto
+          -43 52 84 -36 83 5 -11 -7 13 -11 17 -4 8 8 -13 5 blend
+          hhcurveto
+          -51 -147 -19 32 1 -3 -105 callsubr
+          159 857 -56 7 -159 -858 -1 1 3 -3 26 -44 -3 5 1 -1 -2 4 6 blend
+          rlineto
+        </CharString>
+      </CharStrings>
+      <VarStore Format="1">
+        <Format value="1"/>
+        <VarRegionList>
+          <!-- RegionAxisCount=1 -->
+          <!-- RegionCount=2 -->
+          <Region index="0">
+            <VarRegionAxis index="0">
+              <StartCoord value="-1.0"/>
+              <PeakCoord value="-1.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>
+          </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>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+  <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>
+
+  <GSUB>
+    <Version value="0x00010001"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=2 -->
+            <FeatureIndex index="0" value="1"/>
+            <FeatureIndex index="1" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=2 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="rvrn"/>
+        <Feature>
+          <!-- LookupCount=0 -->
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=2 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="dollar" out="glyph00003"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="dollar" out="glyph00003"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+    <FeatureVariations>
+      <Version value="0x00010000"/>
+      <!-- FeatureVariationCount=1 -->
+      <FeatureVariationRecord index="0">
+        <ConditionSet>
+          <!-- ConditionCount=1 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="0"/>
+            <FilterRangeMinValue value="-1.0"/>
+            <FilterRangeMaxValue value="0.36707"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=1 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=1 -->
+              <LookupListIndex index="0" value="1"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+    </FeatureVariations>
+  </GSUB>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=2 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.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>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=5 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=0 -->
+        <Item index="0" value="[]"/>
+        <Item index="1" value="[]"/>
+        <Item index="2" value="[]"/>
+        <Item index="3" value="[]"/>
+        <Item index="4" value="[]"/>
+      </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=2 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.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>
+        </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="[-8, 14]"/>
+        <Item index="1" value="[-5, 9]"/>
+      </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=1 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=0 -->
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <avar>
+    <segment axis="wght">
+      <mapping from="-1.0" to="-1.0"/>
+      <mapping from="-0.5" to="-0.7283"/>
+      <mapping from="0.0" to="0.0"/>
+      <mapping from="0.2" to="0.1867"/>
+      <mapping from="0.4" to="0.36707"/>
+      <mapping from="0.6" to="0.7215"/>
+      <mapping from="1.0" to="1.0"/>
+    </segment>
+  </avar>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>200.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- ExtraLight -->
+    <!-- PostScript: TestCFF2Roman-ExtraLight -->
+    <NamedInstance flags="0x0" postscriptNameID="258" subfamilyNameID="257">
+      <coord axis="wght" value="200.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <!-- PostScript: TestCFF2Roman-Light -->
+    <NamedInstance flags="0x0" postscriptNameID="260" subfamilyNameID="259">
+      <coord axis="wght" value="300.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <!-- PostScript: TestCFF2Roman-Regular -->
+    <NamedInstance flags="0x0" postscriptNameID="262" subfamilyNameID="261">
+      <coord axis="wght" value="400.0"/>
+    </NamedInstance>
+
+    <!-- Medium -->
+    <!-- PostScript: TestCFF2Roman-Medium -->
+    <NamedInstance flags="0x0" postscriptNameID="264" subfamilyNameID="263">
+      <coord axis="wght" value="500.0"/>
+    </NamedInstance>
+
+    <!-- Semibold -->
+    <!-- PostScript: TestCFF2Roman-Semibold -->
+    <NamedInstance flags="0x0" postscriptNameID="266" subfamilyNameID="265">
+      <coord axis="wght" value="600.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <!-- PostScript: TestCFF2Roman-Bold -->
+    <NamedInstance flags="0x0" postscriptNameID="268" subfamilyNameID="267">
+      <coord axis="wght" value="700.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <!-- PostScript: TestCFF2Roman-Black -->
+    <NamedInstance flags="0x0" postscriptNameID="270" subfamilyNameID="269">
+      <coord axis="wght" value="900.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</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
index 8cbd8a6..23c240e 100755
--- a/Tests/varLib/data/master_ttx_varfont_ttf/Mutator_IUP.ttx
+++ b/Tests/varLib/data/master_ttx_varfont_ttf/Mutator_IUP.ttx
@@ -405,11 +405,13 @@
       </VarData>
     </VarStore>
     <AdvWidthMap>
-      <Map index="0" outer="0" inner="1"/>
-      <Map index="1" outer="0" inner="1"/>
-      <Map index="2" outer="0" inner="1"/>
-      <Map index="3" outer="0" inner="1"/>
-      <Map index="4" outer="0" inner="0"/>
+      <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>
 
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/features.fea b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/features.fea
new file mode 100644
index 0000000..88e4c41
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/features.fea
@@ -0,0 +1,23 @@
+# automatic
+@Uppercase = [ N O Odieresis ];
+
+# Prefix: Languagesystems
+# automatic
+languagesystem DFLT dflt;
+languagesystem latn dflt;
+languagesystem latn NLD;
+
+
+feature cpsp {
+pos @Uppercase <25 0 50 0>;
+
+} cpsp;
+
+table GDEF {
+  # automatic
+  GlyphClassDef
+    [N O Odieresis n o odieresis], # Base
+    , # Liga
+    [dieresiscomb], # Mark
+    ;
+} GDEF;
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/fontinfo.plist
new file mode 100644
index 0000000..b909a16
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/fontinfo.plist
@@ -0,0 +1,93 @@
+<?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>
+    <real>600.0</real>
+    <key>capHeight</key>
+    <real>500.0</real>
+    <key>copyright</key>
+    <string>Copyright 2017 by Jens Kutilek</string>
+    <key>descender</key>
+    <real>-150.0</real>
+    <key>familyName</key>
+    <string>Test Family 4</string>
+    <key>guidelines</key>
+    <array>
+      <dict>
+        <key>angle</key>
+        <real>180.0</real>
+        <key>x</key>
+        <real>125.0</real>
+        <key>y</key>
+        <real>175.0</real>
+      </dict>
+    </array>
+    <key>italicAngle</key>
+    <real>-15.0</real>
+    <key>openTypeHeadCreated</key>
+    <string>2017/10/21 13:31:38</string>
+    <key>openTypeNameDesigner</key>
+    <string>Jens Kutilek after the ISO 3098 standard</string>
+    <key>openTypeNameDesignerURL</key>
+    <string>https://www.kutilek.de/</string>
+    <key>openTypeNameManufacturer</key>
+    <string>Jens Kutilek</string>
+    <key>openTypeNameManufacturerURL</key>
+    <string>https://www.kutilek.de/</string>
+    <key>openTypeOS2Type</key>
+    <array>
+      <integer>3</integer>
+    </array>
+    <key>openTypeOS2VendorID</key>
+    <string>jens</string>
+    <key>openTypeOS2WinAscent</key>
+    <integer>700</integer>
+    <key>openTypeOS2WinDescent</key>
+    <integer>250</integer>
+    <key>postscriptBlueValues</key>
+    <array>
+      <real>-10.0</real>
+      <real>0.0</real>
+      <real>350.0</real>
+      <real>360.0</real>
+      <real>500.0</real>
+      <real>510.0</real>
+    </array>
+    <key>postscriptFamilyBlues</key>
+    <array/>
+    <key>postscriptFamilyOtherBlues</key>
+    <array/>
+    <key>postscriptOtherBlues</key>
+    <array>
+      <real>-160.0</real>
+      <real>-150.0</real>
+    </array>
+    <key>postscriptStemSnapH</key>
+    <array>
+      <integer>50</integer>
+    </array>
+    <key>postscriptStemSnapV</key>
+    <array>
+      <integer>50</integer>
+    </array>
+    <key>postscriptUnderlinePosition</key>
+    <integer>-100</integer>
+    <key>postscriptUnderlineThickness</key>
+    <integer>50</integer>
+    <key>styleMapFamilyName</key>
+    <string>Test Family 4 15</string>
+    <key>styleMapStyleName</key>
+    <string>italic</string>
+    <key>styleName</key>
+    <string>Italic 15</string>
+    <key>unitsPerEm</key>
+    <integer>750</integer>
+    <key>versionMajor</key>
+    <integer>1</integer>
+    <key>versionMinor</key>
+    <integer>0</integer>
+    <key>xHeight</key>
+    <real>350.0</real>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/N_.glif b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/N_.glif
new file mode 100644
index 0000000..ef80903
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/N_.glif
@@ -0,0 +1,41 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="N" format="2">
+  <anchor x="153.0" y="0.0" name="bottom"/>
+  <anchor x="287.0" y="500.0" name="top"/>
+  <outline>
+    <contour>
+      <point x="34.0" y="21.0" type="move"/>
+      <point x="156.0" y="479.0" type="line"/>
+      <point x="284.0" y="21.0" type="line"/>
+      <point x="406.0" y="479.0" type="line"/>
+    </contour>
+    <contour>
+      <point x="156.0" y="454.0" type="curve" smooth="yes"/>
+      <point x="170.0" y="454.0"/>
+      <point x="181.0" y="465.0"/>
+      <point x="181.0" y="479.0" type="curve" smooth="yes"/>
+      <point x="181.0" y="493.0"/>
+      <point x="170.0" y="504.0"/>
+      <point x="156.0" y="504.0" type="curve" smooth="yes"/>
+      <point x="142.0" y="504.0"/>
+      <point x="131.0" y="493.0"/>
+      <point x="131.0" y="479.0" type="curve" smooth="yes"/>
+      <point x="131.0" y="465.0"/>
+      <point x="142.0" y="454.0"/>
+    </contour>
+    <contour>
+      <point x="284.0" y="-4.0" type="curve" smooth="yes"/>
+      <point x="298.0" y="-4.0"/>
+      <point x="309.0" y="7.0"/>
+      <point x="309.0" y="21.0" type="curve" smooth="yes"/>
+      <point x="309.0" y="35.0"/>
+      <point x="298.0" y="46.0"/>
+      <point x="284.0" y="46.0" type="curve" smooth="yes"/>
+      <point x="270.0" y="46.0"/>
+      <point x="259.0" y="35.0"/>
+      <point x="259.0" y="21.0" type="curve" smooth="yes"/>
+      <point x="259.0" y="7.0"/>
+      <point x="270.0" y="-4.0"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/O_.glif b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/O_.glif
new file mode 100644
index 0000000..ac3160a
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/O_.glif
@@ -0,0 +1,27 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="O" format="2">
+  <anchor x="153.0" y="0.0" name="bottom"/>
+  <anchor x="220.0" y="250.0" name="center"/>
+  <anchor x="316.0" y="10.0" name="ogonek"/>
+  <anchor x="287.0" y="500.0" name="top"/>
+  <anchor x="107.0" y="500.0" name="topleft"/>
+  <anchor x="467.0" y="500.0" name="topright"/>
+  <outline>
+    <contour>
+      <point x="66.0" y="140.0" type="curve"/>
+      <point x="125.0" y="360.0" type="line"/>
+      <point x="143.0" y="429.0"/>
+      <point x="214.0" y="485.0"/>
+      <point x="283.0" y="485.0" type="curve"/>
+      <point x="352.0" y="485.0"/>
+      <point x="393.0" y="429.0"/>
+      <point x="375.0" y="360.0" type="curve"/>
+      <point x="316.0" y="140.0" type="line"/>
+      <point x="297.0" y="71.0"/>
+      <point x="226.0" y="15.0"/>
+      <point x="157.0" y="15.0" type="curve"/>
+      <point x="88.0" y="15.0"/>
+      <point x="47.0" y="71.0"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/contents.plist b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/contents.plist
new file mode 100644
index 0000000..e7bf835
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/contents.plist
@@ -0,0 +1,14 @@
+<?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>N</key>
+    <string>N_.glif</string>
+    <key>O</key>
+    <string>O_.glif</string>
+    <key>dieresiscomb</key>
+    <string>dieresiscomb.glif</string>
+    <key>o</key>
+    <string>o.glif</string>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/dieresiscomb.glif b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/dieresiscomb.glif
new file mode 100644
index 0000000..8dac7b7
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/dieresiscomb.glif
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="dieresiscomb" format="2">
+  <anchor x="197.0" y="350.0" name="_top"/>
+  <anchor x="230.0" y="475.0" name="top"/>
+  <outline>
+    <contour>
+      <point x="155.0" y="475.0" type="move"/>
+      <point x="149.0" y="450.0" type="line"/>
+    </contour>
+    <contour>
+      <point x="305.0" y="475.0" type="move"/>
+      <point x="299.0" y="450.0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/o.glif b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/o.glif
new file mode 100644
index 0000000..6a2ce9f
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/o.glif
@@ -0,0 +1,37 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="o" format="2">
+  <outline>
+    <contour>
+      <point x="128.0" y="-10.0" type="curve" smooth="yes"/>
+      <point x="194.0" y="-10.0"/>
+      <point x="262.0" y="43.0"/>
+      <point x="279.0" y="108.0" type="curve" smooth="yes"/>
+      <point x="311.0" y="229.0" type="line" smooth="yes"/>
+      <point x="330.0" y="299.0"/>
+      <point x="286.0" y="360.0"/>
+      <point x="214.0" y="360.0" type="curve" smooth="yes"/>
+      <point x="148.0" y="360.0"/>
+      <point x="80.0" y="307.0"/>
+      <point x="63.0" y="242.0" type="curve" smooth="yes"/>
+      <point x="31.0" y="121.0" type="line" smooth="yes"/>
+      <point x="12.0" y="51.0"/>
+      <point x="56.0" y="-10.0"/>
+    </contour>
+    <contour>
+      <point x="128.0" y="40.0" type="curve" smooth="yes"/>
+      <point x="88.0" y="40.0"/>
+      <point x="69.0" y="71.0"/>
+      <point x="79.0" y="108.0" type="curve" smooth="yes"/>
+      <point x="111.0" y="229.0" type="line" smooth="yes"/>
+      <point x="123.0" y="273.0"/>
+      <point x="170.0" y="310.0"/>
+      <point x="214.0" y="310.0" type="curve" smooth="yes"/>
+      <point x="254.0" y="310.0"/>
+      <point x="273.0" y="279.0"/>
+      <point x="263.0" y="242.0" type="curve" smooth="yes"/>
+      <point x="231.0" y="121.0" type="line" smooth="yes"/>
+      <point x="219.0" y="77.0"/>
+      <point x="172.0" y="40.0"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/N_.glif b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/N_.glif
new file mode 100644
index 0000000..d289e0d
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/N_.glif
@@ -0,0 +1,47 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="N" format="2">
+  <advance width="408.0"/>
+  <unicode hex="004E"/>
+  <anchor x="157.0" y="0.0" name="bottom"/>
+  <anchor x="291.0" y="500.0" name="top"/>
+  <outline>
+    <contour>
+      <point x="38.0" y="-4.0" type="curve" smooth="yes"/>
+      <point x="50.0" y="-4.0"/>
+      <point x="59.0" y="4.0"/>
+      <point x="62.0" y="15.0" type="curve" smooth="yes"/>
+      <point x="161.0" y="384.0" type="line"/>
+      <point x="264.0" y="14.0" type="line" smooth="yes"/>
+      <point x="267.0" y="2.0"/>
+      <point x="278.0" y="-4.0"/>
+      <point x="288.0" y="-4.0" type="curve" smooth="yes"/>
+      <point x="299.0" y="-4.0"/>
+      <point x="309.0" y="2.0"/>
+      <point x="312.0" y="14.0" type="curve" smooth="yes"/>
+      <point x="434.0" y="472.0" type="line" smooth="yes"/>
+      <point x="437.0" y="483.0"/>
+      <point x="430.0" y="504.0"/>
+      <point x="410.0" y="504.0" type="curve" smooth="yes"/>
+      <point x="398.0" y="504.0"/>
+      <point x="389.0" y="496.0"/>
+      <point x="386.0" y="485.0" type="curve" smooth="yes"/>
+      <point x="287.0" y="116.0" type="line"/>
+      <point x="184.0" y="486.0" type="line" smooth="yes"/>
+      <point x="181.0" y="498.0"/>
+      <point x="170.0" y="504.0"/>
+      <point x="159.0" y="504.0" type="curve" smooth="yes"/>
+      <point x="149.0" y="504.0"/>
+      <point x="139.0" y="498.0"/>
+      <point x="136.0" y="486.0" type="curve" smooth="yes"/>
+      <point x="14.0" y="28.0" type="line" smooth="yes"/>
+      <point x="10.0" y="12.0"/>
+      <point x="22.0" y="-4.0"/>
+    </contour>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2018/11/20 10:52:27</string>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/O_.glif b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/O_.glif
new file mode 100644
index 0000000..aafee0a
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/O_.glif
@@ -0,0 +1,51 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="O" format="2">
+  <advance width="404.0"/>
+  <unicode hex="004F"/>
+  <anchor x="155.0" y="0.0" name="bottom"/>
+  <anchor x="222.0" y="250.0" name="center"/>
+  <anchor x="318.0" y="10.0" name="ogonek"/>
+  <anchor x="289.0" y="500.0" name="top"/>
+  <anchor x="109.0" y="500.0" name="topleft"/>
+  <anchor x="469.0" y="500.0" name="topright"/>
+  <outline>
+    <contour>
+      <point x="159.0" y="-10.0" type="curve" smooth="yes"/>
+      <point x="239.0" y="-10.0"/>
+      <point x="321.0" y="54.0"/>
+      <point x="342.0" y="133.0" type="curve" smooth="yes"/>
+      <point x="401.0" y="354.0" type="line" smooth="yes"/>
+      <point x="423.0" y="437.0"/>
+      <point x="371.0" y="510.0"/>
+      <point x="285.0" y="510.0" type="curve" smooth="yes"/>
+      <point x="205.0" y="510.0"/>
+      <point x="124.0" y="446.0"/>
+      <point x="103.0" y="366.0" type="curve" smooth="yes"/>
+      <point x="44.0" y="146.0" type="line" smooth="yes"/>
+      <point x="22.0" y="63.0"/>
+      <point x="73.0" y="-10.0"/>
+    </contour>
+    <contour>
+      <point x="159.0" y="40.0" type="curve" smooth="yes"/>
+      <point x="106.0" y="40.0"/>
+      <point x="78.0" y="81.0"/>
+      <point x="92.0" y="133.0" type="curve" smooth="yes"/>
+      <point x="151.0" y="354.0" type="line" smooth="yes"/>
+      <point x="166.0" y="412.0"/>
+      <point x="227.0" y="460.0"/>
+      <point x="285.0" y="460.0" type="curve" smooth="yes"/>
+      <point x="338.0" y="460.0"/>
+      <point x="367.0" y="419.0"/>
+      <point x="353.0" y="366.0" type="curve" smooth="yes"/>
+      <point x="294.0" y="146.0" type="line" smooth="yes"/>
+      <point x="278.0" y="88.0"/>
+      <point x="217.0" y="40.0"/>
+    </contour>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2017/10/27 21:28:25</string>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/O_dieresis.glif b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/O_dieresis.glif
new file mode 100644
index 0000000..93bca95
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/O_dieresis.glif
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="Odieresis" format="2">
+  <advance width="404.0"/>
+  <unicode hex="00D6"/>
+  <anchor x="155.0" y="0.0" name="bottom"/>
+  <anchor x="222.0" y="250.0" name="center"/>
+  <anchor x="318.0" y="10.0" name="ogonek"/>
+  <anchor x="322.0" y="625.0" name="top"/>
+  <anchor x="109.0" y="500.0" name="topleft"/>
+  <anchor x="469.0" y="500.0" name="topright"/>
+  <outline>
+    <component base="O"/>
+    <component base="dieresiscomb" xOffset="92.0" yOffset="150.0"/>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2017/10/27 21:28:35</string>
+      <key>public.markColor</key>
+      <string>0,0.67,0.91,1</string>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..6a3662f
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.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>N</key>
+    <string>N_.glif</string>
+    <key>O</key>
+    <string>O_.glif</string>
+    <key>Odieresis</key>
+    <string>O_dieresis.glif</string>
+    <key>dieresiscomb</key>
+    <string>dieresiscomb.glif</string>
+    <key>n</key>
+    <string>n.glif</string>
+    <key>o</key>
+    <string>o.glif</string>
+    <key>odieresis</key>
+    <string>odieresis.glif</string>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/dieresiscomb.glif b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/dieresiscomb.glif
new file mode 100644
index 0000000..224df4d
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/dieresiscomb.glif
@@ -0,0 +1,48 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="dieresiscomb" format="2">
+  <unicode hex="0308"/>
+  <anchor x="197.0" y="350.0" name="_top"/>
+  <anchor x="230.0" y="475.0" name="top"/>
+  <outline>
+    <contour>
+      <point x="300.0" y="425.0" type="curve" smooth="yes"/>
+      <point x="312.0" y="425.0"/>
+      <point x="321.0" y="433.0"/>
+      <point x="324.0" y="445.0" type="curve" smooth="yes"/>
+      <point x="330.0" y="469.0" type="line" smooth="yes"/>
+      <point x="334.0" y="484.0"/>
+      <point x="322.0" y="500.0"/>
+      <point x="306.0" y="500.0" type="curve" smooth="yes"/>
+      <point x="294.0" y="500.0"/>
+      <point x="285.0" y="492.0"/>
+      <point x="282.0" y="480.0" type="curve" smooth="yes"/>
+      <point x="276.0" y="456.0" type="line" smooth="yes"/>
+      <point x="272.0" y="441.0"/>
+      <point x="284.0" y="425.0"/>
+    </contour>
+    <contour>
+      <point x="150.0" y="425.0" type="curve" smooth="yes"/>
+      <point x="162.0" y="425.0"/>
+      <point x="171.0" y="433.0"/>
+      <point x="174.0" y="445.0" type="curve" smooth="yes"/>
+      <point x="180.0" y="469.0" type="line" smooth="yes"/>
+      <point x="184.0" y="484.0"/>
+      <point x="172.0" y="500.0"/>
+      <point x="156.0" y="500.0" type="curve" smooth="yes"/>
+      <point x="144.0" y="500.0"/>
+      <point x="135.0" y="492.0"/>
+      <point x="132.0" y="480.0" type="curve" smooth="yes"/>
+      <point x="126.0" y="456.0" type="line" smooth="yes"/>
+      <point x="122.0" y="441.0"/>
+      <point x="134.0" y="425.0"/>
+    </contour>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2018/11/20 11:01:28</string>
+      <key>com.schriftgestaltung.Glyphs.originalWidth</key>
+      <real>300.0</real>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/n.glif b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/n.glif
new file mode 100644
index 0000000..1313dd4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/n.glif
@@ -0,0 +1,44 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="n" format="2">
+  <advance width="348.0"/>
+  <unicode hex="006E"/>
+  <anchor x="128.0" y="0.0" name="bottom"/>
+  <anchor x="222.0" y="350.0" name="top"/>
+  <outline>
+    <contour>
+      <point x="34.0" y="-4.0" type="curve" smooth="yes"/>
+      <point x="46.0" y="-4.0"/>
+      <point x="55.0" y="4.0"/>
+      <point x="58.0" y="15.0" type="curve" smooth="yes"/>
+      <point x="134.0" y="300.0" type="line"/>
+      <point x="238.0" y="300.0" type="line" smooth="yes"/>
+      <point x="266.0" y="300.0"/>
+      <point x="278.0" y="282.0"/>
+      <point x="271.0" y="255.0" type="curve" smooth="yes"/>
+      <point x="210.0" y="28.0" type="line" smooth="yes"/>
+      <point x="205.0" y="11.0"/>
+      <point x="218.0" y="-4.0"/>
+      <point x="234.0" y="-4.0" type="curve" smooth="yes"/>
+      <point x="246.0" y="-4.0"/>
+      <point x="255.0" y="3.0"/>
+      <point x="258.0" y="15.0" type="curve" smooth="yes"/>
+      <point x="319.0" y="242.0" type="line" smooth="yes"/>
+      <point x="335.0" y="301.0"/>
+      <point x="299.0" y="350.0"/>
+      <point x="238.0" y="350.0" type="curve" smooth="yes"/>
+      <point x="118.0" y="350.0" type="line" smooth="yes"/>
+      <point x="102.993" y="350.0"/>
+      <point x="94.0" y="343.0"/>
+      <point x="90.0" y="329.0" type="curve" smooth="yes"/>
+      <point x="10.0" y="28.0" type="line" smooth="yes"/>
+      <point x="5.0" y="11.0"/>
+      <point x="18.0" y="-4.0"/>
+    </contour>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2017/10/27 21:28:25</string>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/o.glif b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/o.glif
new file mode 100644
index 0000000..ac3ff7e
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/o.glif
@@ -0,0 +1,50 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="o" format="2">
+  <advance width="342.0"/>
+  <unicode hex="006F"/>
+  <anchor x="124.0" y="0.0" name="bottom"/>
+  <anchor x="171.0" y="175.0" name="center"/>
+  <anchor x="267.0" y="10.0" name="ogonek"/>
+  <anchor x="218.0" y="350.0" name="top"/>
+  <anchor x="373.0" y="350.0" name="topright"/>
+  <outline>
+    <contour>
+      <point x="129.0" y="-10.0" type="curve" smooth="yes"/>
+      <point x="199.0" y="-10.0"/>
+      <point x="260.0" y="39.0"/>
+      <point x="280.0" y="110.0" type="curve" smooth="yes"/>
+      <point x="312.0" y="226.0" type="line" smooth="yes"/>
+      <point x="333.0" y="303.0"/>
+      <point x="291.0" y="360.0"/>
+      <point x="215.0" y="360.0" type="curve" smooth="yes"/>
+      <point x="145.0" y="360.0"/>
+      <point x="84.0" y="311.0"/>
+      <point x="64.0" y="240.0" type="curve" smooth="yes"/>
+      <point x="32.0" y="124.0" type="line" smooth="yes"/>
+      <point x="11.0" y="47.0"/>
+      <point x="53.0" y="-10.0"/>
+    </contour>
+    <contour>
+      <point x="129.0" y="40.0" type="curve" smooth="yes"/>
+      <point x="86.0" y="40.0"/>
+      <point x="68.0" y="66.0"/>
+      <point x="80.0" y="110.0" type="curve" smooth="yes"/>
+      <point x="112.0" y="226.0" type="line" smooth="yes"/>
+      <point x="126.0" y="277.0"/>
+      <point x="167.0" y="310.0"/>
+      <point x="215.0" y="310.0" type="curve" smooth="yes"/>
+      <point x="258.0" y="310.0"/>
+      <point x="276.0" y="284.0"/>
+      <point x="264.0" y="240.0" type="curve" smooth="yes"/>
+      <point x="232.0" y="124.0" type="line" smooth="yes"/>
+      <point x="218.0" y="73.0"/>
+      <point x="177.0" y="40.0"/>
+    </contour>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2018/11/20 11:01:11</string>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/odieresis.glif b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/odieresis.glif
new file mode 100644
index 0000000..42c39d1
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/odieresis.glif
@@ -0,0 +1,22 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="odieresis" format="2">
+  <advance width="342.0"/>
+  <unicode hex="00F6"/>
+  <anchor x="124.0" y="0.0" name="bottom"/>
+  <anchor x="171.0" y="175.0" name="center"/>
+  <anchor x="267.0" y="10.0" name="ogonek"/>
+  <anchor x="251.0" y="475.0" name="top"/>
+  <anchor x="373.0" y="350.0" name="topright"/>
+  <outline>
+    <component base="o"/>
+    <component base="dieresiscomb" xOffset="21.0"/>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2018/11/20 10:59:58</string>
+      <key>public.markColor</key>
+      <string>0,0.67,0.91,1</string>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/groups.plist b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/groups.plist
new file mode 100644
index 0000000..f9e8ed3
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/groups.plist
@@ -0,0 +1,34 @@
+<?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.O</key>
+    <array>
+      <string>O</string>
+      <string>Odieresis</string>
+    </array>
+    <key>public.kern1.n</key>
+    <array>
+      <string>n</string>
+    </array>
+    <key>public.kern1.o</key>
+    <array>
+      <string>o</string>
+      <string>odieresis</string>
+    </array>
+    <key>public.kern2.O</key>
+    <array>
+      <string>O</string>
+      <string>Odieresis</string>
+    </array>
+    <key>public.kern2.n</key>
+    <array>
+      <string>n</string>
+    </array>
+    <key>public.kern2.o</key>
+    <array>
+      <string>o</string>
+      <string>odieresis</string>
+    </array>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/layercontents.plist b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/layercontents.plist
new file mode 100644
index 0000000..7120d0b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/layercontents.plist
@@ -0,0 +1,14 @@
+<?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>
+      <string>public.background</string>
+      <string>glyphs.public.background</string>
+    </array>
+  </array>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/lib.plist
new file mode 100644
index 0000000..b9f2b92
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/lib.plist
@@ -0,0 +1,86 @@
+<?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>com.schriftgestaltung.appVersion</key>
+    <string>1179</string>
+    <key>com.schriftgestaltung.customName</key>
+    <string>Italic 15</string>
+    <key>com.schriftgestaltung.customParameter.GSFont.Axes</key>
+    <array>
+      <dict>
+        <key>Name</key>
+        <string>Slant</string>
+        <key>Tag</key>
+        <string>slnt</string>
+      </dict>
+    </array>
+    <key>com.schriftgestaltung.customParameter.GSFont.DisplayStrings</key>
+    <array>
+      <string>o/dieresiscomb ö</string>
+    </array>
+    <key>com.schriftgestaltung.customParameter.GSFont.Variation Font Origin</key>
+    <string>EB3D7718-A203-47FB-ABD4-8B7A501887ED</string>
+    <key>com.schriftgestaltung.customParameter.GSFont.disablesAutomaticAlignment</key>
+    <false/>
+    <key>com.schriftgestaltung.customParameter.GSFont.useNiceNames</key>
+    <integer>1</integer>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.Link Metrics With Master</key>
+    <string>Regular</string>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.Master Name</key>
+    <string>Italic 15</string>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.customValue</key>
+    <real>-15.0</real>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.customValue1</key>
+    <real>16.0</real>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.customValue2</key>
+    <real>0.0</real>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.customValue3</key>
+    <real>0.0</real>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.iconName</key>
+    <string></string>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.weightValue</key>
+    <real>-15.0</real>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.widthValue</key>
+    <real>100.0</real>
+    <key>com.schriftgestaltung.customValue</key>
+    <real>-15.0</real>
+    <key>com.schriftgestaltung.customValue1</key>
+    <real>16.0</real>
+    <key>com.schriftgestaltung.fontMasterOrder</key>
+    <integer>1</integer>
+    <key>com.schriftgestaltung.glyphOrder</key>
+    <false/>
+    <key>com.schriftgestaltung.keyboardIncrement</key>
+    <integer>1</integer>
+    <key>com.schriftgestaltung.weight</key>
+    <string>Regular</string>
+    <key>com.schriftgestaltung.weightValue</key>
+    <real>-15.0</real>
+    <key>com.schriftgestaltung.width</key>
+    <string>Regular</string>
+    <key>com.schriftgestaltung.widthValue</key>
+    <real>100.0</real>
+    <key>noodleExtremesAndInflections</key>
+    <integer>0</integer>
+    <key>noodleRemoveOverlap</key>
+    <integer>1</integer>
+    <key>noodleThickness</key>
+    <string>50.0</string>
+    <key>public.glyphOrder</key>
+    <array>
+      <string>N</string>
+      <string>O</string>
+      <string>Odieresis</string>
+      <string>n</string>
+      <string>o</string>
+      <string>odieresis</string>
+      <string>dieresiscomb</string>
+    </array>
+    <key>public.postscriptNames</key>
+    <dict>
+      <key>dieresiscomb</key>
+      <string>uni0308</string>
+    </dict>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/metainfo.plist
new file mode 100644
index 0000000..7b8b34a
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Italic15.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>com.github.fonttools.ufoLib</string>
+    <key>formatVersion</key>
+    <integer>3</integer>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/features.fea b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/features.fea
new file mode 100644
index 0000000..88e4c41
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/features.fea
@@ -0,0 +1,23 @@
+# automatic
+@Uppercase = [ N O Odieresis ];
+
+# Prefix: Languagesystems
+# automatic
+languagesystem DFLT dflt;
+languagesystem latn dflt;
+languagesystem latn NLD;
+
+
+feature cpsp {
+pos @Uppercase <25 0 50 0>;
+
+} cpsp;
+
+table GDEF {
+  # automatic
+  GlyphClassDef
+    [N O Odieresis n o odieresis], # Base
+    , # Liga
+    [dieresiscomb], # Mark
+    ;
+} GDEF;
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/fontinfo.plist b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/fontinfo.plist
new file mode 100644
index 0000000..7ff1edb
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/fontinfo.plist
@@ -0,0 +1,93 @@
+<?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>
+    <real>600.0</real>
+    <key>capHeight</key>
+    <real>500.0</real>
+    <key>copyright</key>
+    <string>Copyright 2017 by Jens Kutilek</string>
+    <key>descender</key>
+    <real>-150.0</real>
+    <key>familyName</key>
+    <string>Test Family 4</string>
+    <key>guidelines</key>
+    <array>
+      <dict>
+        <key>angle</key>
+        <real>180.0</real>
+        <key>x</key>
+        <real>125.0</real>
+        <key>y</key>
+        <real>175.0</real>
+      </dict>
+    </array>
+    <key>italicAngle</key>
+    <real>-0.0</real>
+    <key>openTypeHeadCreated</key>
+    <string>2017/10/21 13:31:38</string>
+    <key>openTypeNameDesigner</key>
+    <string>Jens Kutilek after the ISO 3098 standard</string>
+    <key>openTypeNameDesignerURL</key>
+    <string>https://www.kutilek.de/</string>
+    <key>openTypeNameManufacturer</key>
+    <string>Jens Kutilek</string>
+    <key>openTypeNameManufacturerURL</key>
+    <string>https://www.kutilek.de/</string>
+    <key>openTypeOS2Type</key>
+    <array>
+      <integer>3</integer>
+    </array>
+    <key>openTypeOS2VendorID</key>
+    <string>jens</string>
+    <key>openTypeOS2WinAscent</key>
+    <integer>700</integer>
+    <key>openTypeOS2WinDescent</key>
+    <integer>250</integer>
+    <key>postscriptBlueValues</key>
+    <array>
+      <real>-10.0</real>
+      <real>0.0</real>
+      <real>350.0</real>
+      <real>360.0</real>
+      <real>500.0</real>
+      <real>510.0</real>
+    </array>
+    <key>postscriptFamilyBlues</key>
+    <array/>
+    <key>postscriptFamilyOtherBlues</key>
+    <array/>
+    <key>postscriptOtherBlues</key>
+    <array>
+      <real>-160.0</real>
+      <real>-150.0</real>
+    </array>
+    <key>postscriptStemSnapH</key>
+    <array>
+      <integer>50</integer>
+    </array>
+    <key>postscriptStemSnapV</key>
+    <array>
+      <integer>50</integer>
+    </array>
+    <key>postscriptUnderlinePosition</key>
+    <integer>-100</integer>
+    <key>postscriptUnderlineThickness</key>
+    <integer>50</integer>
+    <key>styleMapFamilyName</key>
+    <string>Test Family 4</string>
+    <key>styleMapStyleName</key>
+    <string>regular</string>
+    <key>styleName</key>
+    <string>Regular</string>
+    <key>unitsPerEm</key>
+    <integer>750</integer>
+    <key>versionMajor</key>
+    <integer>1</integer>
+    <key>versionMinor</key>
+    <integer>0</integer>
+    <key>xHeight</key>
+    <real>350.0</real>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/N_.glif b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/N_.glif
new file mode 100644
index 0000000..941a558
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/N_.glif
@@ -0,0 +1,31 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="N" format="2">
+  <outline>
+    <contour>
+      <point x="324.0" y="-22.0" type="line" smooth="yes"/>
+      <point x="336.0" y="-44.0"/>
+      <point x="354.0" y="-40.0"/>
+      <point x="354.0" y="-14.0" type="curve" smooth="yes"/>
+      <point x="354.0" y="479.0" type="line" smooth="yes"/>
+      <point x="354.0" y="493.0"/>
+      <point x="343.0" y="504.0"/>
+      <point x="329.0" y="504.0" type="curve" smooth="yes"/>
+      <point x="315.0" y="504.0"/>
+      <point x="304.0" y="493.0"/>
+      <point x="304.0" y="479.0" type="curve" smooth="yes"/>
+      <point x="304.0" y="119.0" type="line"/>
+      <point x="84.0" y="522.0" type="line" smooth="yes"/>
+      <point x="72.0" y="544.0"/>
+      <point x="54.0" y="540.0"/>
+      <point x="54.0" y="514.0" type="curve" smooth="yes"/>
+      <point x="54.0" y="21.0" type="line" smooth="yes"/>
+      <point x="54.0" y="7.0"/>
+      <point x="65.0" y="-4.0"/>
+      <point x="79.0" y="-4.0" type="curve" smooth="yes"/>
+      <point x="93.0" y="-4.0"/>
+      <point x="104.0" y="7.0"/>
+      <point x="104.0" y="21.0" type="curve" smooth="yes"/>
+      <point x="104.0" y="381.0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/O_.glif b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/O_.glif
new file mode 100644
index 0000000..dc9a72e
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/O_.glif
@@ -0,0 +1,28 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="O" format="2">
+  <anchor x="202.0" y="0.0" name="bottom"/>
+  <anchor x="202.0" y="250.0" name="center"/>
+  <anchor x="362.0" y="10.0" name="ogonek"/>
+  <anchor x="202.0" y="500.0" name="top"/>
+  <anchor x="22.0" y="500.0" name="topleft"/>
+  <anchor x="382.0" y="500.0" name="topright"/>
+  <outline>
+    <contour>
+      <point x="77.0" y="150.0" type="curve"/>
+      <point x="77.0" y="350.0" type="line"/>
+      <point x="77.0" y="419.0"/>
+      <point x="133.0" y="475.0"/>
+      <point x="202.0" y="475.0" type="curve"/>
+      <point x="271.0" y="475.0"/>
+      <point x="327.0" y="419.0"/>
+      <point x="327.0" y="350.0" type="curve"/>
+      <point x="327.0" y="250.0" type="line"/>
+      <point x="327.0" y="150.0" type="line"/>
+      <point x="327.0" y="81.0"/>
+      <point x="271.0" y="25.0"/>
+      <point x="202.0" y="25.0" type="curve"/>
+      <point x="133.0" y="25.0"/>
+      <point x="77.0" y="81.0"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/contents.plist b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/contents.plist
new file mode 100644
index 0000000..8382c79
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/contents.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>N</key>
+    <string>N_.glif</string>
+    <key>O</key>
+    <string>O_.glif</string>
+    <key>dieresiscomb</key>
+    <string>dieresiscomb.glif</string>
+    <key>n</key>
+    <string>n.glif</string>
+    <key>o</key>
+    <string>o.glif</string>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/dieresiscomb.glif b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/dieresiscomb.glif
new file mode 100644
index 0000000..11fb86e
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/dieresiscomb.glif
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="dieresiscomb" format="2">
+  <anchor x="150.0" y="350.0" name="_top"/>
+  <anchor x="150.0" y="475.0" name="top"/>
+  <outline>
+    <contour>
+      <point x="75.0" y="475.0" type="move"/>
+      <point x="75.0" y="450.0" type="line"/>
+    </contour>
+    <contour>
+      <point x="225.0" y="475.0" type="move"/>
+      <point x="225.0" y="450.0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/n.glif b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/n.glif
new file mode 100644
index 0000000..2d1a13e
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/n.glif
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="n" format="2">
+  <outline>
+    <contour>
+      <point x="75.0" y="21.0" type="move"/>
+      <point x="75.0" y="325.0" type="line"/>
+      <point x="198.0" y="325.0" type="line" smooth="yes"/>
+      <point x="243.0" y="325.0"/>
+      <point x="275.0" y="293.0"/>
+      <point x="275.0" y="248.0" type="curve" smooth="yes"/>
+      <point x="275.0" y="21.0" type="line"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/o.glif b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/o.glif
new file mode 100644
index 0000000..2e9a61b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/o.glif
@@ -0,0 +1,37 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="o" format="2">
+  <outline>
+    <contour>
+      <point x="171.0" y="-10.0" type="curve" smooth="yes"/>
+      <point x="240.0" y="-10.0"/>
+      <point x="296.0" y="46.0"/>
+      <point x="296.0" y="115.0" type="curve" smooth="yes"/>
+      <point x="296.0" y="235.0" type="line" smooth="yes"/>
+      <point x="296.0" y="304.0"/>
+      <point x="240.0" y="360.0"/>
+      <point x="171.0" y="360.0" type="curve" smooth="yes"/>
+      <point x="102.0" y="360.0"/>
+      <point x="46.0" y="304.0"/>
+      <point x="46.0" y="235.0" type="curve" smooth="yes"/>
+      <point x="46.0" y="115.0" type="line" smooth="yes"/>
+      <point x="46.0" y="46.0"/>
+      <point x="102.0" y="-10.0"/>
+    </contour>
+    <contour>
+      <point x="171.0" y="40.0" type="curve" smooth="yes"/>
+      <point x="130.0" y="40.0"/>
+      <point x="96.0" y="74.0"/>
+      <point x="96.0" y="115.0" type="curve" smooth="yes"/>
+      <point x="96.0" y="235.0" type="line" smooth="yes"/>
+      <point x="96.0" y="276.0"/>
+      <point x="130.0" y="310.0"/>
+      <point x="171.0" y="310.0" type="curve" smooth="yes"/>
+      <point x="212.0" y="310.0"/>
+      <point x="246.0" y="276.0"/>
+      <point x="246.0" y="235.0" type="curve" smooth="yes"/>
+      <point x="246.0" y="115.0" type="line" smooth="yes"/>
+      <point x="246.0" y="74.0"/>
+      <point x="212.0" y="40.0"/>
+    </contour>
+  </outline>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/N_.glif b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/N_.glif
new file mode 100644
index 0000000..a588dac
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/N_.glif
@@ -0,0 +1,47 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="N" format="2">
+  <advance width="408.0"/>
+  <unicode hex="004E"/>
+  <anchor x="204.0" y="0.0" name="bottom"/>
+  <anchor x="204.0" y="500.0" name="top"/>
+  <outline>
+    <contour>
+      <point x="79.0" y="-4.0" type="curve" smooth="yes"/>
+      <point x="93.0" y="-4.0"/>
+      <point x="104.0" y="7.0"/>
+      <point x="104.0" y="21.0" type="curve" smooth="yes"/>
+      <point x="104.0" y="381.0" type="line"/>
+      <point x="307.0" y="9.0" type="line" smooth="yes"/>
+      <point x="311.0" y="2.0"/>
+      <point x="317.0" y="-4.0"/>
+      <point x="329.0" y="-4.0" type="curve" smooth="yes"/>
+      <point x="343.0" y="-4.0"/>
+      <point x="354.0" y="7.0"/>
+      <point x="354.0" y="21.0" type="curve" smooth="yes"/>
+      <point x="354.0" y="479.0" type="line" smooth="yes"/>
+      <point x="354.0" y="493.0"/>
+      <point x="343.0" y="504.0"/>
+      <point x="329.0" y="504.0" type="curve" smooth="yes"/>
+      <point x="315.0" y="504.0"/>
+      <point x="304.0" y="493.0"/>
+      <point x="304.0" y="479.0" type="curve" smooth="yes"/>
+      <point x="304.0" y="119.0" type="line"/>
+      <point x="101.0" y="491.0" type="line" smooth="yes"/>
+      <point x="97.0" y="498.0"/>
+      <point x="91.0" y="504.0"/>
+      <point x="79.0" y="504.0" type="curve" smooth="yes"/>
+      <point x="65.0" y="504.0"/>
+      <point x="54.0" y="493.0"/>
+      <point x="54.0" y="479.0" type="curve" smooth="yes"/>
+      <point x="54.0" y="21.0" type="line" smooth="yes"/>
+      <point x="54.0" y="7.0"/>
+      <point x="65.0" y="-4.0"/>
+    </contour>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2018/11/20 10:52:27</string>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/O_.glif b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/O_.glif
new file mode 100644
index 0000000..f3979ed
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/O_.glif
@@ -0,0 +1,51 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="O" format="2">
+  <advance width="404.0"/>
+  <unicode hex="004F"/>
+  <anchor x="202.0" y="0.0" name="bottom"/>
+  <anchor x="202.0" y="250.0" name="center"/>
+  <anchor x="362.0" y="10.0" name="ogonek"/>
+  <anchor x="202.0" y="500.0" name="top"/>
+  <anchor x="22.0" y="500.0" name="topleft"/>
+  <anchor x="382.0" y="500.0" name="topright"/>
+  <outline>
+    <contour>
+      <point x="202.0" y="-10.0" type="curve" smooth="yes"/>
+      <point x="284.0" y="-10.0"/>
+      <point x="352.0" y="58.0"/>
+      <point x="352.0" y="140.0" type="curve" smooth="yes"/>
+      <point x="352.0" y="360.0" type="line" smooth="yes"/>
+      <point x="352.0" y="442.0"/>
+      <point x="284.0" y="510.0"/>
+      <point x="202.0" y="510.0" type="curve" smooth="yes"/>
+      <point x="120.0" y="510.0"/>
+      <point x="52.0" y="442.0"/>
+      <point x="52.0" y="360.0" type="curve" smooth="yes"/>
+      <point x="52.0" y="140.0" type="line" smooth="yes"/>
+      <point x="52.0" y="58.0"/>
+      <point x="120.0" y="-10.0"/>
+    </contour>
+    <contour>
+      <point x="202.0" y="40.0" type="curve" smooth="yes"/>
+      <point x="147.0" y="40.0"/>
+      <point x="102.0" y="85.0"/>
+      <point x="102.0" y="140.0" type="curve" smooth="yes"/>
+      <point x="102.0" y="360.0" type="line" smooth="yes"/>
+      <point x="102.0" y="415.0"/>
+      <point x="147.0" y="460.0"/>
+      <point x="202.0" y="460.0" type="curve" smooth="yes"/>
+      <point x="257.0" y="460.0"/>
+      <point x="302.0" y="415.0"/>
+      <point x="302.0" y="360.0" type="curve" smooth="yes"/>
+      <point x="302.0" y="140.0" type="line" smooth="yes"/>
+      <point x="302.0" y="85.0"/>
+      <point x="257.0" y="40.0"/>
+    </contour>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2017/10/27 21:28:25</string>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/O_dieresis.glif b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/O_dieresis.glif
new file mode 100644
index 0000000..22fcd37
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/O_dieresis.glif
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="Odieresis" format="2">
+  <advance width="404.0"/>
+  <unicode hex="00D6"/>
+  <anchor x="202.0" y="0.0" name="bottom"/>
+  <anchor x="202.0" y="250.0" name="center"/>
+  <anchor x="362.0" y="10.0" name="ogonek"/>
+  <anchor x="202.0" y="625.0" name="top"/>
+  <anchor x="22.0" y="500.0" name="topleft"/>
+  <anchor x="382.0" y="500.0" name="topright"/>
+  <outline>
+    <component base="O"/>
+    <component base="dieresiscomb" xOffset="52.0" yOffset="150.0"/>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2017/10/27 21:28:35</string>
+      <key>public.markColor</key>
+      <string>0,0.67,0.91,1</string>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/contents.plist b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/contents.plist
new file mode 100644
index 0000000..6a3662f
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-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>N</key>
+    <string>N_.glif</string>
+    <key>O</key>
+    <string>O_.glif</string>
+    <key>Odieresis</key>
+    <string>O_dieresis.glif</string>
+    <key>dieresiscomb</key>
+    <string>dieresiscomb.glif</string>
+    <key>n</key>
+    <string>n.glif</string>
+    <key>o</key>
+    <string>o.glif</string>
+    <key>odieresis</key>
+    <string>odieresis.glif</string>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/dieresiscomb.glif b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/dieresiscomb.glif
new file mode 100644
index 0000000..b3cfef5
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/dieresiscomb.glif
@@ -0,0 +1,48 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="dieresiscomb" format="2">
+  <unicode hex="0308"/>
+  <anchor x="150.0" y="350.0" name="_top"/>
+  <anchor x="150.0" y="475.0" name="top"/>
+  <outline>
+    <contour>
+      <point x="225.0" y="425.0" type="curve" smooth="yes"/>
+      <point x="239.0" y="425.0"/>
+      <point x="250.0" y="436.0"/>
+      <point x="250.0" y="450.0" type="curve" smooth="yes"/>
+      <point x="250.0" y="475.0" type="line" smooth="yes"/>
+      <point x="250.0" y="489.0"/>
+      <point x="239.0" y="500.0"/>
+      <point x="225.0" y="500.0" type="curve" smooth="yes"/>
+      <point x="211.0" y="500.0"/>
+      <point x="200.0" y="489.0"/>
+      <point x="200.0" y="475.0" type="curve" smooth="yes"/>
+      <point x="200.0" y="450.0" type="line" smooth="yes"/>
+      <point x="200.0" y="436.0"/>
+      <point x="211.0" y="425.0"/>
+    </contour>
+    <contour>
+      <point x="75.0" y="425.0" type="curve" smooth="yes"/>
+      <point x="89.0" y="425.0"/>
+      <point x="100.0" y="436.0"/>
+      <point x="100.0" y="450.0" type="curve" smooth="yes"/>
+      <point x="100.0" y="475.0" type="line" smooth="yes"/>
+      <point x="100.0" y="489.0"/>
+      <point x="89.0" y="500.0"/>
+      <point x="75.0" y="500.0" type="curve" smooth="yes"/>
+      <point x="61.0" y="500.0"/>
+      <point x="50.0" y="489.0"/>
+      <point x="50.0" y="475.0" type="curve" smooth="yes"/>
+      <point x="50.0" y="450.0" type="line" smooth="yes"/>
+      <point x="50.0" y="436.0"/>
+      <point x="61.0" y="425.0"/>
+    </contour>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2018/11/20 11:01:28</string>
+      <key>com.schriftgestaltung.Glyphs.originalWidth</key>
+      <real>300.0</real>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/n.glif b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/n.glif
new file mode 100644
index 0000000..221ce82
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/n.glif
@@ -0,0 +1,44 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="n" format="2">
+  <advance width="348.0"/>
+  <unicode hex="006E"/>
+  <anchor x="175.0" y="0.0" name="bottom"/>
+  <anchor x="175.0" y="350.0" name="top"/>
+  <outline>
+    <contour>
+      <point x="75.0" y="-4.0" type="curve" smooth="yes"/>
+      <point x="89.0" y="-4.0"/>
+      <point x="100.0" y="7.0"/>
+      <point x="100.0" y="21.0" type="curve" smooth="yes"/>
+      <point x="100.0" y="300.0" type="line"/>
+      <point x="198.0" y="300.0" type="line" smooth="yes"/>
+      <point x="229.0" y="300.0"/>
+      <point x="250.0" y="279.0"/>
+      <point x="250.0" y="248.0" type="curve" smooth="yes"/>
+      <point x="250.0" y="21.0" type="line" smooth="yes"/>
+      <point x="250.0" y="7.0"/>
+      <point x="261.0" y="-4.0"/>
+      <point x="275.0" y="-4.0" type="curve" smooth="yes"/>
+      <point x="289.0" y="-4.0"/>
+      <point x="300.0" y="7.0"/>
+      <point x="300.0" y="21.0" type="curve" smooth="yes"/>
+      <point x="300.0" y="248.0" type="line" smooth="yes"/>
+      <point x="300.0" y="307.0"/>
+      <point x="257.0" y="350.0"/>
+      <point x="198.0" y="350.0" type="curve" smooth="yes"/>
+      <point x="75.0" y="350.0" type="line" smooth="yes"/>
+      <point x="59.0" y="350.0"/>
+      <point x="50.0" y="341.0"/>
+      <point x="50.0" y="325.0" type="curve" smooth="yes"/>
+      <point x="50.0" y="21.0" type="line" smooth="yes"/>
+      <point x="50.0" y="7.0"/>
+      <point x="61.0" y="-4.0"/>
+    </contour>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2017/10/27 21:28:25</string>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/o.glif b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/o.glif
new file mode 100644
index 0000000..417e4ff
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/o.glif
@@ -0,0 +1,50 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="o" format="2">
+  <advance width="342.0"/>
+  <unicode hex="006F"/>
+  <anchor x="171.0" y="0.0" name="bottom"/>
+  <anchor x="171.0" y="175.0" name="center"/>
+  <anchor x="311.0" y="10.0" name="ogonek"/>
+  <anchor x="171.0" y="350.0" name="top"/>
+  <anchor x="326.0" y="350.0" name="topright"/>
+  <outline>
+    <contour>
+      <point x="171.0" y="-10.0" type="curve" smooth="yes"/>
+      <point x="244.0" y="-10.0"/>
+      <point x="296.0" y="43.0"/>
+      <point x="296.0" y="117.0" type="curve" smooth="yes"/>
+      <point x="296.0" y="233.0" type="line" smooth="yes"/>
+      <point x="296.0" y="307.0"/>
+      <point x="244.0" y="360.0"/>
+      <point x="171.0" y="360.0" type="curve" smooth="yes"/>
+      <point x="98.0" y="360.0"/>
+      <point x="46.0" y="307.0"/>
+      <point x="46.0" y="233.0" type="curve" smooth="yes"/>
+      <point x="46.0" y="117.0" type="line" smooth="yes"/>
+      <point x="46.0" y="43.0"/>
+      <point x="98.0" y="-10.0"/>
+    </contour>
+    <contour>
+      <point x="171.0" y="40.0" type="curve" smooth="yes"/>
+      <point x="126.0" y="40.0"/>
+      <point x="96.0" y="70.0"/>
+      <point x="96.0" y="117.0" type="curve" smooth="yes"/>
+      <point x="96.0" y="233.0" type="line" smooth="yes"/>
+      <point x="96.0" y="280.0"/>
+      <point x="126.0" y="310.0"/>
+      <point x="171.0" y="310.0" type="curve" smooth="yes"/>
+      <point x="216.0" y="310.0"/>
+      <point x="246.0" y="280.0"/>
+      <point x="246.0" y="233.0" type="curve" smooth="yes"/>
+      <point x="246.0" y="117.0" type="line" smooth="yes"/>
+      <point x="246.0" y="70.0"/>
+      <point x="216.0" y="40.0"/>
+    </contour>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2018/11/20 11:01:11</string>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/odieresis.glif b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/odieresis.glif
new file mode 100644
index 0000000..b0f2a59
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/odieresis.glif
@@ -0,0 +1,22 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<glyph name="odieresis" format="2">
+  <advance width="342.0"/>
+  <unicode hex="00F6"/>
+  <anchor x="171.0" y="0.0" name="bottom"/>
+  <anchor x="171.0" y="175.0" name="center"/>
+  <anchor x="311.0" y="10.0" name="ogonek"/>
+  <anchor x="171.0" y="475.0" name="top"/>
+  <anchor x="326.0" y="350.0" name="topright"/>
+  <outline>
+    <component base="o"/>
+    <component base="dieresiscomb" xOffset="21.0"/>
+  </outline>
+  <lib>
+    <dict>
+      <key>com.schriftgestaltung.Glyphs.lastChange</key>
+      <string>2018/11/20 10:59:58</string>
+      <key>public.markColor</key>
+      <string>0,0.67,0.91,1</string>
+    </dict>
+  </lib>
+</glyph>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/groups.plist b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/groups.plist
new file mode 100644
index 0000000..f9e8ed3
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/groups.plist
@@ -0,0 +1,34 @@
+<?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.O</key>
+    <array>
+      <string>O</string>
+      <string>Odieresis</string>
+    </array>
+    <key>public.kern1.n</key>
+    <array>
+      <string>n</string>
+    </array>
+    <key>public.kern1.o</key>
+    <array>
+      <string>o</string>
+      <string>odieresis</string>
+    </array>
+    <key>public.kern2.O</key>
+    <array>
+      <string>O</string>
+      <string>Odieresis</string>
+    </array>
+    <key>public.kern2.n</key>
+    <array>
+      <string>n</string>
+    </array>
+    <key>public.kern2.o</key>
+    <array>
+      <string>o</string>
+      <string>odieresis</string>
+    </array>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/kerning.plist b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/kerning.plist
new file mode 100644
index 0000000..8da68f2
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/kerning.plist
@@ -0,0 +1,468 @@
+<?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.A</key>
+    <dict>
+      <key>public.kern2.O</key>
+      <real>-5.0</real>
+      <key>public.kern2.T</key>
+      <real>-40.0</real>
+      <key>public.kern2.V</key>
+      <real>-40.0</real>
+      <key>public.kern2.W</key>
+      <real>-25.0</real>
+      <key>public.kern2.Y</key>
+      <real>-50.0</real>
+      <key>public.kern2.f</key>
+      <real>-15.0</real>
+      <key>public.kern2.hyphen</key>
+      <real>-10.0</real>
+      <key>public.kern2.quote</key>
+      <real>-80.0</real>
+      <key>public.kern2.w</key>
+      <real>-25.0</real>
+      <key>public.kern2.y</key>
+      <real>-30.0</real>
+    </dict>
+    <key>public.kern1.B</key>
+    <dict>
+      <key>public.kern2.quote</key>
+      <real>-10.0</real>
+    </dict>
+    <key>public.kern1.C</key>
+    <dict>
+      <key>public.kern2.O</key>
+      <real>-5.0</real>
+    </dict>
+    <key>public.kern1.E</key>
+    <dict>
+      <key>public.kern2.O</key>
+      <real>-5.0</real>
+    </dict>
+    <key>public.kern1.F</key>
+    <dict>
+      <key>public.kern2.A</key>
+      <real>-35.0</real>
+      <key>public.kern2.a</key>
+      <real>-25.0</real>
+      <key>public.kern2.o</key>
+      <real>-20.0</real>
+      <key>public.kern2.period</key>
+      <real>-40.0</real>
+      <key>public.kern2.u</key>
+      <real>-15.0</real>
+    </dict>
+    <key>public.kern1.K</key>
+    <dict>
+      <key>public.kern2.O</key>
+      <real>-10.0</real>
+    </dict>
+    <key>public.kern1.L</key>
+    <dict>
+      <key>public.kern2.O</key>
+      <real>-20.0</real>
+      <key>public.kern2.T</key>
+      <real>-60.0</real>
+      <key>public.kern2.V</key>
+      <real>-65.0</real>
+      <key>public.kern2.W</key>
+      <real>-50.0</real>
+      <key>public.kern2.Y</key>
+      <real>-70.0</real>
+      <key>public.kern2.hyphen</key>
+      <real>-30.0</real>
+      <key>public.kern2.quote</key>
+      <real>-130.0</real>
+    </dict>
+    <key>public.kern1.O</key>
+    <dict>
+      <key>public.kern2.A</key>
+      <real>-5.0</real>
+      <key>public.kern2.J</key>
+      <real>-15.0</real>
+      <key>public.kern2.T</key>
+      <real>-15.0</real>
+      <key>public.kern2.V</key>
+      <real>-5.0</real>
+      <key>public.kern2.W</key>
+      <real>-15.0</real>
+      <key>public.kern2.X</key>
+      <real>-15.0</real>
+      <key>public.kern2.Y</key>
+      <real>-20.0</real>
+      <key>public.kern2.quote</key>
+      <real>-5.0</real>
+    </dict>
+    <key>public.kern1.P</key>
+    <dict>
+      <key>public.kern2.A</key>
+      <real>-30.0</real>
+      <key>public.kern2.J</key>
+      <real>-80.0</real>
+      <key>public.kern2.period</key>
+      <real>-50.0</real>
+      <key>public.kern2.quote</key>
+      <real>15.0</real>
+    </dict>
+    <key>public.kern1.R</key>
+    <dict>
+      <key>public.kern2.T</key>
+      <real>-15.0</real>
+      <key>public.kern2.W</key>
+      <real>-5.0</real>
+      <key>public.kern2.Y</key>
+      <real>-10.0</real>
+      <key>public.kern2.o</key>
+      <real>-10.0</real>
+      <key>public.kern2.quote</key>
+      <real>10.0</real>
+    </dict>
+    <key>public.kern1.S</key>
+    <dict>
+      <key>public.kern2.quote</key>
+      <real>-10.0</real>
+    </dict>
+    <key>public.kern1.T</key>
+    <dict>
+      <key>public.kern2.A</key>
+      <real>-40.0</real>
+      <key>public.kern2.J</key>
+      <real>-50.0</real>
+      <key>public.kern2.O</key>
+      <real>-15.0</real>
+      <key>public.kern2.a</key>
+      <real>-50.0</real>
+      <key>public.kern2.hyphen</key>
+      <real>-50.0</real>
+      <key>public.kern2.n</key>
+      <real>-40.0</real>
+      <key>public.kern2.o</key>
+      <real>-50.0</real>
+      <key>public.kern2.period</key>
+      <real>-60.0</real>
+      <key>public.kern2.s</key>
+      <real>-50.0</real>
+      <key>public.kern2.u</key>
+      <real>-40.0</real>
+      <key>public.kern2.w</key>
+      <real>-40.0</real>
+      <key>public.kern2.y</key>
+      <real>-40.0</real>
+    </dict>
+    <key>public.kern1.V</key>
+    <dict>
+      <key>public.kern2.A</key>
+      <real>-40.0</real>
+      <key>public.kern2.O</key>
+      <real>-5.0</real>
+      <key>public.kern2.a</key>
+      <real>-45.0</real>
+      <key>public.kern2.f</key>
+      <real>-15.0</real>
+      <key>public.kern2.hyphen</key>
+      <real>-30.0</real>
+      <key>public.kern2.n</key>
+      <real>-30.0</real>
+      <key>public.kern2.o</key>
+      <real>-45.0</real>
+      <key>public.kern2.period</key>
+      <real>-70.0</real>
+      <key>public.kern2.quote</key>
+      <real>-10.0</real>
+      <key>public.kern2.s</key>
+      <real>-35.0</real>
+      <key>public.kern2.u</key>
+      <real>-35.0</real>
+      <key>public.kern2.w</key>
+      <real>-35.0</real>
+      <key>public.kern2.x</key>
+      <real>-40.0</real>
+      <key>public.kern2.y</key>
+      <real>-35.0</real>
+      <key>public.kern2.z</key>
+      <real>-40.0</real>
+    </dict>
+    <key>public.kern1.W</key>
+    <dict>
+      <key>public.kern2.A</key>
+      <real>-25.0</real>
+      <key>public.kern2.O</key>
+      <real>-15.0</real>
+      <key>public.kern2.a</key>
+      <real>-25.0</real>
+      <key>public.kern2.f</key>
+      <real>-15.0</real>
+      <key>public.kern2.hyphen</key>
+      <real>-20.0</real>
+      <key>public.kern2.n</key>
+      <real>-20.0</real>
+      <key>public.kern2.o</key>
+      <real>-30.0</real>
+      <key>public.kern2.period</key>
+      <real>-40.0</real>
+      <key>public.kern2.quote</key>
+      <real>-10.0</real>
+      <key>public.kern2.s</key>
+      <real>-20.0</real>
+      <key>public.kern2.u</key>
+      <real>-30.0</real>
+      <key>public.kern2.w</key>
+      <real>-25.0</real>
+      <key>public.kern2.x</key>
+      <real>-30.0</real>
+      <key>public.kern2.y</key>
+      <real>-35.0</real>
+      <key>public.kern2.z</key>
+      <real>-25.0</real>
+    </dict>
+    <key>public.kern1.X</key>
+    <dict>
+      <key>public.kern2.O</key>
+      <real>-15.0</real>
+      <key>public.kern2.quote</key>
+      <real>-10.0</real>
+    </dict>
+    <key>public.kern1.Y</key>
+    <dict>
+      <key>public.kern2.A</key>
+      <real>-50.0</real>
+      <key>public.kern2.O</key>
+      <real>-20.0</real>
+      <key>public.kern2.a</key>
+      <real>-60.0</real>
+      <key>public.kern2.f</key>
+      <real>-20.0</real>
+      <key>public.kern2.hyphen</key>
+      <real>-60.0</real>
+      <key>public.kern2.n</key>
+      <real>-40.0</real>
+      <key>public.kern2.o</key>
+      <real>-60.0</real>
+      <key>public.kern2.period</key>
+      <real>-60.0</real>
+      <key>public.kern2.quote</key>
+      <real>-5.0</real>
+      <key>public.kern2.s</key>
+      <real>-45.0</real>
+      <key>public.kern2.u</key>
+      <real>-40.0</real>
+    </dict>
+    <key>public.kern1.Z</key>
+    <dict>
+      <key>public.kern2.quote</key>
+      <real>-5.0</real>
+    </dict>
+    <key>public.kern1.c</key>
+    <dict>
+      <key>public.kern2.hyphen</key>
+      <real>-10.0</real>
+      <key>public.kern2.quote</key>
+      <real>20.0</real>
+    </dict>
+    <key>public.kern1.f</key>
+    <dict>
+      <key>public.kern2.hyphen</key>
+      <real>-15.0</real>
+      <key>public.kern2.o</key>
+      <real>-15.0</real>
+      <key>public.kern2.period</key>
+      <real>-40.0</real>
+      <key>public.kern2.quote</key>
+      <real>45.0</real>
+    </dict>
+    <key>public.kern1.hyphen</key>
+    <dict>
+      <key>public.kern2.A</key>
+      <real>-10.0</real>
+      <key>public.kern2.T</key>
+      <real>-50.0</real>
+      <key>public.kern2.V</key>
+      <real>-30.0</real>
+      <key>public.kern2.W</key>
+      <real>-20.0</real>
+      <key>public.kern2.Y</key>
+      <real>-60.0</real>
+      <key>public.kern2.f</key>
+      <real>-5.0</real>
+      <key>public.kern2.j</key>
+      <real>-5.0</real>
+      <key>public.kern2.period</key>
+      <real>-20.0</real>
+      <key>public.kern2.quote</key>
+      <real>-40.0</real>
+      <key>public.kern2.s</key>
+      <real>-5.0</real>
+      <key>public.kern2.w</key>
+      <real>-10.0</real>
+      <key>public.kern2.x</key>
+      <real>-25.0</real>
+      <key>public.kern2.y</key>
+      <real>-10.0</real>
+      <key>public.kern2.z</key>
+      <real>-20.0</real>
+    </dict>
+    <key>public.kern1.k</key>
+    <dict>
+      <key>public.kern2.hyphen</key>
+      <real>-5.0</real>
+    </dict>
+    <key>public.kern1.l</key>
+    <dict>
+      <key>public.kern2.hyphen</key>
+      <real>-10.0</real>
+      <key>public.kern2.period</key>
+      <real>15.0</real>
+      <key>public.kern2.quote</key>
+      <real>-15.0</real>
+    </dict>
+    <key>public.kern1.n</key>
+    <dict>
+      <key>public.kern2.quote</key>
+      <real>-10.0</real>
+      <key>public.kern2.y</key>
+      <real>-5.0</real>
+    </dict>
+    <key>public.kern1.o</key>
+    <dict>
+      <key>public.kern2.period</key>
+      <real>-10.0</real>
+      <key>public.kern2.quote</key>
+      <real>-10.0</real>
+      <key>public.kern2.w</key>
+      <real>-8.0</real>
+      <key>public.kern2.y</key>
+      <real>-8.0</real>
+    </dict>
+    <key>public.kern1.period</key>
+    <dict>
+      <key>public.kern2.T</key>
+      <real>-60.0</real>
+      <key>public.kern2.V</key>
+      <real>-70.0</real>
+      <key>public.kern2.W</key>
+      <real>-40.0</real>
+      <key>public.kern2.Y</key>
+      <real>-60.0</real>
+      <key>public.kern2.f</key>
+      <real>-20.0</real>
+      <key>public.kern2.hyphen</key>
+      <real>-20.0</real>
+      <key>public.kern2.l</key>
+      <real>-5.0</real>
+      <key>public.kern2.o</key>
+      <real>-10.0</real>
+      <key>public.kern2.quote</key>
+      <real>-60.0</real>
+      <key>public.kern2.u</key>
+      <real>-10.0</real>
+      <key>public.kern2.w</key>
+      <real>-30.0</real>
+      <key>public.kern2.x</key>
+      <real>10.0</real>
+      <key>public.kern2.y</key>
+      <real>-30.0</real>
+      <key>public.kern2.z</key>
+      <real>5.0</real>
+    </dict>
+    <key>public.kern1.quote</key>
+    <dict>
+      <key>public.kern2.A</key>
+      <real>-90.0</real>
+      <key>public.kern2.J</key>
+      <real>-110.0</real>
+      <key>public.kern2.O</key>
+      <real>-10.0</real>
+      <key>public.kern2.T</key>
+      <real>10.0</real>
+      <key>public.kern2.a</key>
+      <real>-30.0</real>
+      <key>public.kern2.f</key>
+      <real>5.0</real>
+      <key>public.kern2.hyphen</key>
+      <real>-90.0</real>
+      <key>public.kern2.n</key>
+      <real>-10.0</real>
+      <key>public.kern2.o</key>
+      <real>-25.0</real>
+      <key>public.kern2.period</key>
+      <real>-80.0</real>
+      <key>public.kern2.s</key>
+      <real>-15.0</real>
+      <key>public.kern2.u</key>
+      <real>-5.0</real>
+      <key>public.kern2.z</key>
+      <real>-5.0</real>
+    </dict>
+    <key>public.kern1.r</key>
+    <dict>
+      <key>public.kern2.hyphen</key>
+      <real>-5.0</real>
+      <key>public.kern2.period</key>
+      <real>-35.0</real>
+      <key>public.kern2.quote</key>
+      <real>20.0</real>
+    </dict>
+    <key>public.kern1.s</key>
+    <dict>
+      <key>public.kern2.hyphen</key>
+      <real>-10.0</real>
+    </dict>
+    <key>public.kern1.t</key>
+    <dict>
+      <key>public.kern2.hyphen</key>
+      <real>-15.0</real>
+      <key>public.kern2.o</key>
+      <real>-15.0</real>
+      <key>public.kern2.period</key>
+      <real>-40.0</real>
+      <key>public.kern2.quote</key>
+      <real>20.0</real>
+    </dict>
+    <key>public.kern1.u</key>
+    <dict>
+      <key>public.kern2.quote</key>
+      <real>15.0</real>
+    </dict>
+    <key>public.kern1.w</key>
+    <dict>
+      <key>public.kern2.hyphen</key>
+      <real>-10.0</real>
+      <key>public.kern2.o</key>
+      <real>-8.0</real>
+      <key>public.kern2.period</key>
+      <real>-30.0</real>
+      <key>public.kern2.quote</key>
+      <real>15.0</real>
+    </dict>
+    <key>public.kern1.x</key>
+    <dict>
+      <key>public.kern2.hyphen</key>
+      <real>-25.0</real>
+      <key>public.kern2.period</key>
+      <real>10.0</real>
+      <key>public.kern2.quote</key>
+      <real>15.0</real>
+    </dict>
+    <key>public.kern1.y</key>
+    <dict>
+      <key>public.kern2.hyphen</key>
+      <real>-10.0</real>
+      <key>public.kern2.o</key>
+      <real>-8.0</real>
+      <key>public.kern2.period</key>
+      <real>-30.0</real>
+      <key>public.kern2.quote</key>
+      <real>15.0</real>
+    </dict>
+    <key>public.kern1.z</key>
+    <dict>
+      <key>public.kern2.hyphen</key>
+      <real>-10.0</real>
+      <key>public.kern2.period</key>
+      <real>5.0</real>
+      <key>public.kern2.quote</key>
+      <real>20.0</real>
+    </dict>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/layercontents.plist b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/layercontents.plist
new file mode 100644
index 0000000..7120d0b
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/layercontents.plist
@@ -0,0 +1,14 @@
+<?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>
+      <string>public.background</string>
+      <string>glyphs.public.background</string>
+    </array>
+  </array>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/lib.plist b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/lib.plist
new file mode 100644
index 0000000..7e5ced4
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/lib.plist
@@ -0,0 +1,80 @@
+<?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>com.schriftgestaltung.appVersion</key>
+    <string>1179</string>
+    <key>com.schriftgestaltung.customParameter.GSFont.Axes</key>
+    <array>
+      <dict>
+        <key>Name</key>
+        <string>Slant</string>
+        <key>Tag</key>
+        <string>slnt</string>
+      </dict>
+    </array>
+    <key>com.schriftgestaltung.customParameter.GSFont.DisplayStrings</key>
+    <array>
+      <string>o/dieresiscomb ö</string>
+    </array>
+    <key>com.schriftgestaltung.customParameter.GSFont.Variation Font Origin</key>
+    <string>EB3D7718-A203-47FB-ABD4-8B7A501887ED</string>
+    <key>com.schriftgestaltung.customParameter.GSFont.disablesAutomaticAlignment</key>
+    <false/>
+    <key>com.schriftgestaltung.customParameter.GSFont.useNiceNames</key>
+    <integer>1</integer>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.Master Name</key>
+    <string>Regular</string>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.customValue</key>
+    <real>0.0</real>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.customValue1</key>
+    <real>16.0</real>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.customValue2</key>
+    <real>0.0</real>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.customValue3</key>
+    <real>0.0</real>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.iconName</key>
+    <string></string>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.weightValue</key>
+    <real>0.0</real>
+    <key>com.schriftgestaltung.customParameter.GSFontMaster.widthValue</key>
+    <real>100.0</real>
+    <key>com.schriftgestaltung.customValue1</key>
+    <real>16.0</real>
+    <key>com.schriftgestaltung.fontMasterOrder</key>
+    <integer>0</integer>
+    <key>com.schriftgestaltung.glyphOrder</key>
+    <false/>
+    <key>com.schriftgestaltung.keyboardIncrement</key>
+    <integer>1</integer>
+    <key>com.schriftgestaltung.weight</key>
+    <string>Regular</string>
+    <key>com.schriftgestaltung.weightValue</key>
+    <real>0.0</real>
+    <key>com.schriftgestaltung.width</key>
+    <string>Regular</string>
+    <key>com.schriftgestaltung.widthValue</key>
+    <real>100.0</real>
+    <key>noodleExtremesAndInflections</key>
+    <integer>0</integer>
+    <key>noodleRemoveOverlap</key>
+    <integer>1</integer>
+    <key>noodleThickness</key>
+    <string>50.0</string>
+    <key>public.glyphOrder</key>
+    <array>
+      <string>N</string>
+      <string>O</string>
+      <string>Odieresis</string>
+      <string>n</string>
+      <string>o</string>
+      <string>odieresis</string>
+      <string>dieresiscomb</string>
+    </array>
+    <key>public.postscriptNames</key>
+    <dict>
+      <key>dieresiscomb</key>
+      <string>uni0308</string>
+    </dict>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/metainfo.plist b/Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/metainfo.plist
new file mode 100644
index 0000000..7b8b34a
--- /dev/null
+++ b/Tests/varLib/data/master_ufo/TestFamily4-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>com.github.fonttools.ufoLib</string>
+    <key>formatVersion</key>
+    <integer>3</integer>
+  </dict>
+</plist>
diff --git a/Tests/varLib/data/master_vpal_test/master_vpal_test_0.ttx b/Tests/varLib/data/master_vpal_test/master_vpal_test_0.ttx
new file mode 100644
index 0000000..cd454b8
--- /dev/null
+++ b/Tests/varLib/data/master_vpal_test/master_vpal_test_0.ttx
@@ -0,0 +1,485 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.42">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uni3001"/>
+    <GlyphID id="2" name="uni30FB"/>
+    <GlyphID id="3" name="uniFF1A"/>
+    <GlyphID id="4" name="uniFF2D"/>
+    <GlyphID id="5" name="uni3073"/>
+    <GlyphID id="6" name="uni3074"/>
+    <GlyphID id="7" name="uni307B"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.004"/>
+    <checkSumAdjustment value="0x90c6f91d"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun  5 14:40:00 2019"/>
+    <modified value="Tue Jun 11 21:27:36 2019"/>
+    <xMin value="64"/>
+    <yMin value="-52"/>
+    <xMax value="974"/>
+    <yMax value="798"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="64"/>
+    <minRightSideBearing value="26"/>
+    <xMaxExtent value="974"/>
+    <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="8"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="962"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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 00000111 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00010000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="12289"/>
+    <usLastCharIndex value="65325"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_KanjiFullEX
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.004;UKWN;MasterSet_KanjiFullEX-w0.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_KanjiFullEX
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.004;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_KanjiFullEX-w0.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x3001" name="uni3001"/><!-- IDEOGRAPHIC COMMA -->
+      <map code="0x3073" name="uni3073"/><!-- HIRAGANA LETTER BI -->
+      <map code="0x3074" name="uni3074"/><!-- HIRAGANA LETTER PI -->
+      <map code="0x307b" name="uni307B"/><!-- HIRAGANA LETTER HO -->
+      <map code="0x30fb" name="uni30FB"/><!-- KATAKANA MIDDLE DOT -->
+      <map code="0xff1a" name="uniFF1A"/><!-- FULLWIDTH COLON -->
+      <map code="0xff2d" name="uniFF2D"/><!-- FULLWIDTH LATIN CAPITAL LETTER M -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="88" language="0" nGroups="6">
+      <map code="0x3001" name="uni3001"/><!-- IDEOGRAPHIC COMMA -->
+      <map code="0x3073" name="uni3073"/><!-- HIRAGANA LETTER BI -->
+      <map code="0x3074" name="uni3074"/><!-- HIRAGANA LETTER PI -->
+      <map code="0x307b" name="uni307B"/><!-- HIRAGANA LETTER HO -->
+      <map code="0x30fb" name="uni30FB"/><!-- KATAKANA MIDDLE DOT -->
+      <map code="0xff1a" name="uniFF1A"/><!-- FULLWIDTH COLON -->
+      <map code="0xff2d" name="uniFF2D"/><!-- FULLWIDTH LATIN CAPITAL LETTER M -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x3001" name="uni3001"/><!-- IDEOGRAPHIC COMMA -->
+      <map code="0x3073" name="uni3073"/><!-- HIRAGANA LETTER BI -->
+      <map code="0x3074" name="uni3074"/><!-- HIRAGANA LETTER PI -->
+      <map code="0x307b" name="uni307B"/><!-- HIRAGANA LETTER HO -->
+      <map code="0x30fb" name="uni30FB"/><!-- KATAKANA MIDDLE DOT -->
+      <map code="0xff1a" name="uniFF1A"/><!-- FULLWIDTH COLON -->
+      <map code="0xff2d" name="uniFF2D"/><!-- FULLWIDTH LATIN CAPITAL LETTER M -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="88" language="0" nGroups="6">
+      <map code="0x3001" name="uni3001"/><!-- IDEOGRAPHIC COMMA -->
+      <map code="0x3073" name="uni3073"/><!-- HIRAGANA LETTER BI -->
+      <map code="0x3074" name="uni3074"/><!-- HIRAGANA LETTER PI -->
+      <map code="0x307b" name="uni307B"/><!-- HIRAGANA LETTER HO -->
+      <map code="0x30fb" name="uni30FB"/><!-- KATAKANA MIDDLE DOT -->
+      <map code="0xff1a" name="uniFF1A"/><!-- FULLWIDTH COLON -->
+      <map code="0xff2d" name="uniFF2D"/><!-- FULLWIDTH LATIN CAPITAL LETTER M -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_KanjiFullEX-w0.00">
+      <FamilyName value="MasterSet_KanjiFullEX"/>
+      <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="64 -52 974 798"/>
+      <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="1000"/>
+        <nominalWidthX value="532"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -282 endchar
+        </CharString>
+        <CharString name="uni3001">
+          291 -43 rmoveto
+          29 24 rlineto
+          -74 84 -84 83 -73 58 rrcurveto
+          -25 -24 rlineto
+          72 -56 87 -85 68 -84 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni3073">
+          706 699 rmoveto
+          -32 -10 rlineto
+          50 -192 56 -132 112 -115 rrcurveto
+          24 26 rlineto
+          -143 124 -45 182 -22 117 rrcurveto
+          -592 -57 rmoveto
+          3 -38 rlineto
+          18 2 18 3 17 2 rrcurveto
+          43 6 101 12 63 13 rrcurveto
+          -83 -80 -128 -166 0 -210 rrcurveto
+          0 -150 92 -88 154 0 rrcurveto
+          294 0 65 313 -48 302 rrcurveto
+          -29 79 rlineto
+          64 -402 -96 -263 -249 0 rrcurveto
+          -109 0 -110 48 0 166 rrcurveto
+          0 219 171 196 58 42 rrcurveto
+          14 5 30 6 12 3 rrcurveto
+          -9 30 rlineto
+          -52 -21 -170 -26 -83 -3 rrcurveto
+          -18 -1 -21 0 -12 1 rrcurveto
+          677 114 rmoveto
+          -26 -11 rlineto
+          22 -34 33 -63 20 -42 rrcurveto
+          30 14 rlineto
+          -21 41 -37 62 -21 33 rrcurveto
+          97 40 rmoveto
+          -26 -13 rlineto
+          24 -33 31 -59 21 -44 rrcurveto
+          30 14 rlineto
+          -23 43 -36 60 -21 32 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni3074">
+          706 699 rmoveto
+          -32 -10 rlineto
+          50 -192 56 -132 112 -115 rrcurveto
+          24 26 rlineto
+          -143 124 -45 182 -22 117 rrcurveto
+          -592 -57 rmoveto
+          3 -38 rlineto
+          18 2 18 3 17 2 rrcurveto
+          43 6 101 12 63 13 rrcurveto
+          -83 -80 -128 -166 0 -210 rrcurveto
+          0 -150 92 -88 154 0 rrcurveto
+          294 0 65 313 -48 302 rrcurveto
+          -29 79 rlineto
+          64 -402 -96 -263 -249 0 rrcurveto
+          -109 0 -110 48 0 166 rrcurveto
+          0 219 171 196 58 42 rrcurveto
+          14 5 30 6 12 3 rrcurveto
+          -9 30 rlineto
+          -52 -21 -170 -26 -83 -3 rrcurveto
+          -18 -1 -21 0 -12 1 rrcurveto
+          681 51 rmoveto
+          0 42 33 33 41 0 rrcurveto
+          41 0 33 -33 0 -42 rrcurveto
+          0 -41 -33 -33 -41 0 rrcurveto
+          -42 0 -32 33 0 41 rrcurveto
+          -30 0 rmoveto
+          0 -57 46 -47 58 0 rrcurveto
+          57 0 48 47 0 57 rrcurveto
+          0 58 -48 47 -57 0 rrcurveto
+          -58 0 -46 -47 0 -58 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni307B">
+          703 676 rmoveto
+          -28 0 rlineto
+          0 -63 0 -106 0 -61 rrcurveto
+          0 -103 14 -162 0 -52 rrcurveto
+          0 -59 -17 -50 -97 0 rrcurveto
+          -91 0 -58 31 0 63 rrcurveto
+          0 53 62 37 87 0 rrcurveto
+          125 0 119 -56 94 -98 rrcurveto
+          19 31 rlineto
+          -90 81 -113 69 -154 0 rrcurveto
+          -130 0 -47 -64 0 -53 rrcurveto
+          0 -80 70 -42 105 0 rrcurveto
+          85 0 58 36 0 74 rrcurveto
+          0 68 -13 166 0 113 rrcurveto
+          0 61 0 96 0 70 rrcurveto
+          -284 -213 rmoveto
+          0 -31 rlineto
+          168 -10 194 12 125 13 rrcurveto
+          0 31 rlineto
+          -129 -15 -187 -13 -171 13 rrcurveto
+          16 230 rmoveto
+          0 -30 rlineto
+          139 -7 200 11 108 11 rrcurveto
+          0 30 rlineto
+          -111 -15 -197 -11 -139 11 rrcurveto
+          -207 51 rmoveto
+          -37 4 rlineto
+          0 -11 -1 -14 -3 -19 rrcurveto
+          -13 -88 -37 -183 0 -143 rrcurveto
+          0 -135 16 -105 20 -74 rrcurveto
+          28 3 rlineto
+          -1 7 -2 12 -1 9 rrcurveto
+          -1 13 2 16 3 14 rrcurveto
+          9 43 39 105 22 55 rrcurveto
+          -20 17 rlineto
+          -20 -48 -32 -89 -18 -56 rrcurveto
+          -10 78 -5 58 0 78 rrcurveto
+          0 124 26 166 25 119 rrcurveto
+          3 18 4 13 4 13 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni30FB">
+          500 465 rmoveto
+          -47 0 -38 -38 0 -47 rrcurveto
+          0 -47 38 -38 47 0 rrcurveto
+          47 0 38 38 0 47 rrcurveto
+          0 47 -38 38 -47 0 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uniFF1A">
+          500 572 rmoveto
+          28 0 28 19 0 37 rrcurveto
+          0 37 -28 19 -28 0 rrcurveto
+          -28 0 -28 -19 0 -37 rrcurveto
+          0 -37 28 -19 28 0 rrcurveto
+          0 -502 rmoveto
+          28 0 28 19 0 37 rrcurveto
+          0 37 -28 19 -28 0 rrcurveto
+          -28 0 -28 -19 0 -37 rrcurveto
+          0 -37 28 -19 28 0 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uniFF2D">
+          180 0 rmoveto
+          35 0 rlineto
+          0 502 rlineto
+          0 57 -1 74 -2 59 rrcurveto
+          5 0 rlineto
+          67 -160 rlineto
+          196 -451 rlineto
+          39 0 rlineto
+          195 451 rlineto
+          69 160 rlineto
+          5 0 rlineto
+          -2 -59 -3 -74 0 -57 rrcurveto
+          0 -502 rlineto
+          37 0 rlineto
+          0 726 rlineto
+          -59 0 rlineto
+          -191 -438 rlineto
+          -23 -54 -20 -55 -24 -55 rrcurveto
+          -4 0 rlineto
+          -24 55 -23 55 -23 54 rrcurveto
+          -190 438 rlineto
+          -59 0 rlineto
+          0 -726 rlineto
+          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="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="vpal"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=5 -->
+        <SinglePos index="0" Format="2">
+          <Coverage>
+            <Glyph value="uni3001"/>
+            <Glyph value="uniFF1A"/>
+          </Coverage>
+          <ValueFormat value="5"/>
+          <!-- ValueCount=2 -->
+          <Value index="0" XPlacement="-9" XAdvance="-500"/>
+          <Value index="1" XPlacement="-250" XAdvance="-500"/>
+        </SinglePos>
+        <SinglePos index="1" Format="2">
+          <Coverage>
+            <Glyph value="uni30FB"/>
+          </Coverage>
+          <ValueFormat value="7"/>
+          <!-- ValueCount=1 -->
+          <Value index="0" XPlacement="-250" YPlacement="1" XAdvance="-500"/>
+        </SinglePos>
+        <SinglePos index="2" Format="2">
+          <Coverage>
+            <Glyph value="uni3073"/>
+            <Glyph value="uni3074"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <!-- ValueCount=2 -->
+          <Value index="0" XAdvance="-36"/>
+          <Value index="1" XAdvance="-30"/>
+        </SinglePos>
+        <SinglePos index="3" Format="2">
+          <Coverage>
+            <Glyph value="uni307B"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <!-- ValueCount=1 -->
+          <Value index="0" XPlacement="11"/>
+        </SinglePos>
+        <SinglePos index="4" Format="2">
+          <Coverage>
+            <Glyph value="uniFF2D"/>
+          </Coverage>
+          <ValueFormat value="5"/>
+          <!-- ValueCount=1 -->
+          <Value index="0" XPlacement="12" XAdvance="23"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <hmtx>
+    <mtx name=".notdef" width="250" lsb="0"/>
+    <mtx name="uni3001" width="1000" lsb="64"/>
+    <mtx name="uni3073" width="1000" lsb="114"/>
+    <mtx name="uni3074" width="1000" lsb="114"/>
+    <mtx name="uni307B" width="1000" lsb="137"/>
+    <mtx name="uni30FB" width="1000" lsb="415"/>
+    <mtx name="uniFF1A" width="1000" lsb="444"/>
+    <mtx name="uniFF2D" width="1000" lsb="180"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_vpal_test/master_vpal_test_1.ttx b/Tests/varLib/data/master_vpal_test/master_vpal_test_1.ttx
new file mode 100644
index 0000000..e1b77c8
--- /dev/null
+++ b/Tests/varLib/data/master_vpal_test/master_vpal_test_1.ttx
@@ -0,0 +1,473 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.42">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uni3001"/>
+    <GlyphID id="2" name="uni30FB"/>
+    <GlyphID id="3" name="uniFF1A"/>
+    <GlyphID id="4" name="uniFF2D"/>
+    <GlyphID id="5" name="uni3073"/>
+    <GlyphID id="6" name="uni3074"/>
+    <GlyphID id="7" name="uni307B"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.004"/>
+    <checkSumAdjustment value="0xfa2dc49e"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Jun  5 14:40:08 2019"/>
+    <modified value="Tue Jun 11 21:27:55 2019"/>
+    <xMin value="33"/>
+    <yMin value="-87"/>
+    <xMax value="992"/>
+    <yMax value="855"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="33"/>
+    <minRightSideBearing value="8"/>
+    <xMaxExtent value="992"/>
+    <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="8"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="971"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="10"/>
+      <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 00000111 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00010000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 00000000"/>
+    <usFirstCharIndex value="12289"/>
+    <usLastCharIndex value="65325"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00100000 00000010 00000000 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_KanjiFullEX
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.004;UKWN;MasterSet_KanjiFullEX-w1000.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_KanjiFullEX
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.004;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      MasterSet_KanjiFullEX-w1000.00
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x3001" name="uni3001"/><!-- IDEOGRAPHIC COMMA -->
+      <map code="0x3073" name="uni3073"/><!-- HIRAGANA LETTER BI -->
+      <map code="0x3074" name="uni3074"/><!-- HIRAGANA LETTER PI -->
+      <map code="0x307b" name="uni307B"/><!-- HIRAGANA LETTER HO -->
+      <map code="0x30fb" name="uni30FB"/><!-- KATAKANA MIDDLE DOT -->
+      <map code="0xff1a" name="uniFF1A"/><!-- FULLWIDTH COLON -->
+      <map code="0xff2d" name="uniFF2D"/><!-- FULLWIDTH LATIN CAPITAL LETTER M -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="88" language="0" nGroups="6">
+      <map code="0x3001" name="uni3001"/><!-- IDEOGRAPHIC COMMA -->
+      <map code="0x3073" name="uni3073"/><!-- HIRAGANA LETTER BI -->
+      <map code="0x3074" name="uni3074"/><!-- HIRAGANA LETTER PI -->
+      <map code="0x307b" name="uni307B"/><!-- HIRAGANA LETTER HO -->
+      <map code="0x30fb" name="uni30FB"/><!-- KATAKANA MIDDLE DOT -->
+      <map code="0xff1a" name="uniFF1A"/><!-- FULLWIDTH COLON -->
+      <map code="0xff2d" name="uniFF2D"/><!-- FULLWIDTH LATIN CAPITAL LETTER M -->
+    </cmap_format_12>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x3001" name="uni3001"/><!-- IDEOGRAPHIC COMMA -->
+      <map code="0x3073" name="uni3073"/><!-- HIRAGANA LETTER BI -->
+      <map code="0x3074" name="uni3074"/><!-- HIRAGANA LETTER PI -->
+      <map code="0x307b" name="uni307B"/><!-- HIRAGANA LETTER HO -->
+      <map code="0x30fb" name="uni30FB"/><!-- KATAKANA MIDDLE DOT -->
+      <map code="0xff1a" name="uniFF1A"/><!-- FULLWIDTH COLON -->
+      <map code="0xff2d" name="uniFF2D"/><!-- FULLWIDTH LATIN CAPITAL LETTER M -->
+    </cmap_format_4>
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="88" language="0" nGroups="6">
+      <map code="0x3001" name="uni3001"/><!-- IDEOGRAPHIC COMMA -->
+      <map code="0x3073" name="uni3073"/><!-- HIRAGANA LETTER BI -->
+      <map code="0x3074" name="uni3074"/><!-- HIRAGANA LETTER PI -->
+      <map code="0x307b" name="uni307B"/><!-- HIRAGANA LETTER HO -->
+      <map code="0x30fb" name="uni30FB"/><!-- KATAKANA MIDDLE DOT -->
+      <map code="0xff1a" name="uniFF1A"/><!-- FULLWIDTH COLON -->
+      <map code="0xff2d" name="uniFF2D"/><!-- FULLWIDTH LATIN CAPITAL LETTER M -->
+    </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>
+    <major value="1"/>
+    <minor value="0"/>
+    <CFFFont name="MasterSet_KanjiFullEX-w1000.00">
+      <FamilyName value="MasterSet_KanjiFullEX"/>
+      <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="33 -87 992 855"/>
+      <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="1000"/>
+        <nominalWidthX value="705"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -455 endchar
+        </CharString>
+        <CharString name="uni3001">
+          245 -76 rmoveto
+          129 111 rlineto
+          -44 56 -100 103 -70 58 rrcurveto
+          -127 -109 rlineto
+          69 -61 84 -86 59 -72 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni3073">
+          729 763 rmoveto
+          -152 -42 rlineto
+          75 -225 86 -176 109 -123 rrcurveto
+          97 140 rlineto
+          -155 146 -41 152 -19 128 rrcurveto
+          -677 -48 rmoveto
+          11 -156 rlineto
+          24 5 15 2 22 3 rrcurveto
+          26 3 53 6 31 1 rrcurveto
+          -96 -130 -50 -109 0 -149 rrcurveto
+          0 -190 149 -88 163 0 rrcurveto
+          344 0 43 303 -58 260 rrcurveto
+          -142 223 rlineto
+          96 -376 -59 -246 -221 0 rrcurveto
+          -92 0 -67 46 0 103 rrcurveto
+          0 184 125 143 71 54 rrcurveto
+          16 10 18 8 15 6 rrcurveto
+          -46 134 rlineto
+          -69 -25 -166 -20 -98 -5 rrcurveto
+          -19 -1 -20 0 -19 1 rrcurveto
+          768 103 rmoveto
+          -88 -27 rlineto
+          22 -45 16 -54 15 -49 rrcurveto
+          87 28 rlineto
+          -11 41 -23 62 -18 44 rrcurveto
+          110 37 rmoveto
+          -85 -28 rlineto
+          21 -44 19 -57 14 -46 rrcurveto
+          88 28 rlineto
+          -13 40 -22 63 -22 44 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni3074">
+          729 763 rmoveto
+          -152 -42 rlineto
+          75 -225 86 -176 109 -123 rrcurveto
+          97 140 rlineto
+          -155 146 -41 152 -19 128 rrcurveto
+          -677 -48 rmoveto
+          11 -156 rlineto
+          24 5 15 2 22 3 rrcurveto
+          26 3 53 6 31 1 rrcurveto
+          -96 -130 -50 -109 0 -149 rrcurveto
+          0 -190 149 -88 163 0 rrcurveto
+          344 0 43 303 -58 260 rrcurveto
+          -142 223 rlineto
+          96 -376 -59 -246 -221 0 rrcurveto
+          -92 0 -67 46 0 103 rrcurveto
+          0 184 125 143 71 54 rrcurveto
+          16 10 18 8 15 6 rrcurveto
+          -46 134 rlineto
+          -69 -25 -166 -20 -98 -5 rrcurveto
+          -19 -1 -20 0 -19 1 rrcurveto
+          772 16 rmoveto
+          0 27 22 22 27 0 rrcurveto
+          27 0 22 -22 0 -27 rrcurveto
+          0 -27 -22 -22 -27 0 rrcurveto
+          -27 0 -22 22 0 27 rrcurveto
+          -70 0 rmoveto
+          0 -66 53 -53 66 0 rrcurveto
+          66 0 53 53 0 66 rrcurveto
+          0 66 -53 53 -66 0 rrcurveto
+          -66 0 -53 -53 0 -66 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni307B">
+          758 687 rmoveto
+          -141 0 rlineto
+          0 -42 0 -111 0 -88 rrcurveto
+          0 -104 8 -126 0 -63 rrcurveto
+          0 -45 -23 -24 -49 0 rrcurveto
+          -37 0 -24 16 0 29 rrcurveto
+          0 28 23 18 48 0 rrcurveto
+          107 0 121 -81 97 -98 rrcurveto
+          79 131 rlineto
+          -51 45 -135 115 -213 0 rrcurveto
+          -137 0 -76 -72 0 -97 rrcurveto
+          0 -116 100 -52 119 0 rrcurveto
+          142 0 55 65 0 93 rrcurveto
+          0 97 -13 99 0 147 rrcurveto
+          0 64 0 96 0 76 rrcurveto
+          -360 -167 rmoveto
+          1 -139 rlineto
+          195 -9 191 7 133 16 rrcurveto
+          0 139 rlineto
+          -148 -18 -178 -11 -194 15 rrcurveto
+          15 235 rmoveto
+          0 -134 rlineto
+          197 -9 162 8 126 14 rrcurveto
+          0 134 rlineto
+          -127 -19 -163 -9 -195 15 rrcurveto
+          -117 26 rmoveto
+          -168 14 rlineto
+          -1 -37 -6 -47 -4 -29 rrcurveto
+          -11 -74 -26 -192 0 -153 rrcurveto
+          0 -136 20 -117 21 -68 rrcurveto
+          139 10 rlineto
+          -1 16 0 18 0 11 rrcurveto
+          0 10 3 23 3 14 rrcurveto
+          12 58 30 101 30 90 rrcurveto
+          -73 59 rlineto
+          -13 -30 -14 -23 -12 -29 rrcurveto
+          -1 4 0 20 0 3 rrcurveto
+          0 93 33 237 12 52 rrcurveto
+          4 18 14 62 9 22 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uni30FB">
+          500 520 rmoveto
+          -77 0 -63 -63 0 -77 rrcurveto
+          0 -77 63 -63 77 0 rrcurveto
+          77 0 63 63 0 77 rrcurveto
+          0 77 -63 63 -77 0 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uniFF1A">
+          500 500 rmoveto
+          60 0 46 46 0 58 rrcurveto
+          0 60 -46 46 -60 0 rrcurveto
+          -60 0 -46 -46 0 -60 rrcurveto
+          0 -58 46 -46 60 0 rrcurveto
+          0 -470 rmoveto
+          60 0 46 46 0 58 rrcurveto
+          0 60 -46 46 -60 0 rrcurveto
+          -60 0 -46 -46 0 -60 rrcurveto
+          0 -58 46 -46 60 0 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uniFF2D">
+          113 0 rmoveto
+          160 0 rlineto
+          0 255 rlineto
+          0 75 -12 113 -9 74 rrcurveto
+          4 0 rlineto
+          72 -182 rlineto
+          121 -272 rlineto
+          98 0 rlineto
+          120 272 rlineto
+          73 182 rlineto
+          5 0 rlineto
+          -10 -74 -12 -113 0 -75 rrcurveto
+          0 -255 rlineto
+          163 0 rlineto
+          0 748 rlineto
+          -194 0 rlineto
+          -135 -325 rlineto
+          -17 -43 -18 -47 -18 -46 rrcurveto
+          -5 0 rlineto
+          -16 46 -19 47 -16 43 rrcurveto
+          -142 325 rlineto
+          -193 0 rlineto
+          0 -748 rlineto
+          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="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="vpal"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=3 -->
+        <SinglePos index="0" Format="2">
+          <Coverage>
+            <Glyph value="uni3001"/>
+            <Glyph value="uni30FB"/>
+            <Glyph value="uniFF1A"/>
+          </Coverage>
+          <ValueFormat value="5"/>
+          <!-- ValueCount=3 -->
+          <Value index="0" XPlacement="-3" XAdvance="-500"/>
+          <Value index="1" XPlacement="-250" XAdvance="-500"/>
+          <Value index="2" XPlacement="-250" XAdvance="-500"/>
+        </SinglePos>
+        <SinglePos index="1" Format="2">
+          <Coverage>
+            <Glyph value="uni3073"/>
+            <Glyph value="uni3074"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <!-- ValueCount=2 -->
+          <Value index="0" XAdvance="-35"/>
+          <Value index="1" XAdvance="-30"/>
+        </SinglePos>
+        <SinglePos index="2" Format="2">
+          <Coverage>
+            <Glyph value="uniFF2D"/>
+            <Glyph value="uni307B"/>
+          </Coverage>
+          <ValueFormat value="1"/>
+          <!-- ValueCount=2 -->
+          <Value index="0" XPlacement="1"/>
+          <Value index="1" XPlacement="10"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <hmtx>
+    <mtx name=".notdef" width="250" lsb="0"/>
+    <mtx name="uni3001" width="1000" lsb="33"/>
+    <mtx name="uni3073" width="1000" lsb="52"/>
+    <mtx name="uni3074" width="1000" lsb="52"/>
+    <mtx name="uni307B" width="1000" lsb="80"/>
+    <mtx name="uni30FB" width="1000" lsb="360"/>
+    <mtx name="uniFF1A" width="1000" lsb="394"/>
+    <mtx name="uniFF2D" width="1000" lsb="113"/>
+  </hmtx>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_vvar_cff2/TestVVAR.0.ttx b/Tests/varLib/data/master_vvar_cff2/TestVVAR.0.ttx
new file mode 100644
index 0000000..d956d8c
--- /dev/null
+++ b/Tests/varLib/data/master_vvar_cff2/TestVVAR.0.ttx
@@ -0,0 +1,821 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uniFF20"/>
+    <GlyphID id="2" name="uniFF21"/>
+    <GlyphID id="3" name="uniFF41"/>
+    <GlyphID id="4" name="uni3042"/>
+    <GlyphID id="5" name="uni6280"/>
+    <GlyphID id="6" name="uni56FD"/>
+    <GlyphID id="7" name="c30719"/>
+    <GlyphID id="8" name="c30790"/>
+    <GlyphID id="9" name="c30816"/>
+    <GlyphID id="10" name="c30843"/>
+    <GlyphID id="11" name="c31621"/>
+    <GlyphID id="12" name="c32051"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.00299"/>
+    <checkSumAdjustment value="0xaa083de0"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Feb 19 17:21:59 2019"/>
+    <modified value="Tue Feb 19 17:21:59 2019"/>
+    <xMin value="19"/>
+    <yMin value="-83"/>
+    <xMax value="966"/>
+    <yMax value="840"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="19"/>
+    <minRightSideBearing value="23"/>
+    <xMaxExtent value="966"/>
+    <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>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="13"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="822"/>
+    <usWeightClass value="250"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="3"/>
+      <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="12354"/>
+    <usLastCharIndex value="65345"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Han Sans VVAR TEST
+    </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.003;UKWN;SHSansVVARTest_75-w0.00
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Han Sans VVAR TEST
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.003;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansVVARTest_75-w0.00
+    </namerecord>
+    <namerecord nameID="8" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Adobe Systems Co., Ltd.
+    </namerecord>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ryoko Nishizuka
+    </namerecord>
+    <namerecord nameID="10" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Masataka HATTORI (variable font production)
+    </namerecord>
+    <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      http://www.adobe.com
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Master3
+    </namerecord>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Han Sans VVAR TEST
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.003;UKWN;SHSansVVARTest_75-w0.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Han Sans VVAR TEST
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.003;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SHSansVVARTest_75-w0.00
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Adobe Systems Co., Ltd.
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Ryoko Nishizuka
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Masataka HATTORI 服部正貴 (variable font production)
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      http://www.adobe.com
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master3
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x3042" name="uni3042"/><!-- HIRAGANA LETTER A -->
+      <map code="0x56fd" name="uni56FD"/><!-- CJK UNIFIED IDEOGRAPH-56FD -->
+      <map code="0x6280" name="uni6280"/><!-- CJK UNIFIED IDEOGRAPH-6280 -->
+      <map code="0xff20" name="uniFF20"/><!-- FULLWIDTH COMMERCIAL AT -->
+      <map code="0xff21" name="uniFF21"/><!-- FULLWIDTH LATIN CAPITAL LETTER A -->
+      <map code="0xff41" name="uniFF41"/><!-- FULLWIDTH LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x3042" name="uni3042"/><!-- HIRAGANA LETTER A -->
+      <map code="0x56fd" name="uni56FD"/><!-- CJK UNIFIED IDEOGRAPH-56FD -->
+      <map code="0x6280" name="uni6280"/><!-- CJK UNIFIED IDEOGRAPH-6280 -->
+      <map code="0xff20" name="uniFF20"/><!-- FULLWIDTH COMMERCIAL AT -->
+      <map code="0xff21" name="uniFF21"/><!-- FULLWIDTH LATIN CAPITAL LETTER A -->
+      <map code="0xff41" name="uniFF41"/><!-- FULLWIDTH LATIN SMALL LETTER A -->
+    </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="SHSansVVARTest_75-w0.00">
+      <version value="1.0"/>
+      <FamilyName value="SHSansVVARTest_75"/>
+      <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="19 -83 966 840"/>
+      <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="1000"/>
+        <nominalWidthX value="857"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -669 endchar
+        </CharString>
+        <CharString name="c30719">
+          667 26 rmoveto
+          -12 22 rlineto
+          -47 -20 -60 -12 -73 0 rrcurveto
+          -168 0 -128 95 0 142 rrcurveto
+          0 170 146 130 211 0 rrcurveto
+          180 0 104 -93 0 -133 rrcurveto
+          0 -100 -81 -70 -60 0 rrcurveto
+          -50 0 -17 22 22 78 rrcurveto
+          49 166 rlineto
+          -31 9 -32 8 -47 0 rrcurveto
+          -150 0 -117 -98 0 -105 rrcurveto
+          0 -65 58 -38 58 0 rrcurveto
+          74 0 53 25 44 55 rrcurveto
+          4 0 rlineto
+          -9 -60 39 -20 43 0 rrcurveto
+          90 0 96 72 0 128 rrcurveto
+          0 140 -120 103 -193 0 rrcurveto
+          -233 0 -168 -141 0 -184 rrcurveto
+          0 -160 149 -102 182 0 rrcurveto
+          70 0 67 11 57 25 rrcurveto
+          -23 379 rmoveto
+          -32 -109 rlineto
+          -20 -69 -71 -69 -88 0 rrcurveto
+          -62 0 -31 34 0 42 rrcurveto
+          0 95 102 86 124 0 rrcurveto
+          38 0 25 -5 15 -5 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="c30790">
+          768 0 rmoveto
+          37 0 rlineto
+          -297 546 rlineto
+          -35 0 rlineto
+          -296 -546 rlineto
+          34 0 rlineto
+          106 190 rlineto
+          346 0 rlineto
+          105 -190 rlineto
+          -439 217 rmoveto
+          62 110 rlineto
+          34 65 32 55 32 67 rrcurveto
+          4 0 rlineto
+          34 -67 30 -54 35 -66 rrcurveto
+          61 -110 rlineto
+          -324 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="c30816">
+          276 98 rmoveto
+          0 -74 92 -33 92 0 rrcurveto
+          80 0 83 33 62 36 rrcurveto
+          2 0 rlineto
+          4 -60 rlineto
+          29 0 rlineto
+          0 255 rlineto
+          0 83 -54 68 -140 0 rrcurveto
+          -94 0 -93 -35 -37 -24 rrcurveto
+          18 -25 rlineto
+          40 23 81 32 83 0 rrcurveto
+          128 0 34 -62 1 -67 rrcurveto
+          -290 -19 -121 -42 0 -89 rrcurveto
+          35 2 rmoveto
+          0 65 93 39 283 19 rrcurveto
+          0 -131 rlineto
+          -79 -48 -75 -25 -70 0 rrcurveto
+          -74 0 -78 23 0 58 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="c30843">
+          411 586 rmoveto
+          0 -5 -1 -11 -1 -12 rrcurveto
+          -7 -56 -23 -131 0 -107 rrcurveto
+          0 -83 26 -102 28 -67 rrcurveto
+          28 10 rlineto
+          -29 66 -27 95 0 79 rrcurveto
+          0 111 18 128 14 56 rrcurveto
+          2 10 4 14 3 4 rrcurveto
+          -35 1 rlineto
+          -254 -92 rmoveto
+          1 -31 rlineto
+          35 -2 78 -2 33 0 rrcurveto
+          147 0 184 11 152 19 rrcurveto
+          -1 29 rlineto
+          -124 -20 -198 -13 -161 0 rrcurveto
+          -25 0 -87 3 -34 6 rrcurveto
+          513 -70 rmoveto
+          -1 -9 -3 -10 -2 -6 rrcurveto
+          -51 -139 -97 -93 -105 -65 rrcurveto
+          -69 -42 -62 -17 -53 0 rrcurveto
+          -42 0 -26 18 0 36 rrcurveto
+          0 84 110 92 126 39 rrcurveto
+          53 17 75 18 77 0 rrcurveto
+          164 0 92 -72 0 -81 rrcurveto
+          0 -117 -146 -66 -168 -14 rrcurveto
+          18 -25 rlineto
+          219 27 106 85 0 109 rrcurveto
+          0 93 -99 84 -191 0 rrcurveto
+          -55 0 -80 -11 -67 -20 rrcurveto
+          -137 -43 -128 -101 0 -100 rrcurveto
+          0 -52 42 -29 59 0 rrcurveto
+          54 0 73 23 64 39 rrcurveto
+          115 68 96 106 63 142 rrcurveto
+          3 8 3 8 4 8 rrcurveto
+          -34 8 rlineto
+          endchar
+        </CharString>
+        <CharString name="c31621">
+          389 497 rmoveto
+          0 -26 rlineto
+          554 0 rlineto
+          0 26 rlineto
+          -554 0 rlineto
+          23 -174 rmoveto
+          0 -27 rlineto
+          479 0 rlineto
+          0 27 rlineto
+          -479 0 rlineto
+          241 308 rmoveto
+          0 -324 rlineto
+          28 0 rlineto
+          0 324 rlineto
+          -28 0 rlineto
+          -158 -332 rmoveto
+          -27 -9 rlineto
+          80 -174 169 -123 229 -55 rrcurveto
+          5 8 7 10 8 6 rrcurveto
+          -229 49 -166 122 -76 166 rrcurveto
+          386 24 rmoveto
+          0 -6 rlineto
+          -86 -194 -242 -117 -228 -43 rrcurveto
+          7 -6 7 -11 3 -8 rrcurveto
+          234 49 243 118 94 211 rrcurveto
+          -19 9 rlineto
+          -6 -2 rlineto
+          -7 0 rlineto
+          -854 -111 rmoveto
+          14 -28 rlineto
+          91 23 125 31 119 30 rrcurveto
+          -5 27 rlineto
+          -128 -33 -131 -31 -85 -19 rrcurveto
+          15 254 rmoveto
+          0 -26 rlineto
+          325 0 rlineto
+          0 26 rlineto
+          -325 0 rlineto
+          164 164 rmoveto
+          0 -650 rlineto
+          0 -11 -6 -3 -13 0 rrcurveto
+          -13 -1 -43 0 -53 1 rrcurveto
+          4 -8 6 -12 2 -7 rrcurveto
+          64 0 34 1 18 4 rrcurveto
+          19 5 9 10 0 22 rrcurveto
+          0 649 rlineto
+          -28 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="c32051">
+          231 479 rmoveto
+          0 -26 rlineto
+          546 0 rlineto
+          0 26 rlineto
+          -546 0 rlineto
+          30 -166 rmoveto
+          0 -27 rlineto
+          487 0 rlineto
+          0 27 rlineto
+          -487 0 rlineto
+          -50 -190 rmoveto
+          0 -26 rlineto
+          592 0 rlineto
+          0 26 rlineto
+          -592 0 rlineto
+          270 348 rmoveto
+          0 -362 rlineto
+          28 0 rlineto
+          0 362 rlineto
+          -28 0 rlineto
+          124 -227 rmoveto
+          46 -28 52 -40 25 -26 rrcurveto
+          22 13 rlineto
+          -25 27 -52 38 -47 28 rrcurveto
+          -21 -12 rlineto
+          -518 347 rmoveto
+          0 -653 rlineto
+          29 0 rlineto
+          0 627 rlineto
+          771 0 rlineto
+          0 -627 rlineto
+          28 0 rlineto
+          0 653 rlineto
+          -828 0 rlineto
+          14 -588 rmoveto
+          0 -26 rlineto
+          799 0 rlineto
+          0 26 rlineto
+          -799 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni3042">
+          -107 304 779 rmoveto
+          0 -6 -1 -13 -1 -16 rrcurveto
+          -5 -73 -17 -177 0 -145 rrcurveto
+          0 -105 19 -135 22 -89 rrcurveto
+          30 9 rlineto
+          -22 88 -20 128 0 100 rrcurveto
+          0 151 13 173 11 74 rrcurveto
+          1 12 3 18 3 5 rrcurveto
+          -36 1 rlineto
+          -188 -126 rmoveto
+          1 -33 rlineto
+          27 -3 58 -3 27 0 rrcurveto
+          110 0 142 16 111 25 rrcurveto
+          0 31 rlineto
+          -95 -26 -144 -19 -125 0 rrcurveto
+          -21 0 -66 5 -25 7 rrcurveto
+          382 -89 rmoveto
+          -1 -11 -2 -9 -3 -9 rrcurveto
+          -37 -188 -69 -128 -76 -89 rrcurveto
+          -51 -56 -47 -22 -39 0 rrcurveto
+          -30 0 -19 24 0 49 rrcurveto
+          0 117 79 126 92 53 rrcurveto
+          39 23 56 23 59 0 rrcurveto
+          120 0 67 -98 0 -110 rrcurveto
+          0 -162 -102 -88 -128 -18 rrcurveto
+          18 -26 rlineto
+          167 36 77 111 0 145 rrcurveto
+          0 122 -78 112 -144 0 rrcurveto
+          -42 0 -61 -15 -50 -27 rrcurveto
+          -105 -57 -96 -133 0 -133 rrcurveto
+          0 -67 34 -37 46 0 rrcurveto
+          44 0 56 30 48 51 rrcurveto
+          83 92 74 143 46 190 rrcurveto
+          4 10 3 7 3 9 rrcurveto
+          -35 10 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni56FD">
+          -107 175 638 rmoveto
+          0 -28 rlineto
+          404 0 rlineto
+          0 28 rlineto
+          -404 0 rlineto
+          22 -225 rmoveto
+          0 -28 rlineto
+          362 0 rlineto
+          0 28 rlineto
+          -362 0 rlineto
+          -36 -254 rmoveto
+          0 -29 rlineto
+          437 0 rlineto
+          0 29 rlineto
+          -437 0 rlineto
+          196 470 rmoveto
+          0 -487 rlineto
+          30 0 rlineto
+          0 487 rlineto
+          -30 0 rlineto
+          95 -303 rmoveto
+          33 -38 37 -54 17 -36 rrcurveto
+          23 16 rlineto
+          -18 36 -37 52 -34 38 rrcurveto
+          -21 -14 rlineto
+          -388 460 rmoveto
+          0 -868 rlineto
+          31 0 rlineto
+          0 841 rlineto
+          562 0 rlineto
+          0 -841 rlineto
+          31 0 rlineto
+          0 868 rlineto
+          -624 0 rlineto
+          16 -788 rmoveto
+          0 -27 rlineto
+          590 0 rlineto
+          0 27 rlineto
+          -590 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni6280">
+          -107 293 659 rmoveto
+          0 -28 rlineto
+          415 0 rlineto
+          0 28 rlineto
+          -415 0 rlineto
+          16 -233 rmoveto
+          0 -28 rlineto
+          355 0 rlineto
+          0 28 rlineto
+          -355 0 rlineto
+          177 414 rmoveto
+          0 -432 rlineto
+          31 0 rlineto
+          0 432 rlineto
+          -31 0 rlineto
+          -108 -439 rmoveto
+          -29 -9 rlineto
+          61 -235 124 -167 171 -73 rrcurveto
+          5 8 9 11 8 6 rrcurveto
+          -169 66 -124 165 -56 228 rrcurveto
+          276 25 rmoveto
+          0 -7 rlineto
+          -62 -262 -176 -156 -173 -58 rrcurveto
+          7 -5 8 -12 2 -9 rrcurveto
+          180 66 178 156 69 278 rrcurveto
+          -20 11 rlineto
+          -6 -2 rlineto
+          -7 0 rlineto
+          -635 -150 rmoveto
+          13 -28 rlineto
+          68 30 95 43 89 40 rrcurveto
+          -5 28 rlineto
+          -96 -43 -99 -43 -65 -27 rrcurveto
+          12 342 rmoveto
+          0 -28 rlineto
+          245 0 rlineto
+          0 28 rlineto
+          -245 0 rlineto
+          119 221 rmoveto
+          0 -873 rlineto
+          0 -15 -5 -4 -9 0 rrcurveto
+          -10 -1 -32 0 -39 1 rrcurveto
+          5 -8 5 -12 2 -7 rrcurveto
+          49 -1 26 1 16 5 rrcurveto
+          16 6 6 10 0 26 rrcurveto
+          0 872 rlineto
+          -30 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uniFF20">
+          -107 501 35 rmoveto
+          -9 27 rlineto
+          -35 -29 -45 -16 -54 0 rrcurveto
+          -126 0 -96 130 0 189 rrcurveto
+          0 226 111 176 155 0 rrcurveto
+          135 0 77 -123 0 -181 rrcurveto
+          0 -133 -60 -95 -44 0 rrcurveto
+          -34 0 -13 29 15 106 rrcurveto
+          35 223 rlineto
+          -22 12 -26 9 -36 0 rrcurveto
+          -114 0 -86 -130 0 -141 rrcurveto
+          0 -85 43 -51 45 0 rrcurveto
+          54 0 40 34 32 75 rrcurveto
+          3 0 rlineto
+          -6 -82 29 -27 34 0 rrcurveto
+          71 0 72 95 0 172 rrcurveto
+          0 185 -93 138 -146 0 rrcurveto
+          -176 0 -126 -187 0 -246 rrcurveto
+          0 -213 113 -136 137 0 rrcurveto
+          53 0 50 15 43 34 rrcurveto
+          -24 509 rmoveto
+          -22 -149 rlineto
+          -15 -95 -50 -94 -61 0 rrcurveto
+          -45 0 -23 46 0 58 rrcurveto
+          0 128 73 119 89 0 rrcurveto
+          26 0 18 -7 10 -6 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uniFF21">
+          -107 569 0 rmoveto
+          41 0 rlineto
+          -220 726 rlineto
+          -42 0 rlineto
+          -219 -726 rlineto
+          38 0 rlineto
+          75 260 rlineto
+          253 0 rlineto
+          74 -260 rlineto
+          -316 288 rmoveto
+          42 153 rlineto
+          25 87 24 73 23 89 rrcurveto
+          3 0 rlineto
+          25 -89 22 -71 25 -89 rrcurveto
+          43 -153 rlineto
+          -232 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uniFF41">
+          -107 202 128 rmoveto
+          0 -99 68 -41 71 0 rrcurveto
+          59 0 62 44 46 49 rrcurveto
+          2 0 rlineto
+          4 -81 rlineto
+          31 0 rlineto
+          0 341 rlineto
+          0 106 -45 90 -106 0 rrcurveto
+          -71 0 -70 -48 -31 -30 rrcurveto
+          17 -26 rlineto
+          32 31 59 43 59 0 rrcurveto
+          92 0 26 -87 2 -93 rrcurveto
+          -217 -26 -90 -56 0 -117 rrcurveto
+          38 2 rmoveto
+          0 90 65 53 204 26 rrcurveto
+          0 -180 rlineto
+          -57 -68 -53 -33 -52 0 rrcurveto
+          -53 0 -54 30 0 82 rrcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <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="vert"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="vrt2"/>
+        <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">
+          <Substitution in="uni3042" out="c30843"/>
+          <Substitution in="uni56FD" out="c32051"/>
+          <Substitution in="uni6280" out="c31621"/>
+          <Substitution in="uniFF20" out="c30719"/>
+          <Substitution in="uniFF21" out="c30790"/>
+          <Substitution in="uniFF41" out="c30816"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni3042" out="c30843"/>
+          <Substitution in="uni56FD" out="c32051"/>
+          <Substitution in="uni6280" out="c31621"/>
+          <Substitution in="uniFF20" out="c30719"/>
+          <Substitution in="uniFF21" out="c30790"/>
+          <Substitution in="uniFF41" out="c30816"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="6"/>
+    <VOriginRecord>
+      <glyphName value="c30719"/>
+      <vOrigin value="660"/>
+    </VOriginRecord>
+    <VOriginRecord>
+      <glyphName value="c30790"/>
+      <vOrigin value="660"/>
+    </VOriginRecord>
+    <VOriginRecord>
+      <glyphName value="c30816"/>
+      <vOrigin value="660"/>
+    </VOriginRecord>
+    <VOriginRecord>
+      <glyphName value="c30843"/>
+      <vOrigin value="660"/>
+    </VOriginRecord>
+    <VOriginRecord>
+      <glyphName value="c31621"/>
+      <vOrigin value="660"/>
+    </VOriginRecord>
+    <VOriginRecord>
+      <glyphName value="c32051"/>
+      <vOrigin value="660"/>
+    </VOriginRecord>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="188" lsb="0"/>
+    <mtx name="c30719" width="1000" lsb="142"/>
+    <mtx name="c30790" width="1000" lsb="177"/>
+    <mtx name="c30816" width="1000" lsb="276"/>
+    <mtx name="c30843" width="1000" lsb="128"/>
+    <mtx name="c31621" width="1000" lsb="27"/>
+    <mtx name="c32051" width="1000" lsb="87"/>
+    <mtx name="uni3042" width="750" lsb="92"/>
+    <mtx name="uni56FD" width="750" lsb="64"/>
+    <mtx name="uni6280" width="750" lsb="19"/>
+    <mtx name="uniFF20" width="750" lsb="105"/>
+    <mtx name="uniFF21" width="750" lsb="129"/>
+    <mtx name="uniFF41" width="750" lsb="202"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="29"/>
+    <minBottomSideBearing value="28"/>
+    <yMaxExtent value="963"/>
+    <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="8"/>
+  </vhea>
+
+  <vmtx>
+    <mtx name=".notdef" height="1000" tsb="880"/>
+    <mtx name="c30719" height="750" tsb="83"/>
+    <mtx name="c30790" height="750" tsb="114"/>
+    <mtx name="c30816" height="750" tsb="254"/>
+    <mtx name="c30843" height="750" tsb="74"/>
+    <mtx name="c31621" height="750" tsb="29"/>
+    <mtx name="c32051" height="750" tsb="69"/>
+    <mtx name="uni3042" height="1000" tsb="101"/>
+    <mtx name="uni56FD" height="1000" tsb="94"/>
+    <mtx name="uni6280" height="1000" tsb="40"/>
+    <mtx name="uniFF20" height="1000" tsb="112"/>
+    <mtx name="uniFF21" height="1000" tsb="154"/>
+    <mtx name="uniFF41" height="1000" tsb="343"/>
+  </vmtx>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_vvar_cff2/TestVVAR.1.ttx b/Tests/varLib/data/master_vvar_cff2/TestVVAR.1.ttx
new file mode 100644
index 0000000..9f67749
--- /dev/null
+++ b/Tests/varLib/data/master_vvar_cff2/TestVVAR.1.ttx
@@ -0,0 +1,821 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="uniFF20"/>
+    <GlyphID id="2" name="uniFF21"/>
+    <GlyphID id="3" name="uniFF41"/>
+    <GlyphID id="4" name="uni3042"/>
+    <GlyphID id="5" name="uni6280"/>
+    <GlyphID id="6" name="uni56FD"/>
+    <GlyphID id="7" name="c30719"/>
+    <GlyphID id="8" name="c30790"/>
+    <GlyphID id="9" name="c30816"/>
+    <GlyphID id="10" name="c30843"/>
+    <GlyphID id="11" name="c31621"/>
+    <GlyphID id="12" name="c32051"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.00299"/>
+    <checkSumAdjustment value="0x1101e854"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Feb 19 17:22:00 2019"/>
+    <modified value="Tue Feb 19 17:22:00 2019"/>
+    <xMin value="11"/>
+    <yMin value="-96"/>
+    <xMax value="987"/>
+    <yMax value="854"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="3"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1160"/>
+    <descent value="-317"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="1000"/>
+    <minLeftSideBearing value="11"/>
+    <minRightSideBearing value="4"/>
+    <xMaxExtent value="987"/>
+    <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>
+    <tableVersion value="0x5000"/>
+    <numGlyphs value="13"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="822"/>
+    <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="228"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="10"/>
+      <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="12354"/>
+    <usLastCharIndex value="65345"/>
+    <sTypoAscender value="880"/>
+    <sTypoDescender value="-120"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1160"/>
+    <usWinDescent value="317"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="380"/>
+    <sCapHeight value="760"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="1"/>
+  </OS_2>
+
+  <name>
+    <namerecord nameID="0" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Han Sans VVAR TEST
+    </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.003;UKWN;SHSansVVARTest_75-w1000.00
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Source Han Sans VVAR TEST
+    </namerecord>
+    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Version 1.003;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SHSansVVARTest_75-w1000.00
+    </namerecord>
+    <namerecord nameID="8" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Adobe Systems Co., Ltd.
+    </namerecord>
+    <namerecord nameID="9" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Ryoko Nishizuka
+    </namerecord>
+    <namerecord nameID="10" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Masataka HATTORI (variable font production)
+    </namerecord>
+    <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      http://www.adobe.com
+    </namerecord>
+    <namerecord nameID="17" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Master4
+    </namerecord>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved.
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Source Han Sans VVAR TEST
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.003;UKWN;SHSansVVARTest_75-w1000.00
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Source Han Sans VVAR TEST
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.003;hotconv 1.0.109;makeotfexe 2.5.65596
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SHSansVVARTest_75-w1000.00
+    </namerecord>
+    <namerecord nameID="8" platformID="3" platEncID="1" langID="0x409">
+      Adobe Systems Co., Ltd.
+    </namerecord>
+    <namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
+      Ryoko Nishizuka
+    </namerecord>
+    <namerecord nameID="10" platformID="3" platEncID="1" langID="0x409">
+      Masataka HATTORI 服部正貴 (variable font production)
+    </namerecord>
+    <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
+      http://www.adobe.com
+    </namerecord>
+    <namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
+      Master4
+    </namerecord>
+  </name>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x3042" name="uni3042"/><!-- HIRAGANA LETTER A -->
+      <map code="0x56fd" name="uni56FD"/><!-- CJK UNIFIED IDEOGRAPH-56FD -->
+      <map code="0x6280" name="uni6280"/><!-- CJK UNIFIED IDEOGRAPH-6280 -->
+      <map code="0xff20" name="uniFF20"/><!-- FULLWIDTH COMMERCIAL AT -->
+      <map code="0xff21" name="uniFF21"/><!-- FULLWIDTH LATIN CAPITAL LETTER A -->
+      <map code="0xff41" name="uniFF41"/><!-- FULLWIDTH LATIN SMALL LETTER A -->
+    </cmap_format_4>
+    <cmap_format_6 platformID="1" platEncID="0" language="0">
+    </cmap_format_6>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x3042" name="uni3042"/><!-- HIRAGANA LETTER A -->
+      <map code="0x56fd" name="uni56FD"/><!-- CJK UNIFIED IDEOGRAPH-56FD -->
+      <map code="0x6280" name="uni6280"/><!-- CJK UNIFIED IDEOGRAPH-6280 -->
+      <map code="0xff20" name="uniFF20"/><!-- FULLWIDTH COMMERCIAL AT -->
+      <map code="0xff21" name="uniFF21"/><!-- FULLWIDTH LATIN CAPITAL LETTER A -->
+      <map code="0xff41" name="uniFF41"/><!-- FULLWIDTH LATIN SMALL LETTER A -->
+    </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="SHSansVVARTest_75-w1000.00">
+      <version value="1.0"/>
+      <FamilyName value="SHSansVVARTest_75"/>
+      <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="11 -96 987 854"/>
+      <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="1000"/>
+        <nominalWidthX value="857"/>
+      </Private>
+      <CharStrings>
+        <CharString name=".notdef">
+          -669 endchar
+        </CharString>
+        <CharString name="c30719">
+          683 21 rmoveto
+          -20 52 rlineto
+          -46 -17 -62 -8 -59 0 rrcurveto
+          -170 0 -122 79 0 145 rrcurveto
+          0 163 152 100 177 0 rrcurveto
+          178 0 96 -86 0 -108 rrcurveto
+          0 -100 -63 -45 -49 2 rrcurveto
+          -33 1 -9 22 12 47 rrcurveto
+          35 160 rlineto
+          -29 12 -55 10 -50 0 rrcurveto
+          -164 0 -104 -95 0 -105 rrcurveto
+          0 -72 54 -42 74 0 rrcurveto
+          61 0 51 22 36 42 rrcurveto
+          3 0 rlineto
+          4 -42 38 -22 55 0 rrcurveto
+          121 0 94 81 0 125 rrcurveto
+          0 145 -142 105 -201 0 rrcurveto
+          -256 0 -171 -154 0 -174 rrcurveto
+          0 -171 167 -103 188 0 rrcurveto
+          80 0 60 8 69 23 rrcurveto
+          -98 352 rmoveto
+          -18 -78 rlineto
+          -10 -49 -46 -34 -44 0 rrcurveto
+          -38 0 -19 18 0 31 rrcurveto
+          0 63 60 52 78 0 rrcurveto
+          15 0 12 -1 10 -2 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="c30790">
+          688 0 rmoveto
+          179 0 rlineto
+          -266 562 rlineto
+          -207 0 rlineto
+          -261 -562 rlineto
+          172 0 rlineto
+          55 125 rlineto
+          272 0 rlineto
+          56 -125 rlineto
+          -293 232 rmoveto
+          24 44 rlineto
+          24 56 27 68 23 58 rrcurveto
+          4 0 rlineto
+          25 -56 28 -70 24 -56 rrcurveto
+          23 -44 rlineto
+          -202 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="c30816">
+          229 121 rmoveto
+          0 -71 73 -59 114 0 rrcurveto
+          74 0 72 25 57 30 rrcurveto
+          5 0 rlineto
+          11 -46 rlineto
+          132 0 rlineto
+          0 242 rlineto
+          0 138 -96 61 -158 0 rrcurveto
+          -96 0 -91 -23 -77 -34 rrcurveto
+          59 -92 rlineto
+          62 25 56 14 57 0 rrcurveto
+          78 0 40 -18 4 -34 rrcurveto
+          -270 -12 -106 -51 0 -95 rrcurveto
+          154 12 rmoveto
+          0 32 49 25 173 6 rrcurveto
+          0 -63 rlineto
+          -42 -19 -45 -17 -51 0 rrcurveto
+          -50 0 -34 11 0 25 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="c30843">
+          361 613 rmoveto
+          1 -15 -2 -21 -2 -15 rrcurveto
+          -12 -81 -17 -104 0 -94 rrcurveto
+          0 -120 29 -115 31 -68 rrcurveto
+          126 33 rlineto
+          -34 78 -29 80 0 133 rrcurveto
+          0 87 16 101 20 74 rrcurveto
+          4 13 4 14 6 17 rrcurveto
+          -141 3 rlineto
+          -230 -68 rmoveto
+          3 -107 rlineto
+          48 -2 78 -3 55 0 rrcurveto
+          152 0 213 10 136 19 rrcurveto
+          -1 108 rlineto
+          -157 -31 -145 -5 -193 0 rrcurveto
+          -66 0 -88 8 -35 3 rrcurveto
+          490 -108 rmoveto
+          -1 -20 -8 -40 -7 -19 rrcurveto
+          -39 -92 -64 -56 -69 -45 rrcurveto
+          -60 -43 -67 -27 -47 0 rrcurveto
+          -32 0 -12 11 0 20 rrcurveto
+          0 38 67 64 97 34 rrcurveto
+          48 16 71 21 93 0 rrcurveto
+          132 0 70 -47 0 -58 rrcurveto
+          0 -58 -70 -76 -196 -17 rrcurveto
+          73 -98 rlineto
+          249 29 80 109 0 105 rrcurveto
+          0 125 -143 81 -186 0 rrcurveto
+          -78 0 -82 -12 -65 -17 rrcurveto
+          -162 -44 -126 -113 0 -108 rrcurveto
+          0 -84 64 -36 74 0 rrcurveto
+          91 0 89 36 65 36 rrcurveto
+          92 47 104 98 57 134 rrcurveto
+          7 18 17 44 8 17 rrcurveto
+          -134 27 rlineto
+          endchar
+        </CharString>
+        <CharString name="c31621">
+          399 539 rmoveto
+          0 -104 rlineto
+          554 0 rlineto
+          0 104 rlineto
+          -554 0 rlineto
+          16 -169 rmoveto
+          0 -102 rlineto
+          426 0 rlineto
+          0 102 rlineto
+          -426 0 rlineto
+          193 273 rmoveto
+          0 -321 rlineto
+          132 0 rlineto
+          0 321 rlineto
+          -132 0 rlineto
+          -41 -370 rmoveto
+          -120 -33 rlineto
+          91 -146 145 -105 217 -53 rrcurveto
+          19 30 39 46 29 24 rrcurveto
+          -202 38 -147 87 -71 112 rrcurveto
+          242 97 rmoveto
+          0 -17 rlineto
+          -59 -167 -186 -106 -249 -42 rrcurveto
+          25 -24 32 -50 13 -30 rrcurveto
+          276 61 199 122 81 229 rrcurveto
+          -86 27 rlineto
+          -22 -3 rlineto
+          -24 0 rlineto
+          -792 -100 rmoveto
+          31 -108 rlineto
+          102 19 129 22 120 24 rrcurveto
+          -16 104 rlineto
+          -131 -24 -140 -23 -95 -14 rrcurveto
+          17 238 rmoveto
+          0 -105 rlineto
+          349 0 rlineto
+          0 105 rlineto
+          -349 0 rlineto
+          122 134 rmoveto
+          0 -596 rlineto
+          0 -11 -5 -3 -14 0 rrcurveto
+          -13 0 -42 0 -37 1 rrcurveto
+          16 -29 16 -45 4 -29 rrcurveto
+          72 0 50 3 36 17 rrcurveto
+          35 17 11 28 0 51 rrcurveto
+          0 596 rlineto
+          -129 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="c32051">
+          252 482 rmoveto
+          0 -92 rlineto
+          490 0 rlineto
+          0 92 rlineto
+          -490 0 rlineto
+          26 -138 rmoveto
+          0 -89 rlineto
+          443 0 rlineto
+          0 89 rlineto
+          -443 0 rlineto
+          -36 -167 rmoveto
+          0 -94 rlineto
+          515 0 rlineto
+          0 94 rlineto
+          -515 0 rlineto
+          194 280 rmoveto
+          0 -319 rlineto
+          122 0 rlineto
+          0 319 rlineto
+          -122 0 rlineto
+          146 -225 rmoveto
+          27 -22 32 -31 15 -18 rrcurveto
+          87 41 rlineto
+          -16 18 -35 29 -27 20 rrcurveto
+          -83 -37 rlineto
+          -513 380 rmoveto
+          0 -686 rlineto
+          132 0 rlineto
+          0 581 rlineto
+          595 0 rlineto
+          0 -581 rlineto
+          137 0 rlineto
+          0 686 rlineto
+          -864 0 rlineto
+          77 -550 rmoveto
+          0 -105 rlineto
+          702 0 rlineto
+          0 105 rlineto
+          -702 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni3042">
+          -107 260 812 rmoveto
+          0 -18 -1 -26 -2 -20 rrcurveto
+          -10 -103 -11 -143 0 -129 rrcurveto
+          0 -153 22 -150 23 -90 rrcurveto
+          118 38 rlineto
+          -28 103 -21 110 0 166 rrcurveto
+          0 121 12 139 15 96 rrcurveto
+          4 16 3 18 6 22 rrcurveto
+          -130 3 rlineto
+          -168 -96 rmoveto
+          3 -128 rlineto
+          40 -2 58 -4 47 0 rrcurveto
+          114 0 167 14 98 25 rrcurveto
+          -1 128 rlineto
+          -126 -39 -96 -8 -152 0 rrcurveto
+          -59 0 -66 9 -27 5 rrcurveto
+          363 -137 rmoveto
+          -1 -23 -7 -48 -6 -23 rrcurveto
+          -27 -131 -41 -83 -43 -63 rrcurveto
+          -43 -57 -52 -35 -34 0 rrcurveto
+          -21 0 -6 16 0 30 rrcurveto
+          0 60 41 91 66 47 rrcurveto
+          34 22 53 26 74 0 rrcurveto
+          92 0 47 -68 0 -82 rrcurveto
+          0 -89 -35 -99 -154 -21 rrcurveto
+          67 -117 rlineto
+          194 37 53 141 0 142 rrcurveto
+          0 160 -116 109 -138 0 rrcurveto
+          -64 0 -63 -16 -48 -23 rrcurveto
+          -127 -58 -94 -150 0 -142 rrcurveto
+          0 -106 52 -46 61 0 rrcurveto
+          75 0 70 46 50 48 rrcurveto
+          65 66 78 134 42 179 rrcurveto
+          6 23 16 51 7 20 rrcurveto
+          -123 32 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni56FD">
+          -107 194 642 rmoveto
+          0 -110 rlineto
+          355 0 rlineto
+          0 110 rlineto
+          -355 0 rlineto
+          18 -190 rmoveto
+          0 -106 rlineto
+          323 0 rlineto
+          0 106 rlineto
+          -323 0 rlineto
+          -23 -227 rmoveto
+          0 -111 rlineto
+          368 0 rlineto
+          0 111 rlineto
+          -368 0 rlineto
+          128 387 rmoveto
+          0 -434 rlineto
+          112 0 rlineto
+          0 434 rlineto
+          -112 0 rlineto
+          114 -300 rmoveto
+          16 -31 20 -43 9 -26 rrcurveto
+          80 49 rlineto
+          -11 26 -22 40 -15 28 rrcurveto
+          -77 -43 rlineto
+          -383 499 rmoveto
+          0 -907 rlineto
+          122 0 rlineto
+          0 784 rlineto
+          407 0 rlineto
+          0 -784 rlineto
+          127 0 rlineto
+          0 907 rlineto
+          -656 0 rlineto
+          72 -741 rmoveto
+          0 -123 rlineto
+          505 0 rlineto
+          0 123 rlineto
+          -505 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uni6280">
+          -107 301 710 rmoveto
+          0 -123 rlineto
+          416 0 rlineto
+          0 123 rlineto
+          -416 0 rlineto
+          10 -226 rmoveto
+          0 -121 rlineto
+          309 0 rlineto
+          0 121 rlineto
+          -309 0 rlineto
+          135 370 rmoveto
+          0 -428 rlineto
+          122 0 rlineto
+          0 428 rlineto
+          -122 0 rlineto
+          -4 -486 rmoveto
+          -112 -37 rlineto
+          72 -202 103 -143 160 -71 rrcurveto
+          17 35 37 54 27 28 rrcurveto
+          -147 54 -106 123 -51 159 rrcurveto
+          148 116 rmoveto
+          0 -21 rlineto
+          -38 -227 -126 -145 -191 -56 rrcurveto
+          22 -29 29 -58 12 -36 rrcurveto
+          217 80 140 160 58 302 rrcurveto
+          -80 34 rlineto
+          -20 -4 rlineto
+          -23 0 rlineto
+          -579 -136 rmoveto
+          27 -128 rlineto
+          78 26 99 32 90 32 rrcurveto
+          -14 123 rlineto
+          -100 -33 -107 -33 -73 -19 rrcurveto
+          13 320 rmoveto
+          0 -123 rlineto
+          267 0 rlineto
+          0 123 rlineto
+          -267 0 rlineto
+          82 185 rmoveto
+          0 -805 rlineto
+          0 -14 -4 -4 -10 -1 rrcurveto
+          -10 0 -31 0 -24 2 rrcurveto
+          14 -34 15 -55 3 -34 rrcurveto
+          56 0 41 4 30 20 rrcurveto
+          31 21 8 32 0 63 rrcurveto
+          0 805 rlineto
+          -119 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uniFF20">
+          -107 516 29 rmoveto
+          -17 64 rlineto
+          -34 -24 -47 -12 -41 0 rrcurveto
+          -127 0 -92 109 0 192 rrcurveto
+          0 219 116 140 126 0 rrcurveto
+          132 0 70 -115 0 -151 rrcurveto
+          0 -133 -42 -64 -35 2 rrcurveto
+          -21 0 -5 30 7 68 rrcurveto
+          23 215 rlineto
+          -20 16 -49 12 -37 0 rrcurveto
+          -127 0 -74 -127 0 -140 rrcurveto
+          0 -94 40 -55 58 0 rrcurveto
+          44 0 36 30 25 59 rrcurveto
+          3 0 rlineto
+          6 -60 29 -29 43 0 rrcurveto
+          98 0 70 105 0 169 rrcurveto
+          0 191 -110 140 -154 0 rrcurveto
+          -198 0 -128 -202 0 -236 rrcurveto
+          0 -224 129 -137 143 0 rrcurveto
+          62 0 43 10 55 32 rrcurveto
+          -90 475 rmoveto
+          -11 -111 rlineto
+          -5 -69 -28 -52 -24 0 rrcurveto
+          -23 0 -12 27 0 44 rrcurveto
+          0 90 37 76 47 0 rrcurveto
+          7 0 5 -2 7 -3 rrcurveto
+          endchar
+        </CharString>
+        <CharString name="uniFF21">
+          -107 498 0 rmoveto
+          166 0 rlineto
+          -192 746 rlineto
+          -195 0 rlineto
+          -188 -746 rlineto
+          162 0 rlineto
+          29 179 rlineto
+          187 0 rlineto
+          31 -179 rlineto
+          -187 306 rmoveto
+          10 73 rlineto
+          15 76 20 87 16 80 rrcurveto
+          3 0 rlineto
+          15 -78 21 -89 15 -76 rrcurveto
+          10 -73 rlineto
+          -125 0 rlineto
+          endchar
+        </CharString>
+        <CharString name="uniFF41">
+          -107 161 157 rmoveto
+          0 -95 51 -74 91 0 rrcurveto
+          53 0 52 34 42 43 rrcurveto
+          3 0 rlineto
+          11 -65 rlineto
+          122 0 rlineto
+          0 326 rlineto
+          0 172 -81 82 -124 0 rrcurveto
+          -72 0 -68 -32 -66 -44 rrcurveto
+          53 -108 rlineto
+          51 32 37 22 37 0 rrcurveto
+          48 0 30 -33 5 -52 rrcurveto
+          -199 -17 -76 -65 0 -126 rrcurveto
+          143 13 rmoveto
+          0 50 27 37 105 9 rrcurveto
+          0 -96 rlineto
+          -24 -33 -27 -23 -35 0 rrcurveto
+          -30 0 -16 17 0 39 rrcurveto
+          endchar
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF>
+
+  <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="vert"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="vrt2"/>
+        <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">
+          <Substitution in="uni3042" out="c30843"/>
+          <Substitution in="uni56FD" out="c32051"/>
+          <Substitution in="uni6280" out="c31621"/>
+          <Substitution in="uniFF20" out="c30719"/>
+          <Substitution in="uniFF21" out="c30790"/>
+          <Substitution in="uniFF41" out="c30816"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni3042" out="c30843"/>
+          <Substitution in="uni56FD" out="c32051"/>
+          <Substitution in="uni6280" out="c31621"/>
+          <Substitution in="uniFF20" out="c30719"/>
+          <Substitution in="uniFF21" out="c30790"/>
+          <Substitution in="uniFF41" out="c30816"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <VORG>
+    <majorVersion value="1"/>
+    <minorVersion value="0"/>
+    <defaultVertOriginY value="880"/>
+    <numVertOriginYMetrics value="6"/>
+    <VOriginRecord>
+      <glyphName value="c30719"/>
+      <vOrigin value="660"/>
+    </VOriginRecord>
+    <VOriginRecord>
+      <glyphName value="c30790"/>
+      <vOrigin value="660"/>
+    </VOriginRecord>
+    <VOriginRecord>
+      <glyphName value="c30816"/>
+      <vOrigin value="660"/>
+    </VOriginRecord>
+    <VOriginRecord>
+      <glyphName value="c30843"/>
+      <vOrigin value="660"/>
+    </VOriginRecord>
+    <VOriginRecord>
+      <glyphName value="c31621"/>
+      <vOrigin value="660"/>
+    </VOriginRecord>
+    <VOriginRecord>
+      <glyphName value="c32051"/>
+      <vOrigin value="660"/>
+    </VOriginRecord>
+  </VORG>
+
+  <hmtx>
+    <mtx name=".notdef" width="188" lsb="0"/>
+    <mtx name="c30719" width="1000" lsb="119"/>
+    <mtx name="c30790" width="1000" lsb="133"/>
+    <mtx name="c30816" width="1000" lsb="229"/>
+    <mtx name="c30843" width="1000" lsb="87"/>
+    <mtx name="c31621" width="1000" lsb="17"/>
+    <mtx name="c32051" width="1000" lsb="69"/>
+    <mtx name="uni3042" width="750" lsb="56"/>
+    <mtx name="uni56FD" width="750" lsb="48"/>
+    <mtx name="uni6280" width="750" lsb="11"/>
+    <mtx name="uniFF20" width="750" lsb="84"/>
+    <mtx name="uniFF21" width="750" lsb="89"/>
+    <mtx name="uniFF41" width="750" lsb="161"/>
+  </hmtx>
+
+  <vhea>
+    <tableVersion value="0x00011000"/>
+    <ascent value="500"/>
+    <descent value="-500"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="17"/>
+    <minBottomSideBearing value="16"/>
+    <yMaxExtent value="976"/>
+    <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="8"/>
+  </vhea>
+
+  <vmtx>
+    <mtx name=".notdef" height="1000" tsb="880"/>
+    <mtx name="c30719" height="750" tsb="68"/>
+    <mtx name="c30790" height="750" tsb="98"/>
+    <mtx name="c30816" height="750" tsb="219"/>
+    <mtx name="c30843" height="750" tsb="47"/>
+    <mtx name="c31621" height="750" tsb="17"/>
+    <mtx name="c32051" height="750" tsb="48"/>
+    <mtx name="uni3042" height="1000" tsb="68"/>
+    <mtx name="uni56FD" height="1000" tsb="69"/>
+    <mtx name="uni6280" height="1000" tsb="26"/>
+    <mtx name="uniFF20" height="1000" tsb="94"/>
+    <mtx name="uniFF21" height="1000" tsb="134"/>
+    <mtx name="uniFF41" height="1000" tsb="300"/>
+  </vmtx>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x0" numSigs="0" version="1"/>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/Build.ttx b/Tests/varLib/data/test_results/Build.ttx
index efb2e88..c802bf3 100644
--- a/Tests/varLib/data/test_results/Build.ttx
+++ b/Tests/varLib/data/test_results/Build.ttx
@@ -3,84 +3,12 @@
 
   <GDEF>
     <Version value="0x00010003"/>
-    <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=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>
+    <GlyphClassDef>
+      <ClassDef glyph="uni0024" class="1"/>
+      <ClassDef glyph="uni0024.nostroke" class="1"/>
+      <ClassDef glyph="uni0041" class="1"/>
+      <ClassDef glyph="uni0061" class="1"/>
+    </GlyphClassDef>
   </GDEF>
 
   <HVAR>
@@ -155,18 +83,15 @@
       <VarData index="0">
         <!-- ItemCount=6 -->
         <NumShorts value="0"/>
-        <!-- VarRegionCount=5 -->
+        <!-- VarRegionCount=2 -->
         <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"/>
-        <Item index="0" value="[0, 0, 0, 0, 0]"/>
-        <Item index="1" value="[14, -28, 0, 0, 0]"/>
-        <Item index="2" value="[-10, 17, 0, 0, 0]"/>
-        <Item index="3" value="[-3, 32, 0, 0, 0]"/>
-        <Item index="4" value="[-7, 63, 0, 0, 0]"/>
-        <Item index="5" value="[-7, 63, 0, 0, 0]"/>
+        <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>
@@ -246,14 +171,11 @@
       <VarData index="0">
         <!-- ItemCount=2 -->
         <NumShorts value="0"/>
-        <!-- VarRegionCount=5 -->
+        <!-- VarRegionCount=2 -->
         <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"/>
-        <Item index="0" value="[-4, 13, 0, 0, 0]"/>
-        <Item index="1" value="[-2, 8, 0, 0, 0]"/>
+        <Item index="0" value="[-4, 13]"/>
+        <Item index="1" value="[-2, 8]"/>
       </VarData>
     </VarStore>
     <ValueRecord index="0">
@@ -369,7 +291,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="18" x="0" y="0"/>
         <delta pt="19" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -392,7 +314,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="-18"/>
+        <delta pt="18" x="0" y="0"/>
         <delta pt="19" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -483,509 +405,6 @@
         <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"/>
@@ -1053,7 +472,7 @@
         <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="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1122,7 +541,7 @@
         <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="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1402,7 +821,7 @@
         <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="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1471,7 +890,7 @@
         <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="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1684,6 +1103,509 @@
         <delta pt="65" 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="0"/>
+        <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="0"/>
+        <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="0"/>
+        <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="0"/>
+        <delta pt="64" x="0" y="0"/>
+      </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>
   </gvar>
 
 </ttFont>
diff --git a/Tests/varLib/data/test_results/Build3.ttx b/Tests/varLib/data/test_results/Build3.ttx
deleted file mode 100644
index 3b96db6..0000000
--- a/Tests/varLib/data/test_results/Build3.ttx
+++ /dev/null
@@ -1,750 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.14">
-
-  <GDEF>
-    <Version value="0x00010003"/>
-    <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=0 -->
-        <NumShorts value="0"/>
-        <!-- VarRegionCount=1 -->
-        <VarRegionIndex index="0" value="0"/>
-      </VarData>
-    </VarStore>
-  </GDEF>
-
-  <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="[22]"/>
-        <Item index="1" value="[13]"/>
-      </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>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/BuildGvarCompositeExplicitDelta.ttx b/Tests/varLib/data/test_results/BuildGvarCompositeExplicitDelta.ttx
new file mode 100644
index 0000000..ce5b55d
--- /dev/null
+++ b/Tests/varLib/data/test_results/BuildGvarCompositeExplicitDelta.ttx
@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.40">
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph="N">
+      <tuple>
+        <coord axis="slnt" value="-1.0"/>
+        <delta pt="0" x="-41" y="0"/>
+        <delta pt="1" x="-43" y="0"/>
+        <delta pt="2" x="-43" y="5"/>
+        <delta pt="3" x="-40" y="7"/>
+        <delta pt="4" x="82" y="7"/>
+        <delta pt="5" x="84" y="5"/>
+        <delta pt="6" x="83" y="0"/>
+        <delta pt="7" x="80" y="0"/>
+        <delta pt="8" x="79" y="0"/>
+        <delta pt="9" x="84" y="-1"/>
+        <delta pt="10" x="83" y="-5"/>
+        <delta pt="11" x="-17" y="-3"/>
+        <delta pt="12" x="82" y="6"/>
+        <delta pt="13" x="84" y="3"/>
+        <delta pt="14" x="82" y="0"/>
+        <delta pt="15" x="81" y="0"/>
+        <delta pt="16" x="85" y="0"/>
+        <delta pt="17" x="82" y="-10"/>
+        <delta pt="18" x="80" y="-7"/>
+        <delta pt="19" x="-42" y="-7"/>
+        <delta pt="20" x="-44" y="-6"/>
+        <delta pt="21" x="-44" y="0"/>
+        <delta pt="22" x="-41" y="0"/>
+        <delta pt="23" x="-39" y="0"/>
+        <delta pt="24" x="-44" y="1"/>
+        <delta pt="25" x="-43" y="5"/>
+        <delta pt="26" x="57" y="3"/>
+        <delta pt="27" x="-42" y="-6"/>
+        <delta pt="28" x="-44" y="-4"/>
+        <delta pt="29" x="-43" y="0"/>
+        <delta pt="30" x="0" y="0"/>
+        <delta pt="31" x="0" y="0"/>
+        <delta pt="32" x="0" y="0"/>
+        <delta pt="33" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="O">
+      <tuple>
+        <coord axis="slnt" value="-1.0"/>
+        <delta pt="0" x="-43" y="0"/>
+        <delta pt="1" x="-45" y="0"/>
+        <delta pt="2" x="-36" y="2"/>
+        <delta pt="3" x="-19" y="6"/>
+        <delta pt="4" x="-8" y="6"/>
+        <delta pt="5" x="51" y="6"/>
+        <delta pt="6" x="62" y="5"/>
+        <delta pt="7" x="76" y="2"/>
+        <delta pt="8" x="84" y="0"/>
+        <delta pt="9" x="83" y="0"/>
+        <delta pt="10" x="85" y="0"/>
+        <delta pt="11" x="77" y="-2"/>
+        <delta pt="12" x="60" y="-5"/>
+        <delta pt="13" x="49" y="-6"/>
+        <delta pt="14" x="-10" y="-7"/>
+        <delta pt="15" x="-20" y="-5"/>
+        <delta pt="16" x="-36" y="-2"/>
+        <delta pt="17" x="-44" y="0"/>
+        <delta pt="18" x="-43" y="0"/>
+        <delta pt="19" x="-42" y="0"/>
+        <delta pt="20" x="-31" y="2"/>
+        <delta pt="21" x="-16" y="4"/>
+        <delta pt="22" x="-8" y="6"/>
+        <delta pt="23" x="51" y="6"/>
+        <delta pt="24" x="58" y="5"/>
+        <delta pt="25" x="72" y="2"/>
+        <delta pt="26" x="82" y="0"/>
+        <delta pt="27" x="83" y="0"/>
+        <delta pt="28" x="81" y="0"/>
+        <delta pt="29" x="71" y="-2"/>
+        <delta pt="30" x="57" y="-5"/>
+        <delta pt="31" x="49" y="-6"/>
+        <delta pt="32" x="-10" y="-7"/>
+        <delta pt="33" x="-17" y="-6"/>
+        <delta pt="34" x="-31" y="-2"/>
+        <delta pt="35" x="-42" y="0"/>
+        <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"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="Odieresis">
+      <tuple>
+        <coord axis="slnt" value="-1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="40" y="0"/>
+        <delta pt="2" x="0" y="0"/>
+        <delta pt="3" x="0" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="n">
+      <tuple>
+        <coord axis="slnt" value="-1.0"/>
+        <delta pt="0" x="-41" y="0"/>
+        <delta pt="1" x="-43" y="0"/>
+        <delta pt="2" x="-44" y="4"/>
+        <delta pt="3" x="-40" y="7"/>
+        <delta pt="4" x="40" y="4"/>
+        <delta pt="5" x="46" y="0"/>
+        <delta pt="6" x="43" y="0"/>
+        <delta pt="7" x="40" y="0"/>
+        <delta pt="8" x="41" y="0"/>
+        <delta pt="9" x="37" y="-3"/>
+        <delta pt="10" x="27" y="-6"/>
+        <delta pt="11" x="19" y="-6"/>
+        <delta pt="12" x="-42" y="-6"/>
+        <delta pt="13" x="-44" y="-5"/>
+        <delta pt="14" x="-43" y="0"/>
+        <delta pt="15" x="-41" y="0"/>
+        <delta pt="16" x="-43" y="0"/>
+        <delta pt="17" x="-44" y="4"/>
+        <delta pt="18" x="-40" y="7"/>
+        <delta pt="19" x="21" y="7"/>
+        <delta pt="20" x="26" y="4"/>
+        <delta pt="21" x="38" y="0"/>
+        <delta pt="22" x="40" y="0"/>
+        <delta pt="23" x="34" y="0"/>
+        <delta pt="24" x="-42" y="-6"/>
+        <delta pt="25" x="-44" y="-4"/>
+        <delta pt="26" x="-43" y="0"/>
+        <delta pt="27" x="0" y="0"/>
+        <delta pt="28" x="0" y="0"/>
+        <delta pt="29" x="0" y="0"/>
+        <delta pt="30" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="o">
+      <tuple>
+        <coord axis="slnt" value="-1.0"/>
+        <delta pt="0" x="-42" y="0"/>
+        <delta pt="1" x="-44" y="0"/>
+        <delta pt="2" x="-38" y="2"/>
+        <delta pt="3" x="-24" y="6"/>
+        <delta pt="4" x="-14" y="7"/>
+        <delta pt="5" x="18" y="7"/>
+        <delta pt="6" x="28" y="6"/>
+        <delta pt="7" x="41" y="3"/>
+        <delta pt="8" x="45" y="0"/>
+        <delta pt="9" x="44" y="0"/>
+        <delta pt="10" x="45" y="0"/>
+        <delta pt="11" x="40" y="-2"/>
+        <delta pt="12" x="27" y="-5"/>
+        <delta pt="13" x="16" y="-7"/>
+        <delta pt="14" x="-16" y="-7"/>
+        <delta pt="15" x="-26" y="-5"/>
+        <delta pt="16" x="-39" y="-3"/>
+        <delta pt="17" x="-44" y="0"/>
+        <delta pt="18" x="-42" y="0"/>
+        <delta pt="19" x="-40" y="0"/>
+        <delta pt="20" x="-24" y="4"/>
+        <delta pt="21" x="-14" y="7"/>
+        <delta pt="22" x="18" y="7"/>
+        <delta pt="23" x="27" y="5"/>
+        <delta pt="24" x="42" y="0"/>
+        <delta pt="25" x="44" y="0"/>
+        <delta pt="26" x="42" y="0"/>
+        <delta pt="27" x="27" y="-4"/>
+        <delta pt="28" x="16" y="-7"/>
+        <delta pt="29" x="-16" y="-7"/>
+        <delta pt="30" x="-25" y="-5"/>
+        <delta pt="31" x="-40" y="0"/>
+        <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"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="odieresis">
+      <tuple>
+        <coord axis="slnt" 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="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="uni0308">
+      <tuple>
+        <coord axis="slnt" value="-1.0"/>
+        <delta pt="0" x="75" y="0"/>
+        <delta pt="1" x="73" y="0"/>
+        <delta pt="2" x="73" y="5"/>
+        <delta pt="3" x="76" y="6"/>
+        <delta pt="4" x="82" y="5"/>
+        <delta pt="5" x="84" y="3"/>
+        <delta pt="6" x="82" y="0"/>
+        <delta pt="7" x="81" y="0"/>
+        <delta pt="8" x="82" y="0"/>
+        <delta pt="9" x="83" y="-6"/>
+        <delta pt="10" x="80" y="-6"/>
+        <delta pt="11" x="74" y="-5"/>
+        <delta pt="12" x="72" y="-4"/>
+        <delta pt="13" x="73" y="0"/>
+        <delta pt="14" x="75" y="0"/>
+        <delta pt="15" x="73" y="0"/>
+        <delta pt="16" x="73" y="5"/>
+        <delta pt="17" x="76" y="6"/>
+        <delta pt="18" x="82" y="5"/>
+        <delta pt="19" x="84" y="3"/>
+        <delta pt="20" x="82" y="0"/>
+        <delta pt="21" x="81" y="0"/>
+        <delta pt="22" x="82" y="0"/>
+        <delta pt="23" x="83" y="-6"/>
+        <delta pt="24" x="80" y="-6"/>
+        <delta pt="25" x="74" y="-5"/>
+        <delta pt="26" x="72" y="-4"/>
+        <delta pt="27" x="73" y="0"/>
+        <delta pt="28" x="0" y="0"/>
+        <delta pt="29" x="0" y="0"/>
+        <delta pt="30" x="0" y="0"/>
+        <delta pt="31" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/BuildMain.ttx b/Tests/varLib/data/test_results/BuildMain.ttx
index bf9a0b4..27d02d1 100644
--- a/Tests/varLib/data/test_results/BuildMain.ttx
+++ b/Tests/varLib/data/test_results/BuildMain.ttx
@@ -55,7 +55,7 @@
          will be recalculated by the compiler -->
     <version value="4"/>
     <xAvgCharWidth value="506"/>
-    <usWeightClass value="400"/>
+    <usWeightClass value="368"/>
     <usWidthClass value="5"/>
     <fsType value="00000000 00000100"/>
     <ySubscriptXSize value="650"/>
@@ -440,6 +440,9 @@
   </glyf>
 
   <name>
+    <namerecord nameID="257" platformID="0" platEncID="4" langID="0x0">
+      کنتراست
+    </namerecord>
     <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
       Weight
     </namerecord>
@@ -494,11 +497,11 @@
     <namerecord nameID="273" platformID="1" platEncID="0" langID="0x0" unicode="True">
       TestFamily-BlackHighContrast
     </namerecord>
-    <namerecord nameID="274" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      Weight
+    <namerecord nameID="257" platformID="1" platEncID="0" langID="0x2" unicode="True">
+      Kontrast
     </namerecord>
-    <namerecord nameID="275" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      Contrast
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x407">
+      Kontrast
     </namerecord>
     <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
       Test Family
@@ -578,12 +581,6 @@
     <namerecord nameID="273" platformID="3" platEncID="1" langID="0x409">
       TestFamily-BlackHighContrast
     </namerecord>
-    <namerecord nameID="274" platformID="3" platEncID="1" langID="0x409">
-      Weight
-    </namerecord>
-    <namerecord nameID="275" platformID="3" platEncID="1" langID="0x409">
-      Contrast
-    </namerecord>
   </name>
 
   <post>
@@ -618,84 +615,12 @@
 
   <GDEF>
     <Version value="0x00010003"/>
-    <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=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>
+    <GlyphClassDef>
+      <ClassDef glyph="uni0024" class="1"/>
+      <ClassDef glyph="uni0024.nostroke" class="1"/>
+      <ClassDef glyph="uni0041" class="1"/>
+      <ClassDef glyph="uni0061" class="1"/>
+    </GlyphClassDef>
   </GDEF>
 
   <HVAR>
@@ -770,18 +695,15 @@
       <VarData index="0">
         <!-- ItemCount=6 -->
         <NumShorts value="0"/>
-        <!-- VarRegionCount=5 -->
+        <!-- VarRegionCount=2 -->
         <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"/>
-        <Item index="0" value="[0, 0, 0, 0, 0]"/>
-        <Item index="1" value="[14, -28, 0, 0, 0]"/>
-        <Item index="2" value="[-10, 17, 0, 0, 0]"/>
-        <Item index="3" value="[-3, 32, 0, 0, 0]"/>
-        <Item index="4" value="[-7, 63, 0, 0, 0]"/>
-        <Item index="5" value="[-7, 63, 0, 0, 0]"/>
+        <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>
@@ -861,14 +783,11 @@
       <VarData index="0">
         <!-- ItemCount=2 -->
         <NumShorts value="0"/>
-        <!-- VarRegionCount=5 -->
+        <!-- VarRegionCount=2 -->
         <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"/>
-        <Item index="0" value="[-4, 13, 0, 0, 0]"/>
-        <Item index="1" value="[-2, 8, 0, 0, 0]"/>
+        <Item index="0" value="[-4, 13]"/>
+        <Item index="1" value="[-2, 8]"/>
       </VarData>
     </VarStore>
     <ValueRecord index="0">
@@ -882,22 +801,23 @@
   </MVAR>
 
   <STAT>
-    <Version value="0x00010000"/>
+    <Version value="0x00010001"/>
     <DesignAxisRecordSize value="8"/>
     <!-- DesignAxisCount=2 -->
     <DesignAxisRecord>
       <Axis index="0">
         <AxisTag value="wght"/>
-        <AxisNameID value="274"/>  <!-- Weight -->
+        <AxisNameID value="256"/>  <!-- Weight -->
         <AxisOrdering value="0"/>
       </Axis>
       <Axis index="1">
         <AxisTag value="cntr"/>
-        <AxisNameID value="275"/>  <!-- Contrast -->
+        <AxisNameID value="257"/>  <!-- Contrast -->
         <AxisOrdering value="1"/>
       </Axis>
     </DesignAxisRecord>
     <!-- AxisValueCount=0 -->
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
   </STAT>
 
   <cvar>
@@ -1017,7 +937,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="18" x="0" y="0"/>
         <delta pt="19" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1040,7 +960,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="-18"/>
+        <delta pt="18" x="0" y="0"/>
         <delta pt="19" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1131,509 +1051,6 @@
         <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"/>
@@ -1701,7 +1118,7 @@
         <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="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1770,7 +1187,7 @@
         <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="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -2050,7 +1467,7 @@
         <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="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -2119,7 +1536,7 @@
         <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="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -2332,6 +1749,515 @@
         <delta pt="65" 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="0"/>
+        <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="0"/>
+        <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="0"/>
+        <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="0"/>
+        <delta pt="64" x="0" y="0"/>
+      </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>
   </gvar>
 
+  <ltag>
+    <version value="1"/>
+    <flags value="0"/>
+    <LanguageTag tag="fa"/>
+  </ltag>
+
 </ttFont>
diff --git a/Tests/varLib/data/test_results/BuildTestCFF2.ttx b/Tests/varLib/data/test_results/BuildTestCFF2.ttx
new file mode 100644
index 0000000..c4b9377
--- /dev/null
+++ b/Tests/varLib/data/test_results/BuildTestCFF2.ttx
@@ -0,0 +1,267 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.42">
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>200.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- ExtraLight -->
+    <!-- PostScript: TestCFF2Roman-ExtraLight -->
+    <NamedInstance flags="0x0" postscriptNameID="258" subfamilyNameID="257">
+      <coord axis="wght" value="200.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <!-- PostScript: TestCFF2Roman-Light -->
+    <NamedInstance flags="0x0" postscriptNameID="260" subfamilyNameID="259">
+      <coord axis="wght" value="300.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <!-- PostScript: TestCFF2Roman-Regular -->
+    <NamedInstance flags="0x0" postscriptNameID="261" subfamilyNameID="2">
+      <coord axis="wght" value="400.0"/>
+    </NamedInstance>
+
+    <!-- Medium -->
+    <!-- PostScript: TestCFF2Roman-Medium -->
+    <NamedInstance flags="0x0" postscriptNameID="263" subfamilyNameID="262">
+      <coord axis="wght" value="500.0"/>
+    </NamedInstance>
+
+    <!-- Semibold -->
+    <!-- PostScript: TestCFF2Roman-Semibold -->
+    <NamedInstance flags="0x0" postscriptNameID="265" subfamilyNameID="264">
+      <coord axis="wght" value="600.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <!-- PostScript: TestCFF2Roman-Bold -->
+    <NamedInstance flags="0x0" postscriptNameID="267" subfamilyNameID="266">
+      <coord axis="wght" value="700.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <!-- PostScript: TestCFF2Roman-Black -->
+    <NamedInstance flags="0x0" postscriptNameID="269" subfamilyNameID="268">
+      <coord axis="wght" value="900.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <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="-12 0 0"/>
+                <blend value="0 0 0"/>
+                <blend value="486 -8 14"/>
+                <blend value="498 0 0"/>
+                <blend value="574 4 -8"/>
+                <blend value="586 0 0"/>
+                <blend value="638 6 -10"/>
+                <blend value="650 0 0"/>
+                <blend value="656 2 -2"/>
+                <blend value="668 0 0"/>
+                <blend value="712 6 -10"/>
+                <blend value="724 0 0"/>
+            </BlueValues>
+            <OtherBlues>
+                <blend value="-217 -17 29"/>
+                <blend value="-205 0 0"/>
+            </OtherBlues>
+            <BlueScale value="0.0625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW>
+                <blend value="67 -39 67"/>
+            </StdHW>
+            <StdVW>
+                <blend value="85 -51 87"/>
+            </StdVW>
+            <LanguageGroup value="0"/>
+            <ExpansionFactor value="0.06"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+          62 22 -38 1 blend
+          hmoveto
+          476 -44 76 1 blend
+          660 -476 44 -76 1 blend
+          -660 hlineto
+          109 59 -61 103 -27 45 2 blend
+          rmoveto
+          73 131 54 102 29 -47 45 -75 10 -18 4 -6 4 blend
+          4 0 52 -102 73 -131 10 -16 -4 6 27 -47 -45 75 4 blend
+          rlineto
+          -256 -76 128 1 blend
+          hlineto
+          -44 52 34 -56 -10 16 2 blend
+          rmoveto
+          461 75 -125 1 blend
+          vlineto
+          127 -232 -127 -229 27 -45 -38 64 -27 45 -37 61 4 blend
+          rlineto
+          171 277 5 -9 15 -25 2 blend
+          rmoveto
+          -50 93 -66 119 234 -6 10 -1 3 -28 48 49 -83 68 -114 5 blend
+          0 -65 -119 -49 -93 -29 47 -49 83 -5 9 1 -3 4 blend
+          rlineto
+          -4 hlineto
+          48 -48 -22 36 22 -36 2 blend
+          rmoveto
+          126 232 26 -44 38 -64 2 blend
+          0 -461 -126 229 -75 125 -26 44 37 -61 3 blend
+          rlineto
+        </CharString>
+        <CharString name="A">
+          31 19 -31 1 blend
+          hmoveto
+          86 -54 90 1 blend
+          hlineto
+          115 366 23 73 21 72 21 76 25 -42 30 -50 5 -9 7 -11 3 -4 -4 6 3 -7 6 -10 8 blend
+          rlinecurve
+          4 hlineto
+          20 -76 22 -72 23 -73 4 -6 -6 10 2 -3 4 -6 5 -9 -7 11 6 blend
+          rrcurveto
+          113 -366 90 25 -40 -30 50 -56 92 3 blend
+          0 -221 656 -96 -15 25 4 -6 68 -112 3 blend
+          0 -221 -656 -15 25 -4 6 2 blend
+          rlineto
+          117 199 -15 24 37 -61 2 blend
+          rmoveto
+          301 68 -301 -68 -8 15 -40 65 8 -15 40 -65 4 blend
+          hlineto
+        </CharString>
+        <CharString name="T">
+          258 26 -44 1 blend
+          hmoveto
+          84 585 217 71 -518 -71 217 -585 -52 88 47 -79 17 -30 -43 73 18 -28 43 -73 17 -30 -47 79 8 blend
+          hlineto
+        </CharString>
+        <CharString name="dollar">
+          248 35 -3 12 -28 4 2 blend
+          rmoveto
+          -39 -45 5 18 -46 -26 -26 6 17 10 6 32 6 0 -3 5 blend
+          hvcurveto
+          53 -36 -17 76 -17 36 -12 -17 -11 2 24 13 4 blend
+          rlineto
+          53 -12 -22 13 -24 -37 -1 8 3 10 0 -9 5 13 -19 5 blend
+          hhcurveto
+          -22 -14 -11 -20 -9 8 -4 6 -13 4 -3 6 -18 8 -5 5 blend
+          hvcurveto
+          -87 4 81 -59 107 2 -3 20 -4 -20 -10 8 5 0 32 5 blend
+          hhcurveto
+          136 82 76 107 82 -41 65 -135 47 -45 27 8 17 -23 8 4 10 -12 16 15 -17 1 3 1 -7 10 -2 9 blend
+          hvcurveto
+          -38 13 19 5 -5 -3 2 blend
+          rlineto
+          -71 23 -40 35 64 -22 -1 16 -1 -2 16 14 -11 4 -15 5 blend
+          vvcurveto
+          75 57 37 74 30 36 -5 -17 42 16 -14 3 -10 11 -14 14 -7 26 12 -1 -9 -9 1 -33 -7 2 10 9 blend
+          vhcurveto
+          -52 36 17 -76 14 -33 11 11 11 -7 -24 9 4 blend
+          rlineto
+          -52 12 25 -14 22 37 -23 -6 -1 -15 12 9 0 -11 17 5 blend
+          hhcurveto
+          19 17 10 21 8 -5 7 -9 12 -3 5 -7 20 -7 -3 5 blend
+          hvcurveto
+          86 -6 -80 60 -101 2 2 -18 -2 13 4 -12 -12 17 -20 5 blend
+          hhcurveto
+          -115 -83 -80 -102 -100 62 -54 105 -37 23 -43 1 -2 29 0 -6 -13 20 7 -17 4 1 -15 -13 16 -5 -2 9 blend
+          hvcurveto
+          37 -13 0 -5 -4 2 2 blend
+          rlineto
+          85 -30 36 -30 -63 29 -5 -22 2 -10 -13 -16 11 -2 10 5 blend
+          vvcurveto
+          -74 -53 -42 -82 -18 19 -12 10 -12 3 -8 10 4 blend
+          vhcurveto
+          31 287 -13 33 40 -12 2 blend
+          rmoveto
+          428 -40 -428 40 0 -11 18 -31 0 11 -18 31 4 blend
+          vlineto
+          -41 -437 19 -38 -12 8 2 blend
+          rmoveto
+          40 437 -40 -437 -18 31 12 -8 18 -31 -12 8 4 blend
+          hlineto
+        </CharString>
+        <CharString name="dollar.a">
+          304 7 -12 1 blend
+          34 rmoveto
+          125 86 65 96 -22 38 2 -3 -9 15 -2 4 4 blend
+          hvcurveto
+          183 -324 -21 110 1 -1 -14 22 -11 17 32 -54 4 blend
+          vvcurveto
+          50 42 32 67 68 36 -21 -36 47 18 -29 15 -24 12 -21 18 -31 8 -13 -2 3 -3 5 -2 4 -3 5 9 blend
+          vhcurveto
+          44 49 -24 40 -29 49 2 blend
+          rlineto
+          44 -46 -54 33 -89 -6 8 5 -7 9 -15 -1 3 4 -8 5 blend
+          hhcurveto
+          -115 -81 -59 -94 16 -26 3 -7 5 -9 6 -10 4 blend
+          hvcurveto
+          -174 324 22 -124 8 -14 14 -22 6 -10 -32 56 4 blend
+          vvcurveto
+          -51 -42 -35 -78 -76 -62 31 37 -52 -19 31 -14 23 -15 25 -25 41 -9 15 -4 7 7 -11 -3 3 12 -20 9 blend
+          vhcurveto
+          -39 -58 21 -35 36 -58 2 blend
+          rlineto
+          -43 52 84 -36 83 5 -11 -7 13 -11 17 -4 8 8 -13 5 blend
+          hhcurveto
+          -51 -147 -19 32 1 -3 2 blend
+          rmoveto
+          159 857 -56 7 -159 -858 56 -6 -1 1 3 -3 26 -44 -3 5 1 -1 -2 4 -26 44 2 -6 8 blend
+          rlineto
+        </CharString>
+      </CharStrings>
+      <VarStore Format="1">
+        <Format value="1"/>
+        <VarRegionList>
+          <!-- RegionAxisCount=1 -->
+          <!-- RegionCount=2 -->
+          <Region index="0">
+            <VarRegionAxis index="0">
+              <StartCoord value="-1.0"/>
+              <PeakCoord value="-1.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>
+          </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>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/FeatureVars.ttx b/Tests/varLib/data/test_results/FeatureVars.ttx
new file mode 100644
index 0000000..ca24f41
--- /dev/null
+++ b/Tests/varLib/data/test_results/FeatureVars.ttx
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.29">
+
+  <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>
+  </fvar>
+
+  <GSUB>
+    <Version value="0x00010001"/>
+    <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="rclt"/>
+        <Feature>
+          <!-- LookupCount=0 -->
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni0024" out="uni0024.nostroke"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni0041" out="uni0061"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni0061" out="uni0041"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+    <FeatureVariations>
+      <Version value="0x00010000"/>
+      <!-- FeatureVariationCount=4 -->
+      <FeatureVariationRecord index="0">
+        <ConditionSet>
+          <!-- ConditionCount=2 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="1"/>
+            <FilterRangeMinValue value="0.75"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+          <ConditionTable index="1" Format="1">
+            <AxisIndex value="0"/>
+            <FilterRangeMinValue value="0.20886"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=1 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=2 -->
+              <LookupListIndex index="0" value="0"/>
+              <LookupListIndex index="1" value="1"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+      <FeatureVariationRecord index="1">
+        <ConditionSet>
+          <!-- ConditionCount=2 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="1"/>
+            <FilterRangeMinValue value="0.0"/>
+            <FilterRangeMaxValue value="0.25"/>
+          </ConditionTable>
+          <ConditionTable index="1" Format="1">
+            <AxisIndex value="0"/>
+            <FilterRangeMinValue value="-1.0"/>
+            <FilterRangeMaxValue value="-0.45654"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=1 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=1 -->
+              <LookupListIndex index="0" value="2"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+      <FeatureVariationRecord index="2">
+        <ConditionSet>
+          <!-- ConditionCount=1 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="1"/>
+            <FilterRangeMinValue value="0.75"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=1 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=1 -->
+              <LookupListIndex index="0" value="1"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+      <FeatureVariationRecord index="3">
+        <ConditionSet>
+          <!-- ConditionCount=1 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="0"/>
+            <FilterRangeMinValue value="0.20886"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=1 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=1 -->
+              <LookupListIndex index="0" value="0"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+    </FeatureVariations>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/FeatureVarsCustomTag.ttx b/Tests/varLib/data/test_results/FeatureVarsCustomTag.ttx
new file mode 100644
index 0000000..3f9e1e0
--- /dev/null
+++ b/Tests/varLib/data/test_results/FeatureVarsCustomTag.ttx
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.29">
+
+  <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>
+  </fvar>
+
+  <GSUB>
+    <Version value="0x00010001"/>
+    <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="calt"/>
+        <Feature>
+          <!-- LookupCount=0 -->
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=3 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni0024" out="uni0024.nostroke"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni0041" out="uni0061"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni0061" out="uni0041"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+    <FeatureVariations>
+      <Version value="0x00010000"/>
+      <!-- FeatureVariationCount=4 -->
+      <FeatureVariationRecord index="0">
+        <ConditionSet>
+          <!-- ConditionCount=2 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="1"/>
+            <FilterRangeMinValue value="0.75"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+          <ConditionTable index="1" Format="1">
+            <AxisIndex value="0"/>
+            <FilterRangeMinValue value="0.20886"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=1 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=2 -->
+              <LookupListIndex index="0" value="0"/>
+              <LookupListIndex index="1" value="1"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+      <FeatureVariationRecord index="1">
+        <ConditionSet>
+          <!-- ConditionCount=2 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="1"/>
+            <FilterRangeMinValue value="0.0"/>
+            <FilterRangeMaxValue value="0.25"/>
+          </ConditionTable>
+          <ConditionTable index="1" Format="1">
+            <AxisIndex value="0"/>
+            <FilterRangeMinValue value="-1.0"/>
+            <FilterRangeMaxValue value="-0.45654"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=1 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=1 -->
+              <LookupListIndex index="0" value="2"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+      <FeatureVariationRecord index="2">
+        <ConditionSet>
+          <!-- ConditionCount=1 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="1"/>
+            <FilterRangeMinValue value="0.75"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=1 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=1 -->
+              <LookupListIndex index="0" value="1"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+      <FeatureVariationRecord index="3">
+        <ConditionSet>
+          <!-- ConditionCount=1 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="0"/>
+            <FilterRangeMinValue value="0.20886"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=1 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=1 -->
+              <LookupListIndex index="0" value="0"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+    </FeatureVariations>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/FeatureVarsWholeRange.ttx b/Tests/varLib/data/test_results/FeatureVarsWholeRange.ttx
new file mode 100644
index 0000000..8ae64da
--- /dev/null
+++ b/Tests/varLib/data/test_results/FeatureVarsWholeRange.ttx
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.9">
+
+  <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>
+  </fvar>
+
+  <GSUB>
+    <Version value="0x00010001"/>
+    <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="rclt"/>
+        <Feature>
+          <!-- LookupCount=0 -->
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni0024" out="uni0024.nostroke"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+    <FeatureVariations>
+      <Version value="0x00010000"/>
+      <!-- FeatureVariationCount=1 -->
+      <FeatureVariationRecord index="0">
+        <ConditionSet>
+          <!-- ConditionCount=0 -->
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=1 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=1 -->
+              <LookupListIndex index="0" value="0"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+    </FeatureVariations>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/FeatureVars_rclt.ttx b/Tests/varLib/data/test_results/FeatureVars_rclt.ttx
new file mode 100644
index 0000000..b889f3a
--- /dev/null
+++ b/Tests/varLib/data/test_results/FeatureVars_rclt.ttx
@@ -0,0 +1,249 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.29">
+
+  <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>
+  </fvar>
+
+  <GSUB>
+    <Version value="0x00010001"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="latn"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="1"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=1 -->
+          <LangSysRecord index="0">
+            <LangSysTag value="NLD "/>
+            <LangSys>
+              <ReqFeatureIndex value="65535"/>
+              <!-- FeatureCount=1 -->
+              <FeatureIndex index="0" value="0"/>
+            </LangSys>
+          </LangSysRecord>
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=2 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="rclt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+      <FeatureRecord index="1">
+        <FeatureTag value="rclt"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="1"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni0041" out="uni0061"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni0041" out="uni0061"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni0024" out="uni0024.nostroke"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni0041" out="uni0061"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="uni0061" out="uni0041"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+    <FeatureVariations>
+      <Version value="0x00010000"/>
+      <!-- FeatureVariationCount=4 -->
+      <FeatureVariationRecord index="0">
+        <ConditionSet>
+          <!-- ConditionCount=2 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="1"/>
+            <FilterRangeMinValue value="0.75"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+          <ConditionTable index="1" Format="1">
+            <AxisIndex value="0"/>
+            <FilterRangeMinValue value="0.20886"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=2 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=3 -->
+              <LookupListIndex index="0" value="0"/>
+              <LookupListIndex index="1" value="2"/>
+              <LookupListIndex index="2" value="3"/>
+            </Feature>
+          </SubstitutionRecord>
+          <SubstitutionRecord index="1">
+            <FeatureIndex value="1"/>
+            <Feature>
+              <!-- LookupCount=3 -->
+              <LookupListIndex index="0" value="1"/>
+              <LookupListIndex index="1" value="2"/>
+              <LookupListIndex index="2" value="3"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+      <FeatureVariationRecord index="1">
+        <ConditionSet>
+          <!-- ConditionCount=2 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="1"/>
+            <FilterRangeMinValue value="0.0"/>
+            <FilterRangeMaxValue value="0.25"/>
+          </ConditionTable>
+          <ConditionTable index="1" Format="1">
+            <AxisIndex value="0"/>
+            <FilterRangeMinValue value="-1.0"/>
+            <FilterRangeMaxValue value="-0.45654"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=2 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=2 -->
+              <LookupListIndex index="0" value="0"/>
+              <LookupListIndex index="1" value="4"/>
+            </Feature>
+          </SubstitutionRecord>
+          <SubstitutionRecord index="1">
+            <FeatureIndex value="1"/>
+            <Feature>
+              <!-- LookupCount=2 -->
+              <LookupListIndex index="0" value="1"/>
+              <LookupListIndex index="1" value="4"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+      <FeatureVariationRecord index="2">
+        <ConditionSet>
+          <!-- ConditionCount=1 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="1"/>
+            <FilterRangeMinValue value="0.75"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=2 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=2 -->
+              <LookupListIndex index="0" value="0"/>
+              <LookupListIndex index="1" value="3"/>
+            </Feature>
+          </SubstitutionRecord>
+          <SubstitutionRecord index="1">
+            <FeatureIndex value="1"/>
+            <Feature>
+              <!-- LookupCount=2 -->
+              <LookupListIndex index="0" value="1"/>
+              <LookupListIndex index="1" value="3"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+      <FeatureVariationRecord index="3">
+        <ConditionSet>
+          <!-- ConditionCount=1 -->
+          <ConditionTable index="0" Format="1">
+            <AxisIndex value="0"/>
+            <FilterRangeMinValue value="0.20886"/>
+            <FilterRangeMaxValue value="1.0"/>
+          </ConditionTable>
+        </ConditionSet>
+        <FeatureTableSubstitution>
+          <Version value="0x00010000"/>
+          <!-- SubstitutionCount=2 -->
+          <SubstitutionRecord index="0">
+            <FeatureIndex value="0"/>
+            <Feature>
+              <!-- LookupCount=2 -->
+              <LookupListIndex index="0" value="0"/>
+              <LookupListIndex index="1" value="2"/>
+            </Feature>
+          </SubstitutionRecord>
+          <SubstitutionRecord index="1">
+            <FeatureIndex value="1"/>
+            <Feature>
+              <!-- LookupCount=2 -->
+              <LookupListIndex index="0" value="1"/>
+              <LookupListIndex index="1" value="2"/>
+            </Feature>
+          </SubstitutionRecord>
+        </FeatureTableSubstitution>
+      </FeatureVariationRecord>
+    </FeatureVariations>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayout.ttx b/Tests/varLib/data/test_results/InterpolateLayout.ttx
index b1ea1e9..81e50fb 100644
--- a/Tests/varLib/data/test_results/InterpolateLayout.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayout.ttx
@@ -93,7 +93,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="A" out="A.sc"/>
         </SingleSubst>
       </Lookup>
@@ -101,7 +101,7 @@
         <LookupType value="1"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <SingleSubst index="0" Format="1">
+        <SingleSubst index="0">
           <Substitution in="a" out="a.alt"/>
         </SingleSubst>
       </Lookup>
@@ -109,7 +109,7 @@
         <LookupType value="2"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <MultipleSubst index="0" Format="1">
+        <MultipleSubst index="0">
           <Substitution in="ampersand" out="a,n,d"/>
         </MultipleSubst>
       </Lookup>
@@ -117,7 +117,7 @@
         <LookupType value="3"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <AlternateSubst index="0" Format="1">
+        <AlternateSubst index="0">
           <AlternateSet glyph="a">
             <Alternate glyph="a.alt"/>
             <Alternate glyph="A.sc"/>
@@ -128,7 +128,7 @@
         <LookupType value="4"/>
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
-        <LigatureSubst index="0" Format="1">
+        <LigatureSubst index="0">
           <LigatureSet glyph="f">
             <Ligature components="t" glyph="f_t"/>
           </LigatureSet>
@@ -141,11 +141,11 @@
         <ChainContextSubst index="0" Format="3">
           <!-- BacktrackGlyphCount=0 -->
           <!-- InputGlyphCount=1 -->
-          <InputCoverage index="0" Format="1">
+          <InputCoverage index="0">
             <Glyph value="a"/>
           </InputCoverage>
           <!-- LookAheadGlyphCount=1 -->
-          <LookAheadCoverage index="0" Format="1">
+          <LookAheadCoverage index="0">
             <Glyph value="t"/>
           </LookAheadCoverage>
           <!-- SubstCount=1 -->
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff.ttx
index cf8c96a..4180a33 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff.ttx
@@ -34,11 +34,11 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="A"/>
           </Coverage>
           <ValueFormat value="5"/>
-          <Value XPlacement="-88" XAdvance="-178"/>
+          <Value XPlacement="-88" XAdvance="-177"/>
         </SinglePos>
       </Lookup>
     </LookupList>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff2.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff2.ttx
index 58f0247..44a7558 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff2.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_diff2.ttx
@@ -34,14 +34,14 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="A"/>
             <Glyph value="a"/>
           </Coverage>
           <ValueFormat value="5"/>
           <!-- ValueCount=2 -->
-          <Value index="0" XPlacement="-88" XAdvance="-178"/>
-          <Value index="1" XPlacement="-28" XAdvance="-52"/>
+          <Value index="0" XPlacement="-88" XAdvance="-177"/>
+          <Value index="1" XPlacement="-27" XAdvance="-52"/>
         </SinglePos>
       </Lookup>
     </LookupList>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_same.ttx
index a61e75f..83407c1 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_same.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_1_same.ttx
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <SinglePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="A"/>
           </Coverage>
           <ValueFormat value="5"/>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff.ttx
index 4f94c37..0aeb497 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff.ttx
@@ -34,14 +34,14 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="A"/>
           </Coverage>
           <ValueFormat1 value="4"/>
           <ValueFormat2 value="0"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
           </ClassDef1>
-          <ClassDef2 Format="1">
+          <ClassDef2>
             <ClassDef glyph="a" class="1"/>
           </ClassDef2>
           <!-- Class1Count=1 -->
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff2.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff2.ttx
index 811ed58..f00c4c3 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff2.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_diff2.ttx
@@ -34,16 +34,16 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="A"/>
             <Glyph value="a"/>
           </Coverage>
           <ValueFormat1 value="4"/>
           <ValueFormat2 value="0"/>
-          <ClassDef1 Format="1">
+          <ClassDef1>
             <ClassDef glyph="a" class="1"/>
           </ClassDef1>
-          <ClassDef2 Format="1">
+          <ClassDef2>
             <ClassDef glyph="a" class="1"/>
           </ClassDef2>
           <!-- Class1Count=2 -->
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_same.ttx
index 9872533..3656964 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_same.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_class_same.ttx
@@ -34,14 +34,14 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="2">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="A"/>
           </Coverage>
           <ValueFormat1 value="4"/>
           <ValueFormat2 value="0"/>
-          <ClassDef1 Format="2">
+          <ClassDef1>
           </ClassDef1>
-          <ClassDef2 Format="1">
+          <ClassDef2>
             <ClassDef glyph="a" class="1"/>
           </ClassDef2>
           <!-- Class1Count=1 -->
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff.ttx
index 113bd0b..f85985b 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff.ttx
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="A"/>
           </Coverage>
           <ValueFormat1 value="4"/>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff2.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff2.ttx
index efc5ee5..b085109 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff2.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_diff2.ttx
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="A"/>
             <Glyph value="a"/>
           </Coverage>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_same.ttx
index 014c1ec..2a2a546 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_same.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_2_spec_same.ttx
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <PairPos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="A"/>
           </Coverage>
           <ValueFormat1 value="4"/>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_diff.ttx
index b640f10..993e0a6 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_diff.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_diff.ttx
@@ -34,18 +34,18 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <CursivePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="a"/>
           </Coverage>
           <!-- EntryExitCount=1 -->
           <EntryExitRecord index="0">
             <EntryAnchor Format="1">
               <XCoordinate value="49"/>
-              <YCoordinate value="28"/>
+              <YCoordinate value="29"/>
             </EntryAnchor>
             <ExitAnchor Format="1">
               <XCoordinate value="444"/>
-              <YCoordinate value="294"/>
+              <YCoordinate value="295"/>
             </ExitAnchor>
           </EntryExitRecord>
         </CursivePos>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_same.ttx
index b7c8a25..1d5ebcd 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_same.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_3_same.ttx
@@ -34,7 +34,7 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <CursivePos index="0" Format="1">
-          <Coverage Format="1">
+          <Coverage>
             <Glyph value="a"/>
           </Coverage>
           <!-- EntryExitCount=1 -->
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_diff.ttx
index a6e0227..7c50f96 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_diff.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_diff.ttx
@@ -34,10 +34,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <MarkBasePos index="0" Format="1">
-          <MarkCoverage Format="1">
+          <MarkCoverage>
             <Glyph value="uni0303"/>
           </MarkCoverage>
-          <BaseCoverage Format="1">
+          <BaseCoverage>
             <Glyph value="a"/>
           </BaseCoverage>
           <!-- ClassCount=1 -->
@@ -55,7 +55,7 @@
             <!-- BaseCount=1 -->
             <BaseRecord index="0">
               <BaseAnchor index="0" Format="1">
-                <XCoordinate value="272"/>
+                <XCoordinate value="273"/>
                 <YCoordinate value="510"/>
               </BaseAnchor>
             </BaseRecord>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_same.ttx
index 9b41519..ab96180 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_same.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_4_same.ttx
@@ -34,10 +34,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <MarkBasePos index="0" Format="1">
-          <MarkCoverage Format="1">
+          <MarkCoverage>
             <Glyph value="uni0303"/>
           </MarkCoverage>
-          <BaseCoverage Format="1">
+          <BaseCoverage>
             <Glyph value="a"/>
           </BaseCoverage>
           <!-- ClassCount=1 -->
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_diff.ttx
index 28480e7..28b5f91 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_diff.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_diff.ttx
@@ -34,10 +34,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <MarkLigPos index="0" Format="1">
-          <MarkCoverage Format="1">
+          <MarkCoverage>
             <Glyph value="uni0330"/>
           </MarkCoverage>
-          <LigatureCoverage Format="1">
+          <LigatureCoverage>
             <Glyph value="f_t"/>
           </LigatureCoverage>
           <!-- ClassCount=1 -->
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_same.ttx
index 4830f9a..0df08c0 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_same.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_5_same.ttx
@@ -34,10 +34,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <MarkLigPos index="0" Format="1">
-          <MarkCoverage Format="1">
+          <MarkCoverage>
             <Glyph value="uni0330"/>
           </MarkCoverage>
-          <LigatureCoverage Format="1">
+          <LigatureCoverage>
             <Glyph value="f_t"/>
           </LigatureCoverage>
           <!-- ClassCount=1 -->
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_diff.ttx
index e639bce..667d4f1 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_diff.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_diff.ttx
@@ -34,10 +34,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <MarkMarkPos index="0" Format="1">
-          <Mark1Coverage Format="1">
+          <Mark1Coverage>
             <Glyph value="uni0303"/>
           </Mark1Coverage>
-          <Mark2Coverage Format="1">
+          <Mark2Coverage>
             <Glyph value="uni0308"/>
           </Mark2Coverage>
           <!-- ClassCount=1 -->
@@ -56,7 +56,7 @@
             <Mark2Record index="0">
               <Mark2Anchor index="0" Format="1">
                 <XCoordinate value="0"/>
-                <YCoordinate value="702"/>
+                <YCoordinate value="703"/>
               </Mark2Anchor>
             </Mark2Record>
           </Mark2Array>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_same.ttx
index 05e4b51..34d0bff 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_same.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_6_same.ttx
@@ -34,10 +34,10 @@
         <LookupFlag value="0"/>
         <!-- SubTableCount=1 -->
         <MarkMarkPos index="0" Format="1">
-          <Mark1Coverage Format="1">
+          <Mark1Coverage>
             <Glyph value="uni0303"/>
           </Mark1Coverage>
-          <Mark2Coverage Format="1">
+          <Mark2Coverage>
             <Glyph value="uni0308"/>
           </Mark2Coverage>
           <!-- ClassCount=1 -->
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_7_diff.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_7_diff.ttx
new file mode 100644
index 0000000..14e1209
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_7_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>
+            <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>
+            <Glyph value="uni0303"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <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="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=1 -->
+            <PosRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=2 -->
+              <Input index="0" value="a"/>
+              <Input index="1" value="uni0303"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="1"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_7_same.ttx b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_7_same.ttx
new file mode 100644
index 0000000..eff24fc
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_7_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>
+            <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>
+            <Glyph value="uni0303"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <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="7"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <ContextPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+          </Coverage>
+          <!-- PosRuleSetCount=1 -->
+          <PosRuleSet index="0">
+            <!-- PosRuleCount=1 -->
+            <PosRule index="0">
+              <!-- GlyphCount=3 -->
+              <!-- PosCount=2 -->
+              <Input index="0" value="a"/>
+              <Input index="1" value="uni0303"/>
+              <PosLookupRecord index="0">
+                <SequenceIndex value="0"/>
+                <LookupListIndex value="0"/>
+              </PosLookupRecord>
+              <PosLookupRecord index="1">
+                <SequenceIndex value="2"/>
+                <LookupListIndex value="1"/>
+              </PosLookupRecord>
+            </PosRule>
+          </PosRuleSet>
+        </ContextPos>
+      </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
deleted file mode 100644
index d14f3b0..0000000
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_8_diff.ttx
+++ /dev/null
@@ -1,116 +0,0 @@
-<?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="272"/>
-                <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
deleted file mode 100644
index b7e86ba..0000000
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_8_same.ttx
+++ /dev/null
@@ -1,116 +0,0 @@
-<?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
index b0f8074..773dc59 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutGPOS_size_feat_same.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutGPOS_size_feat_same.ttx
@@ -25,7 +25,7 @@
           <FeatureParamsSize>
             <DesignSize value="10.0"/>
             <SubfamilyID value="0"/>
-            <SubfamilyNameID value="0"/>  <!-- missing from name table -->
+            <SubfamilyNameID value="0"/>
             <RangeStart value="0.0"/>
             <RangeEnd value="0.0"/>
           </FeatureParamsSize>
diff --git a/Tests/varLib/data/test_results/InterpolateLayoutMain.ttx b/Tests/varLib/data/test_results/InterpolateLayoutMain.ttx
index 0c0af32..49d491f 100644
--- a/Tests/varLib/data/test_results/InterpolateLayoutMain.ttx
+++ b/Tests/varLib/data/test_results/InterpolateLayoutMain.ttx
@@ -496,4 +496,14 @@
     </extraNames>
   </post>
 
+  <GDEF>
+    <Version value="0x00010003"/>
+    <GlyphClassDef>
+      <ClassDef glyph="uni0024" class="1"/>
+      <ClassDef glyph="uni0024.nostroke" class="1"/>
+      <ClassDef glyph="uni0041" class="1"/>
+      <ClassDef glyph="uni0061" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
 </ttFont>
diff --git a/Tests/varLib/data/test_results/InterpolateTestCFF2VF.ttx b/Tests/varLib/data/test_results/InterpolateTestCFF2VF.ttx
new file mode 100644
index 0000000..949e6da
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateTestCFF2VF.ttx
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.32">
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="84"/>
+    <mtx name="A" width="600" lsb="50"/>
+    <mtx name="T" width="600" lsb="50"/>
+    <mtx name="dollar" width="600" lsb="102"/>
+    <mtx name="glyph00003" width="600" lsb="102"/>
+  </hmtx>
+
+  <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 value="-12 0 478 490 570 582 640 652 660 672 722 734"/>
+            <OtherBlues value="-234 -222"/>
+            <BlueScale value="0.0625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="28"/>
+            <StdVW value="34"/>
+            <LanguageGroup value="0"/>
+            <ExpansionFactor value="0.06"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+          84 hmoveto
+          432 660 -432 hlineto
+          48 -628 rmoveto
+          102 176 64 106 rlineto
+          4 hlineto
+          62 -106 100 -176 rlineto
+          -342 42 rmoveto
+          536 vlineto
+          154 -270 rlineto
+          22 26 rmoveto
+          -56 92 -94 168 rlineto
+          302 hlineto
+          -94 -168 -54 -92 rlineto
+          22 -26 rmoveto
+          152 270 rlineto
+          -536 vlineto
+        </CharString>
+        <CharString name="A">
+          50 hmoveto
+          32 hlineto
+          140 396 28 80 24 68 24 82 rlinecurve
+          4 hlineto
+          24 -82 24 -68 28 -80 138 -396 rcurveline
+          34 hlineto
+          -236 660 rlineto
+          -28 hlineto
+          -134 -424 rmoveto
+          293 28 -293 hlineto
+        </CharString>
+        <CharString name="T">
+          284 hmoveto
+          32 632 234 28 -500 -28 234 hlineto
+        </CharString>
+        <CharString name="dollar">
+          311 34 rmoveto
+          103 88 56 94 hvcurveto
+          184 -338 -32 142 vvcurveto
+          68 57 44 85 76 34 -24 -38 44 vhcurveto
+          20 20 rlineto
+          38 -41 -45 32 -85 hhcurveto
+          -99 -78 -54 -88 hvcurveto
+          -166 338 28 -156 vvcurveto
+          -70 -56 -50 -103 -85 -66 38 34 -40 vhcurveto
+          -18 -22 45 -38 73 -40 91 0 rlinecurve
+          -18 566 rmoveto
+          30 hlineto
+          50 0 50 50 vvcurveto
+          -30 hlineto
+          -50 0 -50 -50 vvcurveto
+          -562 vmoveto
+          -148 30 148 vlineto
+        </CharString>
+        <CharString name="glyph00003">
+          311 34 rmoveto
+          103 88 56 94 hvcurveto
+          184 -338 -32 142 vvcurveto
+          68 57 44 85 76 34 -24 -38 44 vhcurveto
+          20 20 rlineto
+          38 -41 -45 32 -85 hhcurveto
+          -99 -78 -54 -88 hvcurveto
+          -166 338 28 -156 vvcurveto
+          -70 -56 -50 -103 -85 -66 38 34 -40 vhcurveto
+          -18 -22 rlineto
+          -38 45 73 -40 91 hhcurveto
+          -70 -146 rmoveto
+          158 860 -30 4 -158 -860 rlineto
+        </CharString>
+      </CharStrings>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/Mutator.ttx b/Tests/varLib/data/test_results/Mutator.ttx
index 9818654..71e5f28 100644
--- a/Tests/varLib/data/test_results/Mutator.ttx
+++ b/Tests/varLib/data/test_results/Mutator.ttx
@@ -55,7 +55,7 @@
          will be recalculated by the compiler -->
     <version value="4"/>
     <xAvgCharWidth value="506"/>
-    <usWeightClass value="400"/>
+    <usWeightClass value="500"/>
     <usWidthClass value="5"/>
     <fsType value="00000000 00000100"/>
     <ySubscriptXSize value="650"/>
@@ -154,7 +154,7 @@
 
     <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="560" yMax="666">
       <contour>
-        <pt x="83" y="0" on="1"/>
+        <pt x="83" y="0" on="1" overlap="1"/>
         <pt x="503" y="666" on="1"/>
         <pt x="557" y="666" on="1"/>
         <pt x="137" y="0" on="1"/>
@@ -190,7 +190,7 @@
 
     <TTGlyph name="uni0024" xMin="51" yMin="-115" xMax="474" yMax="746">
       <contour>
-        <pt x="251" y="31" on="1"/>
+        <pt x="251" y="31" on="1" overlap="1"/>
         <pt x="309" y="31" on="0"/>
         <pt x="379" y="92" on="0"/>
         <pt x="379" y="144" on="1"/>
@@ -262,7 +262,7 @@
 
     <TTGlyph name="uni0024.nostroke" xMin="51" yMin="-115" xMax="474" yMax="746">
       <contour>
-        <pt x="251" y="31" on="1"/>
+        <pt x="251" y="31" on="1" overlap="1"/>
         <pt x="308" y="31" on="0"/>
         <pt x="377" y="90" on="0"/>
         <pt x="377" y="142" on="1"/>
@@ -334,7 +334,7 @@
 
     <TTGlyph name="uni0041" xMin="7" yMin="0" xMax="656" yMax="670">
       <contour>
-        <pt x="7" y="0" on="1"/>
+        <pt x="7" y="0" on="1" overlap="1"/>
         <pt x="7" y="38" on="1"/>
         <pt x="104" y="53" on="1"/>
         <pt x="124" y="53" on="1"/>
@@ -370,7 +370,7 @@
 
     <TTGlyph name="uni0061" xMin="42" yMin="-14" xMax="511" yMax="490">
       <contour>
-        <pt x="42" y="110" on="1"/>
+        <pt x="42" y="110" on="1" overlap="1"/>
         <pt x="42" y="157" on="0"/>
         <pt x="110" y="229" on="0"/>
         <pt x="214" y="265" on="1"/>
@@ -440,66 +440,6 @@
   </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="274" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      Weight
-    </namerecord>
-    <namerecord nameID="275" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      Contrast
-    </namerecord>
     <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
       Test Family
     </namerecord>
@@ -524,66 +464,6 @@
     <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>
-    <namerecord nameID="274" platformID="3" platEncID="1" langID="0x409">
-      Weight
-    </namerecord>
-    <namerecord nameID="275" platformID="3" platEncID="1" langID="0x409">
-      Contrast
-    </namerecord>
   </name>
 
   <post>
@@ -616,4 +496,14 @@
     </extraNames>
   </post>
 
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef>
+      <ClassDef glyph="uni0024" class="1"/>
+      <ClassDef glyph="uni0024.nostroke" class="1"/>
+      <ClassDef glyph="uni0041" class="1"/>
+      <ClassDef glyph="uni0061" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
 </ttFont>
diff --git a/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx b/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx
new file mode 100755
index 0000000..28e0766
--- /dev/null
+++ b/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx
@@ -0,0 +1,297 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.32">
+
+  <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="1"/>
+    <maxStackElements value="2"/>
+    <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>
+
+  <fpgm>
+    <assembly>
+      PUSHB[ ]	/* 1 value pushed */
+      145
+      IDEF[ ]	/* InstructionDefinition */
+      NPUSHW[ ]	/* 3 values pushed */
+      2 -8192 8192
+      ENDF[ ]	/* EndFunctionDefinition */
+    </assembly>
+  </fpgm>
+
+  <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" overlap="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>
+        <assembly>
+          GETVARIATION[ ]	/* GetVariation */
+        </assembly>
+      </instructions>
+    </TTGlyph>
+
+    <TTGlyph name="b" xMin="76" yMin="-12" xMax="426" yMax="628">
+      <contour>
+        <pt x="218" y="468" on="1" overlap="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="0x404"/>
+    </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/data/test_results/Mutator_IUP-instance.ttx b/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
index 8b33fd7..1f7b651 100755
--- a/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
+++ b/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
@@ -57,7 +57,7 @@
     <version value="3"/>
     <xAvgCharWidth value="347"/>
     <usWeightClass value="400"/>
-    <usWidthClass value="5"/>
+    <usWidthClass value="3"/>
     <fsType value="00000000 00000100"/>
     <ySubscriptXSize value="700"/>
     <ySubscriptYSize value="650"/>
@@ -157,7 +157,7 @@
 
     <TTGlyph name="a" xMin="38" yMin="-12" xMax="388" yMax="468">
       <contour>
-        <pt x="312" y="0" on="1"/>
+        <pt x="312" y="0" on="1" overlap="1"/>
         <pt x="312" y="64" on="1"/>
         <pt x="244" y="-12" on="1"/>
         <pt x="180" y="-12" on="1"/>
@@ -185,7 +185,7 @@
 
     <TTGlyph name="b" xMin="76" yMin="-12" xMax="426" yMax="628">
       <contour>
-        <pt x="218" y="468" on="1"/>
+        <pt x="218" y="468" on="1" overlap="1"/>
         <pt x="284" y="468" on="1"/>
         <pt x="426" y="316" on="1"/>
         <pt x="426" y="140" on="1"/>
@@ -214,7 +214,7 @@
     <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"/>
+      <component glyphName="b" x="464" y="456" scale="-0.99994" flags="0x404"/>
     </TTGlyph>
 
     <TTGlyph name="space"/><!-- contains no outline data -->
@@ -237,15 +237,6 @@
     <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>
@@ -261,15 +252,6 @@
     <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>
diff --git a/Tests/varLib/data/test_results/SingleMaster.ttx b/Tests/varLib/data/test_results/SingleMaster.ttx
new file mode 100644
index 0000000..02cfe32
--- /dev/null
+++ b/Tests/varLib/data/test_results/SingleMaster.ttx
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.0">
+
+  <GDEF>
+    <Version value="0x00010003"/>
+    <GlyphClassDef>
+      <ClassDef glyph="uni0024" class="1"/>
+      <ClassDef glyph="uni0024.nostroke" class="1"/>
+      <ClassDef glyph="uni0041" class="1"/>
+      <ClassDef glyph="uni0061" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=0 -->
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=6 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=0 -->
+        <Item index="0" value="[]"/>
+        <Item index="1" value="[]"/>
+        <Item index="2" value="[]"/>
+        <Item index="3" value="[]"/>
+        <Item index="4" value="[]"/>
+        <Item index="5" value="[]"/>
+      </VarData>
+    </VarStore>
+  </HVAR>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=1 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=0 -->
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>400.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>400.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+  </gvar>
+
+  <name>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Weight
+    </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-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>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+  </name>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/SparseMasters.ttx b/Tests/varLib/data/test_results/SparseMasters.ttx
new file mode 100644
index 0000000..a3f8e61
--- /dev/null
+++ b/Tests/varLib/data/test_results/SparseMasters.ttx
@@ -0,0 +1,659 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <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="e"/>
+    <GlyphID id="3" name="s"/>
+    <GlyphID id="4" name="dotabovecomb"/>
+    <GlyphID id="5" name="edotabove"/>
+  </GlyphOrder>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="950"/>
+    <descent value="-250"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="-37"/>
+    <minRightSideBearing value="-50"/>
+    <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="6"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="18"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="17"/>
+    <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="2"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="580"/>
+    <usWeightClass value="350"/>
+    <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="300"/>
+    <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 01000101"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="97"/>
+    <usLastCharIndex value="775"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="950"/>
+    <usWinDescent value="250"/>
+    <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="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="a" width="600" lsb="9"/>
+    <mtx name="dotabovecomb" width="0" lsb="-37"/>
+    <mtx name="e" width="600" lsb="40"/>
+    <mtx name="edotabove" width="600" lsb="40"/>
+    <mtx name="s" width="600" lsb="25"/>
+  </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="0x65" name="e"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x117" name="edotabove"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x307" name="dotabovecomb"/><!-- COMBINING DOT ABOVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x65" name="e"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x117" name="edotabove"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x307" name="dotabovecomb"/><!-- COMBINING DOT ABOVE -->
+    </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="-250" xMax="450" yMax="750">
+      <contour>
+        <pt x="50" y="-250" on="1"/>
+        <pt x="450" y="-250" on="1"/>
+        <pt x="450" y="750" on="1"/>
+        <pt x="50" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-200" on="1"/>
+        <pt x="100" y="700" on="1"/>
+        <pt x="400" y="700" on="1"/>
+        <pt x="400" y="-200" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="9" yMin="-12" xMax="468" yMax="504">
+      <contour>
+        <pt x="468" y="-1" on="1"/>
+        <pt x="366" y="-3" on="1"/>
+        <pt x="363" y="357" on="1"/>
+        <pt x="208" y="397" on="1"/>
+        <pt x="36" y="337" on="1"/>
+        <pt x="9" y="428" on="1"/>
+        <pt x="214" y="504" on="1"/>
+        <pt x="447" y="434" on="1"/>
+      </contour>
+      <contour>
+        <pt x="378" y="263" on="1"/>
+        <pt x="382" y="207" on="1"/>
+        <pt x="88" y="172" on="1"/>
+        <pt x="86" y="126" on="1"/>
+        <pt x="161" y="74" on="1"/>
+        <pt x="383" y="134" on="1"/>
+        <pt x="389" y="71" on="1"/>
+        <pt x="168" y="-12" on="1"/>
+        <pt x="29" y="22" on="1"/>
+        <pt x="26" y="240" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dotabovecomb" xMin="-37" yMin="501" xMax="50" yMax="597">
+      <contour>
+        <pt x="-21" y="597" on="1"/>
+        <pt x="50" y="589" on="1"/>
+        <pt x="41" y="501" on="1"/>
+        <pt x="-37" y="503" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="e" xMin="40" yMin="-18" xMax="576" yMax="513">
+      <contour>
+        <pt x="127" y="228" on="1"/>
+        <pt x="125" y="298" on="1"/>
+        <pt x="480" y="292" on="1"/>
+        <pt x="317" y="416" on="1"/>
+        <pt x="147" y="263" on="1"/>
+        <pt x="229" y="75" on="1"/>
+        <pt x="509" y="129" on="1"/>
+        <pt x="526" y="45" on="1"/>
+        <pt x="188" y="-18" on="1"/>
+        <pt x="40" y="261" on="1"/>
+        <pt x="316" y="513" on="1"/>
+        <pt x="571" y="305" on="1"/>
+        <pt x="576" y="226" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="edotabove" xMin="40" yMin="-18" xMax="576" yMax="693">
+      <component glyphName="e" x="0" y="0" flags="0x204"/>
+      <component glyphName="dotabovecomb" x="313" y="96" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="25" yMin="-13" xMax="582" yMax="530">
+      <contour>
+        <pt x="559" y="459" on="1"/>
+        <pt x="539" y="376" on="1"/>
+        <pt x="326" y="442" on="1"/>
+        <pt x="213" y="366" on="1"/>
+        <pt x="582" y="174" on="1"/>
+        <pt x="304" y="-13" on="1"/>
+        <pt x="25" y="83" on="1"/>
+        <pt x="53" y="174" on="1"/>
+        <pt x="282" y="76" on="1"/>
+        <pt x="427" y="155" on="1"/>
+        <pt x="38" y="343" on="1"/>
+        <pt x="324" y="530" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Weight
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Layer Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      0.000;NONE;LayerFont-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Layer Font Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 0.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      LayerFont-Regular
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </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="dotabovecomb"/>
+      <psName name="edotabove"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010003"/>
+    <GlyphClassDef>
+      <ClassDef glyph="dotabovecomb" class="3"/>
+      <ClassDef glyph="e" class="1"/>
+    </GlyphClassDef>
+    <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="[1]"/>
+        <Item index="1" value="[88]"/>
+      </VarData>
+    </VarStore>
+  </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=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="dotabovecomb"/>
+          </MarkCoverage>
+          <BaseCoverage>
+            <Glyph value="e"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="-2"/>
+                <YCoordinate value="465"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="3">
+                <XCoordinate value="314"/>
+                <YCoordinate value="556"/>
+                <XDeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </XDeviceTable>
+                <YDeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </YDeviceTable>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <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="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="a">
+            <Ligature components="e,s,s" glyph="s"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=3 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.36365"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.36365"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=1 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=0 -->
+        <Item index="0" value="[]"/>
+      </VarData>
+    </VarStore>
+    <AdvWidthMap>
+      <Map glyph=".notdef" outer="0" inner="0"/>
+      <Map glyph="a" outer="0" inner="0"/>
+      <Map glyph="dotabovecomb" outer="0" inner="0"/>
+      <Map glyph="e" outer="0" inner="0"/>
+      <Map glyph="edotabove" outer="0" inner="0"/>
+      <Map glyph="s" outer="0" inner="0"/>
+    </AdvWidthMap>
+  </HVAR>
+
+  <MVAR>
+    <Version value="0x00010000"/>
+    <Reserved value="0"/>
+    <ValueRecordSize value="8"/>
+    <!-- ValueRecordCount=1 -->
+    <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=1 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="0"/>
+        <Item index="0" value="[-25]"/>
+      </VarData>
+    </VarStore>
+    <ValueRecord index="0">
+      <ValueTag value="undo"/>
+      <VarIdx value="0"/>
+    </ValueRecord>
+  </MVAR>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=1 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=0 -->
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>350.0</MinValue>
+      <DefaultValue>350.0</DefaultValue>
+      <MaxValue>625.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph="a">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="-59" y="2"/>
+        <delta pt="2" x="-59" y="-54"/>
+        <delta pt="3" x="0" y="-56"/>
+        <delta pt="4" x="0" y="-56"/>
+        <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="-1" y="-23"/>
+        <delta pt="10" x="77" y="7"/>
+        <delta pt="11" x="77" y="7"/>
+        <delta pt="12" x="40" y="28"/>
+        <delta pt="13" x="0" y="15"/>
+        <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"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="dotabovecomb">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-8" y="28"/>
+        <delta pt="1" x="13" y="16"/>
+        <delta pt="2" x="17" y="-13"/>
+        <delta pt="3" x="-27" y="-20"/>
+        <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"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="e">
+      <tuple>
+        <coord axis="wght" min="0.0" value="0.36365" max="1.0"/>
+        <delta pt="0" x="-1" y="-25"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="-84" y="5"/>
+        <delta pt="3" x="1" y="-29"/>
+        <delta pt="4" x="33" y="1"/>
+        <delta pt="5" x="35" y="41"/>
+        <delta pt="6" x="-2" y="28"/>
+        <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="-27"/>
+        <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"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.36365" value="1.0" max="1.0"/>
+        <delta pt="0" x="70" y="1"/>
+        <delta pt="1" x="70" y="1"/>
+        <delta pt="2" x="-76" y="1"/>
+        <delta pt="3" x="-16" y="-56"/>
+        <delta pt="4" x="70" y="1"/>
+        <delta pt="5" x="15" y="55"/>
+        <delta pt="6" x="15" y="55"/>
+        <delta pt="7" x="2" y="-45"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="-31" y="1"/>
+        <delta pt="10" x="-2" y="35"/>
+        <delta pt="11" x="25" y="-1"/>
+        <delta pt="12" x="25" y="-1"/>
+        <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"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="edotabove">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="1" x="-6" y="91"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="s">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="-2" y="-40"/>
+        <delta pt="2" x="-2" y="-40"/>
+        <delta pt="3" x="55" y="-9"/>
+        <delta pt="4" x="26" y="-33"/>
+        <delta pt="5" x="-20" y="-45"/>
+        <delta pt="6" x="-18" y="-4"/>
+        <delta pt="7" x="-27" y="52"/>
+        <delta pt="8" x="-61" y="43"/>
+        <delta pt="9" x="-80" y="-6"/>
+        <delta pt="10" x="-22" y="55"/>
+        <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"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/TestBASE.ttx b/Tests/varLib/data/test_results/TestBASE.ttx
new file mode 100644
index 0000000..23d9337
--- /dev/null
+++ b/Tests/varLib/data/test_results/TestBASE.ttx
@@ -0,0 +1,477 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.4">
+
+  <BASE>
+    <Version value="0x00010001"/>
+    <HorizAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=5 -->
+        <BaselineTag index="0" value="icfb"/>
+        <BaselineTag index="1" value="icft"/>
+        <BaselineTag index="2" value="ideo"/>
+        <BaselineTag index="3" value="idtp"/>
+        <BaselineTag index="4" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=6 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="3">
+                <Coordinate value="-75"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="1" Format="3">
+                <Coordinate value="835"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="3">
+                <Coordinate value="-75"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="1" Format="3">
+                <Coordinate value="835"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="3">
+                <Coordinate value="-75"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="1" Format="3">
+                <Coordinate value="835"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="hani"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="3">
+                <Coordinate value="-75"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="1" Format="3">
+                <Coordinate value="835"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="4">
+          <BaseScriptTag value="kana"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="3">
+                <Coordinate value="-75"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="1" Format="3">
+                <Coordinate value="835"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="5">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="3">
+                <Coordinate value="-75"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="1" Format="3">
+                <Coordinate value="835"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="-120"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="880"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </HorizAxis>
+    <VertAxis>
+      <BaseTagList>
+        <!-- BaseTagCount=5 -->
+        <BaselineTag index="0" value="icfb"/>
+        <BaselineTag index="1" value="icft"/>
+        <BaselineTag index="2" value="ideo"/>
+        <BaselineTag index="3" value="idtp"/>
+        <BaselineTag index="4" value="romn"/>
+      </BaseTagList>
+      <BaseScriptList>
+        <!-- BaseScriptCount=6 -->
+        <BaseScriptRecord index="0">
+          <BaseScriptTag value="DFLT"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="3">
+                <Coordinate value="45"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="1" Format="3">
+                <Coordinate value="955"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="1">
+          <BaseScriptTag value="cyrl"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="3">
+                <Coordinate value="45"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="1" Format="3">
+                <Coordinate value="955"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="2">
+          <BaseScriptTag value="grek"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="3">
+                <Coordinate value="45"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="1" Format="3">
+                <Coordinate value="955"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="3">
+          <BaseScriptTag value="hani"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="3">
+                <Coordinate value="45"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="1" Format="3">
+                <Coordinate value="955"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="4">
+          <BaseScriptTag value="kana"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="2"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="3">
+                <Coordinate value="45"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="1" Format="3">
+                <Coordinate value="955"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+        <BaseScriptRecord index="5">
+          <BaseScriptTag value="latn"/>
+          <BaseScript>
+            <BaseValues>
+              <DefaultIndex value="4"/>
+              <!-- BaseCoordCount=5 -->
+              <BaseCoord index="0" Format="3">
+                <Coordinate value="45"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="1" Format="3">
+                <Coordinate value="955"/>
+                <DeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </DeviceTable>
+              </BaseCoord>
+              <BaseCoord index="2" Format="1">
+                <Coordinate value="0"/>
+              </BaseCoord>
+              <BaseCoord index="3" Format="1">
+                <Coordinate value="1000"/>
+              </BaseCoord>
+              <BaseCoord index="4" Format="1">
+                <Coordinate value="120"/>
+              </BaseCoord>
+            </BaseValues>
+            <!-- BaseLangSysCount=0 -->
+          </BaseScript>
+        </BaseScriptRecord>
+      </BaseScriptList>
+    </VertAxis>
+    <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="[-17]"/>
+        <Item index="1" value="[17]"/>
+      </VarData>
+    </VarStore>
+  </BASE>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/TestNonMarkingCFF2.ttx b/Tests/varLib/data/test_results/TestNonMarkingCFF2.ttx
new file mode 100644
index 0000000..a78e6a6
--- /dev/null
+++ b/Tests/varLib/data/test_results/TestNonMarkingCFF2.ttx
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.42">
+
+  <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="-12 0"/>
+                <blend value="0 0"/>
+                <blend value="478 8"/>
+                <blend value="490 0"/>
+                <blend value="570 -4"/>
+                <blend value="582 0"/>
+                <blend value="640 -6"/>
+                <blend value="652 0"/>
+                <blend value="660 -2"/>
+                <blend value="672 0"/>
+                <blend value="722 -6"/>
+                <blend value="734 0"/>
+            </BlueValues>
+            <OtherBlues>
+                <blend value="-234 17"/>
+                <blend value="-222 0"/>
+            </OtherBlues>
+            <BlueScale value="0.0625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW>
+                <blend value="28 39"/>
+            </StdHW>
+            <StdVW>
+                <blend value="34 51"/>
+            </StdVW>
+            <LanguageGroup value="0"/>
+            <ExpansionFactor value="0.06"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef">
+        </CharString>
+        <CharString name="A">
+        </CharString>
+      </CharStrings>
+      <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=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=1 -->
+          <VarRegionIndex index="0" value="0"/>
+        </VarData>
+      </VarStore>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx b/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx
new file mode 100644
index 0000000..264a3d4
--- /dev/null
+++ b/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx
@@ -0,0 +1,1308 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.41">
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>200.0</MinValue>
+      <DefaultValue>200.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- Kanji-w0.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w0.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="258" subfamilyNameID="257">
+      <coord axis="wght" value="200.0"/>
+    </NamedInstance>
+
+    <!-- Kanji-w239.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w239.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="260" subfamilyNameID="259">
+      <coord axis="wght" value="324.6875"/>
+    </NamedInstance>
+
+    <!-- Kanji-w240.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w240.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="262" subfamilyNameID="261">
+      <coord axis="wght" value="325.0"/>
+    </NamedInstance>
+
+    <!-- Kanji-w439.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w439.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="264" subfamilyNameID="263">
+      <coord axis="wght" value="428.82353"/>
+    </NamedInstance>
+
+    <!-- Kanji-w440.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w440.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="266" subfamilyNameID="265">
+      <coord axis="wght" value="429.41176"/>
+    </NamedInstance>
+
+    <!-- Kanji-w499.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w499.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="268" subfamilyNameID="267">
+      <coord axis="wght" value="464.11765"/>
+    </NamedInstance>
+
+    <!-- Kanji-w500.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w500.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="270" subfamilyNameID="269">
+      <coord axis="wght" value="464.70589"/>
+    </NamedInstance>
+
+    <!-- Kanji-w599.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w599.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="272" subfamilyNameID="271">
+      <coord axis="wght" value="535.45454"/>
+    </NamedInstance>
+
+    <!-- Kanji-w600.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w600.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="274" subfamilyNameID="273">
+      <coord axis="wght" value="536.36363"/>
+    </NamedInstance>
+
+    <!-- Kanji-w669.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w669.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="276" subfamilyNameID="275">
+      <coord axis="wght" value="599.09091"/>
+    </NamedInstance>
+
+    <!-- Kanji-w670.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w670.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="278" subfamilyNameID="277">
+      <coord axis="wght" value="600.0"/>
+    </NamedInstance>
+
+    <!-- Kanji-w699.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w699.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="280" subfamilyNameID="279">
+      <coord axis="wght" value="626.36363"/>
+    </NamedInstance>
+
+    <!-- Kanji-w700.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w700.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="282" subfamilyNameID="281">
+      <coord axis="wght" value="627.27272"/>
+    </NamedInstance>
+
+    <!-- Kanji-799.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-799.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="284" subfamilyNameID="283">
+      <coord axis="wght" value="717.27272"/>
+    </NamedInstance>
+
+    <!-- Kanji-w800.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w800.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="286" subfamilyNameID="285">
+      <coord axis="wght" value="718.18182"/>
+    </NamedInstance>
+
+    <!-- Kanji-w889.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w889.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="288" subfamilyNameID="287">
+      <coord axis="wght" value="799.09091"/>
+    </NamedInstance>
+
+    <!-- Kanji-w890.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w890.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="290" subfamilyNameID="289">
+      <coord axis="wght" value="800.0"/>
+    </NamedInstance>
+
+    <!-- Kanji-w1000.00 -->
+    <!-- PostScript: SHSansJPVFTest-Kanji-w1000.00 -->
+    <NamedInstance flags="0x0" postscriptNameID="292" subfamilyNameID="291">
+      <coord axis="wght" value="900.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <CFF2>
+    <major value="2"/>
+    <minor value="0"/>
+    <CFFFont name="CFF2Font">
+      <FontMatrix value="0.001 0 0 0.001 0 0"/>
+      <FDSelect format="3"/>
+      <FDArray>
+        <FontDict index="0">
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <FamilyBlues value="-250 -240 1100 1110"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+          </Private>
+        </FontDict>
+        <FontDict index="1">
+          <Private>
+            <BlueValues value="-250 -250 1100 1100"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="0"/>
+            <StdHW value="1"/>
+            <StdVW value="1"/>
+            <LanguageGroup value="1"/>
+            <ExpansionFactor value="0.06"/>
+          </Private>
+        </FontDict>
+        <FontDict index="2">
+          <Private>
+            <BlueValues value="0 0"/>
+            <BlueScale value="0.039625"/>
+            <BlueShift value="7"/>
+            <BlueFuzz value="1"/>
+            <LanguageGroup value="0"/>
+            <ExpansionFactor value="0.06"/>
+          </Private>
+        </FontDict>
+      </FDArray>
+      <CharStrings>
+        <CharString name=".notdef" fdSelectIndex="0">
+          -120 50 859 91 -50 50 hstemhm
+          100 50 700 50 vstemhm
+          hintmask 10111000
+          100 -120 rmoveto
+          800 1000 -800 hlineto
+          400 -459 rmoveto
+          -318 409 rlineto
+          636 hlineto
+          -286 -450 rmoveto
+          hintmask 11011000
+          318 409 rlineto
+          -818 vlineto
+          -668 -41 rmoveto
+          318 409 318 -409 rlineto
+          -668 859 rmoveto
+          318 -409 -318 -409 rlineto
+        </CharString>
+        <CharString name="cid00001" fdSelectIndex="2">
+        </CharString>
+        <CharString name="cid00002" fdSelectIndex="2">
+          -12 83 1 126 2 blend
+          hstemhm
+          74 73 -57 40 -26 129 -125 122 4 blend
+          vstemhm
+          hintmask 10100000
+          134 755 107 18 2 blend
+          rmoveto
+          -48 -137 1 blend
+          hlineto
+          8 -565 16 50 2 blend
+          rlineto
+          32 106 1 blend
+          hlineto
+          hintmask 11000000
+          -17 -202 -52 -67 2 blend
+          rmoveto
+          24 14 18 22 25 -14 18 -22 -23 -14 -21 -24 -18 13 -20 22 38 25 26 38 37 -28 25 -37 -34 -30 -22 -38 -40 27 -26 39 16 blend
+          hvcurveto
+        </CharString>
+        <CharString name="cid01177" fdSelectIndex="1">
+          1 vsindex
+          -72 30 253 30 94 30 92 30 65 30 131 45 -30 112 -99 17 -8 0 -12 59 -2 85 -64 2 -92 39 -1 56 -44 2 -63 35 -1 51 -43 1 -62 39 -1 56 -21 0 -31 56 -2 81 -56 2 -81 44 -2 63 -57 3 -81 31 -1 45 -18 0 -27 44 -2 63 16 blend
+          hstemhm
+          193 30 83 30 147 30 173 30 66 30 -20 1 -28 75 -3 107 -74 3 -106 78 -4 111 -99 5 -141 81 -3 116 -79 2 -114 64 -2 92 -81 4 -115 80 -4 114 10 blend
+          vstemhm
+          hintmask 1111100011111000
+          306 142 -19 1 -27 7 0 10 2 blend
+          rmoveto
+          -156 45 -2 64 1 blend
+          vlineto
+          -50 22 -8 79 -42 2 -59 8 -1 11 -18 0 -27 43 -1 62 4 blend
+          vhcurveto
+          17 186 8 -1 11 -68 3 -97 2 blend
+          0 18 8 0 12 1 blend
+          hhcurveto
+          70 13 25 114 5 22 -1 31 17 -1 24 2 1 4 0 -1 -1 7 0 10 5 blend
+          hvcurveto
+          -9 3 -12 4 -9 6 -20 1 -28 3 0 4 -31 1 -45 10 0 15 -13 0 -19 9 -1 13 6 blend
+          rrcurveto
+          -109 -4 -7 -13 -49 -38 -156 33 -1 47 -1 0 -1 1 0 1 2 0 3 10 0 15 9 0 13 60 -3 85 7 blend
+          0 -27 5 0 8 1 blend
+          hhcurveto
+          -59 -10 5 22 11 0 16 2 -1 2 -1 0 -2 4 0 6 4 blend
+          hvcurveto
+          157 -47 2 -67 1 blend
+          vlineto
+          63 34 -74 3 -106 -25 1 -36 2 blend
+          rmoveto
+          65 -30 74 -47 37 -37 -7 1 -10 5 -1 7 -3 0 -4 5 0 7 -3 0 -4 6 0 9 6 blend
+          rrcurveto
+          20 22 -37 36 -75 47 -65 28 48 -2 68 47 -2 67 -1 0 -1 -6 1 -8 2 0 3 -8 0 -11 8 -1 11 -6 0 -9 8 blend
+          rlinecurve
+          320 -64 -49 3 -69 -32 1 -46 2 blend
+          rmoveto
+          76 -49 83 -75 38 -12 0 -18 -6 1 -8 -13 0 -19 -4 0 -6 -9 1 -12 5 blend
+          -54 rrcurveto
+          23 19 -38 54 -84 73 -76 49 69 -3 98 35 -2 50 5 0 8 2 0 3 11 0 16 2 0 2 13 -1 18 2 0 4 8 blend
+          rlinecurve
+          -557 -5 -85 4 -121 -6 0 -9 2 blend
+          rmoveto
+          -28 -68 -50 -72 -77 -40 5 -1 6 1 0 1 3 1 5 6 0 9 13 -1 18 1 0 1 6 blend
+          rrcurveto
+          24 -17 79 42 47 74 31 71 62 -3 89 -42 2 -60 -7 1 -10 5 -1 7 -6 0 -8 1 0 1 -2 -1 -4 4 1 7 8 blend
+          rlinecurve
+          -117 625 -26 2 -36 42 -3 59 2 blend
+          rmoveto
+          -30 775 30 -57 3 -81 -3 0 -5 57 -3 81 3 blend
+          vlineto
+          -818 -176 -1 0 -2 12 0 18 2 blend
+          rmoveto
+          -30 869 30 -56 2 -81 3 0 5 56 -2 81 3 blend
+          vlineto
+          hintmask 0000001000100000
+          -455 258 -40 2 -57 -38 2 -54 2 blend
+          rmoveto
+          hintmask 0000000100100000
+          -99 30 -18 0 -27 81 -3 116 2 blend
+          vlineto
+          hintmask 0000001000100000
+          99 18 0 27 1 blend
+          vlineto
+          hintmask 0111010010001000
+          -236 -127 -60 2 -86 -18 0 -27 2 blend
+          rmoveto
+          26 -40 25 -53 9 -36 -12 1 -17 10 0 15 -10 -1 -15 13 0 19 -4 0 -6 11 -1 15 6 blend
+          rrcurveto
+          29 12 -10 35 -25 53 -27 39 76 -3 109 12 0 18 3 1 5 -10 0 -15 10 -1 14 -15 1 -21 11 0 16 -11 0 -16 8 blend
+          rlinecurve
+          393 2 -112 4 -161 -1 0 -2 2 blend
+          rmoveto
+          -16 -38 -31 -57 -23 -35 7 0 11 12 -1 17 13 -1 18 19 0 28 10 0 14 8 -1 11 6 blend
+          rrcurveto
+          27 -12 24 36 26 48 23 46 70 -2 101 -10 1 -14 -8 0 -12 -13 1 -18 -6 -1 -9 -17 0 -25 -1 1 -1 -10 1 -14 8 blend
+          rlinecurve
+          -504 -378 27 -1 39 -8 0 -12 2 blend
+          rmoveto
+          559 -94 -559 -110 5 -157 44 -2 63 110 -5 157 3 blend
+          hlineto
+          216 -52 2 -74 1 blend
+          vmoveto
+          559 -92 -559 -110 5 -157 43 -1 62 110 -5 157 3 blend
+          hlineto
+          -30 122 -75 3 -107 -4 0 -6 2 blend
+          rmoveto
+          -276 619 276 -26 0 -38 45 -2 64 26 0 38 3 blend
+          vlineto
+        </CharString>
+        <CharString name="cid06449" fdSelectIndex="1">
+          2 vsindex
+          -60 30 203 30 -9 9 67 7 -7 14 -14 30 -20 20 80 30 59 30 121 30 18 93 -30 30 -30 108 -23 0 -26 67 2 76 -98 -2 -111 42 0 47 -13 0 -14 13 0 14 -33 0 -37 11 0 13 -11 0 -13 8 0 8 -8 0 -8 53 0 60 -32 0 -36 32 0 36 -52 0 -59 57 1 65 -33 0 -38 53 0 60 -83 -1 -93 54 0 60 -6 -19 -24 33 19 55 -76 -1 -86 76 1 86 -76 -1 -86 59 1 67 26 blend
+          hstemhm
+          77 30 42 30 139 30 23 30 71 10 74 30 15 30 16 30 158 30 28 30 -4 29 -14 0 -16 88 1 99 -82 -1 -92 87 1 98 -130 -1 -146 102 1 114 -73 -1 -82 74 2 84 -112 -2 -126 27 0 30 13 0 15 90 1 101 -126 -1 -142 75 1 84 -68 -1 -76 102 1 115 -144 -1 -162 94 1 105 -79 -1 -88 95 1 106 -81 -1 -91 74 1 83 22 blend
+          vstemhm
+          hintmask 110001011101011101101101
+          53 761 -3 0 -3 31 0 35 2 blend
+          rmoveto
+          -30 896 30 -76 -1 -86 5 0 5 76 1 86 3 blend
+          vlineto
+          -802 -461 2 0 2 -23 0 -26 2 blend
+          rmoveto
+          -30 703 30 -53 0 -60 3 0 4 53 0 60 3 blend
+          vlineto
+          hintmask 000000000000100100000000
+          -532 539 -58 -1 -65 6 0 7 2 blend
+          rmoveto
+          hintmask 000000000010000100000000
+          -171 30 -16 -19 -36 102 1 114 2 blend
+          vlineto
+          hintmask 000000000000100100001000
+          171 16 19 36 1 blend
+          vlineto
+          299 -100 -1 -112 1 blend
+          hmoveto
+          hintmask 000000000010000000001000
+          -171 30 -16 -19 -36 102 1 115 2 blend
+          vlineto
+          hintmask 000000000000100000001000
+          171 16 19 36 1 blend
+          vlineto
+          hintmask 000000111100011010010100
+          -46 -219 -34 0 -39 -64 -1 -72 2 blend
+          rmoveto
+          204 -121 -204 -110 -1 -123 83 1 93 110 1 123 3 blend
+          hlineto
+          -230 121 33 1 38 -83 -1 -93 2 blend
+          rmoveto
+          200 -121 -200 -108 -2 -122 83 1 93 108 2 122 3 blend
+          hlineto
+          -222 121 27 -1 30 -83 -1 -93 2 blend
+          rmoveto
+          192 -121 -192 -101 -1 -114 83 1 93 101 1 114 3 blend
+          hlineto
+          -30 151 -87 -1 -98 -29 0 -33 2 blend
+          rmoveto
+          -181 716 181 -24 0 -27 11 0 12 24 0 27 3 blend
+          vlineto
+          -788 -240 -17 0 -19 9 0 11 2 blend
+          rmoveto
+          -130 30 100 -37 0 -42 88 1 99 -20 0 -23 3 blend
+          vlineto
+          hintmask 000000110000000000000010
+          786 -100 30 130 -150 -1 -168 20 0 23 95 1 106 37 0 42 4 blend
+          hlineto
+          hintmask 000010000000000100000000
+          -610 -123 -56 -1 -63 -44 0 -50 2 blend
+          rmoveto
+          -50 -62 -93 -73 -118 -54 8 -4 10 -9 6 -7 9 0 11 13 0 15 19 0 21 29 0 32 9 0 10 22 0 25 12 0 14 -11 0 -12 19 0 21 -26 0 -30 7 0 8 -16 0 -18 12 blend
+          rrcurveto
+          hintmask 010000000000000001000000
+          121 58 92 75 59 70 3 0 3 -13 0 -14 -10 0 -11 -19 0 -21 -2 0 -2 8 0 8 6 blend
+          rrcurveto
+          124 -78 -89 -1 -100 32 0 36 2 blend
+          rmoveto
+          -7 -6 0 -6 1 blend
+          vlineto
+          -65 -139 -176 -81 -162 -31 6 -6 8 -12 3 -8 16 0 17 30 0 34 36 0 41 26 0 29 -7 0 -8 12 0 13 12 0 14 -16 0 -18 15 0 16 -30 0 -33 5 0 6 -18 0 -21 12 blend
+          rrcurveto
+          hintmask 001000000000000001000000
+          168 37 178 84 72 154 26 0 29 -5 0 -5 -23 0 -26 -12 0 -14 -5 0 -5 6 0 7 6 blend
+          rrcurveto
+          hintmask 110100000000000001100001
+          -19 11 -6 -2 -47 0 -53 13 0 15 -13 0 -15 0 0 -1 4 blend
+          rlineto
+          -333 -72 75 1 85 -55 0 -61 2 blend
+          rmoveto
+          65 -25 75 -46 38 -35 -35 0 -40 8 0 9 -38 0 -42 15 0 17 -18 0 -21 14 0 15 6 blend
+          rrcurveto
+          26 19 -39 34 -76 45 -64 25 49 0 56 31 0 35 19 0 21 -14 0 -16 39 0 44 -18 0 -20 32 0 36 -9 0 -10 8 blend
+          rlinecurve
+          72 55 -55 0 -62 28 0 31 2 blend
+          rmoveto
+          -30 -30 -42 0 -47 -42 0 -47 2 blend
+          rlineto
+          269 30 -14 0 -16 42 0 47 2 blend
+          hlineto
+          74 74 13 0 15 -22 0 -24 2 blend
+          rmoveto
+          -276 80 1 90 1 blend
+          vlineto
+          -52 21 -9 77 -48 0 -54 8 0 9 -21 0 -24 44 0 49 4 blend
+          vhcurveto
+          16 182 8 0 9 -90 -1 -101 2 blend
+          0 18 8 0 9 1 blend
+          hhcurveto
+          62 12 21 88 4 25 0 28 20 0 22 6 0 7 10 0 11 9 0 10 5 blend
+          hvcurveto
+          -9 2 -12 5 -8 6 -24 0 -26 4 0 5 -34 0 -39 12 0 13 -16 0 -18 10 0 11 6 blend
+          rrcurveto
+          -81 25 0 28 1 blend
+          -4 -6 -11 -41 -37 -154 -1 0 -1 0 1 1 11 -1 12 15 1 17 79 1 89 5 blend
+          0 -26 9 0 10 1 blend
+          hhcurveto
+          -56 -9 6 25 17 0 19 2 0 3 -1 -1 -2 4 0 5 4 blend
+          hvcurveto
+          276 -81 -1 -91 1 blend
+          vlineto
+          278 -62 -114 -1 -128 32 0 36 2 blend
+          rmoveto
+          -66 -32 -126 -33 -107 -23 5 -7 5 -10 2 -7 110 22 126 32 81 36 10 0 11 7 0 8 30 0 34 11 0 12 21 0 23 9 0 10 7 0 8 -14 0 -16 9 0 10 -27 0 -30 3 0 4 -15 0 -17 -15 0 -17 -10 0 -11 -12 0 -14 -11 0 -12 3 0 4 -3 0 -4 18 blend
+          rrcurveto
+        </CharString>
+        <CharString name="cid06821" fdSelectIndex="1">
+          3 vsindex
+          -58 30 100 30 70 22 -22 30 94 30 19 31 -17 28 152 20 -20 30 -12 12 66 30 -30 89 -5 30 -30 121 -11 0 -24 36 0 81 -32 0 -74 22 0 52 -17 0 -39 16 1 37 -16 -1 -37 21 0 48 -27 0 -63 21 0 49 -11 0 -26 41 0 93 -47 0 -107 24 0 56 -34 0 -78 11 0 26 -11 0 -26 17 0 39 -15 0 -35 15 0 35 -19 0 -43 12 0 26 -12 0 -26 4 0 8 -5 0 -11 28 0 65 -28 0 -65 23 0 52 28 blend
+          hstemhm
+          127 30 -18 18 199 30 -20 20 -20 30 -24 14 97 30 -11 11 72 31 202 30 87 29 -12 0 -27 44 1 101 -19 -1 -45 19 1 45 -46 -1 -106 37 0 85 -31 0 -71 31 0 71 -31 0 -71 40 0 91 -27 0 -62 18 0 42 -47 0 -108 51 0 117 -27 0 -62 27 0 62 -53 0 -122 43 0 99 -60 -1 -138 52 1 120 -32 0 -73 32 0 72 22 blend
+          vstemhm
+          hintmask 00011000000000000000000100000000
+          193 296 41 0 93 -8 0 -19 2 blend
+          rmoveto
+          625 -94 -625 -84 -1 -192 27 0 63 84 1 192 3 blend
+          hlineto
+          -30 124 -48 0 -110 -6 0 -14 2 blend
+          rmoveto
+          -154 685 154 -15 0 -34 16 0 38 15 0 34 3 blend
+          vlineto
+          hintmask 00100000000000000000100000000000
+          -365 -132 -33 0 -76 1 1 3 2 blend
+          rmoveto
+          -232 -7 -1 -16 1 blend
+          vlineto
+          30 -5 51 0 117 -11 0 -27 2 blend
+          rlineto
+          237 18 1 43 1 blend
+          vlineto
+          hintmask 01000000000010010000010000000000
+          -11 -92 -27 0 -62 1 -1 2 2 blend
+          rmoveto
+          -30 397 30 -22 0 -52 -12 0 -27 22 0 52 3 blend
+          vlineto
+          -760 647 25 0 56 -4 0 -9 2 blend
+          rmoveto
+          -30 811 30 -28 0 -65 -12 0 -27 28 0 65 3 blend
+          vlineto
+          hintmask 00000000000010100000000000000000
+          -823 -13 0 -29 1 blend
+          hmoveto
+          -143 12 0 27 1 blend
+          vlineto
+          -83 -13 -107 -75 -82 4 0 9 3 0 6 5 1 12 -1 0 -1 5 -1 11 5 blend
+          vhcurveto
+          7 -4 11 -9 5 -6 10 0 21 -5 0 -12 20 0 46 -17 0 -38 6 0 15 -8 0 -18 6 blend
+          rrcurveto
+          79 5 0 11 1 blend
+          85 16 118 88 1 1 3 9 0 19 6 0 15 3 blend
+          vvcurveto
+          143 -11 0 -25 1 blend
+          vlineto
+          hintmask 00000000010100001000000000000000
+          199 -25 -46 -1 -106 -23 0 -54 2 blend
+          rmoveto
+          -167 vlineto
+          hintmask 00000000010100000100000000000000
+          30 37 0 85 1 blend
+          167 hlineto
+          hintmask 00000000101000000001000000000000
+          -14 -59 -18 0 -42 8 0 18 2 blend
+          rmoveto
+          -30 185 30 -12 0 -26 -4 0 -9 12 0 26 3 blend
+          vlineto
+          -365 -96 10 0 22 7 0 17 2 blend
+          rmoveto
+          -30 392 30 -17 0 -39 -4 0 -9 17 0 39 3 blend
+          vlineto
+          hintmask 00000011000000000100000000000000
+          -218 -10 -15 0 -33 -6 0 -13 2 blend
+          rmoveto
+          -160 23 0 51 1 blend
+          vlineto
+          -8 -2 0 0 -1 1 blend
+          -3 -11 -1 1 0 3 0 0 1 2 blend
+          vhcurveto
+          -11 -1 -30 2 0 4 1 0 1 4 0 10 3 blend
+          0 -47 13 0 30 1 blend
+          1 5 -9 6 -10 2 -9 4 0 8 -6 0 -13 6 0 13 -11 0 -25 2 0 6 -8 0 -19 6 blend
+          rrcurveto
+          hintmask 00000011000001000010001000000000
+          50 30 -5 0 -11 1 0 2 2 blend
+          0 6 17 3 0 8 5 1 12 2 blend
+          hvcurveto
+          17 5 4 9 21 6 -1 12 4 0 9 1 0 3 4 0 8 11 0 25 5 blend
+          vvcurveto
+          159 -21 0 -46 1 blend
+          vlineto
+          -132 -50 -39 0 -88 1 0 1 2 blend
+          rmoveto
+          -25 -42 -40 -39 -44 -30 8 -4 13 -10 5 -4 41 6 0 12 3 0 8 7 0 16 3 0 5 5 0 13 1 0 4 6 0 13 -3 0 -8 10 0 22 -6 0 -14 5 0 12 -5 0 -10 -3 0 -8 13 blend
+          30 45 -7 0 -14 1 blend
+          47 26 45 -3 0 -8 1 0 1 2 blend
+          rrcurveto
+          153 -7 -13 0 -30 -1 0 -2 2 blend
+          rmoveto
+          35 -27 38 -39 18 -28 -8 3 -11 3 -5 -3 -9 3 -14 6 -7 -3 -5 1 -9 4 -4 0 6 blend
+          rrcurveto
+          24 18 -18 27 -39 39 -34 25 23 1 55 6 -1 12 4 -1 8 -3 4 1 9 -3 13 -6 7 2 7 -3 9 -4 5 4 8 blend
+          rlinecurve
+          115 330 -53 -1 -124 9 1 21 2 blend
+          rmoveto
+          hintmask 10000101000001000000001010000000
+          14 -286 131 -209 160 0 50 1 18 34 6 108 -9 3 -11 5 -9 7 -4 -92 -9 -34 -31 -1 -137 -2 -126 185 -12 281 3 0 8 6 0 14 5 0 10 -10 0 -22 -3 0 -6 0 0 -1 14 0 33 -1 0 -1 11 0 23 -3 0 -8 5 0 12 10 0 24 -10 0 -23 3 0 7 -14 0 -32 8 0 18 -8 0 -17 8 0 17 0 0 -1 11 0 26 0 0 1 4 0 9 5 0 11 1 0 1 29 0 67 0 1 2 8 0 17 0 -1 -1 -2 0 -4 -37 0 -85 30 blend
+          rrcurveto
+          207 -169 -37 0 -85 -4 0 -9 2 blend
+          rmoveto
+          -61 -129 -111 -108 -121 -69 7 -5 12 -11 5 -6 119 74 113 110 66 136 4 15 19 8 14 33 4 28 29 8 5 22 2 28 27 6 2 17 8 1 20 -6 0 -14 14 1 34 -13 -1 -31 6 0 15 -7 0 -17 0 -28 -23 -4 -2 -10 0 -27 -20 -1 -3 -5 -1 -16 -12 -1 -15 -19 18 blend
+          rrcurveto
+          -156 153 -20 -2 -49 -2 0 -3 2 blend
+          rmoveto
+          52 -15 63 -26 34 -1 0 -3 -1 0 -1 0 0 1 0 0 -2 0 0 -2 5 blend
+          -21 rrcurveto
+          15 27 -34 20 -64 24 -51 14 21 0 48 20 0 47 -1 0 -1 1 0 1 0 0 -1 0 0 1 1 0 3 -1 0 -2 8 blend
+          rlinecurve
+          -453 -763 1 0 2 12 0 27 2 blend
+          rmoveto
+          -25 -16 -31 0 -71 -7 0 -17 2 blend
+          rlineto
+          -100 89 146 -18 233 -21 0 -46 -5 0 -12 -13 0 -29 -4 0 -9 -8 0 -18 5 blend
+          hhcurveto
+          249 23 0 53 1 blend
+          hlineto
+          2 8 6 14 6 8 -35 0 -207 2 -1 3 11 0 25 5 1 12 17 0 38 4 0 10 8 0 18 -16 0 -37 -1 0 -3 -1 0 -2 9 blend
+          0 -22 -14 0 -32 1 blend
+          0 -214 0 -150 15 -78 89 24 0 55 0 0 1 18 0 40 -2 0 -5 12 0 28 -1 0 -2 6 blend
+          rrcurveto
+          5 62 -50 0 -114 -10 0 -22 2 blend
+          rmoveto
+          -30 -97 -92 -60 -107 -36 8 -6 12 -11 4 -6 105 41 99 65 32 106 5 0 12 7 0 15 15 0 34 1 0 3 7 0 16 1 0 2 10 0 22 -6 0 -15 18 0 41 -17 0 -37 8 0 18 -8 0 -19 -2 0 -5 4 0 9 -12 0 -27 7 0 16 -1 0 -2 6 0 14 18 blend
+          rrcurveto
+        </CharString>
+        <CharString name="cid07253" fdSelectIndex="1">
+          1 vsindex
+          -80 27 95 49 -48 48 -45 45 -30 30 -16 16 -13 13 49 30 48 30 47 19 -19 30 53 30 -18 18 51 11 -11 30 -22 22 62 30 60 30 15 81 -30 30 -30 102 -10 1 -14 41 -2 59 -53 2 -76 27 -1 38 -26 1 -37 26 -1 37 -27 1 -39 27 -1 39 -27 1 -39 27 -1 39 -13 0 -19 13 0 19 -14 0 -20 14 0 20 -19 1 -27 13 -1 19 -18 1 -26 13 0 19 -18 0 -26 18 0 26 -18 0 -26 23 -1 33 -21 1 -30 42 -2 60 -29 1 -42 29 -1 42 -19 1 -27 7 0 10 -7 0 -10 26 -1 37 -24 1 -34 24 -1 34 -27 1 -39 24 -1 34 -26 1 -37 26 -1 37 -40 1 -45 53 -2 66 -44 2 -62 44 -2 62 -44 2 -62 18 0 23 42 blend
+          hstemhm
+          193 30 -1 30 -15 15 106 29 96 30 142 30 109 30 5 10 -28 1 -40 71 -2 102 -56 2 -80 75 -4 106 -21 2 -29 21 -2 29 -104 5 -148 55 -3 78 -42 3 -59 69 -4 98 -84 4 -120 79 -3 113 -94 3 -135 76 -3 109 -51 2 -73 25 -1 36 16 blend
+          vstemhm
+          hintmask 10000011101100101101000101110000
+          55 767 2 0 3 37 -2 55 2 blend
+          rmoveto
+          -30 892 30 -44 2 -62 -6 0 -9 44 -2 62 3 blend
+          vlineto
+          hintmask 00000000000000000000100000000000
+          -637 72 -28 1 -40 -26 2 -39 2 blend
+          rmoveto
+          hintmask 00000000000000000010000000000000
+          -153 30 -27 0 -27 77 -2 111 2 blend
+          vlineto
+          hintmask 00000000000000000000100000100000
+          153 27 0 27 1 blend
+          vlineto
+          315 -89 3 -128 1 blend
+          hmoveto
+          hintmask 00000000000000000010000000100000
+          -153 30 -27 0 -27 79 -3 113 2 blend
+          vlineto
+          hintmask 00000000000100101100110000110000
+          153 27 0 27 1 blend
+          vlineto
+          -462 -288 8 0 12 -11 0 -16 2 blend
+          rmoveto
+          571 -62 -571 -102 3 -147 27 -1 39 102 -3 147 3 blend
+          hlineto
+          152 -29 1 -42 1 blend
+          vmoveto
+          571 -60 -571 -102 3 -147 26 -1 37 102 -3 147 3 blend
+          hlineto
+          -30 -71 2 -102 1 blend
+          90 rmoveto
+          -212 631 212 -23 1 -32 45 -2 64 23 -1 32 3 blend
+          vlineto
+          -776 -263 -22 1 -31 -4 0 -5 2 blend
+          rmoveto
+          -30 905 30 -42 2 -60 10 0 14 42 -2 60 3 blend
+          vlineto
+          hintmask 00000001100000000000000100000000
+          -716 -160 36 -1 52 -26 2 -37 2 blend
+          rmoveto
+          -30 554 30 -13 0 -19 -59 2 -85 13 0 19 3 blend
+          vlineto
+          -554 -78 59 -2 85 5 -1 7 2 blend
+          rmoveto
+          -30 563 30 -13 1 -19 -56 1 -81 13 -1 19 3 blend
+          vlineto
+          hintmask 00000010000000000000001000000000
+          -578 -79 2 1 4 6 0 8 2 blend
+          rmoveto
+          hintmask 00001000000000000000001000001000
+          -30 617 -27 1 -39 4 -1 5 2 blend
+          vlineto
+          hintmask 00000010000001000000000000001000
+          30 27 -1 39 1 blend
+          vlineto
+          -477 382 -24 2 -34 8 0 12 2 blend
+          rmoveto
+          -46 -92 -113 -104 -167 -65 7 -5 10 -9 5 -8 6 -1 8 -5 -1 -8 17 0 25 11 0 16 -3 -1 -5 6 0 9 12 0 18 -11 0 -16 18 0 26 -27 1 -39 6 -1 8 -16 1 -23 12 blend
+          rrcurveto
+          hintmask 00000100010010010000000001000000
+          172 70 111 106 55 101 14 0 20 3 0 5 -6 0 -8 1 0 1 3 0 4 28 -1 41 6 blend
+          rrcurveto
+          298 -65 -24 0 -35 3 0 4 2 blend
+          rmoveto
+          -25 -12 -55 2 -79 -15 0 -22 2 blend
+          rlineto
+          62 -80 121 -81 100 -38 5 8 9 11 7 6 -101 33 -119 76 -59 77 2 0 3 -14 1 -20 -10 1 -14 2 0 3 20 0 29 1 0 2 9 -1 13 18 -1 25 20 -1 28 26 -1 38 14 0 21 14 -1 19 -13 0 -19 -7 0 -10 9 0 13 -18 2 -25 4 -1 5 -7 0 -10 18 blend
+          rrcurveto
+          -211 -88 -39 3 -55 -12 1 -17 2 blend
+          rmoveto
+          -239 30 239 -2 -1 -4 69 -4 98 2 1 4 3 blend
+          vlineto
+          hintmask 10000010000000000000000000001000
+          316 -223 -74 3 -106 11 -1 15 2 blend
+          rmoveto
+          -6 -4 0 -6 1 blend
+          vlineto
+          -8 -87 -7 -34 -10 -10 2 0 3 24 -1 35 -1 1 -1 6 0 9 1 -1 1 1 0 1 6 blend
+          rrcurveto
+          -6 -1 0 -1 1 blend
+          -6 -6 -1 -12 2 0 3 1 blend
+          hhcurveto
+          -11 -31 1 0 1 10 0 15 2 blend
+          1 3 -34 0 -1 -1 9 0 13 2 blend
+          hvcurveto
+          5 -8 3 -13 6 -1 8 -11 1 -16 5 0 8 -19 1 -26 4 blend
+          1 -8 28 -2 30 -1 14 1 -14 1 -20 7 -1 9 1 0 1 2 0 3 2 -1 2 3 1 5 0 1 1 7 blend
+          21 0 10 4 10 9 16 15 7 35 2 -1 2 8 -1 11 2 0 3 5 0 7 5 0 8 3 -1 4 3 0 4 2 1 4 3 0 4 9 blend
+          9 89 -15 1 -21 1 blend
+          rrcurveto
+          7 1 1 12 6 -1 8 1 0 1 1 -1 1 9 0 13 4 blend
+          0 hhcurveto
+          -660 -34 -57 3 -82 -8 0 -11 2 blend
+          rmoveto
+          -17 -46 1 0 2 7 0 10 2 blend
+          -32 -46 -46 5 0 7 5 0 7 2 blend
+          -23 20 -21 56 -2 81 -24 0 -35 2 blend
+          rcurveline
+          hintmask 10010000000000000000000000000000
+          52 28 31 51 17 46 -4 -1 -7 0 1 1 -4 1 -5 -7 0 -10 1 -1 1 0 0 -1 6 blend
+          rrcurveto
+          hintmask 00100000000000000000000010000000
+          110 -3 -67 3 -96 1 0 2 2 blend
+          rmoveto
+          13 -38 10 -49 0 -32 -3 0 -4 4 -1 5 -2 0 -3 2 1 4 -1 1 -1 3 0 4 6 blend
+          rrcurveto
+          29 6 55 -3 78 8 -1 11 2 blend
+          -1 31 -10 50 -15 37 -3 0 -4 0 1 1 -4 0 -6 3 -1 4 -4 1 -5 5 blend
+          rlinecurve
+          hintmask 01000000000000000000000000100000
+          113 -6 -56 3 -80 -7 0 -10 2 blend
+          rmoveto
+          22 -32 20 -44 7 -30 2 0 3 1 -1 1 3 -1 3 1 0 1 2 0 3 5 blend
+          rrcurveto
+          28 10 -8 29 -21 44 -23 32 48 -2 69 15 0 22 -2 1 -2 -1 0 -2 0 -1 -1 -5 1 -6 -1 1 -1 -4 0 -6 8 blend
+          rlinecurve
+          hintmask 00010000001000000000001000000000
+          117 -5 -45 1 -65 -17 1 -24 2 blend
+          rmoveto
+          25 -23 -1 0 -1 2 -1 2 2 blend
+          27 -32 13 -23 -2 1 -2 1 0 2 2 blend
+          rrcurveto
+          21 14 -12 44 -2 63 20 -1 28 0 0 -1 3 blend
+          22 -27 32 -26 22 -2 0 -2 -2 0 -3 1 0 1 -2 1 -2 4 blend
+          rlinecurve
+          -381 267 39 -1 56 7 -1 10 2 blend
+          rmoveto
+          -16 -30 -33 1 -47 -23 1 -33 2 blend
+          rlineto
+          498 30 -42 1 -61 23 -1 33 2 blend
+          hlineto
+          -516 -23 21 0 31 -14 0 -21 2 blend
+          rmoveto
+          hintmask 00000010000000000000001000000000
+          -224 6 0 9 1 blend
+          vlineto
+          hintmask 00000010001000000000000100000000
+          30 247 75 -4 106 10 0 14 2 blend
+          hlineto
+        </CharString>
+        <CharString name="cid13393" fdSelectIndex="1">
+          4 vsindex
+          -50 30 -19 19 114 30 44 30 23 30 -30 114 35 30 316 30 -10 10 37 12 -21 0 -26 66 0 82 -29 21 -10 29 -21 10 -64 0 -80 55 0 69 -79 0 -99 75 0 94 -46 0 -58 56 0 71 -56 0 -71 26 21 59 -18 -25 -54 54 0 68 -76 8 -85 58 0 73 -24 0 -31 24 0 31 -46 -4 -63 30 0 37 20 blend
+          hstemhm
+          82 30 197 30 -26 8 317 30 168 13 -13 0 -16 77 0 96 -109 -1 -136 78 0 97 -77 0 -96 29 0 36 -10 0 -12 84 0 105 -86 0 -108 21 0 27 10 blend
+          vstemhm
+          hintmask 1010101101110110
+          529 746 23 0 29 30 4 43 2 blend
+          rmoveto
+          -30 320 30 -58 0 -73 -29 0 -36 58 0 73 3 blend
+          vlineto
+          -397 -495 15 0 18 12 -4 10 2 blend
+          rmoveto
+          -30 442 30 -56 0 -71 21 0 27 56 0 71 3 blend
+          vlineto
+          -420 149 -6 0 -8 6 -4 2 2 blend
+          rmoveto
+          -30 374 30 -54 0 -68 -25 0 -31 54 0 68 3 blend
+          vlineto
+          -514 -420 34 0 42 -3 4 1 2 blend
+          rmoveto
+          -30 626 30 -66 0 -82 -29 0 -36 66 0 82 3 blend
+          vlineto
+          -531 144 15 0 19 -9 0 -11 2 blend
+          rmoveto
+          -30 460 30 -55 0 -69 -4 0 -5 55 0 69 3 blend
+          vlineto
+          -53 622 -42 0 -53 -6 4 -2 2 blend
+          rmoveto
+          -7 -9 0 -12 1 blend
+          vlineto
+          -86 -171 -222 -118 -188 -45 7 -7 8 -11 3 -8 14 0 18 37 0 46 27 0 34 19 0 24 -7 0 -9 5 0 7 15 0 18 -16 0 -20 17 0 22 -32 0 -40 9 0 11 -19 0 -24 12 blend
+          rrcurveto
+          hintmask 0000000010000010
+          192 51 224 119 94 187 21 0 26 3 0 3 -17 0 -21 -9 0 -11 2 0 2 -3 0 -4 6 blend
+          rrcurveto
+          hintmask 0100010100000110
+          -19 12 -6 -2 -55 0 -68 27 0 34 -12 0 -15 -3 0 -3 4 blend
+          rlineto
+          -323 -32 55 0 69 -25 0 -32 2 blend
+          rmoveto
+          -25 -11 -68 0 -86 -23 0 -28 2 blend
+          rlineto
+          83 -154 177 -116 201 -44 4 8 9 12 7 6 -200 39 -177 113 -79 147 11 0 14 12 0 15 -18 0 -22 21 0 26 -1 0 -1 4 0 5 11 0 13 21 0 26 21 0 27 32 0 40 17 0 21 16 0 20 9 0 11 -10 0 -12 17 0 21 -36 0 -45 1 0 2 -37 0 -47 18 blend
+          rrcurveto
+          59 127 -46 0 -58 9 -4 6 2 blend
+          rmoveto
+          -40 -82 -80 -104 -112 -75 8 -4 10 -9 6 -7 115 80 2 0 2 8 0 10 7 0 9 23 0 29 2 0 3 16 0 20 16 0 20 -12 0 -15 26 0 32 -30 0 -37 10 0 13 -18 0 -23 8 0 10 -4 0 -5 14 blend
+          80 106 47 90 -13 0 -16 11 0 13 14 0 17 3 blend
+          rrcurveto
+          -129 -493 -106 -5 -137 21 6 34 2 blend
+          rmoveto
+          -27 -73 -43 -71 -51 -50 8 -5 13 -9 5 -5 49 52 47 77 29 77 6 0 8 11 0 14 7 0 8 8 0 10 5 0 7 8 0 10 16 0 20 -8 0 -10 28 0 35 -17 -1 -22 15 0 18 -11 0 -14 -3 0 -4 -4 0 -5 -2 0 -2 -1 0 -1 -3 0 -4 -3 1 -3 18 blend
+          rrcurveto
+          124 -1 -66 4 -77 10 15 31 2 blend
+          rmoveto
+          -374 30 374 4 0 5 84 0 105 -4 0 -5 3 blend
+          vlineto
+          hintmask 0000000000101000
+          -586 460 -72 0 -90 2 -21 -24 2 blend
+          rmoveto
+          -875 30 845 209 30 -27 0 -33 77 0 96 -53 0 -66 -79 0 -99 80 0 99 5 blend
+          vlineto
+          -8 -29 0 -36 1 blend
+          hmoveto
+          -7 -29 0 -36 1 blend
+          vlineto
+          -28 -75 -43 -102 -46 -95 14 0 17 10 0 13 11 0 14 -41 0 -51 17 0 22 4 0 5 6 blend
+          rrcurveto
+          hintmask 0001000000010000
+          89 -91 24 -74 -63 -32 0 -40 23 0 28 -11 0 -14 10 0 13 17 0 21 5 blend
+          vvcurveto
+          -33 -6 -35 -19 -13 3 0 4 1 0 1 15 0 18 7 0 9 4 0 5 5 blend
+          vhcurveto
+          -10 -6 -12 3 0 3 0 0 1 1 0 2 3 blend
+          -3 -14 -1 -20 -2 -26 1 -29 4 0 5 1 0 1 7 0 9 2 0 2 13 0 16 -1 0 -1 11 0 14 7 blend
+          2 7 -9 4 -13 11 0 13 -21 0 -26 5 0 6 -33 0 -41 4 blend
+          1 -8 22 -2 27 0 22 -21 0 -27 1 0 2 1 0 1 -3 0 -4 0 0 1 -4 0 -5 6 blend
+          2 19 2 17 5 12 9 3 0 4 2 0 2 3 0 3 2 0 2 4 0 5 3 0 4 6 blend
+          rrcurveto
+          25 17 10 7 0 9 5 0 7 4 0 5 3 blend
+          43 44 22 0 27 1 blend
+          vvcurveto
+          67 -22 76 -86 89 -8 0 -10 9 0 12 -6 0 -8 24 0 30 -11 0 -13 5 blend
+          vhcurveto
+          hintmask 0000000001001000
+          39 84 42 98 33 81 -10 0 -13 -4 0 -5 -8 0 -10 14 0 17 -6 0 -8 7 0 9 6 blend
+          rrcurveto
+          hintmask 0000000000001000
+          -20 14 -6 -2 -60 0 -75 32 0 40 -12 0 -14 -2 0 -3 4 blend
+          rlineto
+        </CharString>
+        <CharString name="cid17290" fdSelectIndex="1">
+          5 vsindex
+          121 30 -22 22 148 30 -30 136 23 30 129 30 116 30 -21 4 -29 52 3 92 -32 23 -21 32 -23 21 -54 9 -83 50 4 90 -50 -4 -90 22 27 62 -2 -43 -47 41 0 69 -44 0 -74 37 0 62 -50 0 -84 36 0 61 14 blend
+          hstemhm
+          167 30 129 30 -16 16 123 30 48 30 -6 29 -29 111 -30 30 -16 16 201 30 1 29 -29 0 -49 64 0 108 -34 0 -57 51 0 85 -29 0 -48 29 0 48 -72 -2 -123 60 2 103 -69 0 -115 46 0 77 -42 0 -70 42 0 70 -42 0 -70 67 0 111 -51 0 -85 51 0 85 -29 0 -48 29 0 48 -79 0 -132 47 0 79 -45 0 -75 42 0 70 22 blend
+          vstemhm
+          hintmask 011011111011001010000000
+          326 793 1 0 2 17 0 29 2 blend
+          rmoveto
+          -280 24 0 40 1 blend
+          vlineto
+          -47 16 -8 59 -31 0 -53 6 0 10 -13 0 -21 20 0 33 4 blend
+          vhcurveto
+          hintmask 000010000000100000000000
+          13 120 4 0 6 -46 0 -76 2 blend
+          0 13 4 0 7 1 blend
+          hhcurveto
+          49 10 20 82 4 12 0 19 12 0 20 3 0 5 2 0 3 4 0 8 5 blend
+          hvcurveto
+          hintmask 101010101000010000000000
+          -10 2 -11 5 -8 6 -12 0 -21 3 0 5 -21 0 -35 6 0 11 -9 0 -14 7 0 11 6 blend
+          rrcurveto
+          -75 19 0 32 1 blend
+          -3 -5 -10 -29 -24 -102 1 0 1 1 0 2 7 0 12 9 0 14 42 0 70 5 blend
+          0 -18 6 0 10 1 blend
+          hhcurveto
+          -38 -6 4 21 10 0 18 2 0 3 0 0 -1 4 0 7 4 blend
+          hvcurveto
+          280 -25 0 -41 1 blend
+          vlineto
+          -41 -464 -40 -8 -74 10 20 41 2 blend
+          rmoveto
+          -30 617 30 -50 -4 -90 -5 12 5 50 4 90 3 blend
+          vlineto
+          -661 -178 11 -4 12 4 -13 -7 2 blend
+          rmoveto
+          -30 689 30 -52 -3 -92 -11 0 -18 52 3 92 3 blend
+          vlineto
+          hintmask 010101100111001000000000
+          -481 284 -27 -2 -48 -32 36 -21 2 blend
+          rmoveto
+          -306 30 306 0 -13 0 60 2 103 0 13 0 3 blend
+          vlineto
+          218 0 -61 0 -102 -1 0 -1 2 blend
+          rmoveto
+          -306 30 306 0 -13 0 61 1 104 0 13 0 3 blend
+          vlineto
+          -417 358 -17 -1 -30 19 -43 -12 2 blend
+          rmoveto
+          -30 217 -116 -217 -30 247 176 -36 0 -61 -52 0 -87 50 0 84 52 0 87 -37 0 -62 -6 0 -10 23 0 39 7 blend
+          vlineto
+          75 -26 0 -44 1 blend
+          hmoveto
+          hintmask 000010100000001001000000
+          -280 24 0 40 1 blend
+          vlineto
+          -47 17 -8 60 -31 0 -53 5 0 9 -13 0 -21 20 0 33 4 blend
+          vhcurveto
+          12 125 5 0 8 -47 0 -78 2 blend
+          0 14 4 0 7 1 blend
+          hhcurveto
+          49 11 20 82 3 12 0 20 12 0 19 3 1 6 2 1 6 5 0 9 5 blend
+          hvcurveto
+          -9 2 -12 4 -8 7 -14 1 -22 3 0 5 -19 -1 -34 7 0 12 -9 0 -14 6 0 10 6 blend
+          rrcurveto
+          -75 19 -1 29 1 blend
+          -3 -5 -10 -30 -25 -105 1 -1 1 8 0 13 8 0 14 42 0 70 4 blend
+          0 -18 6 0 9 1 blend
+          hhcurveto
+          -40 -6 4 21 11 0 19 2 0 3 0 0 -1 4 1 8 4 blend
+          hvcurveto
+          280 -25 -1 -42 1 blend
+          vlineto
+          hintmask 000001110000000110000000
+          -16 -29 0 -48 1 blend
+          hmoveto
+          -30 217 -116 -217 -30 247 176 -36 0 -61 -50 0 -84 50 0 84 50 0 84 -37 0 -62 -3 0 -5 23 0 39 7 blend
+          vlineto
+          -424 -714 -19 0 -32 -12 0 -21 2 blend
+          rmoveto
+          -52 -54 -91 -49 -81 -33 8 -5 11 -13 4 -6 80 36 94 56 56 58 7 0 11 9 0 15 5 0 9 11 0 18 -2 0 -3 9 0 15 13 0 22 -11 0 -18 24 0 39 -22 0 -36 11 0 19 -12 0 -21 4 0 7 -4 0 -6 2 0 2 -2 0 -4 -1 0 -1 3 0 5 18 blend
+          rrcurveto
+          200 -7 -92 0 -154 -5 0 -8 2 blend
+          rmoveto
+          76 -41 90 -62 46 -42 -6 0 -10 5 0 8 -5 0 -7 6 0 10 -4 0 -7 4 0 7 6 blend
+          rrcurveto
+          22 23 -46 42 -91 60 -75 39 60 0 100 29 0 48 0 0 -1 -3 0 -5 3 0 5 -7 0 -11 6 0 11 -7 0 -11 8 blend
+          rlinecurve
+          -499 750 -48 0 -81 6 0 10 2 blend
+          rmoveto
+          -54 -167 -87 -164 -96 -108 7 -6 11 -12 4 -6 98 116 88 165 58 175 7 0 13 15 0 25 10 0 16 14 0 22 11 0 19 10 0 17 9 0 15 -20 0 -33 15 0 24 -44 0 -73 4 0 7 -18 0 -30 4 0 6 4 0 6 3 0 6 19 0 32 0 0 -1 1 0 1 18 blend
+          rrcurveto
+          -113 -214 -60 0 -100 -23 0 -37 2 blend
+          rmoveto
+          -691 30 718 20 0 33 64 0 108 43 0 72 3 blend
+          vlineto
+          -1 -1 0 -3 1 blend
+          2 rlineto
+        </CharString>
+        <CharString name="cid17852" fdSelectIndex="1">
+          5 vsindex
+          -67 29 219 30 154 30 -16 16 150 30 -30 122 -85 30 -18 18 87 30 -30 140 -122 12 -14 0 -22 46 0 78 -59 -3 -106 46 0 77 -53 -9 -92 46 2 81 -18 20 -1 18 -20 1 -54 13 -80 46 2 81 -46 -2 -81 25 31 61 -14 -34 -48 60 0 100 -64 0 -107 64 0 107 -55 0 -92 54 0 90 -54 0 -90 36 0 59 -19 0 -31 37 0 62 22 blend
+          hstemhm
+          51 188 -30 30 -30 149 21 30 -18 18 -13 13 66 30 -12 12 135 30 41 30 172 30 -6 28 -8 0 -14 30 0 50 -62 0 -103 62 0 103 -62 0 -103 32 0 53 -5 0 -7 59 0 98 -24 0 -41 24 0 41 -16 0 -27 16 0 27 -32 0 -53 53 0 88 -33 0 -56 33 0 56 -87 0 -146 63 0 106 -42 0 -70 54 0 90 -99 0 -165 55 0 91 -42 0 -70 45 0 75 24 blend
+          vstemhm
+          hintmask 000000100001000000000000
+          51 612 -8 0 -14 29 0 49 2 blend
+          rmoveto
+          -30 -60 0 -100 1 blend
+          vlineto
+          hintmask 000000100000010000000000
+          307 30 60 0 100 1 blend
+          hlineto
+          hintmask 000000010010100100000000
+          -149 228 -32 0 -53 -20 0 -34 2 blend
+          rmoveto
+          -918 30 918 -19 0 -32 62 0 103 19 0 32 3 blend
+          vlineto
+          -36 -238 -55 0 -91 -32 0 -53 2 blend
+          rmoveto
+          -31 -160 -74 -193 -68 -95 7 -5 10 -11 6 -8 70 101 74 203 33 160 6 0 10 25 0 42 13 0 21 23 0 37 4 0 7 1 0 2 8 0 14 -18 0 -30 13 0 21 -27 0 -44 4 0 7 -19 0 -32 1 0 2 6 0 10 -12 0 -20 -2 0 -3 -2 0 -4 -1 0 -2 18 blend
+          rrcurveto
+          4 -143 19 0 32 77 0 128 2 blend
+          rmoveto
+          -21 -16 25 -26 72 -92 21 -33 -23 0 -38 -34 0 -57 1 0 2 -15 0 -24 -12 0 -21 -6 0 -11 2 0 3 -18 0 -29 8 blend
+          rlinecurve
+          24 24 -18 25 -81 96 -22 22 28 0 48 63 0 105 2 0 2 -1 0 -2 1 0 3 10 0 16 1 0 1 1 0 2 8 blend
+          rlinecurve
+          157 278 1 0 1 -14 0 -23 2 blend
+          rmoveto
+          hintmask 000000001000000100000000
+          -30 559 -54 0 -90 -17 3 -23 2 blend
+          vlineto
+          hintmask 010000000010000000100000
+          30 54 0 90 1 blend
+          vlineto
+          -457 -518 29 -3 43 -9 -3 -20 2 blend
+          rmoveto
+          -30 176 30 -46 0 -77 -17 0 -27 46 0 77 3 blend
+          vlineto
+          hintmask 000000000100000001010000
+          -194 120 -3 0 -5 -42 37 -35 2 blend
+          rmoveto
+          -365 30 365 38 -29 45 53 0 88 -38 29 -45 3 blend
+          vlineto
+          135 508 -87 0 -146 33 -34 24 2 blend
+          rmoveto
+          hintmask 000000000010000000010000
+          -122 30 -19 0 -31 63 0 106 2 blend
+          vlineto
+          hintmask 000101000100000000010000
+          122 19 0 31 1 blend
+          vlineto
+          -115 -172 -60 0 -100 -27 34 -19 2 blend
+          rmoveto
+          -288 30 288 11 -24 18 50 0 83 -11 24 -18 3 blend
+          vlineto
+          148 -62 -2 -106 1 blend
+          hmoveto
+          -288 30 288 11 -24 18 50 0 83 -11 24 -18 3 blend
+          vlineto
+          156 -394 -30 2 -47 19 -34 6 2 blend
+          rmoveto
+          -52 -36 -89 -48 -61 -29 7 0 12 2 0 4 14 0 23 3 0 4 11 0 18 4 0 8 6 blend
+          rrcurveto
+          15 -21 62 28 86 41 57 44 25 0 42 -39 0 -66 -10 0 -17 -4 0 -6 -12 0 -19 -3 0 -5 -6 0 -11 -5 0 -9 8 blend
+          rlinecurve
+          hintmask 101010000000000010001100
+          -541 323 10 0 17 44 5 84 2 blend
+          rmoveto
+          -30 517 -150 -517 -30 547 210 -46 -2 -81 -74 0 -123 54 -13 80 74 0 123 -46 -2 -81 -19 0 -32 38 17 82 7 blend
+          vlineto
+          -232 -242 -10 0 -16 -28 29 -27 2 blend
+          rmoveto
+          -344 58 -32 71 1 blend
+          vlineto
+          -47 15 -9 54 -33 -2 -58 3 0 4 -15 0 -25 22 0 37 4 blend
+          vhcurveto
+          hintmask 100000000010001000001010
+          12 100 3 0 5 -47 0 -78 2 blend
+          0 12 4 0 6 1 blend
+          hhcurveto
+          48 10 25 102 3 12 0 20 11 0 19 4 1 9 11 -1 16 5 0 8 5 blend
+          hvcurveto
+          -9 3 -11 4 -8 6 -14 0 -23 3 -1 5 -23 1 -37 8 1 15 -8 -1 -15 8 0 12 6 blend
+          rrcurveto
+          -97 11 1 20 1 blend
+          -3 -4 -14 -29 -21 -84 0 0 1 1 -1 1 10 0 16 10 1 17 43 -1 71 5 blend
+          0 -16 7 0 12 1 blend
+          hhcurveto
+          -33 -6 5 22 13 0 22 3 0 5 -1 0 -2 4 0 7 4 blend
+          hvcurveto
+          344 -59 34 -71 1 blend
+          vlineto
+          -346 -371 -24 0 -41 65 -34 78 2 blend
+          rmoveto
+          10 -31 77 16 100 22 99 21 3 0 5 -54 0 -90 -2 0 -3 -3 0 -5 -9 0 -15 -6 0 -10 -10 0 -17 -5 0 -8 8 blend
+          rlinecurve
+          -2 29 -108 -22 -104 -22 -72 -13 -3 0 -5 52 0 86 9 0 16 6 0 10 8 0 13 6 0 11 4 0 6 4 0 6 8 blend
+          rlinecurve
+          -16 767 -44 0 -72 -13 0 -21 2 blend
+          rmoveto
+          -316 -6 0 -11 1 blend
+          vlineto
+          -142 -7 -194 -74 -141 2 0 2 -2 0 -2 2 0 4 6 0 9 4 blend
+          vhcurveto
+          8 -3 13 -7 5 -6 13 0 21 -7 0 -11 25 0 43 -20 0 -34 11 0 17 -10 0 -17 6 blend
+          rrcurveto
+          75 143 10 205 145 4 0 7 3 0 5 2 0 4 21 0 35 9 0 15 5 blend
+          vvcurveto
+          316 6 0 11 1 blend
+          vlineto
+        </CharString>
+        <CharString name="cid18480" fdSelectIndex="1">
+          3 vsindex
+          -71 30 427 30 153 30 33 111 -30 30 -30 126 -6 0 -13 45 0 102 -58 0 -132 38 0 87 -48 0 -111 38 0 87 -4 -2 -13 21 2 53 -43 0 -99 43 0 99 -43 0 -99 24 0 55 12 blend
+          hstemhm
+          159 30 -19 19 126 30 -6 30 281 30 160 30 18 31 -7 0 -16 50 0 114 -18 0 -42 18 0 42 -71 0 -161 50 0 114 -26 0 -61 48 0 111 -66 0 -150 51 0 115 -68 0 -154 50 0 114 -36 -1 -84 44 1 101 14 blend
+          vstemhm
+          hintmask 1110100101110000
+          58 743 -1 0 -2 26 0 60 2 blend
+          rmoveto
+          -30 887 30 -43 0 -99 2 0 5 43 0 99 3 blend
+          vlineto
+          hintmask 0000010010000000
+          -630 96 -29 0 -66 -19 0 -44 2 blend
+          rmoveto
+          hintmask 0001000010000000
+          -207 30 -2 -2 -9 50 0 114 2 blend
+          vlineto
+          hintmask 0000010010100000
+          207 2 2 9 1 blend
+          vlineto
+          305 -44 0 -100 1 blend
+          hmoveto
+          hintmask 0001000000100000
+          -207 30 -2 -2 -9 51 0 115 2 blend
+          vlineto
+          hintmask 0010011000100000
+          207 2 2 9 1 blend
+          vlineto
+          -521 -240 -36 0 -82 2 0 4 2 blend
+          rmoveto
+          -206 -5 0 -10 1 blend
+          vlineto
+          -137 -15 -184 -109 -136 5 0 11 3 0 6 5 0 10 -1 0 -1 8 0 19 5 blend
+          vhcurveto
+          7 -3 12 -9 5 -6 12 0 27 -6 0 -13 22 0 51 -15 0 -35 10 0 21 -8 0 -19 6 blend
+          rrcurveto
+          hintmask 1110000101010000
+          112 139 18 194 141 3 0 7 -4 0 -8 1 0 3 11 0 24 4 0 10 5 blend
+          vvcurveto
+          207 5 0 11 1 blend
+          vlineto
+          -19 -18 0 -42 1 blend
+          hmoveto
+          -30 670 -153 -670 -30 700 213 -38 0 -87 -64 0 -144 48 0 111 64 0 144 -38 0 -87 -14 0 -30 28 0 63 7 blend
+          vlineto
+          -531 -249 -15 0 -36 -23 0 -51 2 blend
+          rmoveto
+          -343 50 0 112 1 blend
+          vlineto
+          -66 31 -12 105 -29 0 -66 6 0 14 -13 0 -28 29 0 66 4 blend
+          vhcurveto
+          23 278 5 0 12 -59 0 -134 2 blend
+          0 24 6 0 14 1 blend
+          hhcurveto
+          hintmask 1000000001001000
+          96 15 31 123 8 20 0 44 11 0 26 4 0 8 14 0 32 5 0 11 5 blend
+          hvcurveto
+          -9 3 -13 4 -9 7 -13 0 -30 2 0 5 -21 0 -48 8 0 17 -10 -1 -23 6 0 15 6 blend
+          rrcurveto
+          -117 -6 -11 -21 -69 -56 -236 8 0 18 -1 1 -1 1 -1 1 3 0 7 3 1 9 7 0 15 49 0 112 7 blend
+          0 -41 4 0 8 1 blend
+          hhcurveto
+          -84 -16 11 37 4 0 10 2 0 5 -3 0 -7 1 0 2 4 blend
+          hvcurveto
+          343 -51 0 -115 1 blend
+          vlineto
+          444 -47 -59 0 -135 26 0 59 2 blend
+          rmoveto
+          -101 -52 -195 -56 -169 -40 4 -7 5 -10 3 -7 172 40 193 54 120 56 4 0 8 3 0 7 18 0 43 9 0 19 12 0 26 8 0 19 5 0 12 -10 0 -22 7 0 15 -18 0 -41 1 0 3 -11 0 -25 -8 0 -19 -9 0 -21 -8 0 -18 -8 0 -19 5 0 11 0 0 1 18 blend
+          rrcurveto
+        </CharString>
+        <CharString name="cid22370" fdSelectIndex="1">
+          2 vsindex
+          64 30 77 30 76 30 74 30 72 30 109 30 25 84 -30 30 -30 108 -2 0 -2 42 0 47 -48 0 -54 38 0 43 -48 0 -54 38 0 43 -46 0 -52 42 0 47 -43 0 -48 56 1 63 -72 -1 -81 57 1 64 -8 -32 -41 30 32 65 -65 -1 -73 65 1 73 -65 -1 -73 43 0 49 18 blend
+          hstemhm
+          135 30 21 30 102 30 14 30 205 30 17 30 113 30 19 30 -19 0 -21 87 2 98 -86 -2 -97 99 1 111 -125 -1 -141 98 1 111 -79 -1 -89 75 1 84 -99 -1 -111 75 1 84 -77 -1 -86 100 1 112 -127 -1 -143 105 1 118 -102 -1 -114 94 1 105 16 blend
+          vstemhm
+          hintmask 111111010011001100000000
+          53 761 -3 0 -3 36 0 40 2 blend
+          rmoveto
+          -30 896 30 -65 -1 -73 5 0 5 65 1 73 3 blend
+          vlineto
+          hintmask 000000001001000000000000
+          -631 78 -46 0 -52 -22 0 -24 2 blend
+          rmoveto
+          hintmask 000000100001000000000000
+          -162 30 -8 -32 -41 98 1 111 2 blend
+          vlineto
+          hintmask 000000001001001000000000
+          162 8 32 41 1 blend
+          vlineto
+          296 -105 -1 -118 1 blend
+          hmoveto
+          hintmask 000000100000001000000000
+          -162 30 -8 -32 -41 100 1 112 2 blend
+          vlineto
+          hintmask 000000001000001000000000
+          162 8 32 41 1 blend
+          vlineto
+          hintmask 000011000100110010000000
+          -47 -217 -23 0 -26 -57 -1 -64 2 blend
+          rmoveto
+          209 -109 -209 -101 -1 -113 72 1 81 101 1 113 3 blend
+          hlineto
+          -235 109 24 0 27 -72 -1 -81 2 blend
+          rmoveto
+          205 -109 -205 -99 -1 -111 72 1 81 99 1 111 3 blend
+          hlineto
+          -227 109 18 1 21 -72 -1 -81 2 blend
+          rmoveto
+          197 -109 -197 -93 -2 -105 72 1 81 93 2 105 3 blend
+          hlineto
+          -30 139 -87 -2 -98 -15 0 -17 2 blend
+          rmoveto
+          -169 731 169 -41 0 -46 38 0 42 41 0 46 3 blend
+          vlineto
+          hintmask 111100000010000100000000
+          -650 -375 62 1 70 -32 0 -36 2 blend
+          rmoveto
+          571 -76 -571 -159 -1 -179 48 0 54 159 1 179 3 blend
+          hlineto
+          -30 -38 0 -43 1 blend
+          vmoveto
+          571 -77 -571 -159 -1 -179 48 0 54 159 1 179 3 blend
+          hlineto
+          287 -66 -1 -74 1 blend
+          vmoveto
+          571 -74 -571 -159 -1 -179 46 0 52 159 1 179 3 blend
+          hlineto
+          -30 104 -99 -1 -111 -4 0 -5 2 blend
+          rmoveto
+          -347 631 347 -18 0 -20 45 0 50 18 0 20 3 blend
+          vlineto
+          -216 -389 -86 -1 -96 -31 0 -35 2 blend
+          rmoveto
+          127 -34 121 -39 72 -31 -17 0 -19 2 0 2 -13 0 -15 -2 0 -2 -13 0 -15 3 0 3 6 blend
+          rrcurveto
+          31 22 -78 32 -126 39 -121 136 1 153 39 0 44 1 0 1 -3 0 -3 -8 0 -9 4 0 5 9 0 10 7 blend
+          31 rlinecurve
+          -258 -1 -67 -1 -75 0 0 -1 2 blend
+          rmoveto
+          -81 -39 -128 -36 -107 -23 8 -6 12 -12 5 -6 103 25 130 41 86 43 9 0 10 6 0 7 3 0 4 7 0 8 -4 0 -5 7 0 8 19 0 22 -14 0 -16 32 0 36 -32 0 -36 17 0 19 -19 0 -21 3 0 3 -1 0 -1 5 0 6 2 0 2 1 0 1 4 0 5 18 blend
+          rrcurveto
+        </CharString>
+      </CharStrings>
+      <VarStore Format="1">
+        <Format value="1"/>
+        <VarRegionList>
+          <!-- RegionAxisCount=1 -->
+          <!-- RegionCount=16 -->
+          <Region index="0">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="1">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.669"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="2">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.669"/>
+              <PeakCoord value="0.67"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="3">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.67"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="4">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.889"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="5">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.889"/>
+              <PeakCoord value="0.89"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="6">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.89"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="7">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.439"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="8">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.439"/>
+              <PeakCoord value="0.44"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="9">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.44"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="10">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.799"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="11">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.799"/>
+              <PeakCoord value="0.8"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="12">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.8"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="13">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.0"/>
+              <PeakCoord value="0.599"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="14">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.599"/>
+              <PeakCoord value="0.6"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+          <Region index="15">
+            <VarRegionAxis index="0">
+              <StartCoord value="0.6"/>
+              <PeakCoord value="1.0"/>
+              <EndCoord value="1.0"/>
+            </VarRegionAxis>
+          </Region>
+        </VarRegionList>
+        <!-- VarDataCount=6 -->
+        <VarData index="0">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=1 -->
+          <VarRegionIndex index="0" value="0"/>
+        </VarData>
+        <VarData index="1">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=3 -->
+          <VarRegionIndex index="0" value="1"/>
+          <VarRegionIndex index="1" value="2"/>
+          <VarRegionIndex index="2" value="3"/>
+        </VarData>
+        <VarData index="2">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=3 -->
+          <VarRegionIndex index="0" value="4"/>
+          <VarRegionIndex index="1" value="5"/>
+          <VarRegionIndex index="2" value="6"/>
+        </VarData>
+        <VarData index="3">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=3 -->
+          <VarRegionIndex index="0" value="7"/>
+          <VarRegionIndex index="1" value="8"/>
+          <VarRegionIndex index="2" value="9"/>
+        </VarData>
+        <VarData index="4">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=3 -->
+          <VarRegionIndex index="0" value="10"/>
+          <VarRegionIndex index="1" value="11"/>
+          <VarRegionIndex index="2" value="12"/>
+        </VarData>
+        <VarData index="5">
+          <!-- ItemCount=0 -->
+          <NumShorts value="0"/>
+          <!-- VarRegionCount=3 -->
+          <VarRegionIndex index="0" value="13"/>
+          <VarRegionIndex index="1" value="14"/>
+          <VarRegionIndex index="2" value="15"/>
+        </VarData>
+      </VarStore>
+    </CFFFont>
+
+    <GlobalSubrs>
+      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
+    </GlobalSubrs>
+  </CFF2>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/TestVVAR.ttx b/Tests/varLib/data/test_results/TestVVAR.ttx
new file mode 100644
index 0000000..53c038c
--- /dev/null
+++ b/Tests/varLib/data/test_results/TestVVAR.ttx
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.39">
+
+  <VVAR>
+    <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=1 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=0 -->
+        <Item index="0" value="[]"/>
+      </VarData>
+    </VarStore>
+    <AdvHeightMap>
+      <Map glyph=".notdef" outer="0" inner="0"/>
+      <Map glyph="c30719" outer="0" inner="0"/>
+      <Map glyph="c30790" outer="0" inner="0"/>
+      <Map glyph="c30816" outer="0" inner="0"/>
+      <Map glyph="c30843" outer="0" inner="0"/>
+      <Map glyph="c31621" outer="0" inner="0"/>
+      <Map glyph="c32051" outer="0" inner="0"/>
+      <Map glyph="uni3042" outer="0" inner="0"/>
+      <Map glyph="uni56FD" outer="0" inner="0"/>
+      <Map glyph="uni6280" outer="0" inner="0"/>
+      <Map glyph="uniFF20" outer="0" inner="0"/>
+      <Map glyph="uniFF21" outer="0" inner="0"/>
+      <Map glyph="uniFF41" outer="0" inner="0"/>
+    </AdvHeightMap>
+    <VOrgMap>
+      <Map glyph=".notdef" outer="0" inner="0"/>
+      <Map glyph="c30719" outer="0" inner="0"/>
+      <Map glyph="c30790" outer="0" inner="0"/>
+      <Map glyph="c30816" outer="0" inner="0"/>
+      <Map glyph="c30843" outer="0" inner="0"/>
+      <Map glyph="c31621" outer="0" inner="0"/>
+      <Map glyph="c32051" outer="0" inner="0"/>
+      <Map glyph="uni3042" outer="0" inner="0"/>
+      <Map glyph="uni56FD" outer="0" inner="0"/>
+      <Map glyph="uni6280" outer="0" inner="0"/>
+      <Map glyph="uniFF20" outer="0" inner="0"/>
+      <Map glyph="uniFF21" outer="0" inner="0"/>
+      <Map glyph="uniFF41" outer="0" inner="0"/>
+    </VOrgMap>
+  </VVAR>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/test_vpal.ttx b/Tests/varLib/data/test_results/test_vpal.ttx
new file mode 100644
index 0000000..be61293
--- /dev/null
+++ b/Tests/varLib/data/test_results/test_vpal.ttx
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.42">
+
+  <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="vpal"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=6 -->
+        <SinglePos index="0" Format="2">
+          <Coverage>
+            <Glyph value="uniFF1A"/>
+            <Glyph value="uni3074"/>
+          </Coverage>
+          <ValueFormat value="7"/>
+          <!-- ValueCount=2 -->
+          <Value index="0" XPlacement="-250" YPlacement="0" XAdvance="-500"/>
+          <Value index="1" XPlacement="0" YPlacement="0" XAdvance="-30"/>
+        </SinglePos>
+        <SinglePos index="1" Format="1">
+          <Coverage>
+            <Glyph value="uni3001"/>
+          </Coverage>
+          <ValueFormat value="23"/>
+          <Value XPlacement="-9" YPlacement="0" XAdvance="-500">
+            <XPlaDevice>
+              <StartSize value="0"/>
+              <EndSize value="4"/>
+              <DeltaFormat value="32768"/>
+            </XPlaDevice>
+          </Value>
+        </SinglePos>
+        <SinglePos index="2" Format="1">
+          <Coverage>
+            <Glyph value="uni30FB"/>
+          </Coverage>
+          <ValueFormat value="39"/>
+          <Value XPlacement="-250" YPlacement="1" XAdvance="-500">
+            <YPlaDevice>
+              <StartSize value="0"/>
+              <EndSize value="2"/>
+              <DeltaFormat value="32768"/>
+            </YPlaDevice>
+          </Value>
+        </SinglePos>
+        <SinglePos index="3" Format="1">
+          <Coverage>
+            <Glyph value="uniFF2D"/>
+          </Coverage>
+          <ValueFormat value="87"/>
+          <Value XPlacement="12" YPlacement="0" XAdvance="23">
+            <XPlaDevice>
+              <StartSize value="0"/>
+              <EndSize value="1"/>
+              <DeltaFormat value="32768"/>
+            </XPlaDevice>
+            <XAdvDevice>
+              <StartSize value="0"/>
+              <EndSize value="0"/>
+              <DeltaFormat value="32768"/>
+            </XAdvDevice>
+          </Value>
+        </SinglePos>
+        <SinglePos index="4" Format="1">
+          <Coverage>
+            <Glyph value="uni3073"/>
+          </Coverage>
+          <ValueFormat value="71"/>
+          <Value XPlacement="0" YPlacement="0" XAdvance="-36">
+            <XAdvDevice>
+              <StartSize value="0"/>
+              <EndSize value="3"/>
+              <DeltaFormat value="32768"/>
+            </XAdvDevice>
+          </Value>
+        </SinglePos>
+        <SinglePos index="5" Format="1">
+          <Coverage>
+            <Glyph value="uni307B"/>
+          </Coverage>
+          <ValueFormat value="23"/>
+          <Value XPlacement="11" YPlacement="0" XAdvance="0">
+            <XPlaDevice>
+              <StartSize value="0"/>
+              <EndSize value="2"/>
+              <DeltaFormat value="32768"/>
+            </XPlaDevice>
+          </Value>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_vpal.designspace b/Tests/varLib/data/test_vpal.designspace
new file mode 100644
index 0000000..a736927
--- /dev/null
+++ b/Tests/varLib/data/test_vpal.designspace
@@ -0,0 +1,35 @@
+<?xml version='1.0' encoding='utf-8'?>
+<designspace format="3">
+	<axes>
+		<axis default="0" maximum="1000" minimum="0" name="weight" tag="wght" />
+	</axes>
+	<sources>
+		<source filename="master_vpal_test/master_vpal_test_0.ufo" stylename="w0.00">
+			<info copy="1" />
+			<location>
+				<dimension name="weight" xvalue="0.00" />
+			</location>
+		</source>
+		<source filename="master_vpal_test/master_vpal_test_1.ufo" stylename="w1000.00">
+			<location>
+				<dimension name="weight" xvalue="1000.00" />
+			</location>
+		</source>
+	</sources>
+	<instances>
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-ExtraLight.otf" postscriptfontname="SHSansJPVFTest-ExtraLight" stylename="ExtraLight">
+			<location>
+				<dimension name="weight" xvalue="0" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+		<instance familyname="SHSansJPVFTest" filename="instances/SHSansJPVFTest-Heavy.otf" postscriptfontname="SHSansJPVFTest-Heavy" stylename="Heavy">
+			<location>
+				<dimension name="weight" xvalue="1000" />
+			</location>
+			<kerning />
+			<info />
+		</instance>
+	</instances>
+</designspace>
diff --git a/Tests/varLib/designspace_test.py b/Tests/varLib/designspace_test.py
deleted file mode 100644
index fbdaab3..0000000
--- a/Tests/varLib/designspace_test.py
+++ /dev/null
@@ -1,69 +0,0 @@
-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/featureVars_test.py b/Tests/varLib/featureVars_test.py
new file mode 100644
index 0000000..89675af
--- /dev/null
+++ b/Tests/varLib/featureVars_test.py
@@ -0,0 +1,121 @@
+from fontTools.varLib.featureVars import (
+    overlayFeatureVariations)
+
+
+def test_linear(n = 10):
+    conds = []
+    for i in range(n):
+        end = i / n
+        start = end - 1.
+        region = [{'X': (start, end)}]
+        subst = {'g%.2g'%start: 'g%.2g'%end}
+        conds.append((region, subst))
+    overlaps = overlayFeatureVariations(conds)
+    assert len(overlaps) == 2 * n - 1, overlaps
+    return conds, overlaps
+
+def test_quadratic(n = 10):
+    conds = []
+    for i in range(1, n + 1):
+        region = [{'X': (0, i / n),
+                   'Y': (0, (n + 1 - i) / n)}]
+        subst = {str(i): str(n + 1 - i)}
+        conds.append((region, subst))
+    overlaps = overlayFeatureVariations(conds)
+    assert len(overlaps) == n * (n + 1) // 2, overlaps
+    return conds, overlaps
+
+def _merge_substitutions(substitutions):
+    merged = {}
+    for subst in substitutions:
+        merged.update(subst)
+    return merged
+
+def _match_condition(location, overlaps):
+    for box, substitutions in overlaps:
+        for tag, coord in location.items():
+            start, end = box[tag]
+            if start <= coord <= end:
+                return _merge_substitutions(substitutions)
+    return {}  # no match
+
+def test_overlaps_1():
+    # https://github.com/fonttools/fonttools/issues/1400
+    conds = [
+        ([{'abcd': (4, 9)}], {0: 0}),
+        ([{'abcd': (5, 10)}], {1: 1}),
+        ([{'abcd': (0, 8)}], {2: 2}),
+        ([{'abcd': (3, 7)}], {3: 3}),
+    ]
+    overlaps = overlayFeatureVariations(conds)
+    subst = _match_condition({'abcd': 0}, overlaps)
+    assert subst == {2: 2}
+    subst = _match_condition({'abcd': 1}, overlaps)
+    assert subst == {2: 2}
+    subst = _match_condition({'abcd': 3}, overlaps)
+    assert subst == {2: 2, 3: 3}
+    subst = _match_condition({'abcd': 4}, overlaps)
+    assert subst == {0: 0, 2: 2, 3: 3}
+    subst = _match_condition({'abcd': 5}, overlaps)
+    assert subst == {0: 0, 1: 1, 2: 2, 3: 3}
+    subst = _match_condition({'abcd': 7}, overlaps)
+    assert subst == {0: 0, 1: 1, 2: 2, 3: 3}
+    subst = _match_condition({'abcd': 8}, overlaps)
+    assert subst == {0: 0, 1: 1, 2: 2}
+    subst = _match_condition({'abcd': 9}, overlaps)
+    assert subst == {0: 0, 1: 1}
+    subst = _match_condition({'abcd': 10}, overlaps)
+    assert subst == {1: 1}
+
+def test_overlaps_2():
+    # https://github.com/fonttools/fonttools/issues/1400
+    conds = [
+        ([{'abcd': (1, 9)}], {0: 0}),
+        ([{'abcd': (8, 10)}], {1: 1}),
+        ([{'abcd': (3, 4)}], {2: 2}),
+        ([{'abcd': (1, 10)}], {3: 3}),
+    ]
+    overlaps = overlayFeatureVariations(conds)
+    subst = _match_condition({'abcd': 0}, overlaps)
+    assert subst == {}
+    subst = _match_condition({'abcd': 1}, overlaps)
+    assert subst == {0: 0, 3: 3}
+    subst = _match_condition({'abcd': 2}, overlaps)
+    assert subst == {0: 0, 3: 3}
+    subst = _match_condition({'abcd': 3}, overlaps)
+    assert subst == {0: 0, 2: 2, 3: 3}
+    subst = _match_condition({'abcd': 5}, overlaps)
+    assert subst == {0: 0, 3: 3}
+    subst = _match_condition({'abcd': 10}, overlaps)
+    assert subst == {1: 1, 3: 3}
+
+
+def run(test, n, quiet):
+
+    print()
+    print("%s:" % test.__name__)
+    input, output = test(n)
+    if quiet:
+        print(len(output))
+    else:
+        print()
+        print("Input:")
+        pprint(input)
+        print()
+        print("Output:")
+        pprint(output)
+        print()
+
+if __name__ == "__main__":
+    import sys
+    from pprint import pprint
+    quiet = False
+    n = 3
+    if len(sys.argv) > 1 and sys.argv[1] == '-q':
+        quiet = True
+        del sys.argv[1]
+    if len(sys.argv) > 1:
+        n = int(sys.argv[1])
+
+    run(test_linear, n=n, quiet=quiet)
+    run(test_quadratic, n=n, quiet=quiet)
diff --git a/Tests/varLib/instancer/conftest.py b/Tests/varLib/instancer/conftest.py
new file mode 100644
index 0000000..0ac8091
--- /dev/null
+++ b/Tests/varLib/instancer/conftest.py
@@ -0,0 +1,13 @@
+import os
+from fontTools import ttLib
+import pytest
+
+
+TESTDATA = os.path.join(os.path.dirname(__file__), "data")
+
+
+@pytest.fixture
+def varfont():
+    f = ttLib.TTFont()
+    f.importXML(os.path.join(TESTDATA, "PartialInstancerTest-VF.ttx"))
+    return f
diff --git a/Tests/varLib/instancer/data/PartialInstancerTest-VF.ttx b/Tests/varLib/instancer/data/PartialInstancerTest-VF.ttx
new file mode 100644
index 0000000..268b506
--- /dev/null
+++ b/Tests/varLib/instancer/data/PartialInstancerTest-VF.ttx
@@ -0,0 +1,1143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.42">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="hyphen"/>
+    <GlyphID id="2" name="space"/>
+    <GlyphID id="3" name="minus"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.001"/>
+    <checkSumAdjustment value="0xb77caef"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000001"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar  5 00:05:14 2019"/>
+    <modified value="Wed Jun  5 15:42:16 2019"/>
+    <xMin value="40"/>
+    <yMin value="-200"/>
+    <xMax value="450"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="600"/>
+    <minLeftSideBearing value="40"/>
+    <minRightSideBearing value="40"/>
+    <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="4"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="4"/>
+    <maxPoints value="8"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="4"/>
+    <maxCompositeContours value="1"/>
+    <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="4"/>
+    <xAvgCharWidth value="474"/>
+    <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="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="GOOG"/>
+    <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="500" lsb="50"/>
+    <mtx name="hyphen" width="322" lsb="40"/>
+    <mtx name="minus" width="422" lsb="40"/>
+    <mtx name="space" width="600" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x2d" name="hyphen"/><!-- HYPHEN-MINUS -->
+      <map code="0x2212" name="minus"/><!-- MINUS SIGN -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x2d" name="hyphen"/><!-- HYPHEN-MINUS -->
+      <map code="0x2212" name="minus"/><!-- MINUS SIGN -->
+    </cmap_format_4>
+  </cmap>
+
+  <cvt>
+    <cv index="0" value="500"/>
+    <cv index="1" value="-400"/>
+    <cv index="2" value="180"/>
+    <cv index="3" value="250"/>
+  </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="50" y="800" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="100" y="750" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="hyphen" xMin="40" yMin="229" xMax="282" yMax="307">
+      <contour>
+        <pt x="40" y="229" on="1"/>
+        <pt x="40" y="307" on="1"/>
+        <pt x="282" y="307" on="1"/>
+        <pt x="282" y="229" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="minus" xMin="40" yMin="229" xMax="282" yMax="307">
+      <component glyphName="hyphen" x="0" y="0" flags="0x204"/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="257" platformID="0" platEncID="4" langID="0x0">
+      Bräiti
+    </namerecord>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Width
+    </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">
+      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">
+      Regular
+    </namerecord>
+    <namerecord nameID="262" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Medium
+    </namerecord>
+    <namerecord nameID="263" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiBold
+    </namerecord>
+    <namerecord nameID="264" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Bold
+    </namerecord>
+    <namerecord nameID="265" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraBold
+    </namerecord>
+    <namerecord nameID="266" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Black
+    </namerecord>
+    <namerecord nameID="267" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed Thin
+    </namerecord>
+    <namerecord nameID="268" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed ExtraLight
+    </namerecord>
+    <namerecord nameID="269" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed Light
+    </namerecord>
+    <namerecord nameID="270" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed
+    </namerecord>
+    <namerecord nameID="271" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed Medium
+    </namerecord>
+    <namerecord nameID="272" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed SemiBold
+    </namerecord>
+    <namerecord nameID="273" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed Bold
+    </namerecord>
+    <namerecord nameID="274" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed ExtraBold
+    </namerecord>
+    <namerecord nameID="275" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed Black
+    </namerecord>
+    <namerecord nameID="276" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed Thin
+    </namerecord>
+    <namerecord nameID="277" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed ExtraLight
+    </namerecord>
+    <namerecord nameID="278" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed Light
+    </namerecord>
+    <namerecord nameID="279" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed
+    </namerecord>
+    <namerecord nameID="280" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed Medium
+    </namerecord>
+    <namerecord nameID="281" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed SemiBold
+    </namerecord>
+    <namerecord nameID="282" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed Bold
+    </namerecord>
+    <namerecord nameID="283" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed ExtraBold
+    </namerecord>
+    <namerecord nameID="284" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed Black
+    </namerecord>
+    <namerecord nameID="285" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed Thin
+    </namerecord>
+    <namerecord nameID="286" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed ExtraLight
+    </namerecord>
+    <namerecord nameID="287" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed Light
+    </namerecord>
+    <namerecord nameID="288" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed
+    </namerecord>
+    <namerecord nameID="289" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed Medium
+    </namerecord>
+    <namerecord nameID="290" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed SemiBold
+    </namerecord>
+    <namerecord nameID="291" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed Bold
+    </namerecord>
+    <namerecord nameID="292" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed ExtraBold
+    </namerecord>
+    <namerecord nameID="293" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed Black
+    </namerecord>
+    <namerecord nameID="294" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Italic
+    </namerecord>
+    <namerecord nameID="295" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Upright
+    </namerecord>
+    <namerecord nameID="296" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      TestVariableFont-XCdBd
+    </namerecord>
+    <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 Variable Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      2.001;GOOG;TestVariableFont-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Test Variable Font Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      TestVariableFont-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="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="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      Thin
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      ExtraLight
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
+      Medium
+    </namerecord>
+    <namerecord nameID="263" platformID="3" platEncID="1" langID="0x409">
+      SemiBold
+    </namerecord>
+    <namerecord nameID="264" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="265" platformID="3" platEncID="1" langID="0x409">
+      ExtraBold
+    </namerecord>
+    <namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+    <namerecord nameID="267" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed Thin
+    </namerecord>
+    <namerecord nameID="268" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed ExtraLight
+    </namerecord>
+    <namerecord nameID="269" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed Light
+    </namerecord>
+    <namerecord nameID="270" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed
+    </namerecord>
+    <namerecord nameID="271" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed Medium
+    </namerecord>
+    <namerecord nameID="272" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed SemiBold
+    </namerecord>
+    <namerecord nameID="273" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed Bold
+    </namerecord>
+    <namerecord nameID="274" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed ExtraBold
+    </namerecord>
+    <namerecord nameID="275" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed Black
+    </namerecord>
+    <namerecord nameID="276" platformID="3" platEncID="1" langID="0x409">
+      Condensed Thin
+    </namerecord>
+    <namerecord nameID="277" platformID="3" platEncID="1" langID="0x409">
+      Condensed ExtraLight
+    </namerecord>
+    <namerecord nameID="278" platformID="3" platEncID="1" langID="0x409">
+      Condensed Light
+    </namerecord>
+    <namerecord nameID="279" platformID="3" platEncID="1" langID="0x409">
+      Condensed
+    </namerecord>
+    <namerecord nameID="280" platformID="3" platEncID="1" langID="0x409">
+      Condensed Medium
+    </namerecord>
+    <namerecord nameID="281" platformID="3" platEncID="1" langID="0x409">
+      Condensed SemiBold
+    </namerecord>
+    <namerecord nameID="282" platformID="3" platEncID="1" langID="0x409">
+      Condensed Bold
+    </namerecord>
+    <namerecord nameID="283" platformID="3" platEncID="1" langID="0x409">
+      Condensed ExtraBold
+    </namerecord>
+    <namerecord nameID="284" platformID="3" platEncID="1" langID="0x409">
+      Condensed Black
+    </namerecord>
+    <namerecord nameID="285" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed Thin
+    </namerecord>
+    <namerecord nameID="286" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed ExtraLight
+    </namerecord>
+    <namerecord nameID="287" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed Light
+    </namerecord>
+    <namerecord nameID="288" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed
+    </namerecord>
+    <namerecord nameID="289" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed Medium
+    </namerecord>
+    <namerecord nameID="290" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed SemiBold
+    </namerecord>
+    <namerecord nameID="291" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed Bold
+    </namerecord>
+    <namerecord nameID="292" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed ExtraBold
+    </namerecord>
+    <namerecord nameID="293" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed Black
+    </namerecord>
+    <namerecord nameID="294" platformID="3" platEncID="1" langID="0x409">
+      Italic
+    </namerecord>
+    <namerecord nameID="295" platformID="3" platEncID="1" langID="0x409">
+      Upright
+    </namerecord>
+    <namerecord nameID="296" platformID="3" platEncID="1" langID="0x409">
+      TestVariableFont-XCdBd
+    </namerecord>
+    <namerecord nameID="297" platformID="3" platEncID="1" langID="0x409">
+      Normal
+    </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>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=2 -->
+        <!-- RegionCount=7 -->
+        <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="0.61"/>
+            <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.61"/>
+            <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="3">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="4">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="5">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.61"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="6">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.61"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=2 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=5 -->
+        <VarRegionIndex index="0" value="2"/>
+        <VarRegionIndex index="1" value="3"/>
+        <VarRegionIndex index="2" value="4"/>
+        <VarRegionIndex index="3" value="5"/>
+        <VarRegionIndex index="4" value="6"/>
+        <Item index="0" value="[-4, -48, -11, 31, 55]"/>
+        <Item index="1" value="[0, 0, 0, 0, 0]"/>
+      </VarData>
+    </VarStore>
+    <AdvWidthMap>
+      <Map glyph=".notdef" outer="0" inner="1"/>
+      <Map glyph="hyphen" outer="0" inner="0"/>
+      <Map glyph="minus" outer="0" inner="0"/>
+      <Map glyph="space" outer="0" inner="1"/>
+    </AdvWidthMap>
+  </HVAR>
+
+  <MVAR>
+    <Version value="0x00010000"/>
+    <Reserved value="0"/>
+    <ValueRecordSize value="8"/>
+    <!-- ValueRecordCount=4 -->
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=2 -->
+        <!-- RegionCount=3 -->
+        <Region index="0">
+          <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="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=2 -->
+      <VarData index="0">
+        <!-- ItemCount=3 -->
+        <NumShorts value="1"/>
+        <!-- VarRegionCount=3 -->
+        <VarRegionIndex index="0" value="0"/>
+        <VarRegionIndex index="1" value="1"/>
+        <VarRegionIndex index="2" value="2"/>
+        <Item index="0" value="[-100, 0, 20]"/>
+        <Item index="1" value="[100, 0, -20]"/>
+        <Item index="2" value="[50, -30, -20]"/>
+      </VarData>
+      <VarData index="1">
+        <!-- ItemCount=1 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="0"/>
+        <Item index="0" value="[30]"/>
+      </VarData>
+    </VarStore>
+    <ValueRecord index="0">
+      <ValueTag value="strs"/>
+      <VarIdx value="2"/>
+    </ValueRecord>
+    <ValueRecord index="1">
+      <ValueTag value="undo"/>
+      <VarIdx value="0"/>
+    </ValueRecord>
+    <ValueRecord index="2">
+      <ValueTag value="unds"/>
+      <VarIdx value="1"/>
+    </ValueRecord>
+    <ValueRecord index="3">
+      <ValueTag value="xhgt"/>
+      <VarIdx value="65536"/>
+    </ValueRecord>
+  </MVAR>
+
+  <STAT>
+    <Version value="0x00010002"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=3 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="wdth"/>
+        <AxisNameID value="257"/>  <!-- Width -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="2">
+        <AxisTag value="ital"/>
+        <AxisNameID value="294"/>  <!-- Italic -->
+        <AxisOrdering value="2"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=5 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="258"/>  <!-- Thin -->
+        <Value value="100.0"/>
+      </AxisValue>
+      <AxisValue index="1" Format="3">
+        <AxisIndex value="0"/>
+        <Flags value="2"/>
+        <ValueNameID value="261"/>  <!-- Regular -->
+        <Value value="400.0"/>
+        <LinkedValue value="700.0"/>
+      </AxisValue>
+      <AxisValue index="2" Format="2">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="266"/>  <!-- Black -->
+        <NominalValue value="900.0"/>
+        <RangeMinValue value="801.0"/>
+        <RangeMaxValue value="900.0"/>
+      </AxisValue>
+      <AxisValue index="3" Format="4">
+        <!-- AxisCount=1 -->
+        <Flags value="0"/>
+        <ValueNameID value="279"/>  <!-- Condensed -->
+        <AxisValueRecord index="0">
+          <AxisIndex value="1"/>
+          <Value value="79.0"/>
+        </AxisValueRecord>
+      </AxisValue>
+      <AxisValue index="4" Format="3">
+        <AxisIndex value="2"/>
+        <Flags value="2"/>
+        <ValueNameID value="295"/>  <!-- Upright -->
+        <Value value="0.0"/>
+        <LinkedValue value="1.0"/>
+      </AxisValue>
+      <AxisValue index="3" Format="4">
+        <!-- AxisCount=1 -->
+        <Flags value="2"/>
+        <ValueNameID value="297"/>  <!-- Normal -->
+        <AxisValueRecord index="0">
+          <AxisIndex value="1"/>
+          <Value value="100.0"/>
+        </AxisValueRecord>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <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>
+
+  <cvar>
+    <version major="1" minor="0"/>
+    <tuple>
+      <coord axis="wght" value="-1.0"/>
+      <delta cvt="2" value="-30"/>
+    </tuple>
+    <tuple>
+      <coord axis="wdth" value="-1.0"/>
+      <delta cvt="3" value="-50"/>
+    </tuple>
+  </cvar>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>100.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>900.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- Width -->
+    <Axis>
+      <AxisTag>wdth</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>70.0</MinValue>
+      <DefaultValue>100.0</DefaultValue>
+      <MaxValue>100.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+
+    <!-- Thin -->
+    <NamedInstance flags="0x0" subfamilyNameID="258">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- ExtraLight -->
+    <NamedInstance flags="0x0" subfamilyNameID="259">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <NamedInstance flags="0x0" subfamilyNameID="260">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <NamedInstance flags="0x0" subfamilyNameID="261">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Medium -->
+    <NamedInstance flags="0x0" subfamilyNameID="262">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- SemiBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="263">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <NamedInstance flags="0x0" subfamilyNameID="264">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- ExtraBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="265">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <NamedInstance flags="0x0" subfamilyNameID="266">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed Thin -->
+    <NamedInstance flags="0x0" subfamilyNameID="267">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="89.0"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed ExtraLight -->
+    <NamedInstance flags="0x0" subfamilyNameID="268">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="89.0"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed Light -->
+    <NamedInstance flags="0x0" subfamilyNameID="269">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="89.0"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed -->
+    <NamedInstance flags="0x0" subfamilyNameID="270">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="89.0"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed Medium -->
+    <NamedInstance flags="0x0" subfamilyNameID="271">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="89.0"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed SemiBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="272">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="89.0"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed Bold -->
+    <NamedInstance flags="0x0" subfamilyNameID="273">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="89.0"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed ExtraBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="274">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="89.0"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed Black -->
+    <NamedInstance flags="0x0" subfamilyNameID="275">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="89.0"/>
+    </NamedInstance>
+
+    <!-- Condensed Thin -->
+    <NamedInstance flags="0x0" subfamilyNameID="276">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="79.0"/>
+    </NamedInstance>
+
+    <!-- Condensed ExtraLight -->
+    <NamedInstance flags="0x0" subfamilyNameID="277">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="79.0"/>
+    </NamedInstance>
+
+    <!-- Condensed Light -->
+    <NamedInstance flags="0x0" subfamilyNameID="278">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="79.0"/>
+    </NamedInstance>
+
+    <!-- Condensed -->
+    <NamedInstance flags="0x0" subfamilyNameID="279">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="79.0"/>
+    </NamedInstance>
+
+    <!-- Condensed Medium -->
+    <NamedInstance flags="0x0" subfamilyNameID="280">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="79.0"/>
+    </NamedInstance>
+
+    <!-- Condensed SemiBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="281">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="79.0"/>
+    </NamedInstance>
+
+    <!-- Condensed Bold -->
+    <NamedInstance flags="0x0" subfamilyNameID="282">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="79.0"/>
+    </NamedInstance>
+
+    <!-- Condensed ExtraBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="283">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="79.0"/>
+    </NamedInstance>
+
+    <!-- Condensed Black -->
+    <NamedInstance flags="0x0" subfamilyNameID="284">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="79.0"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed Thin -->
+    <NamedInstance flags="0x0" subfamilyNameID="285">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="70.0"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed ExtraLight -->
+    <NamedInstance flags="0x0" subfamilyNameID="286">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="70.0"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed Light -->
+    <NamedInstance flags="0x0" subfamilyNameID="287">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="70.0"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed -->
+    <NamedInstance flags="0x0" subfamilyNameID="288">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="70.0"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed Medium -->
+    <NamedInstance flags="0x0" subfamilyNameID="289">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="70.0"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed SemiBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="290">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="70.0"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed Bold -->
+    <NamedInstance flags="0x0" subfamilyNameID="291">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="70.0"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed ExtraBold -->
+    <!-- PostScript: TestVariableFont-XCdBd -->
+    <NamedInstance flags="0x0" postscriptNameID="296" subfamilyNameID="292">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="70.0"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed Black -->
+    <NamedInstance flags="0x0" subfamilyNameID="293">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="70.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph="hyphen">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="0" y="30"/>
+        <delta pt="1" x="0" y="-23"/>
+        <delta pt="2" x="0" y="-23"/>
+        <delta pt="3" x="0" y="30"/>
+        <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"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.0" value="0.61" max="1.0"/>
+        <delta pt="1" x="-10" y="22"/>
+        <delta pt="3" x="10" y="-22"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.61" value="1.0" max="1.0"/>
+        <delta pt="1" x="-19" y="37"/>
+        <delta pt="3" x="15" y="-37"/>
+        <delta pt="5" x="-4" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="1" x="-13" y="3"/>
+        <delta pt="3" x="-35" y="0"/>
+        <delta pt="5" x="-48" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="1" x="-1" y="-1"/>
+        <delta pt="3" x="-10" y="0"/>
+        <delta pt="5" x="-11" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.0" value="0.61" max="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="1" x="11" y="-1"/>
+        <delta pt="3" x="20" y="-1"/>
+        <delta pt="5" x="31" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.61" value="1.0" max="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="1" x="21" y="-3"/>
+        <delta pt="3" x="34" y="0"/>
+        <delta pt="5" x="55" y="0"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+  <ltag>
+    <version value="1"/>
+    <flags value="0"/>
+    <LanguageTag tag="gsw-LI"/>
+  </ltag>
+
+  <vhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="0"/>
+    <descent value="0"/>
+    <lineGap value="0"/>
+    <advanceHeightMax value="1000"/>
+    <minTopSideBearing value="100"/>
+    <minBottomSideBearing value="-100"/>
+    <yMaxExtent value="1100"/>
+    <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="4"/>
+  </vhea>
+
+  <vmtx>
+    <mtx name=".notdef" height="1000" tsb="100"/>
+    <mtx name="hyphen" height="536" tsb="229"/>
+    <mtx name="minus" height="536" tsb="229"/>
+    <mtx name="space" height="600" tsb="0"/>
+  </vmtx>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/PartialInstancerTest2-VF.ttx b/Tests/varLib/instancer/data/PartialInstancerTest2-VF.ttx
new file mode 100644
index 0000000..cd7ffa0
--- /dev/null
+++ b/Tests/varLib/instancer/data/PartialInstancerTest2-VF.ttx
@@ -0,0 +1,1892 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.0">
+
+  <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="T"/>
+    <GlyphID id="3" name="grave"/>
+    <GlyphID id="4" name="Agrave"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.001"/>
+    <checkSumAdjustment value="0x605c3e60"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Thu Oct 17 14:43:10 2019"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="638"/>
+    <yMax value="944"/>
+    <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="639"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="1"/>
+    <xMaxExtent value="638"/>
+    <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="19"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="32"/>
+    <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="577"/>
+    <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 00100000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="192"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000001 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="536"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="94"/>
+    <mtx name="A" width="639" lsb="0"/>
+    <mtx name="Agrave" width="639" lsb="0"/>
+    <mtx name="T" width="556" lsb="10"/>
+    <mtx name="grave" width="281" lsb="40"/>
+  </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="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </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="94" yMin="0" xMax="505" yMax="714">
+      <contour>
+        <pt x="94" y="0" on="1"/>
+        <pt x="94" y="714" on="1"/>
+        <pt x="505" y="714" on="1"/>
+        <pt x="505" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="145" y="51" on="1"/>
+        <pt x="454" y="51" on="1"/>
+        <pt x="454" y="663" on="1"/>
+        <pt x="145" y="663" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="0" yMin="0" xMax="638" yMax="717">
+      <contour>
+        <pt x="545" y="0" on="1"/>
+        <pt x="459" y="221" on="1"/>
+        <pt x="176" y="221" on="1"/>
+        <pt x="91" y="0" on="1"/>
+        <pt x="0" y="0" on="1"/>
+        <pt x="279" y="717" on="1"/>
+        <pt x="360" y="717" on="1"/>
+        <pt x="638" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="432" y="301" on="1"/>
+        <pt x="352" y="517" on="1"/>
+        <pt x="349" y="525" on="0"/>
+        <pt x="335" y="567" on="0"/>
+        <pt x="322" y="612" on="0"/>
+        <pt x="318" y="624" on="1"/>
+        <pt x="313" y="604" on="0"/>
+        <pt x="302" y="563" on="0"/>
+        <pt x="291" y="529" on="0"/>
+        <pt x="287" y="517" on="1"/>
+        <pt x="206" y="301" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Agrave" xMin="0" yMin="0" xMax="638" yMax="944">
+      <component glyphName="A" x="0" y="0" flags="0x204"/>
+      <component glyphName="grave" x="147" y="178" flags="0x4"/>
+    </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="grave" xMin="40" yMin="606" xMax="241" yMax="766">
+      <contour>
+        <pt x="145" y="766" on="1"/>
+        <pt x="156" y="744" on="0"/>
+        <pt x="189" y="689" on="0"/>
+        <pt x="226" y="637" on="0"/>
+        <pt x="241" y="618" on="1"/>
+        <pt x="241" y="606" on="1"/>
+        <pt x="182" y="606" on="1"/>
+        <pt x="165" y="620" on="0"/>
+        <pt x="123" y="659" on="0"/>
+        <pt x="82" y="702" on="0"/>
+        <pt x="49" y="742" on="0"/>
+        <pt x="40" y="756" on="1"/>
+        <pt x="40" y="766" on="1"/>
+      </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">
+      Width
+    </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">
+      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">
+      Regular
+    </namerecord>
+    <namerecord nameID="262" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Medium
+    </namerecord>
+    <namerecord nameID="263" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiBold
+    </namerecord>
+    <namerecord nameID="264" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Bold
+    </namerecord>
+    <namerecord nameID="265" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraBold
+    </namerecord>
+    <namerecord nameID="266" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Black
+    </namerecord>
+    <namerecord nameID="267" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed Thin
+    </namerecord>
+    <namerecord nameID="268" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed ExtraLight
+    </namerecord>
+    <namerecord nameID="269" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed Light
+    </namerecord>
+    <namerecord nameID="270" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed
+    </namerecord>
+    <namerecord nameID="271" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed Medium
+    </namerecord>
+    <namerecord nameID="272" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed SemiBold
+    </namerecord>
+    <namerecord nameID="273" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed Bold
+    </namerecord>
+    <namerecord nameID="274" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed ExtraBold
+    </namerecord>
+    <namerecord nameID="275" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SemiCondensed Black
+    </namerecord>
+    <namerecord nameID="276" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed Thin
+    </namerecord>
+    <namerecord nameID="277" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed ExtraLight
+    </namerecord>
+    <namerecord nameID="278" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed Light
+    </namerecord>
+    <namerecord nameID="279" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed
+    </namerecord>
+    <namerecord nameID="280" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed Medium
+    </namerecord>
+    <namerecord nameID="281" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed SemiBold
+    </namerecord>
+    <namerecord nameID="282" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed Bold
+    </namerecord>
+    <namerecord nameID="283" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed ExtraBold
+    </namerecord>
+    <namerecord nameID="284" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Condensed Black
+    </namerecord>
+    <namerecord nameID="285" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed Thin
+    </namerecord>
+    <namerecord nameID="286" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed ExtraLight
+    </namerecord>
+    <namerecord nameID="287" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed Light
+    </namerecord>
+    <namerecord nameID="288" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed
+    </namerecord>
+    <namerecord nameID="289" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed Medium
+    </namerecord>
+    <namerecord nameID="290" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed SemiBold
+    </namerecord>
+    <namerecord nameID="291" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed Bold
+    </namerecord>
+    <namerecord nameID="292" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed ExtraBold
+    </namerecord>
+    <namerecord nameID="293" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed Black
+    </namerecord>
+    <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">
+      Noto Sans
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      2.001;GOOG;NotoSans-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Noto Sans Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NotoSans-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>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      Thin
+    </namerecord>
+    <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
+      ExtraLight
+    </namerecord>
+    <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
+      Light
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="262" platformID="3" platEncID="1" langID="0x409">
+      Medium
+    </namerecord>
+    <namerecord nameID="263" platformID="3" platEncID="1" langID="0x409">
+      SemiBold
+    </namerecord>
+    <namerecord nameID="264" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="265" platformID="3" platEncID="1" langID="0x409">
+      ExtraBold
+    </namerecord>
+    <namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+    <namerecord nameID="267" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed Thin
+    </namerecord>
+    <namerecord nameID="268" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed ExtraLight
+    </namerecord>
+    <namerecord nameID="269" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed Light
+    </namerecord>
+    <namerecord nameID="270" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed
+    </namerecord>
+    <namerecord nameID="271" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed Medium
+    </namerecord>
+    <namerecord nameID="272" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed SemiBold
+    </namerecord>
+    <namerecord nameID="273" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed Bold
+    </namerecord>
+    <namerecord nameID="274" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed ExtraBold
+    </namerecord>
+    <namerecord nameID="275" platformID="3" platEncID="1" langID="0x409">
+      SemiCondensed Black
+    </namerecord>
+    <namerecord nameID="276" platformID="3" platEncID="1" langID="0x409">
+      Condensed Thin
+    </namerecord>
+    <namerecord nameID="277" platformID="3" platEncID="1" langID="0x409">
+      Condensed ExtraLight
+    </namerecord>
+    <namerecord nameID="278" platformID="3" platEncID="1" langID="0x409">
+      Condensed Light
+    </namerecord>
+    <namerecord nameID="279" platformID="3" platEncID="1" langID="0x409">
+      Condensed
+    </namerecord>
+    <namerecord nameID="280" platformID="3" platEncID="1" langID="0x409">
+      Condensed Medium
+    </namerecord>
+    <namerecord nameID="281" platformID="3" platEncID="1" langID="0x409">
+      Condensed SemiBold
+    </namerecord>
+    <namerecord nameID="282" platformID="3" platEncID="1" langID="0x409">
+      Condensed Bold
+    </namerecord>
+    <namerecord nameID="283" platformID="3" platEncID="1" langID="0x409">
+      Condensed ExtraBold
+    </namerecord>
+    <namerecord nameID="284" platformID="3" platEncID="1" langID="0x409">
+      Condensed Black
+    </namerecord>
+    <namerecord nameID="285" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed Thin
+    </namerecord>
+    <namerecord nameID="286" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed ExtraLight
+    </namerecord>
+    <namerecord nameID="287" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed Light
+    </namerecord>
+    <namerecord nameID="288" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed
+    </namerecord>
+    <namerecord nameID="289" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed Medium
+    </namerecord>
+    <namerecord nameID="290" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed SemiBold
+    </namerecord>
+    <namerecord nameID="291" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed Bold
+    </namerecord>
+    <namerecord nameID="292" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed ExtraBold
+    </namerecord>
+    <namerecord nameID="293" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed Black
+    </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>
+
+  <GDEF>
+    <Version value="0x00010003"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Agrave" class="1"/>
+      <ClassDef glyph="T" class="1"/>
+    </GlyphClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=4 -->
+      <Coverage index="0">
+      </Coverage>
+      <Coverage index="1">
+      </Coverage>
+      <Coverage index="2">
+      </Coverage>
+      <Coverage index="3">
+      </Coverage>
+    </MarkGlyphSetsDef>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=2 -->
+        <!-- RegionCount=1 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.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="[-8]"/>
+        <Item index="1" value="[53]"/>
+      </VarData>
+    </VarStore>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <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="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <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="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="T"/>
+            <Glyph value="Agrave"/>
+          </Coverage>
+          <ValueFormat1 value="68"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+            <ClassDef glyph="A" class="1"/>
+            <ClassDef glyph="Agrave" class="1"/>
+            <ClassDef glyph="T" class="2"/>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="A" class="1"/>
+            <ClassDef glyph="Agrave" class="1"/>
+            <ClassDef glyph="T" class="2"/>
+          </ClassDef2>
+          <!-- Class1Count=3 -->
+          <!-- 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="0"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-70">
+                <XAdvDevice>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </XAdvDevice>
+              </Value1>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="2">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-70">
+                <XAdvDevice>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </XAdvDevice>
+              </Value1>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="20">
+                <XAdvDevice>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </XAdvDevice>
+              </Value1>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <ScriptTag value="latn"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=2 -->
+        <!-- RegionCount=7 -->
+        <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="0.61"/>
+            <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.61"/>
+            <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="3">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="4">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="5">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.61"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="6">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.61"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=3 -->
+      <VarData index="0">
+        <!-- ItemCount=1 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=5 -->
+        <VarRegionIndex index="0" value="2"/>
+        <VarRegionIndex index="1" value="3"/>
+        <VarRegionIndex index="2" value="4"/>
+        <VarRegionIndex index="3" value="5"/>
+        <VarRegionIndex index="4" value="6"/>
+        <Item index="0" value="[-18, -5, 5, -8, 5]"/>
+      </VarData>
+      <VarData index="1">
+        <!-- ItemCount=1 -->
+        <NumShorts value="1"/>
+        <!-- VarRegionCount=7 -->
+        <VarRegionIndex index="0" value="2"/>
+        <VarRegionIndex index="1" value="0"/>
+        <VarRegionIndex index="2" value="1"/>
+        <VarRegionIndex index="3" value="3"/>
+        <VarRegionIndex index="4" value="4"/>
+        <VarRegionIndex index="5" value="5"/>
+        <VarRegionIndex index="6" value="6"/>
+        <Item index="0" value="[137, -32, 81, -15, -9, -36, -63]"/>
+      </VarData>
+      <VarData index="2">
+        <!-- ItemCount=2 -->
+        <NumShorts value="1"/>
+        <!-- VarRegionCount=7 -->
+        <VarRegionIndex index="0" value="3"/>
+        <VarRegionIndex index="1" value="0"/>
+        <VarRegionIndex index="2" value="1"/>
+        <VarRegionIndex index="3" value="2"/>
+        <VarRegionIndex index="4" value="4"/>
+        <VarRegionIndex index="5" value="5"/>
+        <VarRegionIndex index="6" value="6"/>
+        <Item index="0" value="[-189, -62, 51, 87, -11, 25, 37]"/>
+        <Item index="1" value="[-175, -51, 23, 35, 6, 25, 44]"/>
+      </VarData>
+    </VarStore>
+    <AdvWidthMap>
+      <Map glyph=".notdef" outer="0" inner="0"/>
+      <Map glyph="A" outer="2" inner="0"/>
+      <Map glyph="Agrave" outer="2" inner="0"/>
+      <Map glyph="T" outer="2" inner="1"/>
+      <Map glyph="grave" outer="1" inner="0"/>
+    </AdvWidthMap>
+  </HVAR>
+
+  <MVAR>
+    <Version value="0x00010000"/>
+    <Reserved value="0"/>
+    <ValueRecordSize value="8"/>
+    <!-- ValueRecordCount=2 -->
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=2 -->
+        <!-- RegionCount=7 -->
+        <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="0.61"/>
+            <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.61"/>
+            <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="3">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="4">
+          <VarRegionAxis index="0">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="5">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.61"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="6">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.61"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+          <VarRegionAxis index="1">
+            <StartCoord value="-1.0"/>
+            <PeakCoord value="-1.0"/>
+            <EndCoord value="0.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=2 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=6 -->
+        <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"/>
+        <VarRegionIndex index="5" value="6"/>
+        <Item index="0" value="[-8, 10, 17, 1, -2, -1]"/>
+        <Item index="1" value="[-5, 6, 10, 0, -1, 0]"/>
+      </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="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=2 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="wdth"/>
+        <AxisNameID value="257"/>  <!-- Width -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=13 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="258"/>  <!-- Thin -->
+        <Value value="100.0"/>
+      </AxisValue>
+      <AxisValue index="1" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="259"/>  <!-- ExtraLight -->
+        <Value value="200.0"/>
+      </AxisValue>
+      <AxisValue index="2" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="260"/>  <!-- Light -->
+        <Value value="300.0"/>
+      </AxisValue>
+      <AxisValue index="3" Format="3">
+        <AxisIndex value="0"/>
+        <Flags value="2"/>
+        <ValueNameID value="261"/>  <!-- Regular -->
+        <Value value="400.0"/>
+        <LinkedValue value="700.0"/>
+      </AxisValue>
+      <AxisValue index="4" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="262"/>  <!-- Medium -->
+        <Value value="500.0"/>
+      </AxisValue>
+      <AxisValue index="5" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="263"/>  <!-- SemiBold -->
+        <Value value="600.0"/>
+      </AxisValue>
+      <AxisValue index="6" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="264"/>  <!-- Bold -->
+        <Value value="700.0"/>
+      </AxisValue>
+      <AxisValue index="7" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="265"/>  <!-- ExtraBold -->
+        <Value value="800.0"/>
+      </AxisValue>
+      <AxisValue index="8" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="266"/>  <!-- Black -->
+        <Value value="900.0"/>
+      </AxisValue>
+      <AxisValue index="9" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="2"/>
+        <ValueNameID value="261"/>  <!-- Regular -->
+        <NominalValue value="100.0"/>
+        <RangeMinValue value="93.75"/>
+        <RangeMaxValue value="100.0"/>
+      </AxisValue>
+      <AxisValue index="10" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="270"/>  <!-- SemiCondensed -->
+        <NominalValue value="87.5"/>
+        <RangeMinValue value="81.25"/>
+        <RangeMaxValue value="93.75"/>
+      </AxisValue>
+      <AxisValue index="11" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="279"/>  <!-- Condensed -->
+        <NominalValue value="75.0"/>
+        <RangeMinValue value="68.75"/>
+        <RangeMaxValue value="81.25"/>
+      </AxisValue>
+      <AxisValue index="12" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="288"/>  <!-- ExtraCondensed -->
+        <NominalValue value="62.5"/>
+        <RangeMinValue value="62.5"/>
+        <RangeMaxValue value="68.75"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <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.6667" to="-0.7"/>
+      <mapping from="-0.3333" to="-0.36664"/>
+      <mapping from="0.0" to="0.0"/>
+      <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>256</AxisNameID>
+    </Axis>
+
+    <!-- Width -->
+    <Axis>
+      <AxisTag>wdth</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>62.5</MinValue>
+      <DefaultValue>100.0</DefaultValue>
+      <MaxValue>100.0</MaxValue>
+      <AxisNameID>257</AxisNameID>
+    </Axis>
+
+    <!-- Thin -->
+    <NamedInstance flags="0x0" subfamilyNameID="258">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- ExtraLight -->
+    <NamedInstance flags="0x0" subfamilyNameID="259">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Light -->
+    <NamedInstance flags="0x0" subfamilyNameID="260">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <NamedInstance flags="0x0" subfamilyNameID="261">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Medium -->
+    <NamedInstance flags="0x0" subfamilyNameID="262">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- SemiBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="263">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <NamedInstance flags="0x0" subfamilyNameID="264">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- ExtraBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="265">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- Black -->
+    <NamedInstance flags="0x0" subfamilyNameID="266">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="100.0"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed Thin -->
+    <NamedInstance flags="0x0" subfamilyNameID="267">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="87.5"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed ExtraLight -->
+    <NamedInstance flags="0x0" subfamilyNameID="268">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="87.5"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed Light -->
+    <NamedInstance flags="0x0" subfamilyNameID="269">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="87.5"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed -->
+    <NamedInstance flags="0x0" subfamilyNameID="270">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="87.5"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed Medium -->
+    <NamedInstance flags="0x0" subfamilyNameID="271">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="87.5"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed SemiBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="272">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="87.5"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed Bold -->
+    <NamedInstance flags="0x0" subfamilyNameID="273">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="87.5"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed ExtraBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="274">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="87.5"/>
+    </NamedInstance>
+
+    <!-- SemiCondensed Black -->
+    <NamedInstance flags="0x0" subfamilyNameID="275">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="87.5"/>
+    </NamedInstance>
+
+    <!-- Condensed Thin -->
+    <NamedInstance flags="0x0" subfamilyNameID="276">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="75.0"/>
+    </NamedInstance>
+
+    <!-- Condensed ExtraLight -->
+    <NamedInstance flags="0x0" subfamilyNameID="277">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="75.0"/>
+    </NamedInstance>
+
+    <!-- Condensed Light -->
+    <NamedInstance flags="0x0" subfamilyNameID="278">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="75.0"/>
+    </NamedInstance>
+
+    <!-- Condensed -->
+    <NamedInstance flags="0x0" subfamilyNameID="279">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="75.0"/>
+    </NamedInstance>
+
+    <!-- Condensed Medium -->
+    <NamedInstance flags="0x0" subfamilyNameID="280">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="75.0"/>
+    </NamedInstance>
+
+    <!-- Condensed SemiBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="281">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="75.0"/>
+    </NamedInstance>
+
+    <!-- Condensed Bold -->
+    <NamedInstance flags="0x0" subfamilyNameID="282">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="75.0"/>
+    </NamedInstance>
+
+    <!-- Condensed ExtraBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="283">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="75.0"/>
+    </NamedInstance>
+
+    <!-- Condensed Black -->
+    <NamedInstance flags="0x0" subfamilyNameID="284">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="75.0"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed Thin -->
+    <NamedInstance flags="0x0" subfamilyNameID="285">
+      <coord axis="wght" value="100.0"/>
+      <coord axis="wdth" value="62.5"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed ExtraLight -->
+    <NamedInstance flags="0x0" subfamilyNameID="286">
+      <coord axis="wght" value="200.0"/>
+      <coord axis="wdth" value="62.5"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed Light -->
+    <NamedInstance flags="0x0" subfamilyNameID="287">
+      <coord axis="wght" value="300.0"/>
+      <coord axis="wdth" value="62.5"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed -->
+    <NamedInstance flags="0x0" subfamilyNameID="288">
+      <coord axis="wght" value="400.0"/>
+      <coord axis="wdth" value="62.5"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed Medium -->
+    <NamedInstance flags="0x0" subfamilyNameID="289">
+      <coord axis="wght" value="500.0"/>
+      <coord axis="wdth" value="62.5"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed SemiBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="290">
+      <coord axis="wght" value="600.0"/>
+      <coord axis="wdth" value="62.5"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed Bold -->
+    <NamedInstance flags="0x0" subfamilyNameID="291">
+      <coord axis="wght" value="700.0"/>
+      <coord axis="wdth" value="62.5"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed ExtraBold -->
+    <NamedInstance flags="0x0" subfamilyNameID="292">
+      <coord axis="wght" value="800.0"/>
+      <coord axis="wdth" value="62.5"/>
+    </NamedInstance>
+
+    <!-- ExtraCondensed Black -->
+    <NamedInstance flags="0x0" subfamilyNameID="293">
+      <coord axis="wght" value="900.0"/>
+      <coord axis="wdth" value="62.5"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph=".notdef">
+      <tuple>
+        <coord axis="wght" min="0.61" value="1.0" max="1.0"/>
+        <delta pt="0" x="-9" y="0"/>
+        <delta pt="4" x="-9" y="0"/>
+        <delta pt="9" x="-18" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="-9" y="0"/>
+        <delta pt="4" x="-9" y="0"/>
+        <delta pt="9" x="-5" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" 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="0"/>
+        <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="5" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.0" value="0.61" max="1.0"/>
+        <coord axis="wdth" 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="0"/>
+        <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="-8" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.61" value="1.0" max="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="9" y="0"/>
+        <delta pt="4" x="9" y="0"/>
+        <delta pt="9" x="5" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="A">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="4" y="0"/>
+        <delta pt="1" x="-14" y="50"/>
+        <delta pt="2" x="-41" y="50"/>
+        <delta pt="3" x="-63" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="3" y="-1"/>
+        <delta pt="6" x="-53" y="-1"/>
+        <delta pt="7" x="-61" y="0"/>
+        <delta pt="8" x="3" y="-5"/>
+        <delta pt="9" x="-27" y="77"/>
+        <delta pt="10" x="-28" y="81"/>
+        <delta pt="11" x="-24" y="65"/>
+        <delta pt="12" x="-22" y="52"/>
+        <delta pt="13" x="-25" y="58"/>
+        <delta pt="14" x="-25" y="63"/>
+        <delta pt="15" x="-25" y="73"/>
+        <delta pt="16" x="-26" y="78"/>
+        <delta pt="17" x="-27" y="76"/>
+        <delta pt="18" x="-62" y="-5"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="-62" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.0" value="0.61" max="1.0"/>
+        <delta pt="0" x="-18" y="0"/>
+        <delta pt="1" x="16" y="-51"/>
+        <delta pt="2" x="39" y="-51"/>
+        <delta pt="3" x="72" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-27" y="0"/>
+        <delta pt="6" x="77" y="0"/>
+        <delta pt="7" x="52" y="0"/>
+        <delta pt="8" x="7" y="-4"/>
+        <delta pt="9" x="35" y="-54"/>
+        <delta pt="10" x="33" y="-45"/>
+        <delta pt="11" x="31" y="-35"/>
+        <delta pt="12" x="28" y="-26"/>
+        <delta pt="13" x="27" y="-18"/>
+        <delta pt="14" x="27" y="-18"/>
+        <delta pt="15" x="21" y="-36"/>
+        <delta pt="16" x="17" y="-55"/>
+        <delta pt="17" x="17" y="-54"/>
+        <delta pt="18" x="47" y="-4"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="51" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.61" value="1.0" max="1.0"/>
+        <delta pt="0" x="-30" y="0"/>
+        <delta pt="1" x="21" y="-87"/>
+        <delta pt="2" x="72" y="-87"/>
+        <delta pt="3" x="121" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-46" y="0"/>
+        <delta pt="6" x="130" y="0"/>
+        <delta pt="7" x="88" y="0"/>
+        <delta pt="8" x="8" y="-9"/>
+        <delta pt="9" x="57" y="-108"/>
+        <delta pt="10" x="55" y="-97"/>
+        <delta pt="11" x="51" y="-68"/>
+        <delta pt="12" x="46" y="-37"/>
+        <delta pt="13" x="45" y="-25"/>
+        <delta pt="14" x="46" y="-29"/>
+        <delta pt="15" x="40" y="-60"/>
+        <delta pt="16" x="34" y="-96"/>
+        <delta pt="17" x="32" y="-108"/>
+        <delta pt="18" x="82" y="-9"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="87" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="-184" y="0"/>
+        <delta pt="1" x="-150" y="2"/>
+        <delta pt="2" x="-34" y="2"/>
+        <delta pt="3" x="-1" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-106" y="-3"/>
+        <delta pt="6" x="-86" y="-3"/>
+        <delta pt="7" x="-188" y="0"/>
+        <delta pt="8" x="-139" y="0"/>
+        <delta pt="9" x="-112" y="18"/>
+        <delta pt="10" x="-112" y="29"/>
+        <delta pt="11" x="-105" y="22"/>
+        <delta pt="12" x="-97" y="11"/>
+        <delta pt="13" x="-95" y="14"/>
+        <delta pt="14" x="-91" y="19"/>
+        <delta pt="15" x="-85" y="26"/>
+        <delta pt="16" x="-81" y="25"/>
+        <delta pt="17" x="-81" y="19"/>
+        <delta pt="18" x="-51" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="-189" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="-14" y="0"/>
+        <delta pt="1" x="-11" y="8"/>
+        <delta pt="2" x="-7" y="8"/>
+        <delta pt="3" x="-1" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="-6" y="1"/>
+        <delta pt="6" x="-13" y="1"/>
+        <delta pt="7" x="-12" y="0"/>
+        <delta pt="8" x="-18" y="10"/>
+        <delta pt="9" x="-5" y="0"/>
+        <delta pt="10" x="-5" y="-6"/>
+        <delta pt="11" x="-8" y="1"/>
+        <delta pt="12" x="-11" y="6"/>
+        <delta pt="13" x="-9" y="-1"/>
+        <delta pt="14" x="-11" y="-5"/>
+        <delta pt="15" x="-12" y="-6"/>
+        <delta pt="16" x="-10" y="-3"/>
+        <delta pt="17" x="-9" y="0"/>
+        <delta pt="18" x="7" y="10"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="-11" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.0" value="0.61" max="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="37" y="0"/>
+        <delta pt="1" x="18" y="0"/>
+        <delta pt="2" x="3" y="0"/>
+        <delta pt="3" x="-15" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="27" y="0"/>
+        <delta pt="6" x="-1" y="0"/>
+        <delta pt="7" x="24" y="0"/>
+        <delta pt="8" x="22" y="-6"/>
+        <delta pt="9" x="8" y="-1"/>
+        <delta pt="10" x="9" y="-5"/>
+        <delta pt="11" x="11" y="-5"/>
+        <delta pt="12" x="13" y="-7"/>
+        <delta pt="13" x="13" y="-12"/>
+        <delta pt="14" x="13" y="-14"/>
+        <delta pt="15" x="18" y="-3"/>
+        <delta pt="16" x="21" y="6"/>
+        <delta pt="17" x="20" y="0"/>
+        <delta pt="18" x="3" y="-6"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="25" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.61" value="1.0" max="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="61" y="0"/>
+        <delta pt="1" x="35" y="3"/>
+        <delta pt="2" x="-3" y="3"/>
+        <delta pt="3" x="-27" y="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="47" y="0"/>
+        <delta pt="6" x="-6" y="0"/>
+        <delta pt="7" x="36" y="0"/>
+        <delta pt="8" x="38" y="-7"/>
+        <delta pt="9" x="13" y="18"/>
+        <delta pt="10" x="13" y="15"/>
+        <delta pt="11" x="16" y="1"/>
+        <delta pt="12" x="20" y="-17"/>
+        <delta pt="13" x="20" y="-24"/>
+        <delta pt="14" x="18" y="-24"/>
+        <delta pt="15" x="23" y="-5"/>
+        <delta pt="16" x="27" y="16"/>
+        <delta pt="17" x="28" y="19"/>
+        <delta pt="18" x="-1" y="-7"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="37" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+        <delta pt="22" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="T">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="1" x="6" y="0"/>
+        <delta pt="3" x="-8" y="54"/>
+        <delta pt="5" x="-42" y="0"/>
+        <delta pt="7" x="-58" y="54"/>
+        <delta pt="9" x="-51" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.0" value="0.61" max="1.0"/>
+        <delta pt="1" x="-19" y="0"/>
+        <delta pt="3" x="10" y="-47"/>
+        <delta pt="5" x="14" y="0"/>
+        <delta pt="7" x="42" y="-47"/>
+        <delta pt="9" x="23" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.61" value="1.0" max="1.0"/>
+        <delta pt="1" x="-34" y="0"/>
+        <delta pt="3" x="15" y="-79"/>
+        <delta pt="5" x="21" y="0"/>
+        <delta pt="7" x="69" y="-79"/>
+        <delta pt="9" x="35" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="1" x="-85" y="0"/>
+        <delta pt="3" x="0" y="3"/>
+        <delta pt="5" x="-175" y="0"/>
+        <delta pt="7" x="-91" y="3"/>
+        <delta pt="9" x="-175" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="3" x="5" y="-3"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="7" x="6" y="-3"/>
+        <delta pt="9" x="6" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.0" value="0.61" max="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="1" x="18" y="0"/>
+        <delta pt="3" x="-6" y="4"/>
+        <delta pt="5" x="30" y="0"/>
+        <delta pt="7" x="8" y="4"/>
+        <delta pt="9" x="25" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.61" value="1.0" max="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="1" x="32" y="0"/>
+        <delta pt="3" x="-8" y="8"/>
+        <delta pt="5" x="52" y="0"/>
+        <delta pt="7" x="14" y="8"/>
+        <delta pt="9" x="44" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="grave">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="0" x="-75" y="0"/>
+        <delta pt="1" x="-71" y="1"/>
+        <delta pt="2" x="-56" y="-1"/>
+        <delta pt="3" x="-39" y="-5"/>
+        <delta pt="4" x="-32" y="-6"/>
+        <delta pt="5" x="-32" y="0"/>
+        <delta pt="6" x="6" y="0"/>
+        <delta pt="7" x="3" y="3"/>
+        <delta pt="8" x="4" y="3"/>
+        <delta pt="9" x="6" y="1"/>
+        <delta pt="10" x="4" y="0"/>
+        <delta pt="11" x="0" y="3"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="-32" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.0" value="0.61" max="1.0"/>
+        <delta pt="0" x="62" y="0"/>
+        <delta pt="1" x="66" y="0"/>
+        <delta pt="2" x="74" y="0"/>
+        <delta pt="3" x="80" y="1"/>
+        <delta pt="4" x="81" y="1"/>
+        <delta pt="5" x="81" y="0"/>
+        <delta pt="6" x="41" y="0"/>
+        <delta pt="7" x="39" y="-1"/>
+        <delta pt="8" x="30" y="-1"/>
+        <delta pt="9" x="18" y="0"/>
+        <delta pt="10" x="5" 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="81" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.61" value="1.0" max="1.0"/>
+        <delta pt="0" x="105" y="0"/>
+        <delta pt="1" x="111" y="0"/>
+        <delta pt="2" x="125" y="1"/>
+        <delta pt="3" x="136" y="2"/>
+        <delta pt="4" x="137" y="2"/>
+        <delta pt="5" x="137" y="0"/>
+        <delta pt="6" x="69" y="0"/>
+        <delta pt="7" x="66" y="-1"/>
+        <delta pt="8" x="51" y="-1"/>
+        <delta pt="9" x="31" y="-1"/>
+        <delta pt="10" x="9" 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="137" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="-8" y="0"/>
+        <delta pt="1" x="-8" y="-1"/>
+        <delta pt="2" x="-10" y="-1"/>
+        <delta pt="3" x="-14" y="-1"/>
+        <delta pt="4" x="-15" y="-1"/>
+        <delta pt="5" x="-15" y="0"/>
+        <delta pt="6" x="-8" y="0"/>
+        <delta pt="7" x="-6" y="-1"/>
+        <delta pt="8" x="-2" y="-2"/>
+        <delta pt="9" x="0" y="-2"/>
+        <delta pt="10" x="1" y="-1"/>
+        <delta pt="11" x="0" y="1"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="-15" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="9" y="-3"/>
+        <delta pt="1" x="12" y="-8"/>
+        <delta pt="2" x="3" y="-1"/>
+        <delta pt="3" x="-7" y="9"/>
+        <delta pt="4" x="-9" y="6"/>
+        <delta pt="5" x="-9" y="0"/>
+        <delta pt="6" x="-11" y="0"/>
+        <delta pt="7" x="-7" y="-2"/>
+        <delta pt="8" x="-5" y="-4"/>
+        <delta pt="9" x="-3" y="-5"/>
+        <delta pt="10" x="-2" y="-4"/>
+        <delta pt="11" x="0" y="-4"/>
+        <delta pt="12" x="0" y="-3"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="-9" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.0" value="0.61" max="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="-10" y="0"/>
+        <delta pt="1" x="-16" y="2"/>
+        <delta pt="2" x="-25" y="0"/>
+        <delta pt="3" x="-32" y="-2"/>
+        <delta pt="4" x="-36" y="1"/>
+        <delta pt="5" x="-36" y="0"/>
+        <delta pt="6" x="-37" y="0"/>
+        <delta pt="7" x="-33" y="0"/>
+        <delta pt="8" x="-25" y="1"/>
+        <delta pt="9" x="-15" y="2"/>
+        <delta pt="10" x="-5" y="2"/>
+        <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="-36" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.61" value="1.0" max="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="0" x="-20" y="0"/>
+        <delta pt="1" x="-28" y="4"/>
+        <delta pt="2" x="-43" y="-1"/>
+        <delta pt="3" x="-57" y="-4"/>
+        <delta pt="4" x="-63" y="1"/>
+        <delta pt="5" x="-63" y="0"/>
+        <delta pt="6" x="-62" y="0"/>
+        <delta pt="7" x="-56" y="-1"/>
+        <delta pt="8" x="-42" y="1"/>
+        <delta pt="9" x="-26" y="4"/>
+        <delta pt="10" x="-9" y="3"/>
+        <delta pt="11" x="0" y="-1"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="-63" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="Agrave">
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <delta pt="1" x="-16" y="8"/>
+        <delta pt="3" x="-62" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.0" value="0.61" max="1.0"/>
+        <delta pt="1" x="-33" y="-10"/>
+        <delta pt="3" x="51" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.61" value="1.0" max="1.0"/>
+        <delta pt="1" x="-35" y="-17"/>
+        <delta pt="3" x="87" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="1" x="-115" y="-14"/>
+        <delta pt="3" x="-189" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" value="-1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="1" x="-5" y="-4"/>
+        <delta pt="3" x="-11" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.0" value="0.61" max="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="1" x="64" y="8"/>
+        <delta pt="3" x="25" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" min="0.61" value="1.0" max="1.0"/>
+        <coord axis="wdth" value="-1.0"/>
+        <delta pt="1" x="86" y="14"/>
+        <delta pt="3" x="37" y="0"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/PartialInstancerTest3-VF.ttx b/Tests/varLib/instancer/data/PartialInstancerTest3-VF.ttx
new file mode 100644
index 0000000..01c7d05
--- /dev/null
+++ b/Tests/varLib/instancer/data/PartialInstancerTest3-VF.ttx
@@ -0,0 +1,439 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.15">
+
+  <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="D"/>
+    <GlyphID id="5" name="E"/>
+    <GlyphID id="6" name="F"/>
+    <GlyphID id="7" name="space"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xc3d4abe6"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Sep 23 12:54:22 2020"/>
+    <modified value="Tue Sep 29 18:06:03 2020"/>
+    <xMin value="-152"/>
+    <yMin value="-200"/>
+    <xMax value="1059"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="600"/>
+    <minLeftSideBearing value="-152"/>
+    <minRightSideBearing value="-559"/>
+    <xMaxExtent value="1059"/>
+    <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="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="513"/>
+    <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="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="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="70"/>
+    <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="500" lsb="50"/>
+    <mtx name="A" width="500" lsb="153"/>
+    <mtx name="B" width="500" lsb="-152"/>
+    <mtx name="C" width="500" lsb="-133"/>
+    <mtx name="D" width="500" lsb="-97"/>
+    <mtx name="E" width="500" lsb="-87"/>
+    <mtx name="F" width="500" lsb="-107"/>
+    <mtx name="space" width="600" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <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 -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <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 -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="153" yMin="-66" xMax="350" yMax="646">
+      <contour>
+        <pt x="153" y="646" on="1"/>
+        <pt x="350" y="646" on="1"/>
+        <pt x="350" y="-66" on="1"/>
+        <pt x="153" y="-66" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="B" xMin="-152" yMin="39" xMax="752" yMax="592">
+      <contour>
+        <pt x="-152" y="448" on="1"/>
+        <pt x="752" y="448" on="1"/>
+        <pt x="752" y="215" on="1"/>
+        <pt x="-152" y="215" on="1"/>
+      </contour>
+      <contour>
+        <pt x="129" y="592" on="1"/>
+        <pt x="401" y="592" on="1"/>
+        <pt x="401" y="39" on="1"/>
+        <pt x="129" y="39" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="C" xMin="-133" yMin="-66" xMax="771" yMax="646">
+      <component glyphName="A" x="-250" y="0" flags="0x4"/>
+      <component glyphName="B" x="19" y="-28" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="D" xMin="-97" yMin="-66" xMax="1059" yMax="646">
+      <component glyphName="A" x="-250" y="0" flags="0x4"/>
+      <component glyphName="B" x="307" y="-28" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="E" xMin="-87" yMin="-87" xMax="801" yMax="650">
+      <component glyphName="A" x="450" y="-77" scalex="0.9397" scale01="0.34204" scale10="-0.34204" scaley="0.9397" flags="0x4"/>
+      <component glyphName="A" x="8" y="4" flags="0x4"/>
+      <component glyphName="A" x="-240" y="0" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="-107" yMin="-95" xMax="837" yMax="650">
+      <component glyphName="A" x="501" y="-114" scalex="0.866" scale01="0.5" scale10="-0.5" scaley="0.866" flags="0x4"/>
+      <component glyphName="A" x="-12" y="4" flags="0x4"/>
+      <component glyphName="A" x="-260" y="0" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </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">
+      Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Remove Overlaps Test
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;RemoveOverlapsTest-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Remove Overlaps Test Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      RemoveOverlapsTest-Regular
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </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>
+
+  <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=8 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=0 -->
+        <Item index="0" value="[]"/>
+        <Item index="1" value="[]"/>
+        <Item index="2" value="[]"/>
+        <Item index="3" value="[]"/>
+        <Item index="4" value="[]"/>
+        <Item index="5" value="[]"/>
+        <Item index="6" value="[]"/>
+        <Item index="7" value="[]"/>
+      </VarData>
+    </VarStore>
+  </HVAR>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=1 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=0 -->
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>400.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>700.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- Regular -->
+    <NamedInstance flags="0x0" subfamilyNameID="257">
+      <coord axis="wght" value="400.0"/>
+    </NamedInstance>
+
+    <!-- Regular -->
+    <NamedInstance flags="0x0" subfamilyNameID="257">
+      <coord axis="wght" value="700.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph="A">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-40" y="0"/>
+        <delta pt="1" x="40" y="0"/>
+        <delta pt="2" x="40" y="0"/>
+        <delta pt="3" x="-40" y="0"/>
+        <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"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="B">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-40" y="0"/>
+        <delta pt="2" x="40" y="0"/>
+        <delta pt="5" x="40" y="20"/>
+        <delta pt="7" x="-40" y="-20"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="C">
+      <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="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="D">
+      <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="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="E">
+      <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="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="F">
+      <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="0"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/PartialInstancerTest4-VF.ttx b/Tests/varLib/instancer/data/PartialInstancerTest4-VF.ttx
new file mode 100644
index 0000000..8d445b0
--- /dev/null
+++ b/Tests/varLib/instancer/data/PartialInstancerTest4-VF.ttx
@@ -0,0 +1,463 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.24">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="T"/>
+    <GlyphID id="2" name="o"/>
+    <GlyphID id="3" name="space"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x95e7c646"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue May 25 09:10:37 2021"/>
+    <modified value="Tue May 25 10:10:18 2021"/>
+    <xMin value="32"/>
+    <yMin value="-200"/>
+    <xMax value="567"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="600"/>
+    <minLeftSideBearing value="32"/>
+    <minRightSideBearing value="33"/>
+    <xMaxExtent value="567"/>
+    <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>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="4"/>
+    <maxPoints value="32"/>
+    <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="575"/>
+    <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="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="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="111"/>
+    <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="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="T" width="600" lsb="32"/>
+    <mtx name="o" width="600" lsb="58"/>
+    <mtx name="space" width="600" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0x6f" name="o"/><!-- LATIN SMALL LETTER O -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="32" yMin="0" xMax="567" yMax="710">
+      <contour>
+        <pt x="32" y="710" on="1"/>
+        <pt x="567" y="710" on="1"/>
+        <pt x="567" y="627" on="1"/>
+        <pt x="32" y="627" on="1"/>
+      </contour>
+      <contour>
+        <pt x="230" y="710" on="1"/>
+        <pt x="370" y="710" on="1"/>
+        <pt x="370" y="0" on="1"/>
+        <pt x="230" y="0" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="o" xMin="58" yMin="-15" xMax="542" yMax="533">
+      <contour>
+        <pt x="300" y="-15" on="1"/>
+        <pt x="233" y="-15" on="0"/>
+        <pt x="123" y="59" on="0"/>
+        <pt x="58" y="184" on="0"/>
+        <pt x="58" y="259" on="1"/>
+        <pt x="58" y="335" on="0"/>
+        <pt x="123" y="459" on="0"/>
+        <pt x="233" y="533" on="0"/>
+        <pt x="300" y="533" on="1"/>
+        <pt x="367" y="533" on="0"/>
+        <pt x="477" y="459" on="0"/>
+        <pt x="542" y="335" on="0"/>
+        <pt x="542" y="259" on="1"/>
+        <pt x="542" y="184" on="0"/>
+        <pt x="477" y="59" on="0"/>
+        <pt x="367" y="-15" on="0"/>
+      </contour>
+      <contour>
+        <pt x="300" y="50" on="1"/>
+        <pt x="343" y="50" on="0"/>
+        <pt x="413" y="107" on="0"/>
+        <pt x="455" y="202" on="0"/>
+        <pt x="455" y="260" on="1"/>
+        <pt x="455" y="318" on="0"/>
+        <pt x="413" y="413" on="0"/>
+        <pt x="343" y="470" on="0"/>
+        <pt x="300" y="470" on="1"/>
+        <pt x="257" y="470" on="0"/>
+        <pt x="187" y="413" on="0"/>
+        <pt x="145" y="318" on="0"/>
+        <pt x="145" y="260" on="1"/>
+        <pt x="145" y="202" on="0"/>
+        <pt x="187" y="107" on="0"/>
+        <pt x="257" y="50" on="0"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </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">
+      Regular
+    </namerecord>
+    <namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Bold
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      New Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;NewFont-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      New Font Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NewFont-Regular
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      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>
+
+  <GDEF>
+    <Version value="0x00010003"/>
+    <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=1 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="0"/>
+        <Item index="0" value="[-50]"/>
+      </VarData>
+    </VarStore>
+  </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="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="T"/>
+          </Coverage>
+          <ValueFormat1 value="64"/>
+          <ValueFormat2 value="0"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="o"/>
+              <Value1>
+                <XAdvDevice>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </XAdvDevice>
+              </Value1>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>400.0</MinValue>
+      <DefaultValue>400.0</DefaultValue>
+      <MaxValue>700.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+
+    <!-- Regular -->
+    <NamedInstance flags="0x0" subfamilyNameID="257">
+      <coord axis="wght" value="400.0"/>
+    </NamedInstance>
+
+    <!-- Bold -->
+    <NamedInstance flags="0x0" subfamilyNameID="258">
+      <coord axis="wght" value="700.0"/>
+    </NamedInstance>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph="T">
+      <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="-27"/>
+        <delta pt="3" x="0" y="-27"/>
+        <delta pt="4" x="-30" y="0"/>
+        <delta pt="5" x="28" y="0"/>
+        <delta pt="6" x="28" y="0"/>
+        <delta pt="7" x="-30" 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"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="o">
+      <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="0"/>
+        <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="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="30"/>
+        <delta pt="17" x="0" y="30"/>
+        <delta pt="18" x="-15" y="15"/>
+        <delta pt="19" x="-30" y="0"/>
+        <delta pt="20" x="-30" y="0"/>
+        <delta pt="21" x="-30" y="0"/>
+        <delta pt="22" x="-15" y="-15"/>
+        <delta pt="23" x="0" y="-30"/>
+        <delta pt="24" x="0" y="-30"/>
+        <delta pt="25" x="0" y="-30"/>
+        <delta pt="26" x="15" y="-15"/>
+        <delta pt="27" x="30" y="0"/>
+        <delta pt="28" x="30" y="0"/>
+        <delta pt="29" x="30" y="0"/>
+        <delta pt="30" x="15" y="15"/>
+        <delta pt="31" x="0" y="30"/>
+        <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"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-100,100.ttx b/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-100,100.ttx
new file mode 100644
index 0000000..776a92f
--- /dev/null
+++ b/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-100,100.ttx
@@ -0,0 +1,544 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.41">
+
+  <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="T"/>
+    <GlyphID id="3" name="grave"/>
+    <GlyphID id="4" name="Agrave"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.001"/>
+    <checkSumAdjustment value="0x90f1c28"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Thu Oct 17 14:43:10 2019"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="577"/>
+    <yMax value="952"/>
+    <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="600"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="577"/>
+    <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="19"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="32"/>
+    <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="577"/>
+    <usWeightClass value="100"/>
+    <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="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 00100000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="192"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000001 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="528"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="94"/>
+    <mtx name="A" width="577" lsb="0"/>
+    <mtx name="Agrave" width="577" lsb="0"/>
+    <mtx name="T" width="505" lsb="2"/>
+    <mtx name="grave" width="249" lsb="40"/>
+  </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="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </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="94" yMin="0" xMax="505" yMax="714">
+      <contour>
+        <pt x="94" y="0" on="1" overlap="1"/>
+        <pt x="94" y="714" on="1"/>
+        <pt x="505" y="714" on="1"/>
+        <pt x="505" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="145" y="51" on="1"/>
+        <pt x="454" y="51" on="1"/>
+        <pt x="454" y="663" on="1"/>
+        <pt x="145" y="663" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="0" yMin="0" xMax="577" yMax="716">
+      <contour>
+        <pt x="549" y="0" on="1" overlap="1"/>
+        <pt x="445" y="271" on="1"/>
+        <pt x="135" y="271" on="1"/>
+        <pt x="28" y="0" on="1"/>
+        <pt x="0" y="0" on="1"/>
+        <pt x="282" y="716" on="1"/>
+        <pt x="307" y="716" on="1"/>
+        <pt x="577" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="435" y="296" on="1"/>
+        <pt x="325" y="594" on="1"/>
+        <pt x="321" y="606" on="0"/>
+        <pt x="311" y="632" on="0"/>
+        <pt x="300" y="664" on="0"/>
+        <pt x="293" y="682" on="1"/>
+        <pt x="288" y="667" on="0"/>
+        <pt x="277" y="636" on="0"/>
+        <pt x="265" y="607" on="0"/>
+        <pt x="260" y="593" on="1"/>
+        <pt x="144" y="296" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Agrave" xMin="0" yMin="0" xMax="577" yMax="952">
+      <component glyphName="A" x="0" y="0" flags="0x604"/>
+      <component glyphName="grave" x="131" y="186" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="2" yMin="0" xMax="503" yMax="714">
+      <contour>
+        <pt x="265" y="0" on="1" overlap="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="grave" xMin="40" yMin="606" xMax="209" yMax="766">
+      <contour>
+        <pt x="70" y="766" on="1" overlap="1"/>
+        <pt x="85" y="745" on="0"/>
+        <pt x="133" y="688" on="0"/>
+        <pt x="187" y="632" on="0"/>
+        <pt x="209" y="612" on="1"/>
+        <pt x="209" y="606" on="1"/>
+        <pt x="188" y="606" on="1"/>
+        <pt x="168" y="623" on="0"/>
+        <pt x="127" y="662" on="0"/>
+        <pt x="88" y="703" on="0"/>
+        <pt x="53" y="742" on="0"/>
+        <pt x="40" y="759" on="1"/>
+        <pt x="40" y="766" on="1"/>
+      </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">
+      Width
+    </namerecord>
+    <namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Thin
+    </namerecord>
+    <namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <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">
+      Noto Sans
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      2.001;GOOG;NotoSans-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Noto Sans Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NotoSans-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>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      Thin
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </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>
+
+  <GDEF>
+    <Version value="0x00010002"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Agrave" class="1"/>
+      <ClassDef glyph="T" class="1"/>
+    </GlyphClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=4 -->
+      <Coverage index="0">
+      </Coverage>
+      <Coverage index="1">
+      </Coverage>
+      <Coverage index="2">
+      </Coverage>
+      <Coverage index="3">
+      </Coverage>
+    </MarkGlyphSetsDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <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="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <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="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="T"/>
+            <Glyph value="Agrave"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+            <ClassDef glyph="T" class="1"/>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="A" class="1"/>
+            <ClassDef glyph="Agrave" class="1"/>
+            <ClassDef glyph="T" class="2"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=3 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-70"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-70"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="20"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <ScriptTag value="latn"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=2 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="wdth"/>
+        <AxisNameID value="257"/>  <!-- Width -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=2 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="258"/>  <!-- Thin -->
+        <Value value="100.0"/>
+      </AxisValue>
+      <AxisValue index="1" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="261"/>  <!-- Regular -->
+        <NominalValue value="100.0"/>
+        <RangeMinValue value="93.75"/>
+        <RangeMaxValue value="100.0"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-100,62.5.ttx b/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-100,62.5.ttx
new file mode 100644
index 0000000..61bc41c
--- /dev/null
+++ b/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-100,62.5.ttx
@@ -0,0 +1,544 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.41">
+
+  <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="T"/>
+    <GlyphID id="3" name="grave"/>
+    <GlyphID id="4" name="Agrave"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.001"/>
+    <checkSumAdjustment value="0x31525751"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Thu Oct 17 14:43:10 2019"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="496"/>
+    <yMax value="931"/>
+    <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="600"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="496"/>
+    <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="19"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="32"/>
+    <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="577"/>
+    <usWeightClass value="100"/>
+    <usWidthClass value="2"/>
+    <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="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 00100000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="192"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000001 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="527"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="85"/>
+    <mtx name="A" width="377" lsb="0"/>
+    <mtx name="Agrave" width="377" lsb="0"/>
+    <mtx name="T" width="336" lsb="7"/>
+    <mtx name="grave" width="225" lsb="40"/>
+  </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="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </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="85" yMin="0" xMax="496" yMax="714">
+      <contour>
+        <pt x="85" y="0" on="1" overlap="1"/>
+        <pt x="85" y="714" on="1"/>
+        <pt x="496" y="714" on="1"/>
+        <pt x="496" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="136" y="51" on="1"/>
+        <pt x="445" y="51" on="1"/>
+        <pt x="445" y="663" on="1"/>
+        <pt x="136" y="663" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="0" yMin="0" xMax="377" yMax="714">
+      <contour>
+        <pt x="351" y="0" on="1" overlap="1"/>
+        <pt x="284" y="281" on="1"/>
+        <pt x="94" y="281" on="1"/>
+        <pt x="26" y="0" on="1"/>
+        <pt x="0" y="0" on="1"/>
+        <pt x="170" y="714" on="1"/>
+        <pt x="208" y="714" on="1"/>
+        <pt x="377" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="278" y="306" on="1"/>
+        <pt x="208" y="612" on="1"/>
+        <pt x="204" y="629" on="0"/>
+        <pt x="198" y="655" on="0"/>
+        <pt x="192" y="681" on="0"/>
+        <pt x="189" y="695" on="1"/>
+        <pt x="186" y="681" on="0"/>
+        <pt x="180" y="656" on="0"/>
+        <pt x="174" y="629" on="0"/>
+        <pt x="170" y="612" on="1"/>
+        <pt x="100" y="306" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Agrave" xMin="0" yMin="0" xMax="377" yMax="931">
+      <component glyphName="A" x="0" y="0" flags="0x604"/>
+      <component glyphName="grave" x="11" y="168" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="7" yMin="0" xMax="328" yMax="714">
+      <contour>
+        <pt x="180" y="0" on="1" overlap="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="grave" xMin="40" yMin="606" xMax="185" yMax="763">
+      <contour>
+        <pt x="71" y="763" on="1" overlap="1"/>
+        <pt x="89" y="736" on="0"/>
+        <pt x="126" y="686" on="0"/>
+        <pt x="166" y="640" on="0"/>
+        <pt x="185" y="617" on="1"/>
+        <pt x="185" y="606" on="1"/>
+        <pt x="169" y="606" on="1"/>
+        <pt x="155" y="620" on="0"/>
+        <pt x="120" y="656" on="0"/>
+        <pt x="85" y="696" on="0"/>
+        <pt x="52" y="737" on="0"/>
+        <pt x="40" y="756" on="1"/>
+        <pt x="40" y="763" on="1"/>
+      </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">
+      Width
+    </namerecord>
+    <namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Thin
+    </namerecord>
+    <namerecord nameID="288" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed
+    </namerecord>
+    <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">
+      Noto Sans
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      2.001;GOOG;NotoSans-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Noto Sans Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NotoSans-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>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
+      Thin
+    </namerecord>
+    <namerecord nameID="288" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed
+    </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>
+
+  <GDEF>
+    <Version value="0x00010002"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Agrave" class="1"/>
+      <ClassDef glyph="T" class="1"/>
+    </GlyphClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=4 -->
+      <Coverage index="0">
+      </Coverage>
+      <Coverage index="1">
+      </Coverage>
+      <Coverage index="2">
+      </Coverage>
+      <Coverage index="3">
+      </Coverage>
+    </MarkGlyphSetsDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <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="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <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="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="T"/>
+            <Glyph value="Agrave"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+            <ClassDef glyph="T" class="1"/>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="A" class="1"/>
+            <ClassDef glyph="Agrave" class="1"/>
+            <ClassDef glyph="T" class="2"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=3 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-17"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-17"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="12"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <ScriptTag value="latn"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=2 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="wdth"/>
+        <AxisNameID value="257"/>  <!-- Width -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=2 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="258"/>  <!-- Thin -->
+        <Value value="100.0"/>
+      </AxisValue>
+      <AxisValue index="1" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="288"/>  <!-- ExtraCondensed -->
+        <NominalValue value="62.5"/>
+        <RangeMinValue value="62.5"/>
+        <RangeMaxValue value="68.75"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-400,100.ttx b/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-400,100.ttx
new file mode 100644
index 0000000..c2d2057
--- /dev/null
+++ b/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-400,100.ttx
@@ -0,0 +1,539 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.41">
+
+  <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="T"/>
+    <GlyphID id="3" name="grave"/>
+    <GlyphID id="4" name="Agrave"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.001"/>
+    <checkSumAdjustment value="0x4b2d3480"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Thu Oct 17 14:43:10 2019"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="638"/>
+    <yMax value="944"/>
+    <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="639"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="1"/>
+    <xMaxExtent value="638"/>
+    <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="19"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="32"/>
+    <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="577"/>
+    <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 00100000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="192"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000001 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="536"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="94"/>
+    <mtx name="A" width="639" lsb="0"/>
+    <mtx name="Agrave" width="639" lsb="0"/>
+    <mtx name="T" width="556" lsb="10"/>
+    <mtx name="grave" width="281" lsb="40"/>
+  </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="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </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="94" yMin="0" xMax="505" yMax="714">
+      <contour>
+        <pt x="94" y="0" on="1" overlap="1"/>
+        <pt x="94" y="714" on="1"/>
+        <pt x="505" y="714" on="1"/>
+        <pt x="505" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="145" y="51" on="1"/>
+        <pt x="454" y="51" on="1"/>
+        <pt x="454" y="663" on="1"/>
+        <pt x="145" y="663" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="0" yMin="0" xMax="638" yMax="717">
+      <contour>
+        <pt x="545" y="0" on="1" overlap="1"/>
+        <pt x="459" y="221" on="1"/>
+        <pt x="176" y="221" on="1"/>
+        <pt x="91" y="0" on="1"/>
+        <pt x="0" y="0" on="1"/>
+        <pt x="279" y="717" on="1"/>
+        <pt x="360" y="717" on="1"/>
+        <pt x="638" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="432" y="301" on="1"/>
+        <pt x="352" y="517" on="1"/>
+        <pt x="349" y="525" on="0"/>
+        <pt x="335" y="567" on="0"/>
+        <pt x="322" y="612" on="0"/>
+        <pt x="318" y="624" on="1"/>
+        <pt x="313" y="604" on="0"/>
+        <pt x="302" y="563" on="0"/>
+        <pt x="291" y="529" on="0"/>
+        <pt x="287" y="517" on="1"/>
+        <pt x="206" y="301" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Agrave" xMin="0" yMin="0" xMax="638" yMax="944">
+      <component glyphName="A" x="0" y="0" flags="0x604"/>
+      <component glyphName="grave" x="147" y="178" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="10" yMin="0" xMax="545" yMax="714">
+      <contour>
+        <pt x="323" y="0" on="1" overlap="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="grave" xMin="40" yMin="606" xMax="241" yMax="766">
+      <contour>
+        <pt x="145" y="766" on="1" overlap="1"/>
+        <pt x="156" y="744" on="0"/>
+        <pt x="189" y="689" on="0"/>
+        <pt x="226" y="637" on="0"/>
+        <pt x="241" y="618" on="1"/>
+        <pt x="241" y="606" on="1"/>
+        <pt x="182" y="606" on="1"/>
+        <pt x="165" y="620" on="0"/>
+        <pt x="123" y="659" on="0"/>
+        <pt x="82" y="702" on="0"/>
+        <pt x="49" y="742" on="0"/>
+        <pt x="40" y="756" on="1"/>
+        <pt x="40" y="766" on="1"/>
+      </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">
+      Width
+    </namerecord>
+    <namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <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">
+      Noto Sans
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      2.001;GOOG;NotoSans-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Noto Sans Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NotoSans-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>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </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>
+
+  <GDEF>
+    <Version value="0x00010002"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Agrave" class="1"/>
+      <ClassDef glyph="T" class="1"/>
+    </GlyphClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=4 -->
+      <Coverage index="0">
+      </Coverage>
+      <Coverage index="1">
+      </Coverage>
+      <Coverage index="2">
+      </Coverage>
+      <Coverage index="3">
+      </Coverage>
+    </MarkGlyphSetsDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <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="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <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="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="T"/>
+            <Glyph value="Agrave"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+            <ClassDef glyph="T" class="1"/>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="A" class="1"/>
+            <ClassDef glyph="Agrave" class="1"/>
+            <ClassDef glyph="T" class="2"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=3 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-70"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-70"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="20"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <ScriptTag value="latn"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=2 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="wdth"/>
+        <AxisNameID value="257"/>  <!-- Width -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=2 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="3">
+        <AxisIndex value="0"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="261"/>  <!-- Regular -->
+        <Value value="400.0"/>
+        <LinkedValue value="700.0"/>
+      </AxisValue>
+      <AxisValue index="1" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="261"/>  <!-- Regular -->
+        <NominalValue value="100.0"/>
+        <RangeMinValue value="93.75"/>
+        <RangeMaxValue value="100.0"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-400,62.5.ttx b/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-400,62.5.ttx
new file mode 100644
index 0000000..63eeb0e
--- /dev/null
+++ b/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-400,62.5.ttx
@@ -0,0 +1,545 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.41">
+
+  <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="T"/>
+    <GlyphID id="3" name="grave"/>
+    <GlyphID id="4" name="Agrave"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.001"/>
+    <checkSumAdjustment value="0x39ab2622"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Thu Oct 17 14:43:10 2019"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="496"/>
+    <yMax value="930"/>
+    <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="595"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="496"/>
+    <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="19"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="32"/>
+    <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="577"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="2"/>
+    <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 00100000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="192"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000001 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="537"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="595" lsb="85"/>
+    <mtx name="A" width="450" lsb="0"/>
+    <mtx name="Agrave" width="450" lsb="0"/>
+    <mtx name="T" width="381" lsb="10"/>
+    <mtx name="grave" width="266" lsb="40"/>
+  </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="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </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="85" yMin="0" xMax="496" yMax="714">
+      <contour>
+        <pt x="85" y="0" on="1" overlap="1"/>
+        <pt x="85" y="714" on="1"/>
+        <pt x="496" y="714" on="1"/>
+        <pt x="496" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="136" y="51" on="1"/>
+        <pt x="445" y="51" on="1"/>
+        <pt x="445" y="663" on="1"/>
+        <pt x="136" y="663" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="0" yMin="0" xMax="450" yMax="714">
+      <contour>
+        <pt x="361" y="0" on="1" overlap="1"/>
+        <pt x="309" y="223" on="1"/>
+        <pt x="142" y="223" on="1"/>
+        <pt x="90" y="0" on="1"/>
+        <pt x="0" y="0" on="1"/>
+        <pt x="173" y="714" on="1"/>
+        <pt x="274" y="714" on="1"/>
+        <pt x="450" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="293" y="301" on="1"/>
+        <pt x="240" y="535" on="1"/>
+        <pt x="237" y="554" on="0"/>
+        <pt x="230" y="589" on="0"/>
+        <pt x="225" y="623" on="0"/>
+        <pt x="223" y="638" on="1"/>
+        <pt x="222" y="623" on="0"/>
+        <pt x="217" y="589" on="0"/>
+        <pt x="210" y="554" on="0"/>
+        <pt x="206" y="536" on="1"/>
+        <pt x="155" y="301" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Agrave" xMin="0" yMin="0" xMax="450" yMax="930">
+      <component glyphName="A" x="0" y="0" flags="0x604"/>
+      <component glyphName="grave" x="32" y="164" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="10" yMin="0" xMax="370" yMax="714">
+      <contour>
+        <pt x="232" y="0" on="1" overlap="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="grave" xMin="40" yMin="606" xMax="226" yMax="766">
+      <contour>
+        <pt x="137" y="766" on="1" overlap="1"/>
+        <pt x="148" y="743" on="0"/>
+        <pt x="179" y="688" on="0"/>
+        <pt x="212" y="636" on="0"/>
+        <pt x="226" y="617" on="1"/>
+        <pt x="226" y="606" on="1"/>
+        <pt x="174" y="606" on="1"/>
+        <pt x="159" y="619" on="0"/>
+        <pt x="121" y="657" on="0"/>
+        <pt x="82" y="700" on="0"/>
+        <pt x="50" y="741" on="0"/>
+        <pt x="40" y="757" on="1"/>
+        <pt x="40" y="766" on="1"/>
+      </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">
+      Width
+    </namerecord>
+    <namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="288" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed
+    </namerecord>
+    <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">
+      Noto Sans
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      2.001;GOOG;NotoSans-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Noto Sans Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NotoSans-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>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="288" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed
+    </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>
+
+  <GDEF>
+    <Version value="0x00010002"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Agrave" class="1"/>
+      <ClassDef glyph="T" class="1"/>
+    </GlyphClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=4 -->
+      <Coverage index="0">
+      </Coverage>
+      <Coverage index="1">
+      </Coverage>
+      <Coverage index="2">
+      </Coverage>
+      <Coverage index="3">
+      </Coverage>
+    </MarkGlyphSetsDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <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="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <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="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="T"/>
+            <Glyph value="Agrave"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+            <ClassDef glyph="T" class="1"/>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="A" class="1"/>
+            <ClassDef glyph="Agrave" class="1"/>
+            <ClassDef glyph="T" class="2"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=3 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-17"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-17"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="12"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <ScriptTag value="latn"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=2 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="wdth"/>
+        <AxisNameID value="257"/>  <!-- Width -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=2 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="3">
+        <AxisIndex value="0"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="261"/>  <!-- Regular -->
+        <Value value="400.0"/>
+        <LinkedValue value="700.0"/>
+      </AxisValue>
+      <AxisValue index="1" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="288"/>  <!-- ExtraCondensed -->
+        <NominalValue value="62.5"/>
+        <RangeMinValue value="62.5"/>
+        <RangeMaxValue value="68.75"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-900,100.ttx b/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-900,100.ttx
new file mode 100644
index 0000000..013ba1e
--- /dev/null
+++ b/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-900,100.ttx
@@ -0,0 +1,544 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.41">
+
+  <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="T"/>
+    <GlyphID id="3" name="grave"/>
+    <GlyphID id="4" name="Agrave"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.001"/>
+    <checkSumAdjustment value="0x7b5e7903"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Thu Oct 17 14:43:10 2019"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="726"/>
+    <yMax value="927"/>
+    <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="726"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="726"/>
+    <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="19"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="32"/>
+    <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="577"/>
+    <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="332"/>
+    <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 00100000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="192"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000001 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="553"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="582" lsb="85"/>
+    <mtx name="A" width="726" lsb="0"/>
+    <mtx name="Agrave" width="726" lsb="0"/>
+    <mtx name="T" width="591" lsb="25"/>
+    <mtx name="grave" width="418" lsb="40"/>
+  </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="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </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="85" yMin="0" xMax="496" yMax="714">
+      <contour>
+        <pt x="85" y="0" on="1" overlap="1"/>
+        <pt x="85" y="714" on="1"/>
+        <pt x="496" y="714" on="1"/>
+        <pt x="496" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="136" y="51" on="1"/>
+        <pt x="445" y="51" on="1"/>
+        <pt x="445" y="663" on="1"/>
+        <pt x="136" y="663" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="0" yMin="0" xMax="726" yMax="717">
+      <contour>
+        <pt x="515" y="0" on="1" overlap="1"/>
+        <pt x="480" y="134" on="1"/>
+        <pt x="248" y="134" on="1"/>
+        <pt x="212" y="0" on="1"/>
+        <pt x="0" y="0" on="1"/>
+        <pt x="233" y="717" on="1"/>
+        <pt x="490" y="717" on="1"/>
+        <pt x="726" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="440" y="292" on="1"/>
+        <pt x="409" y="409" on="1"/>
+        <pt x="404" y="428" on="0"/>
+        <pt x="386" y="499" on="0"/>
+        <pt x="368" y="575" on="0"/>
+        <pt x="363" y="599" on="1"/>
+        <pt x="359" y="575" on="0"/>
+        <pt x="342" y="503" on="0"/>
+        <pt x="325" y="433" on="0"/>
+        <pt x="319" y="409" on="1"/>
+        <pt x="288" y="292" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Agrave" xMin="0" yMin="0" xMax="726" yMax="927">
+      <component glyphName="A" x="0" y="0" flags="0x604"/>
+      <component glyphName="grave" x="112" y="161" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="25" yMin="0" xMax="566" yMax="714">
+      <contour>
+        <pt x="392" y="0" on="1" overlap="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="grave" xMin="40" yMin="606" xMax="378" yMax="766">
+      <contour>
+        <pt x="250" y="766" on="1" overlap="1"/>
+        <pt x="267" y="744" on="0"/>
+        <pt x="314" y="690" on="0"/>
+        <pt x="362" y="639" on="0"/>
+        <pt x="378" y="620" on="1"/>
+        <pt x="378" y="606" on="1"/>
+        <pt x="251" y="606" on="1"/>
+        <pt x="231" y="619" on="0"/>
+        <pt x="174" y="658" on="0"/>
+        <pt x="113" y="701" on="0"/>
+        <pt x="58" y="742" on="0"/>
+        <pt x="40" y="756" on="1"/>
+        <pt x="40" y="766" on="1"/>
+      </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">
+      Width
+    </namerecord>
+    <namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="266" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Black
+    </namerecord>
+    <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">
+      Noto Sans
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      2.001;GOOG;NotoSans-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Noto Sans Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NotoSans-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>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </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>
+
+  <GDEF>
+    <Version value="0x00010002"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Agrave" class="1"/>
+      <ClassDef glyph="T" class="1"/>
+    </GlyphClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=4 -->
+      <Coverage index="0">
+      </Coverage>
+      <Coverage index="1">
+      </Coverage>
+      <Coverage index="2">
+      </Coverage>
+      <Coverage index="3">
+      </Coverage>
+    </MarkGlyphSetsDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <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="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <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="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="T"/>
+            <Glyph value="Agrave"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+            <ClassDef glyph="T" class="1"/>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="A" class="1"/>
+            <ClassDef glyph="Agrave" class="1"/>
+            <ClassDef glyph="T" class="2"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=3 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-70"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-70"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="20"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <ScriptTag value="latn"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=2 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="wdth"/>
+        <AxisNameID value="257"/>  <!-- Width -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=2 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="266"/>  <!-- Black -->
+        <Value value="900.0"/>
+      </AxisValue>
+      <AxisValue index="1" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="2"/>  <!-- ElidableAxisValueName -->
+        <ValueNameID value="261"/>  <!-- Regular -->
+        <NominalValue value="100.0"/>
+        <RangeMinValue value="93.75"/>
+        <RangeMaxValue value="100.0"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-900,62.5.ttx b/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-900,62.5.ttx
new file mode 100644
index 0000000..45e34cb
--- /dev/null
+++ b/Tests/varLib/instancer/data/test_results/PartialInstancerTest2-VF-instance-900,62.5.ttx
@@ -0,0 +1,544 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.41">
+
+  <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="T"/>
+    <GlyphID id="3" name="grave"/>
+    <GlyphID id="4" name="Agrave"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="2.001"/>
+    <checkSumAdjustment value="0x7f9149e4"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Tue Mar 15 19:50:39 2016"/>
+    <modified value="Thu Oct 17 14:43:10 2019"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="574"/>
+    <yMax value="927"/>
+    <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="582"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="574"/>
+    <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="19"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="32"/>
+    <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="577"/>
+    <usWeightClass value="900"/>
+    <usWidthClass value="2"/>
+    <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="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 00100000 00011111"/>
+    <ulUnicodeRange3 value="00001000 00000000 00000000 00101001"/>
+    <ulUnicodeRange4 value="00000000 00010000 00000000 00000000"/>
+    <achVendID value="GOOG"/>
+    <fsSelection value="00000001 01000000"/>
+    <usFirstCharIndex value="65"/>
+    <usLastCharIndex value="192"/>
+    <sTypoAscender value="1069"/>
+    <sTypoDescender value="-293"/>
+    <sTypoLineGap value="0"/>
+    <usWinAscent value="1069"/>
+    <usWinDescent value="293"/>
+    <ulCodePageRange1 value="00000000 00000000 00000001 10011111"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="553"/>
+    <sCapHeight value="714"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="582" lsb="85"/>
+    <mtx name="A" width="574" lsb="0"/>
+    <mtx name="Agrave" width="574" lsb="0"/>
+    <mtx name="T" width="460" lsb="17"/>
+    <mtx name="grave" width="340" lsb="40"/>
+  </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="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
+      <map code="0x54" name="T"/><!-- LATIN CAPITAL LETTER T -->
+      <map code="0xc0" name="Agrave"/><!-- LATIN CAPITAL LETTER A WITH GRAVE -->
+    </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="85" yMin="0" xMax="496" yMax="714">
+      <contour>
+        <pt x="85" y="0" on="1" overlap="1"/>
+        <pt x="85" y="714" on="1"/>
+        <pt x="496" y="714" on="1"/>
+        <pt x="496" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="136" y="51" on="1"/>
+        <pt x="445" y="51" on="1"/>
+        <pt x="445" y="663" on="1"/>
+        <pt x="136" y="663" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="0" yMin="0" xMax="574" yMax="714">
+      <contour>
+        <pt x="392" y="0" on="1" overlap="1"/>
+        <pt x="365" y="139" on="1"/>
+        <pt x="211" y="139" on="1"/>
+        <pt x="184" y="0" on="1"/>
+        <pt x="0" y="0" on="1"/>
+        <pt x="174" y="714" on="1"/>
+        <pt x="398" y="714" on="1"/>
+        <pt x="574" y="0" on="1"/>
+      </contour>
+      <contour>
+        <pt x="339" y="285" on="1"/>
+        <pt x="310" y="445" on="1"/>
+        <pt x="305" y="472" on="0"/>
+        <pt x="297" y="522" on="0"/>
+        <pt x="291" y="569" on="0"/>
+        <pt x="288" y="589" on="1"/>
+        <pt x="286" y="570" on="0"/>
+        <pt x="280" y="524" on="0"/>
+        <pt x="271" y="474" on="0"/>
+        <pt x="266" y="447" on="1"/>
+        <pt x="236" y="285" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="Agrave" xMin="0" yMin="0" xMax="574" yMax="927">
+      <component glyphName="A" x="0" y="0" flags="0x604"/>
+      <component glyphName="grave" x="83" y="161" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="T" xMin="17" yMin="0" xMax="443" yMax="714">
+      <contour>
+        <pt x="315" y="0" on="1" overlap="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="grave" xMin="40" yMin="606" xMax="300" yMax="766">
+      <contour>
+        <pt x="222" y="766" on="1" overlap="1"/>
+        <pt x="231" y="747" on="0"/>
+        <pt x="261" y="688" on="0"/>
+        <pt x="291" y="634" on="0"/>
+        <pt x="300" y="620" on="1"/>
+        <pt x="300" y="606" on="1"/>
+        <pt x="181" y="606" on="1"/>
+        <pt x="169" y="617" on="0"/>
+        <pt x="130" y="657" on="0"/>
+        <pt x="87" y="703" on="0"/>
+        <pt x="50" y="744" on="0"/>
+        <pt x="40" y="756" on="1"/>
+        <pt x="40" y="766" on="1"/>
+      </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">
+      Width
+    </namerecord>
+    <namerecord nameID="266" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Black
+    </namerecord>
+    <namerecord nameID="288" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      ExtraCondensed
+    </namerecord>
+    <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">
+      Noto Sans
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      2.001;GOOG;NotoSans-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Noto Sans Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 2.001
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      NotoSans-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>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
+      Width
+    </namerecord>
+    <namerecord nameID="266" platformID="3" platEncID="1" langID="0x409">
+      Black
+    </namerecord>
+    <namerecord nameID="288" platformID="3" platEncID="1" langID="0x409">
+      ExtraCondensed
+    </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>
+
+  <GDEF>
+    <Version value="0x00010002"/>
+    <GlyphClassDef>
+      <ClassDef glyph="A" class="1"/>
+      <ClassDef glyph="Agrave" class="1"/>
+      <ClassDef glyph="T" class="1"/>
+    </GlyphClassDef>
+    <MarkGlyphSetsDef>
+      <MarkSetTableFormat value="1"/>
+      <!-- MarkSetCount=4 -->
+      <Coverage index="0">
+      </Coverage>
+      <Coverage index="1">
+      </Coverage>
+      <Coverage index="2">
+      </Coverage>
+      <Coverage index="3">
+      </Coverage>
+    </MarkGlyphSetsDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <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="cyrl"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <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="8"/><!-- ignoreMarks -->
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="T"/>
+            <Glyph value="Agrave"/>
+          </Coverage>
+          <ValueFormat1 value="4"/>
+          <ValueFormat2 value="0"/>
+          <ClassDef1>
+            <ClassDef glyph="T" class="1"/>
+          </ClassDef1>
+          <ClassDef2>
+            <ClassDef glyph="A" class="1"/>
+            <ClassDef glyph="Agrave" class="1"/>
+            <ClassDef glyph="T" class="2"/>
+          </ClassDef2>
+          <!-- Class1Count=2 -->
+          <!-- Class2Count=3 -->
+          <Class1Record index="0">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="-17"/>
+            </Class2Record>
+          </Class1Record>
+          <Class1Record index="1">
+            <Class2Record index="0">
+              <Value1 XAdvance="0"/>
+            </Class2Record>
+            <Class2Record index="1">
+              <Value1 XAdvance="-17"/>
+            </Class2Record>
+            <Class2Record index="2">
+              <Value1 XAdvance="12"/>
+            </Class2Record>
+          </Class1Record>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=4 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=0 -->
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="1">
+        <ScriptTag value="cyrl"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="2">
+        <ScriptTag value="grek"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+      <ScriptRecord index="3">
+        <ScriptTag value="latn"/>
+        <Script>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=0 -->
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=0 -->
+    </LookupList>
+  </GSUB>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=2 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="1"/>
+      </Axis>
+      <Axis index="1">
+        <AxisTag value="wdth"/>
+        <AxisNameID value="257"/>  <!-- Width -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=2 -->
+    <AxisValueArray>
+      <AxisValue index="0" Format="1">
+        <AxisIndex value="0"/>
+        <Flags value="0"/>
+        <ValueNameID value="266"/>  <!-- Black -->
+        <Value value="900.0"/>
+      </AxisValue>
+      <AxisValue index="1" Format="2">
+        <AxisIndex value="1"/>
+        <Flags value="0"/>
+        <ValueNameID value="288"/>  <!-- ExtraCondensed -->
+        <NominalValue value="62.5"/>
+        <RangeMinValue value="62.5"/>
+        <RangeMaxValue value="68.75"/>
+      </AxisValue>
+    </AxisValueArray>
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/test_results/PartialInstancerTest3-VF-instance-400-no-overlap-flags.ttx b/Tests/varLib/instancer/data/test_results/PartialInstancerTest3-VF-instance-400-no-overlap-flags.ttx
new file mode 100644
index 0000000..fc6310d
--- /dev/null
+++ b/Tests/varLib/instancer/data/test_results/PartialInstancerTest3-VF-instance-400-no-overlap-flags.ttx
@@ -0,0 +1,305 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.15">
+
+  <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="D"/>
+    <GlyphID id="5" name="E"/>
+    <GlyphID id="6" name="F"/>
+    <GlyphID id="7" name="space"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x98c89e17"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Sep 23 12:54:22 2020"/>
+    <modified value="Tue Sep 29 18:06:03 2020"/>
+    <xMin value="-152"/>
+    <yMin value="-200"/>
+    <xMax value="1059"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="600"/>
+    <minLeftSideBearing value="-152"/>
+    <minRightSideBearing value="-559"/>
+    <xMaxExtent value="1059"/>
+    <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="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="513"/>
+    <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="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="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="70"/>
+    <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="500" lsb="50"/>
+    <mtx name="A" width="500" lsb="153"/>
+    <mtx name="B" width="500" lsb="-152"/>
+    <mtx name="C" width="500" lsb="-133"/>
+    <mtx name="D" width="500" lsb="-97"/>
+    <mtx name="E" width="500" lsb="-87"/>
+    <mtx name="F" width="500" lsb="-107"/>
+    <mtx name="space" width="600" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <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 -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <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 -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="153" yMin="-66" xMax="350" yMax="646">
+      <contour>
+        <pt x="153" y="646" on="1"/>
+        <pt x="350" y="646" on="1"/>
+        <pt x="350" y="-66" on="1"/>
+        <pt x="153" y="-66" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="B" xMin="-152" yMin="39" xMax="752" yMax="592">
+      <contour>
+        <pt x="-152" y="448" on="1"/>
+        <pt x="752" y="448" on="1"/>
+        <pt x="752" y="215" on="1"/>
+        <pt x="-152" y="215" on="1"/>
+      </contour>
+      <contour>
+        <pt x="129" y="592" on="1"/>
+        <pt x="401" y="592" on="1"/>
+        <pt x="401" y="39" on="1"/>
+        <pt x="129" y="39" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="C" xMin="-133" yMin="-66" xMax="771" yMax="646">
+      <component glyphName="A" x="-250" y="0" flags="0x4"/>
+      <component glyphName="B" x="19" y="-28" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="D" xMin="-97" yMin="-66" xMax="1059" yMax="646">
+      <component glyphName="A" x="-250" y="0" flags="0x4"/>
+      <component glyphName="B" x="307" y="-28" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="E" xMin="-87" yMin="-87" xMax="801" yMax="650">
+      <component glyphName="A" x="450" y="-77" scalex="0.9397" scale01="0.34204" scale10="-0.34204" scaley="0.9397" flags="0x4"/>
+      <component glyphName="A" x="8" y="4" flags="0x4"/>
+      <component glyphName="A" x="-240" y="0" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="-107" yMin="-95" xMax="837" yMax="650">
+      <component glyphName="A" x="501" y="-114" scalex="0.866" scale01="0.5" scale10="-0.5" scaley="0.866" flags="0x4"/>
+      <component glyphName="A" x="-12" y="4" flags="0x4"/>
+      <component glyphName="A" x="-260" y="0" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Weight
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Remove Overlaps Test
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;RemoveOverlapsTest-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Remove Overlaps Test Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      RemoveOverlapsTest-Regular
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </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>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=1 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=0 -->
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/test_results/PartialInstancerTest3-VF-instance-400-no-overlaps.ttx b/Tests/varLib/instancer/data/test_results/PartialInstancerTest3-VF-instance-400-no-overlaps.ttx
new file mode 100644
index 0000000..3e18c9b
--- /dev/null
+++ b/Tests/varLib/instancer/data/test_results/PartialInstancerTest3-VF-instance-400-no-overlaps.ttx
@@ -0,0 +1,343 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.15">
+
+  <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="D"/>
+    <GlyphID id="5" name="E"/>
+    <GlyphID id="6" name="F"/>
+    <GlyphID id="7" name="space"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x1cd9cd87"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Sep 23 12:54:22 2020"/>
+    <modified value="Tue Sep 29 18:06:03 2020"/>
+    <xMin value="-152"/>
+    <yMin value="-200"/>
+    <xMax value="1059"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="600"/>
+    <minLeftSideBearing value="-152"/>
+    <minRightSideBearing value="-559"/>
+    <xMaxExtent value="1059"/>
+    <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="20"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="16"/>
+    <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="513"/>
+    <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="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="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="70"/>
+    <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="500" lsb="50"/>
+    <mtx name="A" width="500" lsb="153"/>
+    <mtx name="B" width="500" lsb="-152"/>
+    <mtx name="C" width="500" lsb="-133"/>
+    <mtx name="D" width="500" lsb="-97"/>
+    <mtx name="E" width="500" lsb="-87"/>
+    <mtx name="F" width="500" lsb="-107"/>
+    <mtx name="space" width="600" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <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 -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <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 -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="153" yMin="-66" xMax="350" yMax="646">
+      <contour>
+        <pt x="153" y="646" on="1"/>
+        <pt x="350" y="646" on="1"/>
+        <pt x="350" y="-66" on="1"/>
+        <pt x="153" y="-66" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="B" xMin="-152" yMin="39" xMax="752" yMax="592">
+      <contour>
+        <pt x="-152" y="448" on="1"/>
+        <pt x="129" y="448" on="1"/>
+        <pt x="129" y="592" on="1"/>
+        <pt x="401" y="592" on="1"/>
+        <pt x="401" y="448" on="1"/>
+        <pt x="752" y="448" on="1"/>
+        <pt x="752" y="215" on="1"/>
+        <pt x="401" y="215" on="1"/>
+        <pt x="401" y="39" on="1"/>
+        <pt x="129" y="39" on="1"/>
+        <pt x="129" y="215" on="1"/>
+        <pt x="-152" y="215" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="C" xMin="-133" yMin="-66" xMax="771" yMax="646">
+      <contour>
+        <pt x="-97" y="646" on="1"/>
+        <pt x="100" y="646" on="1"/>
+        <pt x="100" y="420" on="1"/>
+        <pt x="148" y="420" on="1"/>
+        <pt x="148" y="564" on="1"/>
+        <pt x="420" y="564" on="1"/>
+        <pt x="420" y="420" on="1"/>
+        <pt x="771" y="420" on="1"/>
+        <pt x="771" y="187" on="1"/>
+        <pt x="420" y="187" on="1"/>
+        <pt x="420" y="11" on="1"/>
+        <pt x="148" y="11" on="1"/>
+        <pt x="148" y="187" on="1"/>
+        <pt x="100" y="187" on="1"/>
+        <pt x="100" y="-66" on="1"/>
+        <pt x="-97" y="-66" on="1"/>
+        <pt x="-97" y="187" on="1"/>
+        <pt x="-133" y="187" on="1"/>
+        <pt x="-133" y="420" on="1"/>
+        <pt x="-97" y="420" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="D" xMin="-97" yMin="-66" xMax="1059" yMax="646">
+      <component glyphName="A" x="-250" y="0" flags="0x4"/>
+      <component glyphName="B" x="307" y="-28" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="E" xMin="-87" yMin="-87" xMax="801" yMax="650">
+      <component glyphName="A" x="450" y="-77" scalex="0.9397" scale01="0.34204" scale10="-0.34204" scaley="0.9397" flags="0x4"/>
+      <component glyphName="A" x="8" y="4" flags="0x4"/>
+      <component glyphName="A" x="-240" y="0" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="-107" yMin="-95" xMax="837" yMax="650">
+      <contour>
+        <pt x="141" y="650" on="1"/>
+        <pt x="338" y="650" on="1"/>
+        <pt x="338" y="538" on="1"/>
+        <pt x="481" y="620" on="1"/>
+        <pt x="837" y="4" on="1"/>
+        <pt x="667" y="-95" on="1"/>
+        <pt x="338" y="474" on="1"/>
+        <pt x="338" y="-62" on="1"/>
+        <pt x="141" y="-62" on="1"/>
+      </contour>
+      <contour>
+        <pt x="-107" y="646" on="1"/>
+        <pt x="90" y="646" on="1"/>
+        <pt x="90" y="-66" on="1"/>
+        <pt x="-107" y="-66" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Weight
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Remove Overlaps Test
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;RemoveOverlapsTest-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Remove Overlaps Test Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      RemoveOverlapsTest-Regular
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </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>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=1 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=0 -->
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/data/test_results/PartialInstancerTest3-VF-instance-700-no-overlaps.ttx b/Tests/varLib/instancer/data/test_results/PartialInstancerTest3-VF-instance-700-no-overlaps.ttx
new file mode 100644
index 0000000..be0353d
--- /dev/null
+++ b/Tests/varLib/instancer/data/test_results/PartialInstancerTest3-VF-instance-700-no-overlaps.ttx
@@ -0,0 +1,367 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.15">
+
+  <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="D"/>
+    <GlyphID id="5" name="E"/>
+    <GlyphID id="6" name="F"/>
+    <GlyphID id="7" name="space"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0xc12af6d1"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Sep 23 12:54:22 2020"/>
+    <modified value="Tue Sep 29 18:06:03 2020"/>
+    <xMin value="-192"/>
+    <yMin value="-200"/>
+    <xMax value="1099"/>
+    <yMax value="800"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <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="600"/>
+    <minLeftSideBearing value="-192"/>
+    <minRightSideBearing value="-599"/>
+    <xMaxExtent value="1099"/>
+    <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="16"/>
+    <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="513"/>
+    <usWeightClass value="700"/>
+    <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="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="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="70"/>
+    <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="500" lsb="50"/>
+    <mtx name="A" width="500" lsb="113"/>
+    <mtx name="B" width="500" lsb="-192"/>
+    <mtx name="C" width="500" lsb="-173"/>
+    <mtx name="D" width="500" lsb="-137"/>
+    <mtx name="E" width="500" lsb="-127"/>
+    <mtx name="F" width="500" lsb="-147"/>
+    <mtx name="space" width="600" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <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 -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <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 -->
+    </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="-200" xMax="450" yMax="800">
+      <contour>
+        <pt x="50" y="-200" on="1"/>
+        <pt x="50" y="800" on="1"/>
+        <pt x="450" y="800" on="1"/>
+        <pt x="450" y="-200" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-150" on="1"/>
+        <pt x="400" y="-150" on="1"/>
+        <pt x="400" y="750" on="1"/>
+        <pt x="100" y="750" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="A" xMin="113" yMin="-66" xMax="390" yMax="646">
+      <contour>
+        <pt x="113" y="646" on="1"/>
+        <pt x="390" y="646" on="1"/>
+        <pt x="390" y="-66" on="1"/>
+        <pt x="113" y="-66" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="B" xMin="-192" yMin="19" xMax="792" yMax="612">
+      <contour>
+        <pt x="-192" y="448" on="1"/>
+        <pt x="89" y="448" on="1"/>
+        <pt x="89" y="612" on="1"/>
+        <pt x="441" y="612" on="1"/>
+        <pt x="441" y="448" on="1"/>
+        <pt x="792" y="448" on="1"/>
+        <pt x="792" y="215" on="1"/>
+        <pt x="441" y="215" on="1"/>
+        <pt x="441" y="19" on="1"/>
+        <pt x="89" y="19" on="1"/>
+        <pt x="89" y="215" on="1"/>
+        <pt x="-192" y="215" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="C" xMin="-173" yMin="-66" xMax="811" yMax="646">
+      <contour>
+        <pt x="-137" y="646" on="1"/>
+        <pt x="140" y="646" on="1"/>
+        <pt x="140" y="584" on="1"/>
+        <pt x="460" y="584" on="1"/>
+        <pt x="460" y="420" on="1"/>
+        <pt x="811" y="420" on="1"/>
+        <pt x="811" y="187" on="1"/>
+        <pt x="460" y="187" on="1"/>
+        <pt x="460" y="-9" on="1"/>
+        <pt x="140" y="-9" on="1"/>
+        <pt x="140" y="-66" on="1"/>
+        <pt x="-137" y="-66" on="1"/>
+        <pt x="-137" y="187" on="1"/>
+        <pt x="-173" y="187" on="1"/>
+        <pt x="-173" y="420" on="1"/>
+        <pt x="-137" y="420" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="D" xMin="-137" yMin="-66" xMax="1099" yMax="646">
+      <contour>
+        <pt x="-137" y="646" on="1"/>
+        <pt x="140" y="646" on="1"/>
+        <pt x="140" y="420" on="1"/>
+        <pt x="396" y="420" on="1"/>
+        <pt x="396" y="584" on="1"/>
+        <pt x="748" y="584" on="1"/>
+        <pt x="748" y="420" on="1"/>
+        <pt x="1099" y="420" on="1"/>
+        <pt x="1099" y="187" on="1"/>
+        <pt x="748" y="187" on="1"/>
+        <pt x="748" y="-9" on="1"/>
+        <pt x="396" y="-9" on="1"/>
+        <pt x="396" y="187" on="1"/>
+        <pt x="140" y="187" on="1"/>
+        <pt x="140" y="-66" on="1"/>
+        <pt x="-137" y="-66" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="E" xMin="-127" yMin="-100" xMax="839" yMax="663">
+      <contour>
+        <pt x="121" y="650" on="1"/>
+        <pt x="398" y="650" on="1"/>
+        <pt x="398" y="592" on="1"/>
+        <pt x="596" y="663" on="1"/>
+        <pt x="839" y="-6" on="1"/>
+        <pt x="579" y="-100" on="1"/>
+        <pt x="398" y="396" on="1"/>
+        <pt x="398" y="-62" on="1"/>
+        <pt x="150" y="-62" on="1"/>
+        <pt x="150" y="-66" on="1"/>
+        <pt x="-127" y="-66" on="1"/>
+        <pt x="-127" y="646" on="1"/>
+        <pt x="121" y="646" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="F" xMin="-147" yMin="-115" xMax="872" yMax="650">
+      <contour>
+        <pt x="101" y="650" on="1"/>
+        <pt x="378" y="650" on="1"/>
+        <pt x="378" y="561" on="1"/>
+        <pt x="516" y="640" on="1"/>
+        <pt x="872" y="24" on="1"/>
+        <pt x="632" y="-115" on="1"/>
+        <pt x="378" y="325" on="1"/>
+        <pt x="378" y="-62" on="1"/>
+        <pt x="130" y="-62" on="1"/>
+        <pt x="130" y="-66" on="1"/>
+        <pt x="-147" y="-66" on="1"/>
+        <pt x="-147" y="646" on="1"/>
+        <pt x="101" y="646" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Weight
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Remove Overlaps Test
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      1.000;NONE;RemoveOverlapsTest-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Remove Overlaps Test Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 1.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      RemoveOverlapsTest-Regular
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </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>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=1 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=0 -->
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+</ttFont>
diff --git a/Tests/varLib/instancer/instancer_test.py b/Tests/varLib/instancer/instancer_test.py
new file mode 100644
index 0000000..cbc313d
--- /dev/null
+++ b/Tests/varLib/instancer/instancer_test.py
@@ -0,0 +1,1963 @@
+from fontTools.misc.py23 import Tag
+from fontTools.misc.fixedTools import floatToFixedToFloat
+from fontTools import ttLib
+from fontTools import designspaceLib
+from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
+from fontTools.ttLib.tables import _f_v_a_r, _g_l_y_f
+from fontTools.ttLib.tables import otTables
+from fontTools.ttLib.tables.TupleVariation import TupleVariation
+from fontTools import varLib
+from fontTools.varLib import instancer
+from fontTools.varLib.mvar import MVAR_ENTRIES
+from fontTools.varLib import builder
+from fontTools.varLib import featureVars
+from fontTools.varLib import models
+import collections
+from copy import deepcopy
+from io import BytesIO, StringIO
+import logging
+import os
+import re
+from types import SimpleNamespace
+import pytest
+
+
+# see Tests/varLib/instancer/conftest.py for "varfont" fixture definition
+
+TESTDATA = os.path.join(os.path.dirname(__file__), "data")
+
+
+@pytest.fixture(params=[True, False], ids=["optimize", "no-optimize"])
+def optimize(request):
+    return request.param
+
+
+@pytest.fixture
+def fvarAxes():
+    wght = _f_v_a_r.Axis()
+    wght.axisTag = Tag("wght")
+    wght.minValue = 100
+    wght.defaultValue = 400
+    wght.maxValue = 900
+    wdth = _f_v_a_r.Axis()
+    wdth.axisTag = Tag("wdth")
+    wdth.minValue = 70
+    wdth.defaultValue = 100
+    wdth.maxValue = 100
+    return [wght, wdth]
+
+
+def _get_coordinates(varfont, glyphname):
+    # converts GlyphCoordinates to a list of (x, y) tuples, so that pytest's
+    # assert will give us a nicer diff
+    return list(varfont["glyf"].getCoordinatesAndControls(glyphname, varfont)[0])
+
+
+class InstantiateGvarTest(object):
+    @pytest.mark.parametrize("glyph_name", ["hyphen"])
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            pytest.param(
+                {"wdth": -1.0},
+                {
+                    "hyphen": [
+                        (27, 229),
+                        (27, 310),
+                        (247, 310),
+                        (247, 229),
+                        (0, 0),
+                        (274, 0),
+                        (0, 536),
+                        (0, 0),
+                    ]
+                },
+                id="wdth=-1.0",
+            ),
+            pytest.param(
+                {"wdth": -0.5},
+                {
+                    "hyphen": [
+                        (33.5, 229),
+                        (33.5, 308.5),
+                        (264.5, 308.5),
+                        (264.5, 229),
+                        (0, 0),
+                        (298, 0),
+                        (0, 536),
+                        (0, 0),
+                    ]
+                },
+                id="wdth=-0.5",
+            ),
+            # an axis pinned at the default normalized location (0.0) means
+            # the default glyf outline stays the same
+            pytest.param(
+                {"wdth": 0.0},
+                {
+                    "hyphen": [
+                        (40, 229),
+                        (40, 307),
+                        (282, 307),
+                        (282, 229),
+                        (0, 0),
+                        (322, 0),
+                        (0, 536),
+                        (0, 0),
+                    ]
+                },
+                id="wdth=0.0",
+            ),
+        ],
+    )
+    def test_pin_and_drop_axis(self, varfont, glyph_name, location, expected, optimize):
+        instancer.instantiateGvar(varfont, location, optimize=optimize)
+
+        assert _get_coordinates(varfont, glyph_name) == expected[glyph_name]
+
+        # check that the pinned axis has been dropped from gvar
+        assert not any(
+            "wdth" in t.axes
+            for tuples in varfont["gvar"].variations.values()
+            for t in tuples
+        )
+
+    def test_full_instance(self, varfont, optimize):
+        instancer.instantiateGvar(
+            varfont, {"wght": 0.0, "wdth": -0.5}, optimize=optimize
+        )
+
+        assert _get_coordinates(varfont, "hyphen") == [
+            (33.5, 229),
+            (33.5, 308.5),
+            (264.5, 308.5),
+            (264.5, 229),
+            (0, 0),
+            (298, 0),
+            (0, 536),
+            (0, 0),
+        ]
+
+        assert "gvar" not in varfont
+
+    def test_composite_glyph_not_in_gvar(self, varfont):
+        """The 'minus' glyph is a composite glyph, which references 'hyphen' as a
+        component, but has no tuple variations in gvar table, so the component offset
+        and the phantom points do not change; however the sidebearings and bounding box
+        do change as a result of the parent glyph 'hyphen' changing.
+        """
+        hmtx = varfont["hmtx"]
+        vmtx = varfont["vmtx"]
+
+        hyphenCoords = _get_coordinates(varfont, "hyphen")
+        assert hyphenCoords == [
+            (40, 229),
+            (40, 307),
+            (282, 307),
+            (282, 229),
+            (0, 0),
+            (322, 0),
+            (0, 536),
+            (0, 0),
+        ]
+        assert hmtx["hyphen"] == (322, 40)
+        assert vmtx["hyphen"] == (536, 229)
+
+        minusCoords = _get_coordinates(varfont, "minus")
+        assert minusCoords == [(0, 0), (0, 0), (422, 0), (0, 536), (0, 0)]
+        assert hmtx["minus"] == (422, 40)
+        assert vmtx["minus"] == (536, 229)
+
+        location = {"wght": -1.0, "wdth": -1.0}
+
+        instancer.instantiateGvar(varfont, location)
+
+        # check 'hyphen' coordinates changed
+        assert _get_coordinates(varfont, "hyphen") == [
+            (26, 259),
+            (26, 286),
+            (237, 286),
+            (237, 259),
+            (0, 0),
+            (263, 0),
+            (0, 536),
+            (0, 0),
+        ]
+        # check 'minus' coordinates (i.e. component offset and phantom points)
+        # did _not_ change
+        assert _get_coordinates(varfont, "minus") == minusCoords
+
+        assert hmtx["hyphen"] == (263, 26)
+        assert vmtx["hyphen"] == (536, 250)
+
+        assert hmtx["minus"] == (422, 26)  # 'minus' left sidebearing changed
+        assert vmtx["minus"] == (536, 250)  # 'minus' top sidebearing too
+
+
+class InstantiateCvarTest(object):
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            pytest.param({"wght": -1.0}, [500, -400, 150, 250], id="wght=-1.0"),
+            pytest.param({"wdth": -1.0}, [500, -400, 180, 200], id="wdth=-1.0"),
+            pytest.param({"wght": -0.5}, [500, -400, 165, 250], id="wght=-0.5"),
+            pytest.param({"wdth": -0.3}, [500, -400, 180, 235], id="wdth=-0.3"),
+        ],
+    )
+    def test_pin_and_drop_axis(self, varfont, location, expected):
+        instancer.instantiateCvar(varfont, location)
+
+        assert list(varfont["cvt "].values) == expected
+
+        # check that the pinned axis has been dropped from cvar
+        pinned_axes = location.keys()
+        assert not any(
+            axis in t.axes for t in varfont["cvar"].variations for axis in pinned_axes
+        )
+
+    def test_full_instance(self, varfont):
+        instancer.instantiateCvar(varfont, {"wght": -0.5, "wdth": -0.5})
+
+        assert list(varfont["cvt "].values) == [500, -400, 165, 225]
+
+        assert "cvar" not in varfont
+
+
+class InstantiateMVARTest(object):
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            pytest.param(
+                {"wght": 1.0},
+                {"strs": 100, "undo": -200, "unds": 150, "xhgt": 530},
+                id="wght=1.0",
+            ),
+            pytest.param(
+                {"wght": 0.5},
+                {"strs": 75, "undo": -150, "unds": 100, "xhgt": 515},
+                id="wght=0.5",
+            ),
+            pytest.param(
+                {"wght": 0.0},
+                {"strs": 50, "undo": -100, "unds": 50, "xhgt": 500},
+                id="wght=0.0",
+            ),
+            pytest.param(
+                {"wdth": -1.0},
+                {"strs": 20, "undo": -100, "unds": 50, "xhgt": 500},
+                id="wdth=-1.0",
+            ),
+            pytest.param(
+                {"wdth": -0.5},
+                {"strs": 35, "undo": -100, "unds": 50, "xhgt": 500},
+                id="wdth=-0.5",
+            ),
+            pytest.param(
+                {"wdth": 0.0},
+                {"strs": 50, "undo": -100, "unds": 50, "xhgt": 500},
+                id="wdth=0.0",
+            ),
+        ],
+    )
+    def test_pin_and_drop_axis(self, varfont, location, expected):
+        mvar = varfont["MVAR"].table
+        # initially we have two VarData: the first contains deltas associated with 3
+        # regions: 1 with only wght, 1 with only wdth, and 1 with both wght and wdth
+        assert len(mvar.VarStore.VarData) == 2
+        assert mvar.VarStore.VarRegionList.RegionCount == 3
+        assert mvar.VarStore.VarData[0].VarRegionCount == 3
+        assert all(len(item) == 3 for item in mvar.VarStore.VarData[0].Item)
+        # The second VarData has deltas associated only with 1 region (wght only).
+        assert mvar.VarStore.VarData[1].VarRegionCount == 1
+        assert all(len(item) == 1 for item in mvar.VarStore.VarData[1].Item)
+
+        instancer.instantiateMVAR(varfont, location)
+
+        for mvar_tag, expected_value in expected.items():
+            table_tag, item_name = MVAR_ENTRIES[mvar_tag]
+            assert getattr(varfont[table_tag], item_name) == expected_value
+
+        # check that regions and accompanying deltas have been dropped
+        num_regions_left = len(mvar.VarStore.VarRegionList.Region)
+        assert num_regions_left < 3
+        assert mvar.VarStore.VarRegionList.RegionCount == num_regions_left
+        assert mvar.VarStore.VarData[0].VarRegionCount == num_regions_left
+        # VarData subtables have been merged
+        assert len(mvar.VarStore.VarData) == 1
+
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            pytest.param(
+                {"wght": 1.0, "wdth": 0.0},
+                {"strs": 100, "undo": -200, "unds": 150},
+                id="wght=1.0,wdth=0.0",
+            ),
+            pytest.param(
+                {"wght": 0.0, "wdth": -1.0},
+                {"strs": 20, "undo": -100, "unds": 50},
+                id="wght=0.0,wdth=-1.0",
+            ),
+            pytest.param(
+                {"wght": 0.5, "wdth": -0.5},
+                {"strs": 55, "undo": -145, "unds": 95},
+                id="wght=0.5,wdth=-0.5",
+            ),
+            pytest.param(
+                {"wght": 1.0, "wdth": -1.0},
+                {"strs": 50, "undo": -180, "unds": 130},
+                id="wght=0.5,wdth=-0.5",
+            ),
+        ],
+    )
+    def test_full_instance(self, varfont, location, expected):
+        instancer.instantiateMVAR(varfont, location)
+
+        for mvar_tag, expected_value in expected.items():
+            table_tag, item_name = MVAR_ENTRIES[mvar_tag]
+            assert getattr(varfont[table_tag], item_name) == expected_value
+
+        assert "MVAR" not in varfont
+
+
+class InstantiateHVARTest(object):
+    # the 'expectedDeltas' below refer to the VarData item deltas for the "hyphen"
+    # glyph in the PartialInstancerTest-VF.ttx test font, that are left after
+    # partial instancing
+    @pytest.mark.parametrize(
+        "location, expectedRegions, expectedDeltas",
+        [
+            ({"wght": -1.0}, [{"wdth": (-1.0, -1.0, 0)}], [-59]),
+            ({"wght": 0}, [{"wdth": (-1.0, -1.0, 0)}], [-48]),
+            ({"wght": 1.0}, [{"wdth": (-1.0, -1.0, 0)}], [7]),
+            (
+                {"wdth": -1.0},
+                [
+                    {"wght": (-1.0, -1.0, 0.0)},
+                    {"wght": (0.0, 0.6099854, 1.0)},
+                    {"wght": (0.6099854, 1.0, 1.0)},
+                ],
+                [-11, 31, 51],
+            ),
+            ({"wdth": 0}, [{"wght": (0.6099854, 1.0, 1.0)}], [-4]),
+        ],
+    )
+    def test_partial_instance(self, varfont, location, expectedRegions, expectedDeltas):
+        instancer.instantiateHVAR(varfont, location)
+
+        assert "HVAR" in varfont
+        hvar = varfont["HVAR"].table
+        varStore = hvar.VarStore
+
+        regions = varStore.VarRegionList.Region
+        fvarAxes = [a for a in varfont["fvar"].axes if a.axisTag not in location]
+        regionDicts = [reg.get_support(fvarAxes) for reg in regions]
+        assert len(regionDicts) == len(expectedRegions)
+        for region, expectedRegion in zip(regionDicts, expectedRegions):
+            assert region.keys() == expectedRegion.keys()
+            for axisTag, support in region.items():
+                assert support == pytest.approx(expectedRegion[axisTag])
+
+        assert len(varStore.VarData) == 1
+        assert varStore.VarData[0].ItemCount == 2
+
+        assert hvar.AdvWidthMap is not None
+        advWithMap = hvar.AdvWidthMap.mapping
+
+        assert advWithMap[".notdef"] == advWithMap["space"]
+        varIdx = advWithMap[".notdef"]
+        # these glyphs have no metrics variations in the test font
+        assert varStore.VarData[varIdx >> 16].Item[varIdx & 0xFFFF] == (
+            [0] * varStore.VarData[0].VarRegionCount
+        )
+
+        varIdx = advWithMap["hyphen"]
+        assert varStore.VarData[varIdx >> 16].Item[varIdx & 0xFFFF] == expectedDeltas
+
+    def test_full_instance(self, varfont):
+        instancer.instantiateHVAR(varfont, {"wght": 0, "wdth": 0})
+
+        assert "HVAR" not in varfont
+
+    def test_partial_instance_keep_empty_table(self, varfont):
+        # Append an additional dummy axis to fvar, for which the current HVAR table
+        # in our test 'varfont' contains no variation data.
+        # Instancing the other two wght and wdth axes should leave HVAR table empty,
+        # to signal there are variations to the glyph's advance widths.
+        fvar = varfont["fvar"]
+        axis = _f_v_a_r.Axis()
+        axis.axisTag = "TEST"
+        fvar.axes.append(axis)
+
+        instancer.instantiateHVAR(varfont, {"wght": 0, "wdth": 0})
+
+        assert "HVAR" in varfont
+
+        varStore = varfont["HVAR"].table.VarStore
+
+        assert varStore.VarRegionList.RegionCount == 0
+        assert not varStore.VarRegionList.Region
+        assert varStore.VarRegionList.RegionAxisCount == 1
+
+
+class InstantiateItemVariationStoreTest(object):
+    def test_VarRegion_get_support(self):
+        axisOrder = ["wght", "wdth", "opsz"]
+        regionAxes = {"wdth": (-1.0, -1.0, 0.0), "wght": (0.0, 1.0, 1.0)}
+        region = builder.buildVarRegion(regionAxes, axisOrder)
+
+        assert len(region.VarRegionAxis) == 3
+        assert region.VarRegionAxis[2].PeakCoord == 0
+
+        fvarAxes = [SimpleNamespace(axisTag=axisTag) for axisTag in axisOrder]
+
+        assert region.get_support(fvarAxes) == regionAxes
+
+    @pytest.fixture
+    def varStore(self):
+        return builder.buildVarStore(
+            builder.buildVarRegionList(
+                [
+                    {"wght": (-1.0, -1.0, 0)},
+                    {"wght": (0, 0.5, 1.0)},
+                    {"wght": (0.5, 1.0, 1.0)},
+                    {"wdth": (-1.0, -1.0, 0)},
+                    {"wght": (-1.0, -1.0, 0), "wdth": (-1.0, -1.0, 0)},
+                    {"wght": (0, 0.5, 1.0), "wdth": (-1.0, -1.0, 0)},
+                    {"wght": (0.5, 1.0, 1.0), "wdth": (-1.0, -1.0, 0)},
+                ],
+                ["wght", "wdth"],
+            ),
+            [
+                builder.buildVarData([0, 1, 2], [[100, 100, 100], [100, 100, 100]]),
+                builder.buildVarData(
+                    [3, 4, 5, 6], [[100, 100, 100, 100], [100, 100, 100, 100]]
+                ),
+            ],
+        )
+
+    @pytest.mark.parametrize(
+        "location, expected_deltas, num_regions",
+        [
+            ({"wght": 0}, [[0, 0], [0, 0]], 1),
+            ({"wght": 0.25}, [[50, 50], [0, 0]], 1),
+            ({"wdth": 0}, [[0, 0], [0, 0]], 3),
+            ({"wdth": -0.75}, [[0, 0], [75, 75]], 3),
+            ({"wght": 0, "wdth": 0}, [[0, 0], [0, 0]], 0),
+            ({"wght": 0.25, "wdth": 0}, [[50, 50], [0, 0]], 0),
+            ({"wght": 0, "wdth": -0.75}, [[0, 0], [75, 75]], 0),
+        ],
+    )
+    def test_instantiate_default_deltas(
+        self, varStore, fvarAxes, location, expected_deltas, num_regions
+    ):
+        defaultDeltas = instancer.instantiateItemVariationStore(
+            varStore, fvarAxes, location
+        )
+
+        defaultDeltaArray = []
+        for varidx, delta in sorted(defaultDeltas.items()):
+            major, minor = varidx >> 16, varidx & 0xFFFF
+            if major == len(defaultDeltaArray):
+                defaultDeltaArray.append([])
+            assert len(defaultDeltaArray[major]) == minor
+            defaultDeltaArray[major].append(delta)
+
+        assert defaultDeltaArray == expected_deltas
+        assert varStore.VarRegionList.RegionCount == num_regions
+
+
+class TupleVarStoreAdapterTest(object):
+    def test_instantiate(self):
+        regions = [
+            {"wght": (-1.0, -1.0, 0)},
+            {"wght": (0.0, 1.0, 1.0)},
+            {"wdth": (-1.0, -1.0, 0)},
+            {"wght": (-1.0, -1.0, 0), "wdth": (-1.0, -1.0, 0)},
+            {"wght": (0, 1.0, 1.0), "wdth": (-1.0, -1.0, 0)},
+        ]
+        axisOrder = ["wght", "wdth"]
+        tupleVarData = [
+            [
+                TupleVariation({"wght": (-1.0, -1.0, 0)}, [10, 70]),
+                TupleVariation({"wght": (0.0, 1.0, 1.0)}, [30, 90]),
+                TupleVariation(
+                    {"wght": (-1.0, -1.0, 0), "wdth": (-1.0, -1.0, 0)}, [-40, -100]
+                ),
+                TupleVariation(
+                    {"wght": (0, 1.0, 1.0), "wdth": (-1.0, -1.0, 0)}, [-60, -120]
+                ),
+            ],
+            [
+                TupleVariation({"wdth": (-1.0, -1.0, 0)}, [5, 45]),
+                TupleVariation(
+                    {"wght": (-1.0, -1.0, 0), "wdth": (-1.0, -1.0, 0)}, [-15, -55]
+                ),
+                TupleVariation(
+                    {"wght": (0, 1.0, 1.0), "wdth": (-1.0, -1.0, 0)}, [-35, -75]
+                ),
+            ],
+        ]
+        adapter = instancer._TupleVarStoreAdapter(
+            regions, axisOrder, tupleVarData, itemCounts=[2, 2]
+        )
+
+        defaultDeltaArray = adapter.instantiate({"wght": 0.5})
+
+        assert defaultDeltaArray == [[15, 45], [0, 0]]
+        assert adapter.regions == [{"wdth": (-1.0, -1.0, 0)}]
+        assert adapter.tupleVarData == [
+            [TupleVariation({"wdth": (-1.0, -1.0, 0)}, [-30, -60])],
+            [TupleVariation({"wdth": (-1.0, -1.0, 0)}, [-12, 8])],
+        ]
+
+    def test_rebuildRegions(self):
+        regions = [
+            {"wght": (-1.0, -1.0, 0)},
+            {"wght": (0.0, 1.0, 1.0)},
+            {"wdth": (-1.0, -1.0, 0)},
+            {"wght": (-1.0, -1.0, 0), "wdth": (-1.0, -1.0, 0)},
+            {"wght": (0, 1.0, 1.0), "wdth": (-1.0, -1.0, 0)},
+        ]
+        axisOrder = ["wght", "wdth"]
+        variations = []
+        for region in regions:
+            variations.append(TupleVariation(region, [100]))
+        tupleVarData = [variations[:3], variations[3:]]
+        adapter = instancer._TupleVarStoreAdapter(
+            regions, axisOrder, tupleVarData, itemCounts=[1, 1]
+        )
+
+        adapter.rebuildRegions()
+
+        assert adapter.regions == regions
+
+        del tupleVarData[0][2]
+        tupleVarData[1][0].axes = {"wght": (-1.0, -0.5, 0)}
+        tupleVarData[1][1].axes = {"wght": (0, 0.5, 1.0)}
+
+        adapter.rebuildRegions()
+
+        assert adapter.regions == [
+            {"wght": (-1.0, -1.0, 0)},
+            {"wght": (0.0, 1.0, 1.0)},
+            {"wght": (-1.0, -0.5, 0)},
+            {"wght": (0, 0.5, 1.0)},
+        ]
+
+    def test_roundtrip(self, fvarAxes):
+        regions = [
+            {"wght": (-1.0, -1.0, 0)},
+            {"wght": (0, 0.5, 1.0)},
+            {"wght": (0.5, 1.0, 1.0)},
+            {"wdth": (-1.0, -1.0, 0)},
+            {"wght": (-1.0, -1.0, 0), "wdth": (-1.0, -1.0, 0)},
+            {"wght": (0, 0.5, 1.0), "wdth": (-1.0, -1.0, 0)},
+            {"wght": (0.5, 1.0, 1.0), "wdth": (-1.0, -1.0, 0)},
+        ]
+        axisOrder = [axis.axisTag for axis in fvarAxes]
+
+        itemVarStore = builder.buildVarStore(
+            builder.buildVarRegionList(regions, axisOrder),
+            [
+                builder.buildVarData(
+                    [0, 1, 2, 4, 5, 6],
+                    [[10, -20, 30, -40, 50, -60], [70, -80, 90, -100, 110, -120]],
+                ),
+                builder.buildVarData(
+                    [3, 4, 5, 6], [[5, -15, 25, -35], [45, -55, 65, -75]]
+                ),
+            ],
+        )
+
+        adapter = instancer._TupleVarStoreAdapter.fromItemVarStore(
+            itemVarStore, fvarAxes
+        )
+
+        assert adapter.tupleVarData == [
+            [
+                TupleVariation({"wght": (-1.0, -1.0, 0)}, [10, 70]),
+                TupleVariation({"wght": (0, 0.5, 1.0)}, [-20, -80]),
+                TupleVariation({"wght": (0.5, 1.0, 1.0)}, [30, 90]),
+                TupleVariation(
+                    {"wght": (-1.0, -1.0, 0), "wdth": (-1.0, -1.0, 0)}, [-40, -100]
+                ),
+                TupleVariation(
+                    {"wght": (0, 0.5, 1.0), "wdth": (-1.0, -1.0, 0)}, [50, 110]
+                ),
+                TupleVariation(
+                    {"wght": (0.5, 1.0, 1.0), "wdth": (-1.0, -1.0, 0)}, [-60, -120]
+                ),
+            ],
+            [
+                TupleVariation({"wdth": (-1.0, -1.0, 0)}, [5, 45]),
+                TupleVariation(
+                    {"wght": (-1.0, -1.0, 0), "wdth": (-1.0, -1.0, 0)}, [-15, -55]
+                ),
+                TupleVariation(
+                    {"wght": (0, 0.5, 1.0), "wdth": (-1.0, -1.0, 0)}, [25, 65]
+                ),
+                TupleVariation(
+                    {"wght": (0.5, 1.0, 1.0), "wdth": (-1.0, -1.0, 0)}, [-35, -75]
+                ),
+            ],
+        ]
+        assert adapter.itemCounts == [data.ItemCount for data in itemVarStore.VarData]
+        assert adapter.regions == regions
+        assert adapter.axisOrder == axisOrder
+
+        itemVarStore2 = adapter.asItemVarStore()
+
+        assert [
+            reg.get_support(fvarAxes) for reg in itemVarStore2.VarRegionList.Region
+        ] == regions
+
+        assert itemVarStore2.VarDataCount == 2
+        assert itemVarStore2.VarData[0].VarRegionIndex == [0, 1, 2, 4, 5, 6]
+        assert itemVarStore2.VarData[0].Item == [
+            [10, -20, 30, -40, 50, -60],
+            [70, -80, 90, -100, 110, -120],
+        ]
+        assert itemVarStore2.VarData[1].VarRegionIndex == [3, 4, 5, 6]
+        assert itemVarStore2.VarData[1].Item == [[5, -15, 25, -35], [45, -55, 65, -75]]
+
+
+def makeTTFont(glyphOrder, features):
+    font = ttLib.TTFont()
+    font.setGlyphOrder(glyphOrder)
+    addOpenTypeFeaturesFromString(font, features)
+    font["name"] = ttLib.newTable("name")
+    return font
+
+
+def _makeDSAxesDict(axes):
+    dsAxes = collections.OrderedDict()
+    for axisTag, axisValues in axes:
+        axis = designspaceLib.AxisDescriptor()
+        axis.name = axis.tag = axis.labelNames["en"] = axisTag
+        axis.minimum, axis.default, axis.maximum = axisValues
+        dsAxes[axis.tag] = axis
+    return dsAxes
+
+
+def makeVariableFont(masters, baseIndex, axes, masterLocations):
+    vf = deepcopy(masters[baseIndex])
+    dsAxes = _makeDSAxesDict(axes)
+    fvar = varLib._add_fvar(vf, dsAxes, instances=())
+    axisTags = [axis.axisTag for axis in fvar.axes]
+    normalizedLocs = [models.normalizeLocation(m, dict(axes)) for m in masterLocations]
+    model = models.VariationModel(normalizedLocs, axisOrder=axisTags)
+    varLib._merge_OTL(vf, model, masters, axisTags)
+    return vf
+
+
+def makeParametrizedVF(glyphOrder, features, values, increments):
+    # Create a test VF with given glyphs and parametrized OTL features.
+    # The VF is built from 9 masters (3 x 3 along wght and wdth), with
+    # locations hard-coded and base master at wght=400 and wdth=100.
+    # 'values' is a list of initial values that are interpolated in the
+    # 'features' string, and incremented for each subsequent master by the
+    # given 'increments' (list of 2-tuple) along the two axes.
+    assert values and len(values) == len(increments)
+    assert all(len(i) == 2 for i in increments)
+    masterLocations = [
+        {"wght": 100, "wdth": 50},
+        {"wght": 100, "wdth": 100},
+        {"wght": 100, "wdth": 150},
+        {"wght": 400, "wdth": 50},
+        {"wght": 400, "wdth": 100},  # base master
+        {"wght": 400, "wdth": 150},
+        {"wght": 700, "wdth": 50},
+        {"wght": 700, "wdth": 100},
+        {"wght": 700, "wdth": 150},
+    ]
+    n = len(values)
+    values = list(values)
+    masters = []
+    for _ in range(3):
+        for _ in range(3):
+            master = makeTTFont(glyphOrder, features=features % tuple(values))
+            masters.append(master)
+            for i in range(n):
+                values[i] += increments[i][1]
+        for i in range(n):
+            values[i] += increments[i][0]
+    baseIndex = 4
+    axes = [("wght", (100, 400, 700)), ("wdth", (50, 100, 150))]
+    vf = makeVariableFont(masters, baseIndex, axes, masterLocations)
+    return vf
+
+
+@pytest.fixture
+def varfontGDEF():
+    glyphOrder = [".notdef", "f", "i", "f_i"]
+    features = (
+        "feature liga { sub f i by f_i;} liga;"
+        "table GDEF { LigatureCaretByPos f_i %d; } GDEF;"
+    )
+    values = [100]
+    increments = [(+30, +10)]
+    return makeParametrizedVF(glyphOrder, features, values, increments)
+
+
+@pytest.fixture
+def varfontGPOS():
+    glyphOrder = [".notdef", "V", "A"]
+    features = "feature kern { pos V A %d; } kern;"
+    values = [-80]
+    increments = [(-10, -5)]
+    return makeParametrizedVF(glyphOrder, features, values, increments)
+
+
+@pytest.fixture
+def varfontGPOS2():
+    glyphOrder = [".notdef", "V", "A", "acutecomb"]
+    features = (
+        "markClass [acutecomb] <anchor 150 -10> @TOP_MARKS;"
+        "feature mark {"
+        "  pos base A <anchor %d 450> mark @TOP_MARKS;"
+        "} mark;"
+        "feature kern {"
+        "  pos V A %d;"
+        "} kern;"
+    )
+    values = [200, -80]
+    increments = [(+30, +10), (-10, -5)]
+    return makeParametrizedVF(glyphOrder, features, values, increments)
+
+
+class InstantiateOTLTest(object):
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            ({"wght": -1.0}, 110),  # -60
+            ({"wght": 0}, 170),
+            ({"wght": 0.5}, 200),  # +30
+            ({"wght": 1.0}, 230),  # +60
+            ({"wdth": -1.0}, 160),  # -10
+            ({"wdth": -0.3}, 167),  # -3
+            ({"wdth": 0}, 170),
+            ({"wdth": 1.0}, 180),  # +10
+        ],
+    )
+    def test_pin_and_drop_axis_GDEF(self, varfontGDEF, location, expected):
+        vf = varfontGDEF
+        assert "GDEF" in vf
+
+        instancer.instantiateOTL(vf, location)
+
+        assert "GDEF" in vf
+        gdef = vf["GDEF"].table
+        assert gdef.Version == 0x00010003
+        assert gdef.VarStore
+        assert gdef.LigCaretList
+        caretValue = gdef.LigCaretList.LigGlyph[0].CaretValue[0]
+        assert caretValue.Format == 3
+        assert hasattr(caretValue, "DeviceTable")
+        assert caretValue.DeviceTable.DeltaFormat == 0x8000
+        assert caretValue.Coordinate == expected
+
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            ({"wght": -1.0, "wdth": -1.0}, 100),  # -60 - 10
+            ({"wght": -1.0, "wdth": 0.0}, 110),  # -60
+            ({"wght": -1.0, "wdth": 1.0}, 120),  # -60 + 10
+            ({"wght": 0.0, "wdth": -1.0}, 160),  # -10
+            ({"wght": 0.0, "wdth": 0.0}, 170),
+            ({"wght": 0.0, "wdth": 1.0}, 180),  # +10
+            ({"wght": 1.0, "wdth": -1.0}, 220),  # +60 - 10
+            ({"wght": 1.0, "wdth": 0.0}, 230),  # +60
+            ({"wght": 1.0, "wdth": 1.0}, 240),  # +60 + 10
+        ],
+    )
+    def test_full_instance_GDEF(self, varfontGDEF, location, expected):
+        vf = varfontGDEF
+        assert "GDEF" in vf
+
+        instancer.instantiateOTL(vf, location)
+
+        assert "GDEF" in vf
+        gdef = vf["GDEF"].table
+        assert gdef.Version == 0x00010000
+        assert not hasattr(gdef, "VarStore")
+        assert gdef.LigCaretList
+        caretValue = gdef.LigCaretList.LigGlyph[0].CaretValue[0]
+        assert caretValue.Format == 1
+        assert not hasattr(caretValue, "DeviceTable")
+        assert caretValue.Coordinate == expected
+
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            ({"wght": -1.0}, -85),  # +25
+            ({"wght": 0}, -110),
+            ({"wght": 1.0}, -135),  # -25
+            ({"wdth": -1.0}, -105),  # +5
+            ({"wdth": 0}, -110),
+            ({"wdth": 1.0}, -115),  # -5
+        ],
+    )
+    def test_pin_and_drop_axis_GPOS_kern(self, varfontGPOS, location, expected):
+        vf = varfontGPOS
+        assert "GDEF" in vf
+        assert "GPOS" in vf
+
+        instancer.instantiateOTL(vf, location)
+
+        gdef = vf["GDEF"].table
+        gpos = vf["GPOS"].table
+        assert gdef.Version == 0x00010003
+        assert gdef.VarStore
+
+        assert gpos.LookupList.Lookup[0].LookupType == 2  # PairPos
+        pairPos = gpos.LookupList.Lookup[0].SubTable[0]
+        valueRec1 = pairPos.PairSet[0].PairValueRecord[0].Value1
+        assert valueRec1.XAdvDevice
+        assert valueRec1.XAdvDevice.DeltaFormat == 0x8000
+        assert valueRec1.XAdvance == expected
+
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            ({"wght": -1.0, "wdth": -1.0}, -80),  # +25 + 5
+            ({"wght": -1.0, "wdth": 0.0}, -85),  # +25
+            ({"wght": -1.0, "wdth": 1.0}, -90),  # +25 - 5
+            ({"wght": 0.0, "wdth": -1.0}, -105),  # +5
+            ({"wght": 0.0, "wdth": 0.0}, -110),
+            ({"wght": 0.0, "wdth": 1.0}, -115),  # -5
+            ({"wght": 1.0, "wdth": -1.0}, -130),  # -25 + 5
+            ({"wght": 1.0, "wdth": 0.0}, -135),  # -25
+            ({"wght": 1.0, "wdth": 1.0}, -140),  # -25 - 5
+        ],
+    )
+    def test_full_instance_GPOS_kern(self, varfontGPOS, location, expected):
+        vf = varfontGPOS
+        assert "GDEF" in vf
+        assert "GPOS" in vf
+
+        instancer.instantiateOTL(vf, location)
+
+        assert "GDEF" not in vf
+        gpos = vf["GPOS"].table
+
+        assert gpos.LookupList.Lookup[0].LookupType == 2  # PairPos
+        pairPos = gpos.LookupList.Lookup[0].SubTable[0]
+        valueRec1 = pairPos.PairSet[0].PairValueRecord[0].Value1
+        assert not hasattr(valueRec1, "XAdvDevice")
+        assert valueRec1.XAdvance == expected
+
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            ({"wght": -1.0}, (210, -85)),  # -60, +25
+            ({"wght": 0}, (270, -110)),
+            ({"wght": 0.5}, (300, -122)),  # +30, -12
+            ({"wght": 1.0}, (330, -135)),  # +60, -25
+            ({"wdth": -1.0}, (260, -105)),  # -10, +5
+            ({"wdth": -0.3}, (267, -108)),  # -3, +2
+            ({"wdth": 0}, (270, -110)),
+            ({"wdth": 1.0}, (280, -115)),  # +10, -5
+        ],
+    )
+    def test_pin_and_drop_axis_GPOS_mark_and_kern(
+        self, varfontGPOS2, location, expected
+    ):
+        vf = varfontGPOS2
+        assert "GDEF" in vf
+        assert "GPOS" in vf
+
+        instancer.instantiateOTL(vf, location)
+
+        v1, v2 = expected
+        gdef = vf["GDEF"].table
+        gpos = vf["GPOS"].table
+        assert gdef.Version == 0x00010003
+        assert gdef.VarStore
+        assert gdef.GlyphClassDef
+
+        assert gpos.LookupList.Lookup[0].LookupType == 4  # MarkBasePos
+        markBasePos = gpos.LookupList.Lookup[0].SubTable[0]
+        baseAnchor = markBasePos.BaseArray.BaseRecord[0].BaseAnchor[0]
+        assert baseAnchor.Format == 3
+        assert baseAnchor.XDeviceTable
+        assert baseAnchor.XDeviceTable.DeltaFormat == 0x8000
+        assert not baseAnchor.YDeviceTable
+        assert baseAnchor.XCoordinate == v1
+        assert baseAnchor.YCoordinate == 450
+
+        assert gpos.LookupList.Lookup[1].LookupType == 2  # PairPos
+        pairPos = gpos.LookupList.Lookup[1].SubTable[0]
+        valueRec1 = pairPos.PairSet[0].PairValueRecord[0].Value1
+        assert valueRec1.XAdvDevice
+        assert valueRec1.XAdvDevice.DeltaFormat == 0x8000
+        assert valueRec1.XAdvance == v2
+
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            ({"wght": -1.0, "wdth": -1.0}, (200, -80)),  # -60 - 10, +25 + 5
+            ({"wght": -1.0, "wdth": 0.0}, (210, -85)),  # -60, +25
+            ({"wght": -1.0, "wdth": 1.0}, (220, -90)),  # -60 + 10, +25 - 5
+            ({"wght": 0.0, "wdth": -1.0}, (260, -105)),  # -10, +5
+            ({"wght": 0.0, "wdth": 0.0}, (270, -110)),
+            ({"wght": 0.0, "wdth": 1.0}, (280, -115)),  # +10, -5
+            ({"wght": 1.0, "wdth": -1.0}, (320, -130)),  # +60 - 10, -25 + 5
+            ({"wght": 1.0, "wdth": 0.0}, (330, -135)),  # +60, -25
+            ({"wght": 1.0, "wdth": 1.0}, (340, -140)),  # +60 + 10, -25 - 5
+        ],
+    )
+    def test_full_instance_GPOS_mark_and_kern(self, varfontGPOS2, location, expected):
+        vf = varfontGPOS2
+        assert "GDEF" in vf
+        assert "GPOS" in vf
+
+        instancer.instantiateOTL(vf, location)
+
+        v1, v2 = expected
+        gdef = vf["GDEF"].table
+        gpos = vf["GPOS"].table
+        assert gdef.Version == 0x00010000
+        assert not hasattr(gdef, "VarStore")
+        assert gdef.GlyphClassDef
+
+        assert gpos.LookupList.Lookup[0].LookupType == 4  # MarkBasePos
+        markBasePos = gpos.LookupList.Lookup[0].SubTable[0]
+        baseAnchor = markBasePos.BaseArray.BaseRecord[0].BaseAnchor[0]
+        assert baseAnchor.Format == 1
+        assert not hasattr(baseAnchor, "XDeviceTable")
+        assert not hasattr(baseAnchor, "YDeviceTable")
+        assert baseAnchor.XCoordinate == v1
+        assert baseAnchor.YCoordinate == 450
+
+        assert gpos.LookupList.Lookup[1].LookupType == 2  # PairPos
+        pairPos = gpos.LookupList.Lookup[1].SubTable[0]
+        valueRec1 = pairPos.PairSet[0].PairValueRecord[0].Value1
+        assert not hasattr(valueRec1, "XAdvDevice")
+        assert valueRec1.XAdvance == v2
+
+    def test_GPOS_ValueRecord_XAdvDevice_wtihout_XAdvance(self):
+        # Test VF contains a PairPos adjustment in which the default instance
+        # has no XAdvance but there are deltas in XAdvDevice (VariationIndex).
+        vf = ttLib.TTFont()
+        vf.importXML(os.path.join(TESTDATA, "PartialInstancerTest4-VF.ttx"))
+        pairPos = vf["GPOS"].table.LookupList.Lookup[0].SubTable[0]
+        assert pairPos.ValueFormat1 == 0x40
+        valueRec1 = pairPos.PairSet[0].PairValueRecord[0].Value1
+        assert not hasattr(valueRec1, "XAdvance")
+        assert valueRec1.XAdvDevice.DeltaFormat == 0x8000
+        outer = valueRec1.XAdvDevice.StartSize
+        inner = valueRec1.XAdvDevice.EndSize
+        assert vf["GDEF"].table.VarStore.VarData[outer].Item[inner] == [-50]
+
+        # check that MutatorMerger for ValueRecord doesn't raise AttributeError
+        # when XAdvDevice is present but there's no corresponding XAdvance.
+        instancer.instantiateOTL(vf, {"wght": 0.5})
+
+        pairPos = vf["GPOS"].table.LookupList.Lookup[0].SubTable[0]
+        assert pairPos.ValueFormat1 == 0x4
+        valueRec1 = pairPos.PairSet[0].PairValueRecord[0].Value1
+        assert not hasattr(valueRec1, "XAdvDevice")
+        assert valueRec1.XAdvance == -25
+
+
+class InstantiateAvarTest(object):
+    @pytest.mark.parametrize("location", [{"wght": 0.0}, {"wdth": 0.0}])
+    def test_pin_and_drop_axis(self, varfont, location):
+        instancer.instantiateAvar(varfont, location)
+
+        assert set(varfont["avar"].segments).isdisjoint(location)
+
+    def test_full_instance(self, varfont):
+        instancer.instantiateAvar(varfont, {"wght": 0.0, "wdth": 0.0})
+
+        assert "avar" not in varfont
+
+    @staticmethod
+    def quantizeF2Dot14Floats(mapping):
+        return {
+            floatToFixedToFloat(k, 14): floatToFixedToFloat(v, 14)
+            for k, v in mapping.items()
+        }
+
+    # the following values come from NotoSans-VF.ttf
+    DFLT_WGHT_MAPPING = {
+        -1.0: -1.0,
+        -0.6667: -0.7969,
+        -0.3333: -0.5,
+        0: 0,
+        0.2: 0.18,
+        0.4: 0.38,
+        0.6: 0.61,
+        0.8: 0.79,
+        1.0: 1.0,
+    }
+
+    DFLT_WDTH_MAPPING = {-1.0: -1.0, -0.6667: -0.7, -0.3333: -0.36664, 0: 0, 1.0: 1.0}
+
+    @pytest.fixture
+    def varfont(self):
+        fvarAxes = ("wght", (100, 400, 900)), ("wdth", (62.5, 100, 100))
+        avarSegments = {
+            "wght": self.quantizeF2Dot14Floats(self.DFLT_WGHT_MAPPING),
+            "wdth": self.quantizeF2Dot14Floats(self.DFLT_WDTH_MAPPING),
+        }
+        varfont = ttLib.TTFont()
+        varfont["name"] = ttLib.newTable("name")
+        varLib._add_fvar(varfont, _makeDSAxesDict(fvarAxes), instances=())
+        avar = varfont["avar"] = ttLib.newTable("avar")
+        avar.segments = avarSegments
+        return varfont
+
+    @pytest.mark.parametrize(
+        "axisLimits, expectedSegments",
+        [
+            pytest.param(
+                {"wght": (100, 900)},
+                {"wght": DFLT_WGHT_MAPPING, "wdth": DFLT_WDTH_MAPPING},
+                id="wght=100:900",
+            ),
+            pytest.param(
+                {"wght": (400, 900)},
+                {
+                    "wght": {
+                        -1.0: -1.0,
+                        0: 0,
+                        0.2: 0.18,
+                        0.4: 0.38,
+                        0.6: 0.61,
+                        0.8: 0.79,
+                        1.0: 1.0,
+                    },
+                    "wdth": DFLT_WDTH_MAPPING,
+                },
+                id="wght=400:900",
+            ),
+            pytest.param(
+                {"wght": (100, 400)},
+                {
+                    "wght": {
+                        -1.0: -1.0,
+                        -0.6667: -0.7969,
+                        -0.3333: -0.5,
+                        0: 0,
+                        1.0: 1.0,
+                    },
+                    "wdth": DFLT_WDTH_MAPPING,
+                },
+                id="wght=100:400",
+            ),
+            pytest.param(
+                {"wght": (400, 800)},
+                {
+                    "wght": {
+                        -1.0: -1.0,
+                        0: 0,
+                        0.25: 0.22784,
+                        0.50006: 0.48103,
+                        0.75: 0.77214,
+                        1.0: 1.0,
+                    },
+                    "wdth": DFLT_WDTH_MAPPING,
+                },
+                id="wght=400:800",
+            ),
+            pytest.param(
+                {"wght": (400, 700)},
+                {
+                    "wght": {
+                        -1.0: -1.0,
+                        0: 0,
+                        0.3334: 0.2951,
+                        0.66675: 0.623,
+                        1.0: 1.0,
+                    },
+                    "wdth": DFLT_WDTH_MAPPING,
+                },
+                id="wght=400:700",
+            ),
+            pytest.param(
+                {"wght": (400, 600)},
+                {
+                    "wght": {-1.0: -1.0, 0: 0, 0.5: 0.47363, 1.0: 1.0},
+                    "wdth": DFLT_WDTH_MAPPING,
+                },
+                id="wght=400:600",
+            ),
+            pytest.param(
+                {"wdth": (62.5, 100)},
+                {
+                    "wght": DFLT_WGHT_MAPPING,
+                    "wdth": {
+                        -1.0: -1.0,
+                        -0.6667: -0.7,
+                        -0.3333: -0.36664,
+                        0: 0,
+                        1.0: 1.0,
+                    },
+                },
+                id="wdth=62.5:100",
+            ),
+            pytest.param(
+                {"wdth": (70, 100)},
+                {
+                    "wght": DFLT_WGHT_MAPPING,
+                    "wdth": {
+                        -1.0: -1.0,
+                        -0.8334: -0.85364,
+                        -0.4166: -0.44714,
+                        0: 0,
+                        1.0: 1.0,
+                    },
+                },
+                id="wdth=70:100",
+            ),
+            pytest.param(
+                {"wdth": (75, 100)},
+                {
+                    "wght": DFLT_WGHT_MAPPING,
+                    "wdth": {-1.0: -1.0, -0.49994: -0.52374, 0: 0, 1.0: 1.0},
+                },
+                id="wdth=75:100",
+            ),
+            pytest.param(
+                {"wdth": (77, 100)},
+                {
+                    "wght": DFLT_WGHT_MAPPING,
+                    "wdth": {-1.0: -1.0, -0.54346: -0.56696, 0: 0, 1.0: 1.0},
+                },
+                id="wdth=77:100",
+            ),
+            pytest.param(
+                {"wdth": (87.5, 100)},
+                {"wght": DFLT_WGHT_MAPPING, "wdth": {-1.0: -1.0, 0: 0, 1.0: 1.0}},
+                id="wdth=87.5:100",
+            ),
+        ],
+    )
+    def test_limit_axes(self, varfont, axisLimits, expectedSegments):
+        instancer.instantiateAvar(varfont, axisLimits)
+
+        newSegments = varfont["avar"].segments
+        expectedSegments = {
+            axisTag: self.quantizeF2Dot14Floats(mapping)
+            for axisTag, mapping in expectedSegments.items()
+        }
+        assert newSegments == expectedSegments
+
+    @pytest.mark.parametrize(
+        "invalidSegmentMap",
+        [
+            pytest.param({0.5: 0.5}, id="missing-required-maps-1"),
+            pytest.param({-1.0: -1.0, 1.0: 1.0}, id="missing-required-maps-2"),
+            pytest.param(
+                {-1.0: -1.0, 0: 0, 0.5: 0.5, 0.6: 0.4, 1.0: 1.0},
+                id="retrograde-value-maps",
+            ),
+        ],
+    )
+    def test_drop_invalid_segment_map(self, varfont, invalidSegmentMap, caplog):
+        varfont["avar"].segments["wght"] = invalidSegmentMap
+
+        with caplog.at_level(logging.WARNING, logger="fontTools.varLib.instancer"):
+            instancer.instantiateAvar(varfont, {"wght": (100, 400)})
+
+        assert "Invalid avar" in caplog.text
+        assert "wght" not in varfont["avar"].segments
+
+    def test_isValidAvarSegmentMap(self):
+        assert instancer._isValidAvarSegmentMap("FOOO", {})
+        assert instancer._isValidAvarSegmentMap("FOOO", {-1.0: -1.0, 0: 0, 1.0: 1.0})
+        assert instancer._isValidAvarSegmentMap(
+            "FOOO", {-1.0: -1.0, 0: 0, 0.5: 0.5, 1.0: 1.0}
+        )
+        assert instancer._isValidAvarSegmentMap(
+            "FOOO", {-1.0: -1.0, 0: 0, 0.5: 0.5, 0.7: 0.5, 1.0: 1.0}
+        )
+
+
+class InstantiateFvarTest(object):
+    @pytest.mark.parametrize(
+        "location, instancesLeft",
+        [
+            (
+                {"wght": 400.0},
+                ["Regular", "SemiCondensed", "Condensed", "ExtraCondensed"],
+            ),
+            (
+                {"wght": 100.0},
+                ["Thin", "SemiCondensed Thin", "Condensed Thin", "ExtraCondensed Thin"],
+            ),
+            (
+                {"wdth": 100.0},
+                [
+                    "Thin",
+                    "ExtraLight",
+                    "Light",
+                    "Regular",
+                    "Medium",
+                    "SemiBold",
+                    "Bold",
+                    "ExtraBold",
+                    "Black",
+                ],
+            ),
+            # no named instance at pinned location
+            ({"wdth": 90.0}, []),
+        ],
+    )
+    def test_pin_and_drop_axis(self, varfont, location, instancesLeft):
+        instancer.instantiateFvar(varfont, location)
+
+        fvar = varfont["fvar"]
+        assert {a.axisTag for a in fvar.axes}.isdisjoint(location)
+
+        for instance in fvar.instances:
+            assert set(instance.coordinates).isdisjoint(location)
+
+        name = varfont["name"]
+        assert [
+            name.getDebugName(instance.subfamilyNameID) for instance in fvar.instances
+        ] == instancesLeft
+
+    def test_full_instance(self, varfont):
+        instancer.instantiateFvar(varfont, {"wght": 0.0, "wdth": 0.0})
+
+        assert "fvar" not in varfont
+
+
+class InstantiateSTATTest(object):
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            ({"wght": 400}, ["Regular", "Condensed", "Upright", "Normal"]),
+            ({"wdth": 100}, ["Thin", "Regular", "Black", "Upright", "Normal"]),
+        ],
+    )
+    def test_pin_and_drop_axis(self, varfont, location, expected):
+        instancer.instantiateSTAT(varfont, location)
+
+        stat = varfont["STAT"].table
+        designAxes = {a.AxisTag for a in stat.DesignAxisRecord.Axis}
+
+        assert designAxes == {"wght", "wdth", "ital"}
+
+        name = varfont["name"]
+        valueNames = []
+        for axisValueTable in stat.AxisValueArray.AxisValue:
+            valueName = name.getDebugName(axisValueTable.ValueNameID)
+            valueNames.append(valueName)
+
+        assert valueNames == expected
+
+    def test_skip_table_no_axis_value_array(self, varfont):
+        varfont["STAT"].table.AxisValueArray = None
+
+        instancer.instantiateSTAT(varfont, {"wght": 100})
+
+        assert len(varfont["STAT"].table.DesignAxisRecord.Axis) == 3
+        assert varfont["STAT"].table.AxisValueArray is None
+
+    def test_skip_table_axis_value_array_empty(self, varfont):
+        varfont["STAT"].table.AxisValueArray.AxisValue = []
+
+        instancer.instantiateSTAT(varfont, {"wght": 100})
+
+        assert len(varfont["STAT"].table.DesignAxisRecord.Axis) == 3
+        assert not varfont["STAT"].table.AxisValueArray.AxisValue
+
+    def test_skip_table_no_design_axes(self, varfont):
+        stat = otTables.STAT()
+        stat.Version = 0x00010001
+        stat.populateDefaults()
+        assert not stat.DesignAxisRecord
+        assert not stat.AxisValueArray
+        varfont["STAT"].table = stat
+
+        instancer.instantiateSTAT(varfont, {"wght": 100})
+
+        assert not varfont["STAT"].table.DesignAxisRecord
+
+    @staticmethod
+    def get_STAT_axis_values(stat):
+        axes = stat.DesignAxisRecord.Axis
+        result = []
+        for axisValue in stat.AxisValueArray.AxisValue:
+            if axisValue.Format == 1:
+                result.append((axes[axisValue.AxisIndex].AxisTag, axisValue.Value))
+            elif axisValue.Format == 3:
+                result.append(
+                    (
+                        axes[axisValue.AxisIndex].AxisTag,
+                        (axisValue.Value, axisValue.LinkedValue),
+                    )
+                )
+            elif axisValue.Format == 2:
+                result.append(
+                    (
+                        axes[axisValue.AxisIndex].AxisTag,
+                        (
+                            axisValue.RangeMinValue,
+                            axisValue.NominalValue,
+                            axisValue.RangeMaxValue,
+                        ),
+                    )
+                )
+            elif axisValue.Format == 4:
+                result.append(
+                    tuple(
+                        (axes[rec.AxisIndex].AxisTag, rec.Value)
+                        for rec in axisValue.AxisValueRecord
+                    )
+                )
+            else:
+                raise AssertionError(axisValue.Format)
+        return result
+
+    def test_limit_axes(self, varfont2):
+        instancer.instantiateSTAT(varfont2, {"wght": (400, 500), "wdth": (75, 100)})
+
+        assert len(varfont2["STAT"].table.AxisValueArray.AxisValue) == 5
+        assert self.get_STAT_axis_values(varfont2["STAT"].table) == [
+            ("wght", (400.0, 700.0)),
+            ("wght", 500.0),
+            ("wdth", (93.75, 100.0, 100.0)),
+            ("wdth", (81.25, 87.5, 93.75)),
+            ("wdth", (68.75, 75.0, 81.25)),
+        ]
+
+    def test_limit_axis_value_format_4(self, varfont2):
+        stat = varfont2["STAT"].table
+
+        axisValue = otTables.AxisValue()
+        axisValue.Format = 4
+        axisValue.AxisValueRecord = []
+        for tag, value in (("wght", 575), ("wdth", 90)):
+            rec = otTables.AxisValueRecord()
+            rec.AxisIndex = next(
+                i for i, a in enumerate(stat.DesignAxisRecord.Axis) if a.AxisTag == tag
+            )
+            rec.Value = value
+            axisValue.AxisValueRecord.append(rec)
+        stat.AxisValueArray.AxisValue.append(axisValue)
+
+        instancer.instantiateSTAT(varfont2, {"wght": (100, 600)})
+
+        assert axisValue in varfont2["STAT"].table.AxisValueArray.AxisValue
+
+        instancer.instantiateSTAT(varfont2, {"wdth": (62.5, 87.5)})
+
+        assert axisValue not in varfont2["STAT"].table.AxisValueArray.AxisValue
+
+    def test_unknown_axis_value_format(self, varfont2, caplog):
+        stat = varfont2["STAT"].table
+        axisValue = otTables.AxisValue()
+        axisValue.Format = 5
+        stat.AxisValueArray.AxisValue.append(axisValue)
+
+        with caplog.at_level(logging.WARNING, logger="fontTools.varLib.instancer"):
+            instancer.instantiateSTAT(varfont2, {"wght": 400})
+
+        assert "Unknown AxisValue table format (5)" in caplog.text
+        assert axisValue in varfont2["STAT"].table.AxisValueArray.AxisValue
+
+
+def test_setMacOverlapFlags():
+    flagOverlapCompound = _g_l_y_f.OVERLAP_COMPOUND
+    flagOverlapSimple = _g_l_y_f.flagOverlapSimple
+
+    glyf = ttLib.newTable("glyf")
+    glyf.glyphOrder = ["a", "b", "c"]
+    a = _g_l_y_f.Glyph()
+    a.numberOfContours = 1
+    a.flags = [0]
+    b = _g_l_y_f.Glyph()
+    b.numberOfContours = -1
+    comp = _g_l_y_f.GlyphComponent()
+    comp.flags = 0
+    b.components = [comp]
+    c = _g_l_y_f.Glyph()
+    c.numberOfContours = 0
+    glyf.glyphs = {"a": a, "b": b, "c": c}
+
+    instancer.setMacOverlapFlags(glyf)
+
+    assert a.flags[0] & flagOverlapSimple != 0
+    assert b.components[0].flags & flagOverlapCompound != 0
+
+
+def _strip_ttLibVersion(string):
+    return re.sub(' ttLibVersion=".*"', "", string)
+
+
+@pytest.fixture
+def varfont2():
+    f = ttLib.TTFont(recalcTimestamp=False)
+    f.importXML(os.path.join(TESTDATA, "PartialInstancerTest2-VF.ttx"))
+    return f
+
+
+@pytest.fixture
+def varfont3():
+    f = ttLib.TTFont(recalcTimestamp=False)
+    f.importXML(os.path.join(TESTDATA, "PartialInstancerTest3-VF.ttx"))
+    return f
+
+
+def _dump_ttx(ttFont):
+    # compile to temporary bytes stream, reload and dump to XML
+    tmp = BytesIO()
+    ttFont.save(tmp)
+    tmp.seek(0)
+    ttFont2 = ttLib.TTFont(tmp, recalcBBoxes=False, recalcTimestamp=False)
+    s = StringIO()
+    ttFont2.saveXML(s)
+    return _strip_ttLibVersion(s.getvalue())
+
+
+def _get_expected_instance_ttx(
+    name, *locations, overlap=instancer.OverlapMode.KEEP_AND_SET_FLAGS
+):
+    filename = f"{name}-VF-instance-{','.join(str(loc) for loc in locations)}"
+    if overlap == instancer.OverlapMode.KEEP_AND_DONT_SET_FLAGS:
+        filename += "-no-overlap-flags"
+    elif overlap == instancer.OverlapMode.REMOVE:
+        filename += "-no-overlaps"
+    with open(
+        os.path.join(TESTDATA, "test_results", f"{filename}.ttx"),
+        "r",
+        encoding="utf-8",
+    ) as fp:
+        return _strip_ttLibVersion(fp.read())
+
+
+class InstantiateVariableFontTest(object):
+    @pytest.mark.parametrize(
+        "wght, wdth",
+        [(100, 100), (400, 100), (900, 100), (100, 62.5), (400, 62.5), (900, 62.5)],
+    )
+    def test_multiple_instancing(self, varfont2, wght, wdth):
+        partial = instancer.instantiateVariableFont(varfont2, {"wght": wght})
+        instance = instancer.instantiateVariableFont(partial, {"wdth": wdth})
+
+        expected = _get_expected_instance_ttx("PartialInstancerTest2", wght, wdth)
+
+        assert _dump_ttx(instance) == expected
+
+    def test_default_instance(self, varfont2):
+        instance = instancer.instantiateVariableFont(
+            varfont2, {"wght": None, "wdth": None}
+        )
+
+        expected = _get_expected_instance_ttx("PartialInstancerTest2", 400, 100)
+
+        assert _dump_ttx(instance) == expected
+
+    @pytest.mark.parametrize(
+        "overlap, wght",
+        [
+            (instancer.OverlapMode.KEEP_AND_DONT_SET_FLAGS, 400),
+            (instancer.OverlapMode.REMOVE, 400),
+            (instancer.OverlapMode.REMOVE, 700),
+        ],
+    )
+    def test_overlap(self, varfont3, wght, overlap):
+        pytest.importorskip("pathops")
+
+        location = {"wght": wght}
+
+        instance = instancer.instantiateVariableFont(
+            varfont3, location, overlap=overlap
+        )
+
+        expected = _get_expected_instance_ttx(
+            "PartialInstancerTest3", wght, overlap=overlap
+        )
+
+        assert _dump_ttx(instance) == expected
+
+
+def _conditionSetAsDict(conditionSet, axisOrder):
+    result = {}
+    for cond in conditionSet.ConditionTable:
+        assert cond.Format == 1
+        axisTag = axisOrder[cond.AxisIndex]
+        result[axisTag] = (cond.FilterRangeMinValue, cond.FilterRangeMaxValue)
+    return result
+
+
+def _getSubstitutions(gsub, lookupIndices):
+    subs = {}
+    for index, lookup in enumerate(gsub.LookupList.Lookup):
+        if index in lookupIndices:
+            for subtable in lookup.SubTable:
+                subs.update(subtable.mapping)
+    return subs
+
+
+def makeFeatureVarsFont(conditionalSubstitutions):
+    axes = set()
+    glyphs = set()
+    for region, substitutions in conditionalSubstitutions:
+        for box in region:
+            axes.update(box.keys())
+        glyphs.update(*substitutions.items())
+
+    varfont = ttLib.TTFont()
+    varfont.setGlyphOrder(sorted(glyphs))
+
+    fvar = varfont["fvar"] = ttLib.newTable("fvar")
+    fvar.axes = []
+    for axisTag in sorted(axes):
+        axis = _f_v_a_r.Axis()
+        axis.axisTag = Tag(axisTag)
+        fvar.axes.append(axis)
+
+    featureVars.addFeatureVariations(varfont, conditionalSubstitutions)
+
+    return varfont
+
+
+class InstantiateFeatureVariationsTest(object):
+    @pytest.mark.parametrize(
+        "location, appliedSubs, expectedRecords",
+        [
+            ({"wght": 0}, {}, [({"cntr": (0.75, 1.0)}, {"uni0041": "uni0061"})]),
+            (
+                {"wght": -1.0},
+                {},
+                [
+                    ({"cntr": (0, 0.25)}, {"uni0061": "uni0041"}),
+                    ({"cntr": (0.75, 1.0)}, {"uni0041": "uni0061"}),
+                ],
+            ),
+            (
+                {"wght": 1.0},
+                {"uni0024": "uni0024.nostroke"},
+                [
+                    (
+                        {"cntr": (0.75, 1.0)},
+                        {"uni0024": "uni0024.nostroke", "uni0041": "uni0061"},
+                    )
+                ],
+            ),
+            (
+                {"cntr": 0},
+                {},
+                [
+                    ({"wght": (-1.0, -0.45654)}, {"uni0061": "uni0041"}),
+                    ({"wght": (0.20886, 1.0)}, {"uni0024": "uni0024.nostroke"}),
+                ],
+            ),
+            (
+                {"cntr": 1.0},
+                {"uni0041": "uni0061"},
+                [
+                    (
+                        {"wght": (0.20886, 1.0)},
+                        {"uni0024": "uni0024.nostroke", "uni0041": "uni0061"},
+                    )
+                ],
+            ),
+        ],
+    )
+    def test_partial_instance(self, location, appliedSubs, expectedRecords):
+        font = makeFeatureVarsFont(
+            [
+                ([{"wght": (0.20886, 1.0)}], {"uni0024": "uni0024.nostroke"}),
+                ([{"cntr": (0.75, 1.0)}], {"uni0041": "uni0061"}),
+                (
+                    [{"wght": (-1.0, -0.45654), "cntr": (0, 0.25)}],
+                    {"uni0061": "uni0041"},
+                ),
+            ]
+        )
+
+        instancer.instantiateFeatureVariations(font, location)
+
+        gsub = font["GSUB"].table
+        featureVariations = gsub.FeatureVariations
+
+        assert featureVariations.FeatureVariationCount == len(expectedRecords)
+
+        axisOrder = [a.axisTag for a in font["fvar"].axes if a.axisTag not in location]
+        for i, (expectedConditionSet, expectedSubs) in enumerate(expectedRecords):
+            rec = featureVariations.FeatureVariationRecord[i]
+            conditionSet = _conditionSetAsDict(rec.ConditionSet, axisOrder)
+
+            assert conditionSet == expectedConditionSet
+
+            subsRecord = rec.FeatureTableSubstitution.SubstitutionRecord[0]
+            lookupIndices = subsRecord.Feature.LookupListIndex
+            substitutions = _getSubstitutions(gsub, lookupIndices)
+
+            assert substitutions == expectedSubs
+
+        appliedLookupIndices = gsub.FeatureList.FeatureRecord[0].Feature.LookupListIndex
+
+        assert _getSubstitutions(gsub, appliedLookupIndices) == appliedSubs
+
+    @pytest.mark.parametrize(
+        "location, appliedSubs",
+        [
+            ({"wght": 0, "cntr": 0}, None),
+            ({"wght": -1.0, "cntr": 0}, {"uni0061": "uni0041"}),
+            ({"wght": 1.0, "cntr": 0}, {"uni0024": "uni0024.nostroke"}),
+            ({"wght": 0.0, "cntr": 1.0}, {"uni0041": "uni0061"}),
+            (
+                {"wght": 1.0, "cntr": 1.0},
+                {"uni0041": "uni0061", "uni0024": "uni0024.nostroke"},
+            ),
+            ({"wght": -1.0, "cntr": 0.3}, None),
+        ],
+    )
+    def test_full_instance(self, location, appliedSubs):
+        font = makeFeatureVarsFont(
+            [
+                ([{"wght": (0.20886, 1.0)}], {"uni0024": "uni0024.nostroke"}),
+                ([{"cntr": (0.75, 1.0)}], {"uni0041": "uni0061"}),
+                (
+                    [{"wght": (-1.0, -0.45654), "cntr": (0, 0.25)}],
+                    {"uni0061": "uni0041"},
+                ),
+            ]
+        )
+
+        instancer.instantiateFeatureVariations(font, location)
+
+        gsub = font["GSUB"].table
+        assert not hasattr(gsub, "FeatureVariations")
+
+        if appliedSubs:
+            lookupIndices = gsub.FeatureList.FeatureRecord[0].Feature.LookupListIndex
+            assert _getSubstitutions(gsub, lookupIndices) == appliedSubs
+        else:
+            assert not gsub.FeatureList.FeatureRecord
+
+    def test_unsupported_condition_format(self, caplog):
+        font = makeFeatureVarsFont(
+            [
+                (
+                    [{"wdth": (-1.0, -0.5), "wght": (0.5, 1.0)}],
+                    {"dollar": "dollar.nostroke"},
+                )
+            ]
+        )
+        featureVariations = font["GSUB"].table.FeatureVariations
+        rec1 = featureVariations.FeatureVariationRecord[0]
+        assert len(rec1.ConditionSet.ConditionTable) == 2
+        rec1.ConditionSet.ConditionTable[0].Format = 2
+
+        with caplog.at_level(logging.WARNING, logger="fontTools.varLib.instancer"):
+            instancer.instantiateFeatureVariations(font, {"wdth": 0})
+
+        assert (
+            "Condition table 0 of FeatureVariationRecord 0 "
+            "has unsupported format (2); ignored"
+        ) in caplog.text
+
+        # check that record with unsupported condition format (but whose other
+        # conditions do not reference pinned axes) is kept as is
+        featureVariations = font["GSUB"].table.FeatureVariations
+        assert featureVariations.FeatureVariationRecord[0] is rec1
+        assert len(rec1.ConditionSet.ConditionTable) == 2
+        assert rec1.ConditionSet.ConditionTable[0].Format == 2
+
+    def test_GSUB_FeatureVariations_is_None(self, varfont2):
+        varfont2["GSUB"].table.Version = 0x00010001
+        varfont2["GSUB"].table.FeatureVariations = None
+        tmp = BytesIO()
+        varfont2.save(tmp)
+        varfont = ttLib.TTFont(tmp)
+
+        # DO NOT raise an exception when the optional 'FeatureVariations' attribute is
+        # present but is set to None (e.g. with GSUB 1.1); skip and do nothing.
+        assert varfont["GSUB"].table.FeatureVariations is None
+        instancer.instantiateFeatureVariations(varfont, {"wght": 400, "wdth": 100})
+        assert varfont["GSUB"].table.FeatureVariations is None
+
+
+class LimitTupleVariationAxisRangesTest:
+    def check_limit_single_var_axis_range(self, var, axisTag, axisRange, expected):
+        result = instancer.limitTupleVariationAxisRange(var, axisTag, axisRange)
+        print(result)
+
+        assert len(result) == len(expected)
+        for v1, v2 in zip(result, expected):
+            assert v1.coordinates == pytest.approx(v2.coordinates)
+            assert v1.axes.keys() == v2.axes.keys()
+            for k in v1.axes:
+                p, q = v1.axes[k], v2.axes[k]
+                assert p == pytest.approx(q)
+
+    @pytest.mark.parametrize(
+        "var, axisTag, newMax, expected",
+        [
+            (
+                TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100]),
+                "wdth",
+                0.5,
+                [TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100])],
+            ),
+            (
+                TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100]),
+                "wght",
+                0.5,
+                [TupleVariation({"wght": (0.0, 1.0, 1.0)}, [50, 50])],
+            ),
+            (
+                TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100]),
+                "wght",
+                0.8,
+                [TupleVariation({"wght": (0.0, 1.0, 1.0)}, [80, 80])],
+            ),
+            (
+                TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100]),
+                "wght",
+                1.0,
+                [TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100])],
+            ),
+            (TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100]), "wght", 0.0, []),
+            (TupleVariation({"wght": (0.5, 1.0, 1.0)}, [100, 100]), "wght", 0.4, []),
+            (
+                TupleVariation({"wght": (0.0, 0.5, 1.0)}, [100, 100]),
+                "wght",
+                0.5,
+                [TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100])],
+            ),
+            (
+                TupleVariation({"wght": (0.0, 0.5, 1.0)}, [100, 100]),
+                "wght",
+                0.4,
+                [TupleVariation({"wght": (0.0, 1.0, 1.0)}, [80, 80])],
+            ),
+            (
+                TupleVariation({"wght": (0.0, 0.5, 1.0)}, [100, 100]),
+                "wght",
+                0.6,
+                [TupleVariation({"wght": (0.0, 0.833334, 1.666667)}, [100, 100])],
+            ),
+            (
+                TupleVariation({"wght": (0.0, 0.2, 1.0)}, [100, 100]),
+                "wght",
+                0.4,
+                [
+                    TupleVariation({"wght": (0.0, 0.5, 1.99994)}, [100, 100]),
+                    TupleVariation({"wght": (0.5, 1.0, 1.0)}, [8.33333, 8.33333]),
+                ],
+            ),
+            (
+                TupleVariation({"wght": (0.0, 0.2, 1.0)}, [100, 100]),
+                "wght",
+                0.5,
+                [TupleVariation({"wght": (0.0, 0.4, 1.99994)}, [100, 100])],
+            ),
+            (
+                TupleVariation({"wght": (0.5, 0.5, 1.0)}, [100, 100]),
+                "wght",
+                0.5,
+                [TupleVariation({"wght": (1.0, 1.0, 1.0)}, [100, 100])],
+            ),
+        ],
+    )
+    def test_positive_var(self, var, axisTag, newMax, expected):
+        axisRange = instancer.NormalizedAxisRange(0, newMax)
+        self.check_limit_single_var_axis_range(var, axisTag, axisRange, expected)
+
+    @pytest.mark.parametrize(
+        "var, axisTag, newMin, expected",
+        [
+            (
+                TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100]),
+                "wdth",
+                -0.5,
+                [TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100])],
+            ),
+            (
+                TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100]),
+                "wght",
+                -0.5,
+                [TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [50, 50])],
+            ),
+            (
+                TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100]),
+                "wght",
+                -0.8,
+                [TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [80, 80])],
+            ),
+            (
+                TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100]),
+                "wght",
+                -1.0,
+                [TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100])],
+            ),
+            (TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100]), "wght", 0.0, []),
+            (
+                TupleVariation({"wght": (-1.0, -1.0, -0.5)}, [100, 100]),
+                "wght",
+                -0.4,
+                [],
+            ),
+            (
+                TupleVariation({"wght": (-1.0, -0.5, 0.0)}, [100, 100]),
+                "wght",
+                -0.5,
+                [TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100])],
+            ),
+            (
+                TupleVariation({"wght": (-1.0, -0.5, 0.0)}, [100, 100]),
+                "wght",
+                -0.4,
+                [TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [80, 80])],
+            ),
+            (
+                TupleVariation({"wght": (-1.0, -0.5, 0.0)}, [100, 100]),
+                "wght",
+                -0.6,
+                [TupleVariation({"wght": (-1.666667, -0.833334, 0.0)}, [100, 100])],
+            ),
+            (
+                TupleVariation({"wght": (-1.0, -0.2, 0.0)}, [100, 100]),
+                "wght",
+                -0.4,
+                [
+                    TupleVariation({"wght": (-2.0, -0.5, -0.0)}, [100, 100]),
+                    TupleVariation({"wght": (-1.0, -1.0, -0.5)}, [8.33333, 8.33333]),
+                ],
+            ),
+            (
+                TupleVariation({"wght": (-1.0, -0.2, 0.0)}, [100, 100]),
+                "wght",
+                -0.5,
+                [TupleVariation({"wght": (-2.0, -0.4, 0.0)}, [100, 100])],
+            ),
+            (
+                TupleVariation({"wght": (-1.0, -0.5, -0.5)}, [100, 100]),
+                "wght",
+                -0.5,
+                [TupleVariation({"wght": (-1.0, -1.0, -1.0)}, [100, 100])],
+            ),
+        ],
+    )
+    def test_negative_var(self, var, axisTag, newMin, expected):
+        axisRange = instancer.NormalizedAxisRange(newMin, 0)
+        self.check_limit_single_var_axis_range(var, axisTag, axisRange, expected)
+
+
+@pytest.mark.parametrize(
+    "oldRange, newRange, expected",
+    [
+        ((1.0, -1.0), (-1.0, 1.0), None),  # invalid oldRange min > max
+        ((0.6, 1.0), (0, 0.5), None),
+        ((-1.0, -0.6), (-0.5, 0), None),
+        ((0.4, 1.0), (0, 0.5), (0.8, 1.0)),
+        ((-1.0, -0.4), (-0.5, 0), (-1.0, -0.8)),
+        ((0.4, 1.0), (0, 0.4), (1.0, 1.0)),
+        ((-1.0, -0.4), (-0.4, 0), (-1.0, -1.0)),
+        ((-0.5, 0.5), (-0.4, 0.4), (-1.0, 1.0)),
+        ((0, 1.0), (-1.0, 0), (0, 0)),  # or None?
+        ((-1.0, 0), (0, 1.0), (0, 0)),  # or None?
+    ],
+)
+def test_limitFeatureVariationConditionRange(oldRange, newRange, expected):
+    condition = featureVars.buildConditionTable(0, *oldRange)
+
+    result = instancer._limitFeatureVariationConditionRange(
+        condition, instancer.NormalizedAxisRange(*newRange)
+    )
+
+    assert result == expected
+
+
+@pytest.mark.parametrize(
+    "limits, expected",
+    [
+        (["wght=400", "wdth=100"], {"wght": 400, "wdth": 100}),
+        (["wght=400:900"], {"wght": (400, 900)}),
+        (["slnt=11.4"], {"slnt": pytest.approx(11.399994)}),
+        (["ABCD=drop"], {"ABCD": None}),
+    ],
+)
+def test_parseLimits(limits, expected):
+    assert instancer.parseLimits(limits) == expected
+
+
+@pytest.mark.parametrize(
+    "limits", [["abcde=123", "=0", "wght=:", "wght=1:", "wght=abcd", "wght=x:y"]]
+)
+def test_parseLimits_invalid(limits):
+    with pytest.raises(ValueError, match="invalid location format"):
+        instancer.parseLimits(limits)
+
+
+def test_normalizeAxisLimits_tuple(varfont):
+    normalized = instancer.normalizeAxisLimits(varfont, {"wght": (100, 400)})
+    assert normalized == {"wght": (-1.0, 0)}
+
+
+def test_normalizeAxisLimits_unsupported_range(varfont):
+    with pytest.raises(NotImplementedError, match="Unsupported range"):
+        instancer.normalizeAxisLimits(varfont, {"wght": (401, 700)})
+
+
+def test_normalizeAxisLimits_no_avar(varfont):
+    del varfont["avar"]
+
+    normalized = instancer.normalizeAxisLimits(varfont, {"wght": (400, 500)})
+
+    assert normalized["wght"] == pytest.approx((0, 0.2), 1e-4)
+
+
+def test_normalizeAxisLimits_missing_from_fvar(varfont):
+    with pytest.raises(ValueError, match="not present in fvar"):
+        instancer.normalizeAxisLimits(varfont, {"ZZZZ": 1000})
+
+
+def test_sanityCheckVariableTables(varfont):
+    font = ttLib.TTFont()
+    with pytest.raises(ValueError, match="Missing required table fvar"):
+        instancer.sanityCheckVariableTables(font)
+
+    del varfont["glyf"]
+
+    with pytest.raises(ValueError, match="Can't have gvar without glyf"):
+        instancer.sanityCheckVariableTables(varfont)
+
+
+def test_main(varfont, tmpdir):
+    fontfile = str(tmpdir / "PartialInstancerTest-VF.ttf")
+    varfont.save(fontfile)
+    args = [fontfile, "wght=400"]
+
+    # exits without errors
+    assert instancer.main(args) is None
+
+
+def test_main_exit_nonexistent_file(capsys):
+    with pytest.raises(SystemExit):
+        instancer.main([""])
+    captured = capsys.readouterr()
+
+    assert "No such file ''" in captured.err
+
+
+def test_main_exit_invalid_location(varfont, tmpdir, capsys):
+    fontfile = str(tmpdir / "PartialInstancerTest-VF.ttf")
+    varfont.save(fontfile)
+
+    with pytest.raises(SystemExit):
+        instancer.main([fontfile, "wght:100"])
+    captured = capsys.readouterr()
+
+    assert "invalid location format" in captured.err
+
+
+def test_main_exit_multiple_limits(varfont, tmpdir, capsys):
+    fontfile = str(tmpdir / "PartialInstancerTest-VF.ttf")
+    varfont.save(fontfile)
+
+    with pytest.raises(SystemExit):
+        instancer.main([fontfile, "wght=400", "wght=90"])
+    captured = capsys.readouterr()
+
+    assert "Specified multiple limits for the same axis" in captured.err
diff --git a/Tests/varLib/instancer/names_test.py b/Tests/varLib/instancer/names_test.py
new file mode 100644
index 0000000..9774458
--- /dev/null
+++ b/Tests/varLib/instancer/names_test.py
@@ -0,0 +1,322 @@
+from fontTools.ttLib.tables import otTables
+from fontTools.otlLib.builder import buildStatTable
+from fontTools.varLib import instancer
+
+import pytest
+
+
+def test_pruningUnusedNames(varfont):
+    varNameIDs = instancer.names.getVariationNameIDs(varfont)
+
+    assert varNameIDs == set(range(256, 297 + 1))
+
+    fvar = varfont["fvar"]
+    stat = varfont["STAT"].table
+
+    with instancer.names.pruningUnusedNames(varfont):
+        del fvar.axes[0]  # Weight (nameID=256)
+        del fvar.instances[0]  # Thin (nameID=258)
+        del stat.DesignAxisRecord.Axis[0]  # Weight (nameID=256)
+        del stat.AxisValueArray.AxisValue[0]  # Thin (nameID=258)
+
+    assert not any(n for n in varfont["name"].names if n.nameID in {256, 258})
+
+    with instancer.names.pruningUnusedNames(varfont):
+        del varfont["fvar"]
+        del varfont["STAT"]
+
+    assert not any(n for n in varfont["name"].names if n.nameID in varNameIDs)
+    assert "ltag" not in varfont
+
+
+def _test_name_records(varfont, expected, isNonRIBBI, platforms=[0x409]):
+    nametable = varfont["name"]
+    font_names = {
+        (r.nameID, r.platformID, r.platEncID, r.langID): r.toUnicode()
+        for r in nametable.names
+    }
+    for k in expected:
+        if k[-1] not in platforms:
+            continue
+        assert font_names[k] == expected[k]
+
+    font_nameids = set(i[0] for i in font_names)
+    if isNonRIBBI:
+        assert 16 in font_nameids
+        assert 17 in font_nameids
+
+    if "fvar" not in varfont:
+        assert 25 not in font_nameids
+
+
+@pytest.mark.parametrize(
+    "limits, expected, isNonRIBBI",
+    [
+        # Regular
+        (
+            {"wght": 400},
+            {
+                (1, 3, 1, 0x409): "Test Variable Font",
+                (2, 3, 1, 0x409): "Regular",
+                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Regular",
+                (6, 3, 1, 0x409): "TestVariableFont-Regular",
+            },
+            False,
+        ),
+        # Regular Normal (width axis Normal isn't included since it is elided)
+        (
+            {"wght": 400, "wdth": 100},
+            {
+                (1, 3, 1, 0x409): "Test Variable Font",
+                (2, 3, 1, 0x409): "Regular",
+                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Regular",
+                (6, 3, 1, 0x409): "TestVariableFont-Regular",
+            },
+            False,
+        ),
+        # Black
+        (
+            {"wght": 900},
+            {
+                (1, 3, 1, 0x409): "Test Variable Font Black",
+                (2, 3, 1, 0x409): "Regular",
+                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Black",
+                (6, 3, 1, 0x409): "TestVariableFont-Black",
+                (16, 3, 1, 0x409): "Test Variable Font",
+                (17, 3, 1, 0x409): "Black",
+            },
+            True,
+        ),
+        # Thin
+        (
+            {"wght": 100},
+            {
+                (1, 3, 1, 0x409): "Test Variable Font Thin",
+                (2, 3, 1, 0x409): "Regular",
+                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Thin",
+                (6, 3, 1, 0x409): "TestVariableFont-Thin",
+                (16, 3, 1, 0x409): "Test Variable Font",
+                (17, 3, 1, 0x409): "Thin",
+            },
+            True,
+        ),
+        # Thin Condensed
+        (
+            {"wght": 100, "wdth": 79},
+            {
+                (1, 3, 1, 0x409): "Test Variable Font Thin Condensed",
+                (2, 3, 1, 0x409): "Regular",
+                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-ThinCondensed",
+                (6, 3, 1, 0x409): "TestVariableFont-ThinCondensed",
+                (16, 3, 1, 0x409): "Test Variable Font",
+                (17, 3, 1, 0x409): "Thin Condensed",
+            },
+            True,
+        ),
+        # Condensed with unpinned weights
+        (
+            {"wdth": 79, "wght": instancer.AxisRange(400, 900)},
+            {
+                (1, 3, 1, 0x409): "Test Variable Font Condensed",
+                (2, 3, 1, 0x409): "Regular",
+                (3, 3, 1, 0x409): "2.001;GOOG;TestVariableFont-Condensed",
+                (6, 3, 1, 0x409): "TestVariableFont-Condensed",
+                (16, 3, 1, 0x409): "Test Variable Font",
+                (17, 3, 1, 0x409): "Condensed",
+            },
+            True,
+        ),
+    ],
+)
+def test_updateNameTable_with_registered_axes_ribbi(
+    varfont, limits, expected, isNonRIBBI
+):
+    instancer.names.updateNameTable(varfont, limits)
+    _test_name_records(varfont, expected, isNonRIBBI)
+
+
+def test_updatetNameTable_axis_order(varfont):
+    axes = [
+        dict(
+            tag="wght",
+            name="Weight",
+            values=[
+                dict(value=400, name="Regular"),
+            ],
+        ),
+        dict(
+            tag="wdth",
+            name="Width",
+            values=[
+                dict(value=75, name="Condensed"),
+            ],
+        ),
+    ]
+    nametable = varfont["name"]
+    buildStatTable(varfont, axes)
+    instancer.names.updateNameTable(varfont, {"wdth": 75, "wght": 400})
+    assert nametable.getName(17, 3, 1, 0x409).toUnicode() == "Regular Condensed"
+
+    # Swap the axes so the names get swapped
+    axes[0], axes[1] = axes[1], axes[0]
+
+    buildStatTable(varfont, axes)
+    instancer.names.updateNameTable(varfont, {"wdth": 75, "wght": 400})
+    assert nametable.getName(17, 3, 1, 0x409).toUnicode() == "Condensed Regular"
+
+
+@pytest.mark.parametrize(
+    "limits, expected, isNonRIBBI",
+    [
+        # Regular | Normal
+        (
+            {"wght": 400},
+            {
+                (1, 3, 1, 0x409): "Test Variable Font",
+                (2, 3, 1, 0x409): "Normal",
+            },
+            False,
+        ),
+        # Black | Negreta
+        (
+            {"wght": 900},
+            {
+                (1, 3, 1, 0x409): "Test Variable Font Negreta",
+                (2, 3, 1, 0x409): "Normal",
+                (16, 3, 1, 0x409): "Test Variable Font",
+                (17, 3, 1, 0x409): "Negreta",
+            },
+            True,
+        ),
+        # Black Condensed | Negreta Zhuštěné
+        (
+            {"wght": 900, "wdth": 79},
+            {
+                (1, 3, 1, 0x409): "Test Variable Font Negreta Zhuštěné",
+                (2, 3, 1, 0x409): "Normal",
+                (16, 3, 1, 0x409): "Test Variable Font",
+                (17, 3, 1, 0x409): "Negreta Zhuštěné",
+            },
+            True,
+        ),
+    ],
+)
+def test_updateNameTable_with_multilingual_names(varfont, limits, expected, isNonRIBBI):
+    name = varfont["name"]
+    # langID 0x405 is the Czech Windows langID
+    name.setName("Test Variable Font", 1, 3, 1, 0x405)
+    name.setName("Normal", 2, 3, 1, 0x405)
+    name.setName("Normal", 261, 3, 1, 0x405)  # nameID 261=Regular STAT entry
+    name.setName("Negreta", 266, 3, 1, 0x405)  # nameID 266=Black STAT entry
+    name.setName("Zhuštěné", 279, 3, 1, 0x405)  # nameID 279=Condensed STAT entry
+
+    instancer.names.updateNameTable(varfont, limits)
+    _test_name_records(varfont, expected, isNonRIBBI, platforms=[0x405])
+
+
+def test_updateNameTable_missing_axisValues(varfont):
+    with pytest.raises(ValueError, match="Cannot find Axis Values \['wght=200'\]"):
+        instancer.names.updateNameTable(varfont, {"wght": 200})
+
+
+def test_updateNameTable_missing_stat(varfont):
+    del varfont["STAT"]
+    with pytest.raises(
+        ValueError, match="Cannot update name table since there is no STAT table."
+    ):
+        instancer.names.updateNameTable(varfont, {"wght": 400})
+
+
+@pytest.mark.parametrize(
+    "limits, expected, isNonRIBBI",
+    [
+        # Regular | Normal
+        (
+            {"wght": 400},
+            {
+                (1, 3, 1, 0x409): "Test Variable Font",
+                (2, 3, 1, 0x409): "Italic",
+                (6, 3, 1, 0x409): "TestVariableFont-Italic",
+            },
+            False,
+        ),
+        # Black Condensed Italic
+        (
+            {"wght": 900, "wdth": 79},
+            {
+                (1, 3, 1, 0x409): "Test Variable Font Black Condensed",
+                (2, 3, 1, 0x409): "Italic",
+                (6, 3, 1, 0x409): "TestVariableFont-BlackCondensedItalic",
+                (16, 3, 1, 0x409): "Test Variable Font",
+                (17, 3, 1, 0x409): "Black Condensed Italic",
+            },
+            True,
+        ),
+    ],
+)
+def test_updateNameTable_vf_with_italic_attribute(
+    varfont, limits, expected, isNonRIBBI
+):
+    font_link_axisValue = varfont["STAT"].table.AxisValueArray.AxisValue[4]
+    # Unset ELIDABLE_AXIS_VALUE_NAME flag
+    font_link_axisValue.Flags &= ~instancer.names.ELIDABLE_AXIS_VALUE_NAME
+    font_link_axisValue.ValueNameID = 294  # Roman --> Italic
+
+    instancer.names.updateNameTable(varfont, limits)
+    _test_name_records(varfont, expected, isNonRIBBI)
+
+
+def test_updateNameTable_format4_axisValues(varfont):
+    # format 4 axisValues should dominate the other axisValues
+    stat = varfont["STAT"].table
+
+    axisValue = otTables.AxisValue()
+    axisValue.Format = 4
+    axisValue.Flags = 0
+    varfont["name"].setName("Dominant Value", 297, 3, 1, 0x409)
+    axisValue.ValueNameID = 297
+    axisValue.AxisValueRecord = []
+    for tag, value in (("wght", 900), ("wdth", 79)):
+        rec = otTables.AxisValueRecord()
+        rec.AxisIndex = next(
+            i for i, a in enumerate(stat.DesignAxisRecord.Axis) if a.AxisTag == tag
+        )
+        rec.Value = value
+        axisValue.AxisValueRecord.append(rec)
+    stat.AxisValueArray.AxisValue.append(axisValue)
+
+    instancer.names.updateNameTable(varfont, {"wdth": 79, "wght": 900})
+    expected = {
+        (1, 3, 1, 0x409): "Test Variable Font Dominant Value",
+        (2, 3, 1, 0x409): "Regular",
+        (16, 3, 1, 0x409): "Test Variable Font",
+        (17, 3, 1, 0x409): "Dominant Value",
+    }
+    _test_name_records(varfont, expected, isNonRIBBI=True)
+
+
+def test_updateNameTable_elided_axisValues(varfont):
+    stat = varfont["STAT"].table
+    # set ELIDABLE_AXIS_VALUE_NAME flag for all axisValues
+    for axisValue in stat.AxisValueArray.AxisValue:
+        axisValue.Flags |= instancer.names.ELIDABLE_AXIS_VALUE_NAME
+
+    stat.ElidedFallbackNameID = 266  # Regular --> Black
+    instancer.names.updateNameTable(varfont, {"wght": 400})
+    # Since all axis values are elided, the elided fallback name
+    # must be used to construct the style names. Since we
+    # changed it to Black, we need both a typoSubFamilyName and
+    # the subFamilyName set so it conforms to the RIBBI model.
+    expected = {(2, 3, 1, 0x409): "Regular", (17, 3, 1, 0x409): "Black"}
+    _test_name_records(varfont, expected, isNonRIBBI=True)
+
+
+def test_updateNameTable_existing_subfamily_name_is_not_regular(varfont):
+    # Check the subFamily name will be set to Regular when we update a name
+    # table to a non-RIBBI style and the current subFamily name is a RIBBI
+    # style which isn't Regular.
+    varfont["name"].setName("Bold", 2, 3, 1, 0x409)  # subFamily Regular --> Bold
+
+    instancer.names.updateNameTable(varfont, {"wght": 100})
+    expected = {(2, 3, 1, 0x409): "Regular", (17, 3, 1, 0x409): "Thin"}
+    _test_name_records(varfont, expected, isNonRIBBI=True)
diff --git a/Tests/varLib/interpolatable_test.py b/Tests/varLib/interpolatable_test.py
index 4900d52..a30be71 100644
--- a/Tests/varLib/interpolatable_test.py
+++ b/Tests/varLib/interpolatable_test.py
@@ -1,5 +1,3 @@
-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
diff --git a/Tests/varLib/interpolate_layout_test.py b/Tests/varLib/interpolate_layout_test.py
index 6f6efe0..18dc3a7 100644
--- a/Tests/varLib/interpolate_layout_test.py
+++ b/Tests/varLib/interpolate_layout_test.py
@@ -1,9 +1,8 @@
-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.designspaceLib import DesignSpaceDocument, DesignSpaceDocumentError
 from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
 import difflib
 import os
@@ -62,12 +61,11 @@
         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.
+                # Elide ttFont attributes because ttLibVersion may change.
                 if line.startswith("<ttFont "):
-                    lines.append("<ttFont>" + os.linesep)
+                    lines.append("<ttFont>\n")
                 else:
-                    lines.append(line.rstrip() + os.linesep)
+                    lines.append(line.rstrip() + "\n")
         return lines
 
     def expect_ttx(self, font, expected_ttx, tables):
@@ -157,24 +155,9 @@
         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)
-
+        with self.assertRaisesRegex(DesignSpaceDocumentError, "No axes defined"):
+            instfont = interpolate_layout(ds_path, {'weight': 500})
 
     def test_varlib_interpolate_layout_GPOS_only_size_feat_same_val_ttf(self):
         """Only GPOS; 'size' feature; same values in all masters.
@@ -763,8 +746,8 @@
         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.
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_7_same_val_ttf(self):
+        """Only GPOS; LookupType 7; same values in all masters.
         """
         suffix = '.ttf'
         ds_path = self.get_test_input('InterpolateLayout.designspace')
@@ -796,13 +779,13 @@
         instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
 
         tables = ['GPOS']
-        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_8_same.ttx')
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_7_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.
+    def test_varlib_interpolate_layout_GPOS_only_LookupType_7_diff_val_ttf(self):
+        """Only GPOS; LookupType 7; different values in each master.
         """
         suffix = '.ttf'
         ds_path = self.get_test_input('InterpolateLayout.designspace')
@@ -848,7 +831,7 @@
         instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
 
         tables = ['GPOS']
-        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_8_diff.ttx')
+        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_7_diff.ttx')
         self.expect_ttx(instfont, expected_ttx_path, tables)
         self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
 
diff --git a/Tests/varLib/models_test.py b/Tests/varLib/models_test.py
index 25f1ef0..c220d3d 100644
--- a/Tests/varLib/models_test.py
+++ b/Tests/varLib/models_test.py
@@ -1,7 +1,6 @@
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
 from fontTools.varLib.models import (
-    normalizeLocation, supportScalar, VariationModel)
+    normalizeLocation, supportScalar, VariationModel, VariationModelError)
+import pytest
 
 
 def test_normalizeLocation():
@@ -35,43 +34,121 @@
     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'])
+class VariationModelTest(object):
 
-    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}]
+    @pytest.mark.parametrize(
+        "locations, axisOrder, sortedLocs, supports, deltaWeights",
+        [
+            (
+                [
+                    {'wght': 0.55, 'wdth': 0.0},
+                    {'wght': -0.55, 'wdth': 0.0},
+                    {'wght': -1.0, 'wdth': 0.0},
+                    {'wght': 0.0, 'wdth': 1.0},
+                    {'wght': 0.66, 'wdth': 1.0},
+                    {'wght': 0.66, 'wdth': 0.66},
+                    {'wght': 0.0, 'wdth': 0.0},
+                    {'wght': 1.0, 'wdth': 1.0},
+                    {'wght': 1.0, 'wdth': 0.0},
+                ],
+                ["wght"],
+                [
+                    {},
+                    {'wght': -0.55},
+                    {'wght': -1.0},
+                    {'wght': 0.55},
+                    {'wght': 1.0},
+                    {'wdth': 1.0},
+                    {'wdth': 1.0, 'wght': 1.0},
+                    {'wdth': 1.0, 'wght': 0.66},
+                    {'wdth': 0.66, 'wght': 0.66}
+                ],
+                [
+                    {},
+                    {'wght': (-1.0, -0.55, 0)},
+                    {'wght': (-1.0, -1.0, -0.55)},
+                    {'wght': (0, 0.55, 1.0)},
+                    {'wght': (0.55, 1.0, 1.0)},
+                    {'wdth': (0, 1.0, 1.0)},
+                    {'wdth': (0, 1.0, 1.0), 'wght': (0, 1.0, 1.0)},
+                    {'wdth': (0, 1.0, 1.0), 'wght': (0, 0.66, 1.0)},
+                    {'wdth': (0, 0.66, 1.0), 'wght': (0, 0.66, 1.0)}
+                ],
+                [
+                    {},
+                    {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.7555555555555555,
+                     4: 0.24444444444444444,
+                     5: 1.0,
+                     6: 0.66},
+                    {0: 1.0,
+                     3: 0.7555555555555555,
+                     4: 0.24444444444444444,
+                     5: 0.66,
+                     6: 0.43560000000000006,
+                     7: 0.66}
+                ]
+            ),
+            (
+                [
+                    {},
+                    {'bar': 0.5},
+                    {'bar': 1.0},
+                    {'foo': 1.0},
+                    {'bar': 0.5, 'foo': 1.0},
+                    {'bar': 1.0, 'foo': 1.0},
+                ],
+                None,
+                [
+                    {},
+                    {'bar': 0.5},
+                    {'bar': 1.0},
+                    {'foo': 1.0},
+                    {'bar': 0.5, 'foo': 1.0},
+                    {'bar': 1.0, 'foo': 1.0},
+                ],
+                [
+                    {},
+                    {'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)},
+                ],
+                [
+                    {},
+                    {0: 1.0},
+                    {0: 1.0},
+                    {0: 1.0},
+                    {0: 1.0, 1: 1.0, 3: 1.0},
+                    {0: 1.0, 2: 1.0, 3: 1.0},
+                ],
+            )
+        ]
+    )
+    def test_init(
+        self, locations, axisOrder, sortedLocs, supports, deltaWeights
+    ):
+        model = VariationModel(locations, axisOrder=axisOrder)
 
-    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.25},
-        {0: 1.0,
-         3: 0.75,
-         4: 0.25,
-         5: 0.6666666666666667,
-         6: 0.16666666666666669,
-         7: 0.6666666666666667}]
+        assert model.locations == sortedLocs
+        assert model.supports == supports
+        assert model.deltaWeights == deltaWeights
+
+    def test_init_duplicate_locations(self):
+        with pytest.raises(VariationModelError, match="Locations must be unique."):
+            VariationModel(
+                [
+                    {"foo": 0.0, "bar": 0.0},
+                    {"foo": 1.0, "bar": 1.0},
+                    {"bar": 1.0, "foo": 1.0},
+                ]
+            )
diff --git a/Tests/varLib/mutator_test.py b/Tests/varLib/mutator_test.py
index de794f0..03ad870 100644
--- a/Tests/varLib/mutator_test.py
+++ b/Tests/varLib/mutator_test.py
@@ -1,8 +1,7 @@
-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
+from fontTools.varLib.mutator import instantiateVariableFont as make_instance
 import difflib
 import os
 import shutil
@@ -60,12 +59,11 @@
         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.
+                # Elide ttFont attributes because ttLibVersion may change.
                 if line.startswith("<ttFont "):
-                    lines.append("<ttFont>" + os.linesep)
+                    lines.append("<ttFont>\n")
                 else:
-                    lines.append(line.rstrip() + os.linesep)
+                    lines.append(line.rstrip() + "\n")
         return lines
 
     def expect_ttx(self, font, expected_ttx, tables):
@@ -117,6 +115,27 @@
         expected_ttx_path = self.get_test_output(varfont_name + '.ttx')
         self.expect_ttx(instfont, expected_ttx_path, tables)
 
+    def test_varlib_mutator_getvar_ttf(self):
+        suffix = '.ttf'
+        ttx_dir = self.get_test_input('master_ttx_getvar_ttf')
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'Mutator_Getvar')
+        for path in ttx_paths:
+            self.compile_font(path, suffix, self.tempdir)
+
+        varfont_name = 'Mutator_Getvar'
+        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)
+
     def test_varlib_mutator_iup_ttf(self):
         suffix = '.ttf'
         ufo_dir = self.get_test_input('master_ufo')
@@ -139,6 +158,27 @@
         expected_ttx_path = self.get_test_output(varfont_name + '-instance.ttx')
         self.expect_ttx(instfont, expected_ttx_path, tables)
 
+    def test_varlib_mutator_CFF2(self):
+        suffix = '.otf'
+        ttx_dir = self.get_test_input('master_ttx_varfont_otf')
+
+        self.temp_dir()
+        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestCFF2VF')
+        for path in ttx_paths:
+            self.compile_font(path, suffix, self.tempdir)
+
+        varfont_name = 'TestCFF2VF'
+        varfont_path = os.path.join(self.tempdir, varfont_name + suffix)
+
+        expected_ttx_name = 'InterpolateTestCFF2VF'
+        tables = ["hmtx", "CFF2"]
+        loc = {'wght':float(200)}
+
+        varfont = TTFont(varfont_path)
+        new_font = make_instance(varfont, loc)
+        expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx')
+        self.expect_ttx(new_font, 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
index 4b53c1a..484a2e2 100644
--- a/Tests/varLib/varLib_test.py
+++ b/Tests/varLib/varLib_test.py
@@ -1,14 +1,33 @@
-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
+from fontTools.ttLib import TTFont, newTable
+from fontTools.varLib import build, load_designspace
+from fontTools.varLib.errors import VarLibValidationError
+import fontTools.varLib.errors as varLibErrors
+from fontTools.varLib.mutator import instantiateVariableFont
+from fontTools.varLib import main as varLib_main, load_masters
+from fontTools.varLib import set_default_weight_width_slant
+from fontTools.designspaceLib import (
+    DesignSpaceDocumentError, DesignSpaceDocument, SourceDescriptor,
+)
+from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
 import difflib
+from io import BytesIO
 import os
 import shutil
 import sys
 import tempfile
 import unittest
+import pytest
+
+
+def reload_font(font):
+    """(De)serialize to get final binary layout."""
+    buf = BytesIO()
+    font.save(buf)
+    # Close the font to release filesystem resources so that on Windows the tearDown
+    # method can successfully remove the temporary directory created during setUp.
+    font.close()
+    buf.seek(0)
+    return TTFont(buf)
 
 
 class BuildTest(unittest.TestCase):
@@ -27,10 +46,15 @@
         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)
+    def get_test_input(self, test_file_or_folder, copy=False):
+        parent_dir = os.path.dirname(__file__)
+        path = os.path.join(parent_dir, "data", test_file_or_folder)
+        if copy:
+            copied_path = os.path.join(self.tempdir, test_file_or_folder)
+            shutil.copy2(path, copied_path)
+            return copied_path
+        else:
+            return path
 
     @staticmethod
     def get_test_output(test_file_or_folder):
@@ -60,12 +84,11 @@
         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.
+                # Elide ttFont attributes because ttLibVersion may change.
                 if line.startswith("<ttFont "):
-                    lines.append("<ttFont>" + os.linesep)
+                    lines.append("<ttFont>\n")
                 else:
-                    lines.append(line.rstrip() + os.linesep)
+                    lines.append(line.rstrip() + "\n")
         return lines
 
     def expect_ttx(self, font, expected_ttx, tables):
@@ -94,7 +117,8 @@
         return font, savepath
 
     def _run_varlib_build_test(self, designspace_name, font_name, tables,
-                               expected_ttx_name):
+                               expected_ttx_name, save_before_dump=False,
+                               post_process_master=None):
         suffix = '.ttf'
         ds_path = self.get_test_input(designspace_name + '.designspace')
         ufo_dir = self.get_test_input('master_ufo')
@@ -103,11 +127,19 @@
         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)
+            font, savepath = self.compile_font(path, suffix, self.tempdir)
+            if post_process_master is not None:
+                post_process_master(font, savepath)
 
         finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
         varfont, model, _ = build(ds_path, finder)
 
+        if save_before_dump:
+            # some data (e.g. counts printed in TTX inline comments) is only
+            # calculated at compile time, so before we can compare the TTX
+            # dumps we need to save to a temporary stream, and realod the font
+            varfont = reload_font(varfont)
+
         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)
@@ -126,12 +158,9 @@
 
     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'
-        )
+        ds_path = self.get_test_input('InterpolateLayout3.designspace')
+        with self.assertRaisesRegex(DesignSpaceDocumentError, "No axes defined"):
+            build(ds_path)
 
     def test_varlib_avar_single_axis(self):
         """Designspace file contains a 'weight' axis with <map> elements
@@ -153,7 +182,7 @@
         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:
+        This is to work around an issue with some rasterizers:
         https://github.com/googlei18n/fontmake/issues/295
         https://github.com/fonttools/fonttools/issues/1011
         """
@@ -173,7 +202,7 @@
         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:
+        This is again to work around an issue with some rasterizers:
         https://github.com/googlei18n/fontmake/issues/295
         https://github.com/fonttools/fonttools/issues/1011
         """
@@ -185,12 +214,230 @@
             expected_ttx_name=test_name
         )
 
+    def test_varlib_build_feature_variations(self):
+        """Designspace file contains <rules> element, used to build
+        GSUB FeatureVariations table.
+        """
+        self._run_varlib_build_test(
+            designspace_name="FeatureVars",
+            font_name="TestFamily",
+            tables=["fvar", "GSUB"],
+            expected_ttx_name="FeatureVars",
+            save_before_dump=True,
+        )
+
+    def test_varlib_build_feature_variations_custom_tag(self):
+        """Designspace file contains <rules> element, used to build
+        GSUB FeatureVariations table.
+        """
+        self._run_varlib_build_test(
+            designspace_name="FeatureVarsCustomTag",
+            font_name="TestFamily",
+            tables=["fvar", "GSUB"],
+            expected_ttx_name="FeatureVarsCustomTag",
+            save_before_dump=True,
+        )
+
+    def test_varlib_build_feature_variations_whole_range(self):
+        """Designspace file contains <rules> element specifying the entire design
+        space, used to build GSUB FeatureVariations table.
+        """
+        self._run_varlib_build_test(
+            designspace_name="FeatureVarsWholeRange",
+            font_name="TestFamily",
+            tables=["fvar", "GSUB"],
+            expected_ttx_name="FeatureVarsWholeRange",
+            save_before_dump=True,
+        )
+
+    def test_varlib_build_feature_variations_whole_range_empty(self):
+        """Designspace file contains <rules> element without a condition, specifying
+        the entire design space, used to build GSUB FeatureVariations table.
+        """
+        self._run_varlib_build_test(
+            designspace_name="FeatureVarsWholeRangeEmpty",
+            font_name="TestFamily",
+            tables=["fvar", "GSUB"],
+            expected_ttx_name="FeatureVarsWholeRange",
+            save_before_dump=True,
+        )
+
+    def test_varlib_build_feature_variations_with_existing_rclt(self):
+        """Designspace file contains <rules> element, used to build GSUB
+        FeatureVariations table. <rules> is specified to do its OT processing
+        "last", so a 'rclt' feature will be used or created. This test covers
+        the case when a 'rclt' already exists in the masters.
+
+        We dynamically add a 'rclt' feature to an existing set of test
+        masters, to avoid adding more test data.
+
+        The multiple languages are done to verify whether multiple existing
+        'rclt' features are updated correctly.
+        """
+        def add_rclt(font, savepath):
+            features = """
+            languagesystem DFLT dflt;
+            languagesystem latn dflt;
+            languagesystem latn NLD;
+
+            feature rclt {
+                script latn;
+                language NLD;
+                lookup A {
+                    sub uni0041 by uni0061;
+                } A;
+                language dflt;
+                lookup B {
+                    sub uni0041 by uni0061;
+                } B;
+            } rclt;
+            """
+            addOpenTypeFeaturesFromString(font, features)
+            font.save(savepath)
+        self._run_varlib_build_test(
+            designspace_name="FeatureVars",
+            font_name="TestFamily",
+            tables=["fvar", "GSUB"],
+            expected_ttx_name="FeatureVars_rclt",
+            save_before_dump=True,
+            post_process_master=add_rclt,
+        )
+
+    def test_varlib_gvar_explicit_delta(self):
+        """The variable font contains a composite glyph odieresis which does not
+        need a gvar entry, because all its deltas are 0, but it must be added
+        anyway to work around an issue with macOS 10.14.
+
+        https://github.com/fonttools/fonttools/issues/1381
+        """
+        test_name = 'BuildGvarCompositeExplicitDelta'
+        self._run_varlib_build_test(
+            designspace_name=test_name,
+            font_name='TestFamily4',
+            tables=['gvar'],
+            expected_ttx_name=test_name
+        )
+
+    def test_varlib_nonmarking_CFF2(self):
+        self.temp_dir()
+
+        ds_path = self.get_test_input('TestNonMarkingCFF2.designspace', copy=True)
+        ttx_dir = self.get_test_input("master_non_marking_cff2")
+        expected_ttx_path = self.get_test_output("TestNonMarkingCFF2.ttx")
+
+        for path in self.get_file_list(ttx_dir, '.ttx', 'TestNonMarkingCFF2_'):
+            self.compile_font(path, ".otf", self.tempdir)
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            source.path = os.path.join(
+                self.tempdir, os.path.basename(source.filename).replace(".ufo", ".otf")
+            )
+        ds.updatePaths()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+
+        tables = ["CFF2"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
+    def test_varlib_build_CFF2(self):
+        self.temp_dir()
+
+        ds_path = self.get_test_input('TestCFF2.designspace', copy=True)
+        ttx_dir = self.get_test_input("master_cff2")
+        expected_ttx_path = self.get_test_output("BuildTestCFF2.ttx")
+
+        for path in self.get_file_list(ttx_dir, '.ttx', 'TestCFF2_'):
+            self.compile_font(path, ".otf", self.tempdir)
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            source.path = os.path.join(
+                self.tempdir, os.path.basename(source.filename).replace(".ufo", ".otf")
+            )
+        ds.updatePaths()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+
+        tables = ["fvar", "CFF2"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
+    def test_varlib_build_CFF2_from_CFF2(self):
+        self.temp_dir()
+
+        ds_path = self.get_test_input('TestCFF2Input.designspace', copy=True)
+        ttx_dir = self.get_test_input("master_cff2_input")
+        expected_ttx_path = self.get_test_output("BuildTestCFF2.ttx")
+
+        for path in self.get_file_list(ttx_dir, '.ttx', 'TestCFF2_'):
+            self.compile_font(path, ".otf", self.tempdir)
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            source.path = os.path.join(
+                self.tempdir, os.path.basename(source.filename).replace(".ufo", ".otf")
+            )
+        ds.updatePaths()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+
+        tables = ["fvar", "CFF2"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
+    def test_varlib_build_sparse_CFF2(self):
+        self.temp_dir()
+
+        ds_path = self.get_test_input('TestSparseCFF2VF.designspace', copy=True)
+        ttx_dir = self.get_test_input("master_sparse_cff2")
+        expected_ttx_path = self.get_test_output("TestSparseCFF2VF.ttx")
+
+        for path in self.get_file_list(ttx_dir, '.ttx', 'MasterSet_Kanji-'):
+            self.compile_font(path, ".otf", self.tempdir)
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            source.path = os.path.join(
+                self.tempdir, os.path.basename(source.filename).replace(".ufo", ".otf")
+            )
+        ds.updatePaths()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+
+        tables = ["fvar", "CFF2"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
+    def test_varlib_build_vpal(self):
+        self.temp_dir()
+
+        ds_path = self.get_test_input('test_vpal.designspace', copy=True)
+        ttx_dir = self.get_test_input("master_vpal_test")
+        expected_ttx_path = self.get_test_output("test_vpal.ttx")
+
+        for path in self.get_file_list(ttx_dir, '.ttx', 'master_vpal_test_'):
+            self.compile_font(path, ".otf", self.tempdir)
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            source.path = os.path.join(
+                self.tempdir, os.path.basename(source.filename).replace(".ufo", ".otf")
+            )
+        ds.updatePaths()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+
+        tables = ["GPOS"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
     def test_varlib_main_ttf(self):
         """Mostly for testing varLib.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()
@@ -202,14 +449,531 @@
 
         ds_copy = os.path.join(self.tempdir, 'BuildMain.designspace')
         shutil.copy2(ds_path, ds_copy)
-        varLib_main([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)
 
+    def test_varlib_build_from_ds_object_in_memory_ttfonts(self):
+        ds_path = self.get_test_input("Build.designspace")
+        ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf")
+        expected_ttx_path = self.get_test_output("BuildMain.ttx")
+
+        self.temp_dir()
+        for path in self.get_file_list(ttx_dir, '.ttx', 'TestFamily-'):
+            self.compile_font(path, ".ttf", self.tempdir)
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            filename = os.path.join(
+                self.tempdir, os.path.basename(source.filename).replace(".ufo", ".ttf")
+            )
+            source.font = TTFont(
+                filename, recalcBBoxes=False, recalcTimestamp=False, lazy=True
+            )
+            source.filename = None  # Make sure no file path gets into build()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+        tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
+    def test_varlib_build_from_ttf_paths(self):
+        self.temp_dir()
+
+        ds_path = self.get_test_input("Build.designspace", copy=True)
+        ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf")
+        expected_ttx_path = self.get_test_output("BuildMain.ttx")
+
+        for path in self.get_file_list(ttx_dir, '.ttx', 'TestFamily-'):
+            self.compile_font(path, ".ttf", self.tempdir)
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            source.path = os.path.join(
+                self.tempdir, os.path.basename(source.filename).replace(".ufo", ".ttf")
+            )
+        ds.updatePaths()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+        tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
+    def test_varlib_build_from_ttx_paths(self):
+        ds_path = self.get_test_input("Build.designspace")
+        ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf")
+        expected_ttx_path = self.get_test_output("BuildMain.ttx")
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            source.path = os.path.join(
+                ttx_dir, os.path.basename(source.filename).replace(".ufo", ".ttx")
+            )
+        ds.updatePaths()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+        tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
+    def test_varlib_build_sparse_masters(self):
+        ds_path = self.get_test_input("SparseMasters.designspace")
+        expected_ttx_path = self.get_test_output("SparseMasters.ttx")
+
+        varfont, _, _ = build(ds_path)
+        varfont = reload_font(varfont)
+        tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
+    def test_varlib_build_lazy_masters(self):
+        # See https://github.com/fonttools/fonttools/issues/1808
+        ds_path = self.get_test_input("SparseMasters.designspace")
+        expected_ttx_path = self.get_test_output("SparseMasters.ttx")
+
+        def _open_font(master_path, master_finder=lambda s: s):
+            font = TTFont()
+            font.importXML(master_path)
+            buf = BytesIO()
+            font.save(buf, reorderTables=False)
+            buf.seek(0)
+            font = TTFont(buf, lazy=True)  # reopen in lazy mode, to reproduce #1808
+            return font
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        ds.loadSourceFonts(_open_font)
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+        tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
+    def test_varlib_build_sparse_masters_MVAR(self):
+        import fontTools.varLib.mvar
+
+        ds_path = self.get_test_input("SparseMasters.designspace")
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        load_masters(ds)
+
+        # Trigger MVAR generation so varLib is forced to create deltas with a
+        # sparse master inbetween.
+        font_0_os2 = ds.sources[0].font["OS/2"]
+        font_0_os2.sTypoAscender = 1
+        font_0_os2.sTypoDescender = 1
+        font_0_os2.sTypoLineGap = 1
+        font_0_os2.usWinAscent = 1
+        font_0_os2.usWinDescent = 1
+        font_0_os2.sxHeight = 1
+        font_0_os2.sCapHeight = 1
+        font_0_os2.ySubscriptXSize = 1
+        font_0_os2.ySubscriptYSize = 1
+        font_0_os2.ySubscriptXOffset = 1
+        font_0_os2.ySubscriptYOffset = 1
+        font_0_os2.ySuperscriptXSize = 1
+        font_0_os2.ySuperscriptYSize = 1
+        font_0_os2.ySuperscriptXOffset = 1
+        font_0_os2.ySuperscriptYOffset = 1
+        font_0_os2.yStrikeoutSize = 1
+        font_0_os2.yStrikeoutPosition = 1
+        font_0_vhea = newTable("vhea")
+        font_0_vhea.ascent = 1
+        font_0_vhea.descent = 1
+        font_0_vhea.lineGap = 1
+        font_0_vhea.caretSlopeRise = 1
+        font_0_vhea.caretSlopeRun = 1
+        font_0_vhea.caretOffset = 1
+        ds.sources[0].font["vhea"] = font_0_vhea
+        font_0_hhea = ds.sources[0].font["hhea"]
+        font_0_hhea.caretSlopeRise = 1
+        font_0_hhea.caretSlopeRun = 1
+        font_0_hhea.caretOffset = 1
+        font_0_post = ds.sources[0].font["post"]
+        font_0_post.underlineThickness = 1
+        font_0_post.underlinePosition = 1
+
+        font_2_os2 = ds.sources[2].font["OS/2"]
+        font_2_os2.sTypoAscender = 800
+        font_2_os2.sTypoDescender = 800
+        font_2_os2.sTypoLineGap = 800
+        font_2_os2.usWinAscent = 800
+        font_2_os2.usWinDescent = 800
+        font_2_os2.sxHeight = 800
+        font_2_os2.sCapHeight = 800
+        font_2_os2.ySubscriptXSize = 800
+        font_2_os2.ySubscriptYSize = 800
+        font_2_os2.ySubscriptXOffset = 800
+        font_2_os2.ySubscriptYOffset = 800
+        font_2_os2.ySuperscriptXSize = 800
+        font_2_os2.ySuperscriptYSize = 800
+        font_2_os2.ySuperscriptXOffset = 800
+        font_2_os2.ySuperscriptYOffset = 800
+        font_2_os2.yStrikeoutSize = 800
+        font_2_os2.yStrikeoutPosition = 800
+        font_2_vhea = newTable("vhea")
+        font_2_vhea.ascent = 800
+        font_2_vhea.descent = 800
+        font_2_vhea.lineGap = 800
+        font_2_vhea.caretSlopeRise = 800
+        font_2_vhea.caretSlopeRun = 800
+        font_2_vhea.caretOffset = 800
+        ds.sources[2].font["vhea"] = font_2_vhea
+        font_2_hhea = ds.sources[2].font["hhea"]
+        font_2_hhea.caretSlopeRise = 800
+        font_2_hhea.caretSlopeRun = 800
+        font_2_hhea.caretOffset = 800
+        font_2_post = ds.sources[2].font["post"]
+        font_2_post.underlineThickness = 800
+        font_2_post.underlinePosition = 800
+
+        varfont, _, _ = build(ds)
+        mvar_tags = [vr.ValueTag for vr in varfont["MVAR"].table.ValueRecord]
+        assert all(tag in mvar_tags for tag in fontTools.varLib.mvar.MVAR_ENTRIES)
+
+    def test_varlib_build_VVAR_CFF2(self):
+        self.temp_dir()
+
+        ds_path = self.get_test_input('TestVVAR.designspace', copy=True)
+        ttx_dir = self.get_test_input("master_vvar_cff2")
+        expected_ttx_name = 'TestVVAR'
+        suffix = '.otf'
+
+        for path in self.get_file_list(ttx_dir, '.ttx', 'TestVVAR'):
+            font, savepath = self.compile_font(path, suffix, self.tempdir)
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            source.path = os.path.join(
+                self.tempdir, os.path.basename(source.filename).replace(".ufo", suffix)
+            )
+        ds.updatePaths()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+
+        expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx')
+        tables = ["VVAR"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+        self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
+
+    def test_varlib_build_BASE(self):
+        self.temp_dir()
+
+        ds_path = self.get_test_input('TestBASE.designspace', copy=True)
+        ttx_dir = self.get_test_input("master_base_test")
+        expected_ttx_name = 'TestBASE'
+        suffix = '.otf'
+
+        for path in self.get_file_list(ttx_dir, '.ttx', 'TestBASE'):
+            font, savepath = self.compile_font(path, suffix, self.tempdir)
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            source.path = os.path.join(
+                self.tempdir, os.path.basename(source.filename).replace(".ufo", suffix)
+            )
+        ds.updatePaths()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+
+        expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx')
+        tables = ["BASE"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+        self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
+
+    def test_varlib_build_single_master(self):
+        self._run_varlib_build_test(
+            designspace_name='SingleMaster',
+            font_name='TestFamily',
+            tables=['GDEF', 'HVAR', 'MVAR', 'STAT', 'fvar', 'cvar', 'gvar', 'name'],
+            expected_ttx_name='SingleMaster',
+            save_before_dump=True,
+        )
+
+    def test_kerning_merging(self):
+        """Test the correct merging of class-based pair kerning.
+
+        Problem description at https://github.com/fonttools/fonttools/pull/1638.
+        Test font and Designspace generated by 
+        https://gist.github.com/madig/183d0440c9f7d05f04bd1280b9664bd1.
+        """
+        ds_path = self.get_test_input("KerningMerging.designspace")
+        ttx_dir = self.get_test_input("master_kerning_merging")
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            ttx_dump = TTFont()
+            ttx_dump.importXML(
+                os.path.join(
+                    ttx_dir, os.path.basename(source.filename).replace(".ttf", ".ttx")
+                )
+            )
+            source.font = reload_font(ttx_dump)
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+
+        class_kerning_tables = [
+            t
+            for l in varfont["GPOS"].table.LookupList.Lookup
+            for t in l.SubTable
+            if t.Format == 2
+        ]
+        assert len(class_kerning_tables) == 1
+        class_kerning_table = class_kerning_tables[0]
+
+        # Test that no class kerned against class zero (containing all glyphs not
+        # classed) has a `XAdvDevice` table attached, which in the variable font
+        # context is a "VariationIndex" table and points to kerning deltas in the GDEF
+        # table. Variation deltas of any kerning class against class zero should
+        # probably never exist.
+        for class1_record in class_kerning_table.Class1Record:
+            class2_zero = class1_record.Class2Record[0]
+            assert getattr(class2_zero.Value1, "XAdvDevice", None) is None
+
+        # Assert the variable font's kerning table (without deltas) is equal to the
+        # default font's kerning table. The bug fixed in 
+        # https://github.com/fonttools/fonttools/pull/1638 caused rogue kerning
+        # values to be written to the variable font.
+        assert _extract_flat_kerning(varfont, class_kerning_table) == {
+            ("A", ".notdef"): 0,
+            ("A", "A"): 0,
+            ("A", "B"): -20,
+            ("A", "C"): 0,
+            ("A", "D"): -20,
+            ("B", ".notdef"): 0,
+            ("B", "A"): 0,
+            ("B", "B"): 0,
+            ("B", "C"): 0,
+            ("B", "D"): 0,
+        }
+
+        instance_thin = instantiateVariableFont(varfont, {"wght": 100})
+        instance_thin_kerning_table = (
+            instance_thin["GPOS"].table.LookupList.Lookup[0].SubTable[0]
+        )
+        assert _extract_flat_kerning(instance_thin, instance_thin_kerning_table) == {
+            ("A", ".notdef"): 0,
+            ("A", "A"): 0,
+            ("A", "B"): 0,
+            ("A", "C"): 10,
+            ("A", "D"): 0,
+            ("B", ".notdef"): 0,
+            ("B", "A"): 0,
+            ("B", "B"): 0,
+            ("B", "C"): 10,
+            ("B", "D"): 0,
+        }
+
+        instance_black = instantiateVariableFont(varfont, {"wght": 900})
+        instance_black_kerning_table = (
+            instance_black["GPOS"].table.LookupList.Lookup[0].SubTable[0]
+        )
+        assert _extract_flat_kerning(instance_black, instance_black_kerning_table) == {
+            ("A", ".notdef"): 0,
+            ("A", "A"): 0,
+            ("A", "B"): 0,
+            ("A", "C"): 0,
+            ("A", "D"): 40,
+            ("B", ".notdef"): 0,
+            ("B", "A"): 0,
+            ("B", "B"): 0,
+            ("B", "C"): 0,
+            ("B", "D"): 40,
+        }
+
+    def test_designspace_fill_in_location(self):
+        ds_path = self.get_test_input("VarLibLocationTest.designspace")
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        ds_loaded = load_designspace(ds)
+
+        assert ds_loaded.instances[0].location == {"weight": 0, "width": 50}
+
+    def test_varlib_build_incompatible_features(self):
+        with pytest.raises(
+            varLibErrors.ShouldBeConstant,
+            match = """
+
+Couldn't merge the fonts, because some values were different, but should have
+been the same. This happened while performing the following operation:
+GPOS.table.FeatureList.FeatureCount
+
+The problem is likely to be in Simple Two Axis Bold:
+
+Incompatible features between masters.
+Expected: kern, mark.
+Got: kern.
+"""):
+
+            self._run_varlib_build_test(
+                designspace_name="IncompatibleFeatures",
+                font_name="IncompatibleFeatures",
+                tables=["GPOS"],
+                expected_ttx_name="IncompatibleFeatures",
+                save_before_dump=True,
+            )
+
+    def test_varlib_build_incompatible_lookup_types(self):
+        with pytest.raises(
+            varLibErrors.MismatchedTypes,
+            match = r"MarkBasePos, instead saw PairPos"
+        ):
+            self._run_varlib_build_test(
+                designspace_name="IncompatibleLookupTypes",
+                font_name="IncompatibleLookupTypes",
+                tables=["GPOS"],
+                expected_ttx_name="IncompatibleLookupTypes",
+                save_before_dump=True,
+            )
+
+    def test_varlib_build_incompatible_arrays(self):
+        with pytest.raises(
+            varLibErrors.ShouldBeConstant,
+            match = """
+
+Couldn't merge the fonts, because some values were different, but should have
+been the same. This happened while performing the following operation:
+GPOS.table.ScriptList.ScriptCount
+
+The problem is likely to be in Simple Two Axis Bold:
+Expected to see .ScriptCount==1, instead saw 0"""
+        ):
+            self._run_varlib_build_test(
+                designspace_name="IncompatibleArrays",
+                font_name="IncompatibleArrays",
+                tables=["GPOS"],
+                expected_ttx_name="IncompatibleArrays",
+                save_before_dump=True,
+            )
+
+def test_load_masters_layerName_without_required_font():
+    ds = DesignSpaceDocument()
+    s = SourceDescriptor()
+    s.font = None
+    s.layerName = "Medium"
+    ds.addSource(s)
+
+    with pytest.raises(
+        VarLibValidationError,
+        match="specified a layer name but lacks the required TTFont object",
+    ):
+        load_masters(ds)
+
+
+def _extract_flat_kerning(font, pairpos_table):
+    extracted_kerning = {}
+    for glyph_name_1 in pairpos_table.Coverage.glyphs:
+        class_def_1 = pairpos_table.ClassDef1.classDefs.get(glyph_name_1, 0)
+        for glyph_name_2 in font.getGlyphOrder():
+            class_def_2 = pairpos_table.ClassDef2.classDefs.get(glyph_name_2, 0)
+            kern_value = (
+                pairpos_table.Class1Record[class_def_1]
+                .Class2Record[class_def_2]
+                .Value1.XAdvance
+            )
+            extracted_kerning[(glyph_name_1, glyph_name_2)] = kern_value
+    return extracted_kerning
+
+
+@pytest.fixture
+def ttFont():
+    f = TTFont()
+    f["OS/2"] = newTable("OS/2")
+    f["OS/2"].usWeightClass = 400
+    f["OS/2"].usWidthClass = 100
+    f["post"] = newTable("post")
+    f["post"].italicAngle = 0
+    return f
+
+
+class SetDefaultWeightWidthSlantTest(object):
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            ({"wght": 0}, 1),
+            ({"wght": 1}, 1),
+            ({"wght": 100}, 100),
+            ({"wght": 1000}, 1000),
+            ({"wght": 1001}, 1000),
+        ],
+    )
+    def test_wght(self, ttFont, location, expected):
+        set_default_weight_width_slant(ttFont, location)
+
+        assert ttFont["OS/2"].usWeightClass == expected
+
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            ({"wdth": 0}, 1),
+            ({"wdth": 56}, 1),
+            ({"wdth": 57}, 2),
+            ({"wdth": 62.5}, 2),
+            ({"wdth": 75}, 3),
+            ({"wdth": 87.5}, 4),
+            ({"wdth": 100}, 5),
+            ({"wdth": 112.5}, 6),
+            ({"wdth": 125}, 7),
+            ({"wdth": 150}, 8),
+            ({"wdth": 200}, 9),
+            ({"wdth": 201}, 9),
+            ({"wdth": 1000}, 9),
+        ],
+    )
+    def test_wdth(self, ttFont, location, expected):
+        set_default_weight_width_slant(ttFont, location)
+
+        assert ttFont["OS/2"].usWidthClass == expected
+
+    @pytest.mark.parametrize(
+        "location, expected",
+        [
+            ({"slnt": -91}, -90),
+            ({"slnt": -90}, -90),
+            ({"slnt": 0}, 0),
+            ({"slnt": 11.5}, 11.5),
+            ({"slnt": 90}, 90),
+            ({"slnt": 91}, 90),
+        ],
+    )
+    def test_slnt(self, ttFont, location, expected):
+        set_default_weight_width_slant(ttFont, location)
+
+        assert ttFont["post"].italicAngle == expected
+
+    def test_all(self, ttFont):
+        set_default_weight_width_slant(
+            ttFont, {"wght": 500, "wdth": 150, "slnt": -12.0}
+        )
+
+        assert ttFont["OS/2"].usWeightClass == 500
+        assert ttFont["OS/2"].usWidthClass == 8
+        assert ttFont["post"].italicAngle == -12.0
+
 
 if __name__ == "__main__":
     sys.exit(unittest.main())
diff --git a/Tests/varLib/varStore_test.py b/Tests/varLib/varStore_test.py
new file mode 100644
index 0000000..a1bdd12
--- /dev/null
+++ b/Tests/varLib/varStore_test.py
@@ -0,0 +1,82 @@
+import pytest
+from fontTools.varLib.models import VariationModel
+from fontTools.varLib.varStore import OnlineVarStoreBuilder, VarStoreInstancer
+from fontTools.ttLib import TTFont, newTable
+from fontTools.ttLib.tables._f_v_a_r import Axis
+from fontTools.ttLib.tables.otBase import OTTableReader, OTTableWriter
+from fontTools.ttLib.tables.otTables import VarStore
+
+
+@pytest.mark.parametrize(
+    "locations, masterValues",
+    [
+        (
+            [{}, {"a": 1}],
+            [
+                [10, 20],
+                [100, 2000],
+                [100, 22000],
+            ],
+        ),
+        (
+            [{}, {"a": 1}, {"b": 1}, {"a": 1, "b": 1}],
+            [
+                [10, 20, 40, 60],
+                [100, 2000, 400, 6000],
+                [7100, 22000, 4000, 30000],
+            ],
+        ),
+        (
+            [{}, {"a": 1}],
+            [
+                [10, 20],
+                [42000, 100],
+                [100, 52000],
+            ],
+        ),
+        (
+            [{}, {"a": 1}, {"b": 1}, {"a": 1, "b": 1}],
+            [
+                [10, 20, 40, 60],
+                [40000, 42000, 400, 6000],
+                [100, 22000, 4000, 173000],
+            ],
+        ),
+    ],
+)
+def test_onlineVarStoreBuilder(locations, masterValues):
+    axisTags = sorted({k for loc in locations for k in loc})
+    model = VariationModel(locations)
+    builder = OnlineVarStoreBuilder(axisTags)
+    builder.setModel(model)
+    varIdxs = []
+    for masters in masterValues:
+        _, varIdx = builder.storeMasters(masters)
+        varIdxs.append(varIdx)
+
+    varStore = builder.finish()
+    mapping = varStore.optimize()
+    varIdxs = [mapping[varIdx] for varIdx in varIdxs]
+
+    dummyFont = TTFont()
+    writer = OTTableWriter()
+    varStore.compile(writer, dummyFont)
+    data = writer.getAllData()
+    reader = OTTableReader(data)
+    varStore = VarStore()
+    varStore.decompile(reader, dummyFont)
+
+    fvarAxes = [buildAxis(axisTag) for axisTag in axisTags]
+    instancer = VarStoreInstancer(varStore, fvarAxes)
+    for masters, varIdx in zip(masterValues, varIdxs):
+        base, *rest = masters
+        for expectedValue, loc in zip(masters, locations):
+            instancer.setLocation(loc)
+            value = base + instancer[varIdx]
+            assert expectedValue == value
+
+
+def buildAxis(axisTag):
+    axis = Axis()
+    axis.axisTag = axisTag
+    return axis
diff --git a/Tests/voltLib/lexer_test.py b/Tests/voltLib/lexer_test.py
index 6041cb8..2145d07 100644
--- a/Tests/voltLib/lexer_test.py
+++ b/Tests/voltLib/lexer_test.py
@@ -1,5 +1,3 @@
-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
diff --git a/Tests/voltLib/parser_test.py b/Tests/voltLib/parser_test.py
index 6baf900..0e0191f 100644
--- a/Tests/voltLib/parser_test.py
+++ b/Tests/voltLib/parser_test.py
@@ -1,11 +1,7 @@
-from __future__ import print_function, division, absolute_import
-from __future__ import unicode_literals
+from fontTools.voltLib import ast
 from fontTools.voltLib.error import VoltLibError
 from fontTools.voltLib.parser import Parser
-from io import open
-import os
-import shutil
-import tempfile
+from io import StringIO
 import unittest
 
 
@@ -17,6 +13,13 @@
         if not hasattr(self, "assertRaisesRegex"):
             self.assertRaisesRegex = self.assertRaisesRegexp
 
+    def assertSubEqual(self, sub, glyph_ref, replacement_ref):
+        glyphs = [[g.glyph for g in v] for v in sub.mapping.keys()]
+        replacement = [[g.glyph for g in v] for v in sub.mapping.values()]
+
+        self.assertEqual(glyphs, glyph_ref)
+        self.assertEqual(replacement, replacement_ref)
+
     def test_def_glyph_base(self):
         [def_glyph] = self.parse(
             'DEF_GLYPH ".notdef" ID 0 TYPE BASE END_GLYPH'
@@ -34,7 +37,7 @@
                          ("space", 3, [0x0020], "BASE", None))
 
     def test_def_glyph_base_with_unicodevalues(self):
-        [def_glyph] = self.parse(
+        [def_glyph] = self.parse_(
             'DEF_GLYPH "CR" ID 2 UNICODEVALUES "U+0009" '
             'TYPE BASE END_GLYPH'
         ).statements
@@ -52,7 +55,7 @@
                          ("CR", 2, [0x0009, 0x000D], "BASE", None))
 
     def test_def_glyph_base_with_empty_unicodevalues(self):
-        [def_glyph] = self.parse(
+        [def_glyph] = self.parse_(
             'DEF_GLYPH "i.locl" ID 269 UNICODEVALUES "" '
             'TYPE BASE END_GLYPH'
         ).statements
@@ -76,6 +79,22 @@
                           def_glyph.type, def_glyph.components),
                          ("f_f", 320, None, "LIGATURE", 2))
 
+    def test_def_glyph_mark(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH "brevecomb" ID 320 TYPE MARK END_GLYPH'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         ("brevecomb", 320, None, "MARK", None))
+
+    def test_def_glyph_component(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH "f.f_f" ID 320 TYPE COMPONENT END_GLYPH'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         ("f.f_f", 320, None, "COMPONENT", None))
+
     def test_def_glyph_no_type(self):
         [def_glyph] = self.parse(
             'DEF_GLYPH "glyph20" ID 20 END_GLYPH'
@@ -87,7 +106,7 @@
     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'
+            'DEF_GLYPH "a" ID 4 UNICODE 97 TYPE BASE END_GLYPH'
         ).statements
         self.assertEqual((def_glyphs[0].name, def_glyphs[0].id,
                           def_glyphs[0].unicode, def_glyphs[0].type,
@@ -101,12 +120,12 @@
     def test_def_group_glyphs(self):
         [def_group] = self.parse(
             'DEF_GROUP "aaccented"\n'
-            'ENUM GLYPH "aacute" GLYPH "abreve" GLYPH "acircumflex" '
+            ' 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'
+            'END_GROUP'
         ).statements
-        self.assertEqual((def_group.name, def_group.enum),
+        self.assertEqual((def_group.name, def_group.enum.glyphSet()),
                          ("aaccented",
                           ("aacute", "abreve", "acircumflex", "adieresis",
                            "ae", "agrave", "amacron", "aogonek", "aring",
@@ -115,50 +134,50 @@
     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'
+            ' 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'
+            ' 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'
+            ' ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n'
+            'END_GROUP'
         ).statements
-        self.assertEqual(
-            (test_group.name, test_group.enum),
-            ("TestGroup",
-             (("Group1",), ("Group2",))))
+        groups = [g.group for g in test_group.enum.enum]
+        self.assertEqual((test_group.name, groups),
+                         ("TestGroup", ["Group1", "Group2"]))
 
     def test_def_group_groups_not_yet_defined(self):
-        [group1, test_group1, test_group2, test_group3, group2] = self.parse(
+        [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'
+            ' 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'
+            ' ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n'
             'END_GROUP\n'
             'DEF_GROUP "TestGroup2"\n'
-            'ENUM GROUP "Group2" END_ENUM\n'
+            ' ENUM GROUP "Group2" END_ENUM\n'
             'END_GROUP\n'
             'DEF_GROUP "TestGroup3"\n'
-            'ENUM GROUP "Group2" GROUP "Group1" END_ENUM\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'
+            ' ENUM GLYPH "e" GLYPH "f" GLYPH "g" GLYPH "h" END_ENUM\n'
+            'END_GROUP'
         ).statements
+        groups = [g.group for g in test_group1.enum.enum]
         self.assertEqual(
-            (test_group1.name, test_group1.enum),
-            ("TestGroup1",
-             (("Group1", ), ("Group2", ))))
+            (test_group1.name, groups),
+            ("TestGroup1", ["Group1", "Group2"]))
+        groups = [g.group for g in test_group2.enum.enum]
         self.assertEqual(
-            (test_group2.name, test_group2.enum),
-            ("TestGroup2",
-             (("Group2", ), )))
+            (test_group2.name, groups),
+            ("TestGroup2", ["Group2"]))
+        groups = [g.group for g in test_group3.enum.enum]
         self.assertEqual(
-            (test_group3.name, test_group3.enum),
-            ("TestGroup3",
-             (("Group2", ), ("Group1", ))))
+            (test_group3.name, groups),
+            ("TestGroup3", ["Group2", "Group1"]))
 
     # def test_def_group_groups_undefined(self):
     #     with self.assertRaisesRegex(
@@ -176,28 +195,38 @@
     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" '
+            ' 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'
+            ' 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", ))))
+        items = def_group2.enum.enum
+        self.assertEqual((def_group2.name, items[0].glyphSet(), items[1].group),
+                         ("KERN_lc_a_2ND", ("a",), "aaccented"))
 
     def test_def_group_range(self):
-        [def_group] = self.parse(
+        def_group = self.parse(
+            'DEF_GLYPH "a" ID 163 UNICODE 97 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "agrave" ID 194 UNICODE 224 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "aacute" ID 195 UNICODE 225 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "acircumflex" ID 196 UNICODE 226 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "atilde" ID 197 UNICODE 227 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "c" ID 165 UNICODE 99 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "ccaron" ID 209 UNICODE 269 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "ccedilla" ID 210 UNICODE 231 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "cdotaccent" ID 210 UNICODE 267 TYPE BASE END_GLYPH\n'
             'DEF_GROUP "KERN_lc_a_2ND"\n'
-            'ENUM RANGE "a" TO "atilde" GLYPH "b" RANGE "c" TO "cdotaccent" '
+            ' 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),
+        ).statements[-1]
+        self.assertEqual((def_group.name, def_group.enum.glyphSet()),
                          ("KERN_lc_a_2ND",
-                          (("a", "atilde"), "b", ("c", "cdotaccent"))))
+                          ("a", "agrave", "aacute", "acircumflex", "atilde",
+                           "b", "c", "ccaron", "ccedilla", "cdotaccent")))
 
     def test_group_duplicate(self):
         self.assertRaisesRegex(
@@ -209,7 +238,7 @@
                         'END_GROUP\n'
                         'DEF_GROUP "dupe"\n'
                         'ENUM GLYPH "x" END_ENUM\n'
-                        'END_GROUP\n'
+                        'END_GROUP'
         )
 
     def test_group_duplicate_case_insensitive(self):
@@ -222,12 +251,12 @@
                         'END_GROUP\n'
                         'DEF_GROUP "Dupe"\n'
                         'ENUM GLYPH "x" END_ENUM\n'
-                        'END_GROUP\n'
+                        'END_GROUP'
         )
 
     def test_script_without_langsys(self):
         [script] = self.parse(
-            'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
+            'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
             'END_SCRIPT'
         ).statements
         self.assertEqual((script.name, script.tag, script.langs),
@@ -235,10 +264,10 @@
 
     def test_langsys_normal(self):
         [def_script] = self.parse(
-            'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
-            'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n'
+            'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
+            'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n\n'
             'END_LANGSYS\n'
-            'DEF_LANGSYS NAME "Moldavian" TAG "MOL "\n'
+            'DEF_LANGSYS NAME "Moldavian" TAG "MOL "\n\n'
             'END_LANGSYS\n'
             'END_SCRIPT'
         ).statements
@@ -256,8 +285,8 @@
 
     def test_langsys_no_script_name(self):
         [langsys] = self.parse(
-            'DEF_SCRIPT TAG "latn"\n'
-            'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
+            'DEF_SCRIPT TAG "latn"\n\n'
+            'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n'
             'END_LANGSYS\n'
             'END_SCRIPT'
         ).statements
@@ -274,8 +303,8 @@
                 VoltLibError,
                 r'.*Expected "TAG"'):
             [langsys] = self.parse(
-                'DEF_SCRIPT NAME "Latin"\n'
-                'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
+                'DEF_SCRIPT NAME "Latin"\n\n'
+                'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n'
                 'END_LANGSYS\n'
                 'END_SCRIPT'
             ).statements
@@ -286,12 +315,12 @@
                 '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'
+                'DEF_SCRIPT NAME "Default" TAG "DFLT"\n\n'
+                'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n'
                 'END_LANGSYS\n'
                 'END_SCRIPT\n'
-                'DEF_SCRIPT TAG "DFLT"\n'
-                'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
+                'DEF_SCRIPT TAG "DFLT"\n\n'
+                'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n'
                 'END_LANGSYS\n'
                 'END_SCRIPT'
             ).statements
@@ -307,21 +336,21 @@
                 'END_LANGSYS\n'
                 'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
                 'END_LANGSYS\n'
-                'END_SCRIPT\n'
+                'END_SCRIPT'
             ).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'
+            'DEF_SCRIPT NAME "Default" TAG "DFLT"\n\n'
+            'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n'
             'END_LANGSYS\n'
-            'DEF_LANGSYS NAME "Default" TAG "ROM "\n'
+            'DEF_LANGSYS NAME "Default" TAG "ROM "\n\n'
             'END_LANGSYS\n'
             'END_SCRIPT\n'
-            'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
-            'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
+            'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
+            'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n'
             'END_LANGSYS\n'
-            'DEF_LANGSYS NAME "Default" TAG "ROM "\n'
+            'DEF_LANGSYS NAME "Default" TAG "ROM "\n\n'
             'END_LANGSYS\n'
             'END_SCRIPT'
         ).statements
@@ -332,8 +361,8 @@
 
     def test_langsys_no_lang_name(self):
         [langsys] = self.parse(
-            'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
-            'DEF_LANGSYS TAG "dflt"\n'
+            'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
+            'DEF_LANGSYS TAG "dflt"\n\n'
             'END_LANGSYS\n'
             'END_SCRIPT'
         ).statements
@@ -350,18 +379,18 @@
                 VoltLibError,
                 r'.*Expected "TAG"'):
             [langsys] = self.parse(
-                'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
-                'DEF_LANGSYS NAME "Default"\n'
+                'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
+                'DEF_LANGSYS NAME "Default"\n\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_SCRIPT NAME "Latin" TAG "latn"\n\n'
+            'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n\n'
             'DEF_FEATURE NAME "Fractions" TAG "frac"\n'
-            'LOOKUP "fraclookup"\n'
+            ' LOOKUP "fraclookup"\n'
             'END_FEATURE\n'
             'END_LANGSYS\n'
             'END_SCRIPT'
@@ -373,10 +402,10 @@
                           "frac",
                           ["fraclookup"]))
         [def_script] = self.parse(
-            'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
-            'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n'
+            'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
+            'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n\n'
             'DEF_FEATURE NAME "Kerning" TAG "kern"\n'
-            'LOOKUP "kern1" LOOKUP "kern2"\n'
+            ' LOOKUP "kern1" LOOKUP "kern2"\n'
             'END_FEATURE\n'
             'END_LANGSYS\n'
             'END_SCRIPT'
@@ -433,10 +462,10 @@
     def test_lookup_name_starts_with_letter(self):
         with self.assertRaisesRegex(
             VoltLibError,
-            'Lookup name "\\\lookupname" must start with a letter'
+            r'Lookup name "\\lookupname" must start with a letter'
         ):
             [lookup] = self.parse(
-                'DEF_LOOKUP "\lookupname"\n'
+                'DEF_LOOKUP "\\lookupname"\n'
                 'AS_SUBSTITUTION\n'
                 'SUB GLYPH "a"\n'
                 'WITH GLYPH "a.alt"\n'
@@ -444,6 +473,21 @@
                 'END_SUBSTITUTION\n'
             ).statements
 
+    def test_lookup_comments(self):
+        [lookup] = self.parse(
+            'DEF_LOOKUP "test" PROCESS_BASE PROCESS_MARKS ALL DIRECTION LTR\n'
+            'COMMENTS "Hello\\nWorld"\n'
+            'IN_CONTEXT\n'
+            'END_CONTEXT\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "a"\n'
+            'WITH GLYPH "b"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        self.assertEqual(lookup.name, "test")
+        self.assertEqual(lookup.comments, "Hello\nWorld")
+
     def test_substitution_empty(self):
         with self.assertRaisesRegex(
                 VoltLibError,
@@ -523,17 +567,18 @@
             'END_SUB\n'
             'END_SUBSTITUTION'
         ).statements
-        self.assertEqual((lookup.name, list(lookup.sub.mapping.items())),
-                         ("smcp", [(("a",), ("a.sc",)), (("b",), ("b.sc",))]))
+        self.assertEqual(lookup.name, "smcp")
+        self.assertSubEqual(lookup.sub, [["a"], ["b"]], [["a.sc"], ["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_GROUP "Denominators"\n'
+            ' ENUM GLYPH "one.dnom" GLYPH "two.dnom" END_ENUM\n'
+            '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'
+            'IN_CONTEXT\n'
+            ' LEFT ENUM GROUP "Denominators" GLYPH "fraction" END_ENUM\n'
             'END_CONTEXT\n'
             'AS_SUBSTITUTION\n'
             'SUB GLYPH "one"\n'
@@ -545,26 +590,32 @@
             '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"),)], [])
-        )
+
+        self.assertEqual(lookup.name, "fracdnom")
+        self.assertEqual(context.ex_or_in, "IN_CONTEXT")
+        self.assertEqual(len(context.left), 1)
+        self.assertEqual(len(context.left[0]), 1)
+        self.assertEqual(len(context.left[0][0].enum), 2)
+        self.assertEqual(context.left[0][0].enum[0].group, "Denominators")
+        self.assertEqual(context.left[0][0].enum[1].glyph, "fraction")
+        self.assertEqual(context.right, [])
+        self.assertSubEqual(lookup.sub, [["one"], ["two"]],
+                [["one.dnom"], ["two.dnom"]])
 
     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_GROUP "Hebrew"\n'
+            ' ENUM GLYPH "uni05D0" GLYPH "uni05D1" END_ENUM\n'
+            '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'
+            ' RIGHT GROUP "Hebrew"\n'
+            ' RIGHT GLYPH "one.Hebr"\n'
             'END_CONTEXT\n'
             'IN_CONTEXT\n'
-            'LEFT GROUP "Hebrew"\n'
-            'LEFT GLYPH "one.Hebr"\n'
+            ' LEFT GROUP "Hebrew"\n'
+            ' LEFT GLYPH "one.Hebr"\n'
             'END_CONTEXT\n'
             'AS_SUBSTITUTION\n'
             'SUB GLYPH "dollar"\n'
@@ -574,18 +625,30 @@
         ).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",)], []))
+
+        self.assertEqual(lookup.name, "HebrewCurrency")
+
+        self.assertEqual(context1.ex_or_in, "IN_CONTEXT")
+        self.assertEqual(context1.left, [])
+        self.assertEqual(len(context1.right), 2)
+        self.assertEqual(len(context1.right[0]), 1)
+        self.assertEqual(len(context1.right[1]), 1)
+        self.assertEqual(context1.right[0][0].group, "Hebrew")
+        self.assertEqual(context1.right[1][0].glyph, "one.Hebr")
+
+        self.assertEqual(context2.ex_or_in, "IN_CONTEXT")
+        self.assertEqual(len(context2.left), 2)
+        self.assertEqual(len(context2.left[0]), 1)
+        self.assertEqual(len(context2.left[1]), 1)
+        self.assertEqual(context2.left[0][0].group, "Hebrew")
+        self.assertEqual(context2.left[1][0].glyph, "one.Hebr")
+        self.assertEqual(context2.right, [])
 
     def test_substitution_skip_base(self):
         [group, lookup] = self.parse(
-            'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" '
-            'END_ENUM END_GROUP\n'
+            'DEF_GROUP "SomeMarks"\n'
+            ' ENUM GLYPH "marka" GLYPH "markb" END_ENUM\n'
+            'END_GROUP\n'
             'DEF_LOOKUP "SomeSub" SKIP_BASE PROCESS_MARKS ALL '
             'DIRECTION LTR\n'
             'IN_CONTEXT\n'
@@ -596,15 +659,15 @@
             'END_SUB\n'
             'END_SUBSTITUTION'
         ).statements
-        process_base = lookup.process_base
         self.assertEqual(
-            (lookup.name, process_base),
+            (lookup.name, lookup.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_GROUP "SomeMarks"\n'
+            ' ENUM GLYPH "marka" GLYPH "markb" END_ENUM\n'
+            'END_GROUP\n'
             'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS ALL '
             'DIRECTION LTR\n'
             'IN_CONTEXT\n'
@@ -615,17 +678,16 @@
             'END_SUB\n'
             'END_SUBSTITUTION'
         ).statements
-        process_base = lookup.process_base
         self.assertEqual(
-            (lookup.name, process_base),
+            (lookup.name, lookup.process_base),
             ("SomeSub", True))
 
-    def test_substitution_skip_marks(self):
+    def test_substitution_process_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'
+            'DEF_GROUP "SomeMarks"\n'
+            ' ENUM GLYPH "marka" GLYPH "markb" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS "SomeMarks"\n'
             'IN_CONTEXT\n'
             'END_CONTEXT\n'
             'AS_SUBSTITUTION\n'
@@ -634,45 +696,127 @@
             'END_SUB\n'
             'END_SUBSTITUTION'
         ).statements
-        process_marks = lookup.process_marks
         self.assertEqual(
-            (lookup.name, process_marks),
-            ("SomeSub", False))
+            (lookup.name, lookup.process_marks),
+            ("SomeSub", 'SomeMarks'))
 
-    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'
+    def test_substitution_process_marks_all(self):
+        [lookup] = self.parse(
+            'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS ALL\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),
+            (lookup.name, lookup.process_marks),
+            ("SomeSub", True))
+
+    def test_substitution_process_marks_none(self):
+        [lookup] = self.parse_(
+            'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS "NONE"\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
+        self.assertEqual(
+            (lookup.name, lookup.process_marks),
+            ("SomeSub", False))
+
+    def test_substitution_process_marks_bad(self):
+        with self.assertRaisesRegex(
+                VoltLibError,
+                'Expected ALL, NONE, MARK_GLYPH_SET or an ID'):
+            self.parse(
+                'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" '
+                'END_ENUM END_GROUP\n'
+                'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS SomeMarks '
+                'AS_SUBSTITUTION\n'
+                'SUB GLYPH "A" WITH GLYPH "A.c2sc"\n'
+                'END_SUB\n'
+                'END_SUBSTITUTION'
+            )
+
+    def test_substitution_skip_marks(self):
+        [group, lookup] = self.parse(
+            'DEF_GROUP "SomeMarks"\n'
+            ' ENUM GLYPH "marka" GLYPH "markb" END_ENUM\n'
+            '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
+        self.assertEqual(
+            (lookup.name, lookup.process_marks),
+            ("SomeSub", False))
+
+    def test_substitution_mark_attachment(self):
+        [group, lookup] = self.parse(
+            'DEF_GROUP "SomeMarks"\n'
+            ' ENUM GLYPH "acutecmb" GLYPH "gravecmb" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_LOOKUP "SomeSub" PROCESS_BASE '
+            'PROCESS_MARKS "SomeMarks" DIRECTION RTL\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
+        self.assertEqual(
+            (lookup.name, lookup.process_marks),
+            ("SomeSub", "SomeMarks"))
+
+    def test_substitution_mark_glyph_set(self):
+        [group, lookup] = self.parse(
+            'DEF_GROUP "SomeMarks"\n'
+            ' ENUM GLYPH "acutecmb" GLYPH "gravecmb" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_LOOKUP "SomeSub" PROCESS_BASE '
+            'PROCESS_MARKS MARK_GLYPH_SET "SomeMarks" DIRECTION RTL\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
+        self.assertEqual(
+            (lookup.name, lookup.mark_glyph_set),
             ("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'
+            'DEF_GROUP "SomeMarks"\n'
+            ' ENUM GLYPH "acutecmb" GLYPH "gravecmb" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS ALL '
             'DIRECTION RTL\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),
+            (lookup.name, lookup.process_marks),
             ("SomeSub", True))
 
     def test_substitution_no_reversal(self):
@@ -681,7 +825,7 @@
             'DEF_LOOKUP "Lookup" PROCESS_BASE PROCESS_MARKS ALL '
             'DIRECTION LTR\n'
             'IN_CONTEXT\n'
-            'RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\n'
+            ' RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\n'
             'END_CONTEXT\n'
             'AS_SUBSTITUTION\n'
             'SUB GLYPH "a"\n'
@@ -695,18 +839,24 @@
         )
 
     def test_substitution_reversal(self):
-        [lookup] = self.parse(
+        lookup = self.parse(
+            'DEF_GROUP "DFLT_Num_standardFigures"\n'
+            ' ENUM GLYPH "zero" GLYPH "one" GLYPH "two" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_GROUP "DFLT_Num_numerators"\n'
+            ' ENUM GLYPH "zero.numr" GLYPH "one.numr" GLYPH "two.numr" END_ENUM\n'
+            'END_GROUP\n'
             'DEF_LOOKUP "RevLookup" PROCESS_BASE PROCESS_MARKS ALL '
             'DIRECTION LTR REVERSAL\n'
             'IN_CONTEXT\n'
-            'RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\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
+        ).statements[-1]
         self.assertEqual(
             (lookup.name, lookup.reversal),
             ("RevLookup", True)
@@ -727,11 +877,9 @@
             'END_SUB\n'
             'END_SUBSTITUTION'
         ).statements
-        self.assertEqual((lookup.name, list(lookup.sub.mapping.items())),
-                         ("ccmp",
-                          [(("aacute",), ("a", "acutecomb")),
-                           (("agrave",), ("a", "gravecomb"))]
-                          ))
+        self.assertEqual(lookup.name, "ccmp")
+        self.assertSubEqual(lookup.sub, [["aacute"], ["agrave"]],
+                [["a", "acutecomb"], ["a", "gravecomb"]])
 
     def test_substitution_multiple_to_single(self):
         [lookup] = self.parse(
@@ -748,17 +896,16 @@
             'END_SUB\n'
             'END_SUBSTITUTION'
         ).statements
-        self.assertEqual((lookup.name, list(lookup.sub.mapping.items())),
-                         ("liga",
-                          [(("f", "i"), ("f_i",)),
-                           (("f", "t"), ("f_t",))]))
+        self.assertEqual(lookup.name, "liga")
+        self.assertSubEqual(lookup.sub, [["f", "i"], ["f", "t"]],
+                [["f_i"], ["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 '
+            ' RIGHT ENUM '
             'GLYPH "fraction" '
             'RANGE "zero.numr" TO "nine.numr" '
             'END_ENUM\n'
@@ -769,11 +916,22 @@
             '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"),))]))
+
+        mapping = lookup.sub.mapping
+        glyphs = [[(r.start, r.end) for r in v] for v in mapping.keys()]
+        replacement = [[(r.start, r.end) for r in v] for v in mapping.values()]
+
+        self.assertEqual(lookup.name, "numr")
+        self.assertEqual(glyphs, [[('zero', 'nine')]])
+        self.assertEqual(replacement, [[('zero.numr', 'nine.numr')]])
+
+        self.assertEqual(len(lookup.context[0].right), 1)
+        self.assertEqual(len(lookup.context[0].right[0]), 1)
+        enum = lookup.context[0].right[0][0]
+        self.assertEqual(len(enum.enum), 2)
+        self.assertEqual(enum.enum[0].glyph, "fraction")
+        self.assertEqual((enum.enum[1].start, enum.enum[1].end),
+                ('zero.numr', 'nine.numr'))
 
     # GPOS
     #  ATTACH_CURSIVE
@@ -788,7 +946,7 @@
                 'DEF_LOOKUP "empty_position" PROCESS_BASE PROCESS_MARKS ALL '
                 'DIRECTION LTR\n'
                 'EXCEPT_CONTEXT\n'
-                'LEFT GLYPH "glyph"\n'
+                ' LEFT GLYPH "glyph"\n'
                 'END_CONTEXT\n'
                 'AS_POSITION\n'
                 'END_POSITION'
@@ -807,18 +965,21 @@
             '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'
+            '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'
+            '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'
+            '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'
+            'AT  POS DX 215 DY 450 END_POS END_ANCHOR'
         ).statements
+        pos = lookup.pos
+        coverage = [g.glyph for g in pos.coverage]
+        coverage_to = [[[g.glyph for g in e], a] for (e, a) in pos.coverage_to]
         self.assertEqual(
-            (lookup.name, lookup.pos.coverage, lookup.pos.coverage_to),
-            ("anchor_top", ("a", "e"), [(("acutecomb",), "top"),
-                                        (("gravecomb",), "top")])
+            (lookup.name, coverage, coverage_to),
+            ("anchor_top", ["a", "e"],
+             [[["acutecomb"], "top"], [["gravecomb"], "top"]])
         )
         self.assertEqual(
             (anchor1.name, anchor1.gid, anchor1.glyph_name, anchor1.component,
@@ -850,15 +1011,15 @@
             'IN_CONTEXT\n'
             'END_CONTEXT\n'
             'AS_POSITION\n'
-            'ATTACH_CURSIVE EXIT GLYPH "a" GLYPH "b" ENTER GLYPH "c"\n'
+            'ATTACH_CURSIVE\nEXIT  GLYPH "a" GLYPH "b"\nENTER  GLYPH "c"\n'
             'END_ATTACH\n'
-            'END_POSITION\n'
+            'END_POSITION'
         ).statements
+        exit = [[g.glyph for g in v] for v in lookup.pos.coverages_exit]
+        enter = [[g.glyph for g in v] for v in lookup.pos.coverages_enter]
         self.assertEqual(
-            (lookup.name,
-             lookup.pos.coverages_exit, lookup.pos.coverages_enter),
-            ("SomeLookup",
-             [("a", "b")], [("c",)])
+            (lookup.name, exit, enter),
+            ("SomeLookup", [["a", "b"]], [["c"]])
         )
 
     def test_position_adjust_pair(self):
@@ -869,17 +1030,19 @@
             'END_CONTEXT\n'
             'AS_POSITION\n'
             'ADJUST_PAIR\n'
-            ' FIRST GLYPH "A"\n'
-            ' SECOND GLYPH "V"\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'
+            ' 2 1 BY POS ADV -30 END_POS POS END_POS\n\n'
             'END_ADJUST\n'
-            'END_POSITION\n'
+            'END_POSITION'
         ).statements
+        coverages_1 = [[g.glyph for g in v] for v in lookup.pos.coverages_1]
+        coverages_2 = [[g.glyph for g in v] for v in lookup.pos.coverages_2]
         self.assertEqual(
-            (lookup.name, lookup.pos.coverages_1, lookup.pos.coverages_2,
+            (lookup.name, coverages_1, coverages_2,
              lookup.pos.adjust_pair),
-            ("kern1", [("A",)], [("V",)],
+            ("kern1", [["A"]], [["V"]],
              {(1, 2): ((-30, None, None, {}, {}, {}),
                        (None, None, None, {}, {}, {})),
               (2, 1): ((-30, None, None, {}, {}, {}),
@@ -891,31 +1054,33 @@
             'DEF_LOOKUP "TestLookup" PROCESS_BASE PROCESS_MARKS ALL '
             'DIRECTION LTR\n'
             'IN_CONTEXT\n'
-            # 'LEFT GLYPH "leftGlyph"\n'
-            # 'RIGHT GLYPH "rightGlyph"\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 "glyph1" BY POS ADV 0 DX 123 END_POS'
             ' GLYPH "glyph2" BY POS ADV 0 DX 456 END_POS\n'
             'END_ADJUST\n'
-            'END_POSITION\n'
+            'END_POSITION'
         ).statements
+        pos = lookup.pos
+        adjust = [[[g.glyph for g in a], b] for (a, b) in pos.adjust_single]
         self.assertEqual(
-            (lookup.name, lookup.pos.adjust_single),
+            (lookup.name, adjust),
             ("TestLookup",
-             [(("glyph1",), (0, 123, None, {}, {}, {})),
-              (("glyph2",), (0, 456, None, {}, {}, {}))])
+             [[["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'
+            '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'
+            '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'
+            'COMPONENT 1 AT  POS DX 250 DY 0 END_POS END_ANCHOR'
         ).statements
         self.assertEqual(
             (anchor1.name, anchor1.gid, anchor1.glyph_name, anchor1.component,
@@ -936,6 +1101,22 @@
              False, (None, 250, 0, {}, {}, {}))
         )
 
+    def test_def_anchor_multi_component(self):
+        [anchor1, anchor2] = self.parse(
+            'DEF_ANCHOR "top" ON 120 GLYPH a '
+            'COMPONENT 1 AT  POS DX 250 DY 450 END_POS END_ANCHOR\n'
+            'DEF_ANCHOR "top" ON 120 GLYPH a '
+            'COMPONENT 2 AT  POS DX 250 DY 450 END_POS END_ANCHOR'
+        ).statements
+        self.assertEqual(
+            (anchor1.name, anchor1.gid, anchor1.glyph_name, anchor1.component),
+            ("top", 120, "a", 1)
+        )
+        self.assertEqual(
+            (anchor2.name, anchor2.gid, anchor2.glyph_name, anchor2.component),
+            ("top", 120, "a", 2)
+        )
+
     def test_def_anchor_duplicate(self):
         self.assertRaisesRegex(
             VoltLibError,
@@ -943,15 +1124,15 @@
             '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'
+            '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'
+            'COMPONENT 1 AT  POS DX 250 DY 450 END_POS END_ANCHOR'
         )
 
     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'
+            'COMPONENT 1 LOCKED AT  POS DX 250 DY 450 END_POS END_ANCHOR'
         ).statements
         self.assertEqual(
             (anchor.name, anchor.gid, anchor.glyph_name, anchor.component,
@@ -963,7 +1144,7 @@
     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 '
+            '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(
@@ -975,7 +1156,7 @@
         [grid_ppem, pres_ppem, ppos_ppem] = self.parse(
             'GRID_PPEM 20\n'
             'PRESENTATION_PPEM 72\n'
-            'PPOSITIONING_PPEM 144\n'
+            'PPOSITIONING_PPEM 144'
         ).statements
         self.assertEqual(
             ((grid_ppem.name, grid_ppem.value),
@@ -988,7 +1169,7 @@
     def test_compiler_flags(self):
         [setting1, setting2] = self.parse(
             'COMPILER_USEEXTENSIONLOOKUPS\n'
-            'COMPILER_USEPAIRPOSFORMAT2\n'
+            'COMPILER_USEPAIRPOSFORMAT2'
         ).statements
         self.assertEqual(
             ((setting1.name, setting1.value),
@@ -1001,7 +1182,7 @@
         [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'
+            'CMAP_FORMAT 3 1 4'
         ).statements
         self.assertEqual(
             ((cmap_format1.name, cmap_format1.value),
@@ -1012,22 +1193,42 @@
              ("CMAP_FORMAT", (3, 1, 4)))
         )
 
-    def setUp(self):
-        self.tempdir = None
-        self.num_tempfiles = 0
+    def test_do_not_touch_cmap(self):
+        [option1, option2, option3, option4] = self.parse(
+            'DO_NOT_TOUCH_CMAP\n'
+            'CMAP_FORMAT 0 3 4\n'
+            'CMAP_FORMAT 1 0 6\n'
+            'CMAP_FORMAT 3 1 4'
+        ).statements
+        self.assertEqual(
+            ((option1.name, option1.value),
+             (option2.name, option2.value),
+             (option3.name, option3.value),
+             (option4.name, option4.value)),
+            (("DO_NOT_TOUCH_CMAP", True),
+             ("CMAP_FORMAT", (0, 3, 4)),
+             ("CMAP_FORMAT", (1, 0, 6)),
+             ("CMAP_FORMAT", (3, 1, 4)))
+        )
 
-    def tearDown(self):
-        if self.tempdir:
-            shutil.rmtree(self.tempdir)
+    def test_stop_at_end(self):
+        doc = self.parse_(
+            'DEF_GLYPH ".notdef" ID 0 TYPE BASE END_GLYPH END\0\0\0\0'
+        )
+        [def_glyph] = doc.statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         (".notdef", 0, None, "BASE", None))
+        self.assertEqual(str(doc),
+            '\nDEF_GLYPH ".notdef" ID 0 TYPE BASE END_GLYPH END\n')
+
+    def parse_(self, text):
+        return Parser(StringIO(text)).parse()
 
     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()
+        doc = self.parse_(text)
+        self.assertEqual('\n'.join(str(s) for s in doc.statements), text)
+        return Parser(StringIO(text)).parse()
 
 if __name__ == "__main__":
     import sys
diff --git a/dev-requirements.txt b/dev-requirements.txt
index a34deb2..73eae68 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -2,3 +2,4 @@
 tox>=2.5
 bump2version>=0.5.6
 sphinx>=1.5.5
+mypy>=0.782
diff --git a/fonttools b/fonttools
index 92b390e..90bc63f 100755
--- a/fonttools
+++ b/fonttools
@@ -1,5 +1,4 @@
-#!/usr/bin/env python
-from __future__ import print_function, division, absolute_import
+#!/usr/bin/env python3
 import sys
 import os.path
 
diff --git a/mypy.ini b/mypy.ini
new file mode 100644
index 0000000..7e37b03
--- /dev/null
+++ b/mypy.ini
@@ -0,0 +1,21 @@
+[mypy]
+python_version = 3.6
+files = Lib/fontTools/misc/plistlib
+follow_imports = silent
+ignore_missing_imports = True
+warn_redundant_casts = True
+warn_unused_configs = True
+warn_unused_ignores = True
+
+[mypy-fontTools.misc.plistlib]
+check_untyped_defs = True
+disallow_any_generics = True
+disallow_incomplete_defs = True
+disallow_subclassing_any = True
+disallow_untyped_decorators = True
+disallow_untyped_calls = False
+disallow_untyped_defs = True
+no_implicit_optional = True
+no_implicit_reexport = True
+strict_equality = True
+warn_return_any = True
diff --git a/requirements.txt b/requirements.txt
index c99e116..680ffbb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,13 @@
 # we use the official Brotli module on CPython and the CFFI-based
 # extension 'brotlipy' on PyPy
-brotli==1.0.1; platform_python_implementation != "PyPy"
+brotli==1.0.9; 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
+unicodedata2==13.0.0.post2; python_version < '3.9' and platform_python_implementation != "PyPy"
+scipy==1.5.4; platform_python_implementation != "PyPy"
+munkres==1.1.4; platform_python_implementation == "PyPy"
+zopfli==0.1.6
+fs==2.4.11
+skia-pathops==0.5.1.post1; platform_python_implementation != "PyPy"
+# this is only required to run Tests/cu2qu/{ufo,cli}_test.py
+ufoLib2==0.6.2
+pyobjc==6.2.2; sys_platform == "darwin"
diff --git a/run-tests.sh b/run-tests.sh
deleted file mode 100755
index 0eb84de..0000000
--- a/run-tests.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/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
index 407b686..eb2fbe7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 3.21.0.dev0
+current_version = 4.26.3.dev0
 commit = True
 tag = False
 tag_name = {new_version}
@@ -24,15 +24,9 @@
 search = version="{current_version}"
 replace = version="{new_version}"
 
-[wheel]
-universal = 1
-
 [sdist]
 formats = zip
 
-[aliases]
-test = pytest
-
 [metadata]
 license_file = LICENSE
 
@@ -46,9 +40,15 @@
 python_classes = 
 	*Test
 addopts = 
-	-v
 	-r a
 	--doctest-modules
 	--doctest-ignore-import-errors
 	--pyargs
-
+doctest_optionflags = 
+	ALLOW_UNICODE
+	ELLIPSIS
+filterwarnings = 
+	ignore:readPlist:DeprecationWarning:plistlib_test
+	ignore:writePlist:DeprecationWarning:plistlib_test
+	ignore:some_function:DeprecationWarning:fontTools.ufoLib.utils
+	ignore::DeprecationWarning:fontTools.varLib.designspace
diff --git a/setup.py b/setup.py
index 30826a7..f3ebb73 100755
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
 
 from __future__ import print_function
 import io
@@ -6,7 +6,8 @@
 import os
 from os.path import isfile, join as pjoin
 from glob import glob
-from setuptools import setup, find_packages, Command
+from setuptools import setup, find_packages, Command, Extension
+from setuptools.command.build_ext import build_ext as _build_ext
 from distutils import log
 from distutils.util import convert_path
 import subprocess as sp
@@ -23,12 +24,112 @@
 
 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 []
+setup_requires = []
+
+if {'bdist_wheel'}.intersection(sys.argv):
+	setup_requires.append('wheel')
+
+if {'release'}.intersection(sys.argv):
+	setup_requires.append('bump2version')
+
+try:
+	__import__("cython")
+except ImportError:
+	has_cython = False
+else:
+	has_cython = True
+
+env_with_cython = os.environ.get("FONTTOOLS_WITH_CYTHON")
+with_cython = (
+	True if env_with_cython in {"1", "true", "yes"}
+	else False if env_with_cython in {"0", "false", "no"}
+	else None
+)
+# --with-cython/--without-cython options override environment variables
+opt_with_cython = {'--with-cython'}.intersection(sys.argv)
+opt_without_cython = {'--without-cython'}.intersection(sys.argv)
+if opt_with_cython and opt_without_cython:
+	sys.exit(
+		"error: the options '--with-cython' and '--without-cython' are "
+		"mutually exclusive"
+	)
+elif opt_with_cython:
+	sys.argv.remove("--with-cython")
+	with_cython = True
+elif opt_without_cython:
+	sys.argv.remove("--without-cython")
+	with_cython = False
+
+if with_cython and not has_cython:
+	setup_requires.append("cython")
+
+ext_modules = []
+if with_cython is True or (with_cython is None and has_cython):
+	ext_modules.append(
+		Extension("fontTools.cu2qu.cu2qu", ["Lib/fontTools/cu2qu/cu2qu.py"]),
+	)
+
+extras_require = {
+	# for fontTools.ufoLib: to read/write UFO fonts
+	"ufo": [
+		"fs >= 2.2.0, < 3",
+	],
+	# for fontTools.misc.etree and fontTools.misc.plistlib: use lxml to
+	# read/write XML files (faster/safer than built-in ElementTree)
+	"lxml": [
+		"lxml >= 4.0, < 5",
+	],
+	# for fontTools.sfnt and fontTools.woff2: to compress/uncompress
+	# WOFF 1.0 and WOFF 2.0 webfonts.
+	"woff": [
+		"brotli >= 1.0.1; platform_python_implementation == 'CPython'",
+		"brotlicffi >= 0.8.0; platform_python_implementation != 'CPython'",
+		"zopfli >= 0.1.4",
+	],
+	# for fontTools.unicode and fontTools.unicodedata: to use the latest version
+	# of the Unicode Character Database instead of the built-in unicodedata
+	# which varies between python versions and may be outdated.
+	"unicode": [
+		# the unicodedata2 extension module doesn't work on PyPy.
+		# Python 3.9 already has Unicode 13.0, so the backport is not needed.
+		(
+			"unicodedata2 >= 13.0.0; "
+			"python_version < '3.9' and platform_python_implementation != 'PyPy'"
+		),
+	],
+	# for graphite type tables in ttLib/tables (Silf, Glat, Gloc)
+	"graphite": [
+		"lz4 >= 1.7.4.2"
+	],
+	# for fontTools.interpolatable: to solve the "minimum weight perfect
+	# matching problem in bipartite graphs" (aka Assignment problem)
+	"interpolatable": [
+		# use pure-python alternative on pypy
+		"scipy; platform_python_implementation != 'PyPy'",
+		"munkres; platform_python_implementation == 'PyPy'",
+	],
+	# for fontTools.varLib.plot, to visualize DesignSpaceDocument and resulting
+	# VariationModel
+	"plot": [
+		# TODO: figure out the minimum version of matplotlib that we need
+		"matplotlib",
+	],
+	# for fontTools.misc.symfont, module for symbolic font statistics analysis
+	"symfont": [
+		"sympy",
+	],
+	# To get file creator and type of Macintosh PostScript Type 1 fonts (macOS only)
+	"type1": [
+		"xattr; sys_platform == 'darwin'",
+	],
+	# for fontTools.ttLib.removeOverlaps, to remove overlaps in TTF fonts
+	"pathops": [
+		"skia-pathops >= 0.5.0",
+	],
+}
+# use a special 'all' key as shorthand to includes all the extra dependencies
+extras_require["all"] = sum(extras_require.values(), [])
+
 
 # Trove classifiers for PyPI
 classifiers = {"classifiers": [
@@ -37,7 +138,7 @@
 	"Environment :: Other Environment",
 	"Intended Audience :: Developers",
 	"Intended Audience :: End Users/Desktop",
-	"License :: OSI Approved :: BSD License",
+	"License :: OSI Approved :: MIT License",
 	"Natural Language :: English",
 	"Operating System :: OS Independent",
 	"Programming Language :: Python",
@@ -198,7 +299,7 @@
 		""" Run bumpversion.main() with the specified arguments, and return the
 		new computed version string (cf. 'bumpversion --help' for more info)
 		"""
-		import bumpversion
+		import bumpversion.cli
 
 		args = (
 			(['--verbose'] if self.verbose > 1 else []) +
@@ -211,7 +312,7 @@
 		log.debug("$ bumpversion %s" % " ".join(a.replace(" ", "\\ ") for a in args))
 
 		with capture_logger("bumpversion.list") as out:
-			bumpversion.main(args)
+			bumpversion.cli.main(args)
 
 		last_line = out.getvalue().splitlines()[-1]
 		new_version = last_line.replace("new_version=", "")
@@ -244,26 +345,6 @@
 		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).
 
@@ -307,38 +388,89 @@
 	return data_files
 
 
-setup(
+class cython_build_ext(_build_ext):
+	"""Compile *.pyx source files to *.c using cythonize if Cython is
+	installed and there is a working C compiler, else fall back to pure python dist.
+	"""
+
+	def finalize_options(self):
+		from Cython.Build import cythonize
+
+		# optionally enable line tracing for test coverage support
+		linetrace = os.environ.get("CYTHON_TRACE") == "1"
+
+		self.distribution.ext_modules[:] = cythonize(
+			self.distribution.ext_modules,
+			force=linetrace or self.force,
+			annotate=os.environ.get("CYTHON_ANNOTATE") == "1",
+			quiet=not self.verbose,
+			compiler_directives={
+				"linetrace": linetrace,
+				"language_level": 3,
+				"embedsignature": True,
+			},
+		)
+
+		_build_ext.finalize_options(self)
+
+	def build_extensions(self):
+		try:
+			_build_ext.build_extensions(self)
+		except Exception as e:
+			if with_cython:
+				raise
+			from distutils.errors import DistutilsModuleError
+
+			# optional compilation failed: we delete 'ext_modules' and make sure
+			# the generated wheel is 'pure'
+			del self.distribution.ext_modules[:]
+			try:
+				bdist_wheel = self.get_finalized_command("bdist_wheel")
+			except DistutilsModuleError:
+				# 'bdist_wheel' command not available as wheel is not installed
+				pass
+			else:
+				bdist_wheel.root_is_pure = True
+			log.error('error: building extensions failed: %s' % e)
+
+cmdclass = {"release": release}
+
+if ext_modules:
+    cmdclass["build_ext"] = cython_build_ext
+
+
+setup_params = dict(
 	name="fonttools",
-	version="3.21.0.dev0",
+	version="4.26.3.dev0",
 	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="OpenSource, BSD-style",
+	license="MIT",
 	platforms=["Any"],
+	python_requires=">=3.6",
 	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',
-	],
+	ext_modules=ext_modules,
+	setup_requires=setup_requires,
+	extras_require=extras_require,
 	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,
-	},
+	cmdclass=cmdclass,
 	**classifiers
 )
+
+
+if __name__ == "__main__":
+	setup(**setup_params)
diff --git a/tox.ini b/tox.ini
index cb1ab91..bcbeeed 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,29 +1,36 @@
 [tox]
-envlist = py{27,36}-cov, htmlcov
+minversion = 3.0
+envlist = mypy, py3{6,7,8,9}-cov, htmlcov
+skip_missing_interpreters=true
 
 [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}
+setenv =
+    cy: FONTTOOLS_WITH_CYTHON=1
+# use 'download = true' to have tox install the latest pip inside the virtualenv.
+# We need this to be able to install skia-pathops on Linux, which uses a
+# relatively recent 'manylinux2014' platform tag.
+# https://github.com/tox-dev/tox/issues/791#issuecomment-518713438
+download = true
 deps =
     cov: coverage>=4.3
     pytest
+    pytest-randomly
     -rrequirements.txt
-install_command =
-    pip install -v {opts} {packages}
+    !nolxml: lxml==4.6.1
+extras =
+    ufo
+    woff
+    unicode
+    interpolatable
+    !nolxml: lxml
 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.
+    cy: python -c "from fontTools.cu2qu.cu2qu import COMPILED; assert COMPILED"
+    !cy: python -c "from fontTools.cu2qu.cu2qu import COMPILED; assert not COMPILED"
+    # test with or without coverage, passing extra positonal args to pytest
     cov: coverage run --parallel-mode -m pytest {posargs}
-    nocov: pytest {posargs}
+    !cov: pytest {posargs:Tests fontTools}
 
 [testenv:htmlcov]
-basepython = {env:TOXPYTHON:python3.6}
 deps =
     coverage>=4.3
 skip_install = true
@@ -31,9 +38,15 @@
     coverage combine
     coverage html
 
+[testenv:mypy]
+deps =
+    -r dev-requirements.txt
+skip_install = true
+commands =
+    mypy
+
 [testenv:codecov]
 passenv = *
-basepython = {env:TOXPYTHON:python}
 deps =
     coverage>=4.3
     codecov
@@ -43,11 +56,17 @@
     coverage combine
     codecov --env TOXENV
 
+[testenv:package_readme]
+description = check that the long description is valid (need for PyPi)
+deps = twine >= 1.12.1
+       pip >= 18.0.0
+skip_install = true
+extras =
+commands = pip wheel -w {envtmpdir}/build --no-deps .
+           twine check {envtmpdir}/build/*
+
 [testenv:bdist]
-basepython = {env:TOXPYTHON:python3.6}
 deps =
-    pygments
-    docutils
     setuptools
     wheel
 skip_install = true
@@ -57,12 +76,20 @@
 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 -c 'import shutil; shutil.rmtree("dist", ignore_errors=True)'
     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
+
+[testenv:pypi]
+deps =
+    {[testenv:bdist]deps}
+    twine
+skip_install = true
+passenv = TWINE_USERNAME TWINE_PASSWORD
+commands =
+    {[testenv:bdist]commands}
+    twine upload dist/*.whl dist/*.zip