Upgrade fonttools from 3.31.0 to 3.35.0

Ran `tools/external_updater/updater.sh update fonttools`.

Test: builds
Change-Id: I40f991612bf5a41fdd90fc298aef7b02e5a283ad
diff --git a/.appveyor.yml b/.appveyor.yml
index e6d53c6..f4b2933 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -3,12 +3,17 @@
     - JOB: "2.7 32-bit"
       PYTHON_HOME: "C:\\Python27"
 
-    - JOB: "3.6 64-bit"
-      PYTHON_HOME: "C:\\Python36-x64"
-
     - JOB: "3.7 64-bit"
       PYTHON_HOME: "C:\\Python37-x64"
 
+branches:
+  only:
+    - master
+    # We want to build wip/* branches since these are not usually used for PRs
+    - /^wip\/.*$/
+    # We want to build version tags as well.
+    - /^\d+\.\d+.*$/
+
 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
diff --git a/.travis.yml b/.travis.yml
index 4223e95..0951eaf 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,14 @@
     - TWINE_USERNAME="anthrotype"
     - secure: PJuCmlDuwnojiw3QuDhfNAaU4f/yeJcEcRzJAudA66bwZK7hvxV7Tiy9A17Bm6yO0HbJmmyjsIr8h2e7/PyY6QCaV8RqcMDkQ0UraU16pRsihp0giVXJoWscj2sCP4cNDOBVwSaGAX8yZ2OONc5srESywghzcy8xmgw6O+XFqx4=
 
+branches:
+  only:
+    - master
+    # We want to build wip/* branches since these are not usually used for PRs
+    - /^wip\/.*$/
+    # We want to build version tags as well.
+    - /^\d+\.\d+.*$/
+
 matrix:
   fast_finish: true
   exclude:
@@ -18,7 +26,7 @@
       env: TOXENV=py35-cov
     - python: 3.6
       env:
-        - TOXENV=py36-cov
+        - TOXENV=py36-cov,package_readme
         - BUILD_DIST=true
     - python: 3.7
       env: TOXENV=py37-cov
diff --git a/.travis/after_success.sh b/.travis/after_success.sh
old mode 100755
new mode 100644
diff --git a/.travis/before_install.sh b/.travis/before_install.sh
old mode 100755
new mode 100644
diff --git a/.travis/install.sh b/.travis/install.sh
old mode 100755
new mode 100644
diff --git a/.travis/run.sh b/.travis/run.sh
old mode 100755
new mode 100644
index c6c1fea..ffb0ef7
--- a/.travis/run.sh
+++ b/.travis/run.sh
@@ -11,4 +11,10 @@
 
 # re-run all the XML-related tests, this time without lxml but using the
 # built-in ElementTree library.
-tox -e ${TOXENV:-py}-nolxml -- Tests/ufoLib Tests/misc/etree_test.py Tests/misc/plistlib_test.py
+if [ -z "$TOXENV" ]; then
+    TOXENV="py-nolxml"
+else
+    # strip additional tox envs after the comma, add -nolxml factor
+    TOXENV="${TOXENV%,*}-nolxml"
+fi
+tox -e $TOXENV -- Tests/ufoLib Tests/misc/etree_test.py Tests/misc/plistlib_test.py
diff --git a/Doc/source/designspaceLib/readme.rst b/Doc/source/designspaceLib/readme.rst
index 5af5d63..06bf46f 100644
--- a/Doc/source/designspaceLib/readme.rst
+++ b/Doc/source/designspaceLib/readme.rst
@@ -278,16 +278,17 @@
 -  ``labelNames``: dict. When defining a non-registered axis, it will be
    necessary to define user-facing readable names for the axis. Keyed by
    xml:lang code. Varlib.
--  ``minimum``: number. The minimum value for this axis. MutatorMath +
-   Varlib.
--  ``maximum``: number. The maximum value for this axis. MutatorMath +
-   Varlib.
--  ``default``: number. The default value for this axis, i.e. when a new
-   location is created, this is the value this axis will get.
+-  ``minimum``: number. The minimum value for this axis in user space.
    MutatorMath + Varlib.
--  ``map``: list of input / output values that can describe a warp of
-   user space to designspace coordinates. If no map values are present,
-   it is assumed it is [(minimum, minimum), (maximum, maximum)]. Varlib.
+-  ``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
 
diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py
index 10eab30..e922c48 100644
--- a/Lib/fontTools/__init__.py
+++ b/Lib/fontTools/__init__.py
@@ -5,6 +5,6 @@
 
 log = logging.getLogger(__name__)
 
-version = __version__ = "3.31.0"
+version = __version__ = "3.35.0"
 
 __all__ = ["version", "log", "configLogger"]
diff --git a/Lib/fontTools/cffLib/__init__.py b/Lib/fontTools/cffLib/__init__.py
index 83526ed..4ad0e44 100644
--- a/Lib/fontTools/cffLib/__init__.py
+++ b/Lib/fontTools/cffLib/__init__.py
@@ -623,11 +623,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:
@@ -2336,7 +2331,7 @@
 
 	def decompileAllCharStrings(self):
 		# Make sure that all the Private Dicts have been instantiated.
-		for charString in self.CharStrings.values():
+		for i, charString in enumerate(self.CharStrings.values()):
 			try:
 				charString.decompile()
 			except:
@@ -2424,10 +2419,19 @@
 		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)
 
+	def __getattr__(self, name):
+		if name == "in_cff2":
+			return self._isCFF2
+		value = BaseDict.__getattr__(self, name)
+		return value
+
 	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 5a6942d..caf8c3b 100644
--- a/Lib/fontTools/cffLib/specializer.py
+++ b/Lib/fontTools/cffLib/specializer.py
@@ -21,6 +21,7 @@
 		program.append(token)
 	return program
 
+
 def programToString(program):
 	return ' '.join(str(x) for x in program)
 
@@ -62,6 +63,7 @@
 		commands.append(('', stack))
 	return commands
 
+
 def commandsToProgram(commands):
 	"""Takes a commands list as returned by programToCommands() and converts
 	it back to a T2CharString program list."""
@@ -415,7 +417,9 @@
 				continue
 
 			# Merge adjacent hlineto's and vlineto's.
-			if i and op in {'hlineto', 'vlineto'} and op == commands[i-1][0]:
+			if (i and op in {'hlineto', 'vlineto'} and
+					(op == commands[i-1][0]) and
+					(not isinstance(args[0], list))):
 				_, other_args = commands[i-1]
 				assert len(args) == 1 and len(other_args) == 1
 				commands[i-1] = (op, [other_args[0]+args[0]])
diff --git a/Lib/fontTools/designspaceLib/__init__.py b/Lib/fontTools/designspaceLib/__init__.py
index 24c2247..d9da48c 100644
--- a/Lib/fontTools/designspaceLib/__init__.py
+++ b/Lib/fontTools/designspaceLib/__init__.py
@@ -673,12 +673,6 @@
         self.readInstances()
         self.readLib()
 
-    def getSourcePaths(self, makeGlyphs=True, makeKerning=True, makeInfo=True):
-        paths = []
-        for name in self.documentObject.sources.keys():
-            paths.append(self.documentObject.sources[name][0].path)
-        return paths
-
     def readRules(self):
         # we also need to read any conditions that are outside of a condition set.
         rules = []
@@ -1053,6 +1047,8 @@
         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)
@@ -1061,6 +1057,8 @@
             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()
@@ -1113,21 +1111,11 @@
 
 
         """
+        assert self.path is not None
         for descriptor in self.sources + self.instances:
-            # check what the relative path really should be?
-            expectedFilename = None
-            if descriptor.path is not None and self.path is not None:
-                expectedFilename = self._posixRelativePath(descriptor.path)
-
-            # 3
-            if descriptor.filename is None and descriptor.path is not None and self.path is not None:
+            if descriptor.path is not None:
+                # case 3 and 4: filename gets updated and relativized
                 descriptor.filename = self._posixRelativePath(descriptor.path)
-                continue
-
-            # 4
-            if descriptor.filename is not None and descriptor.path is not None and self.path is not None:
-                if descriptor.filename is not expectedFilename:
-                    descriptor.filename = expectedFilename
 
     def addSource(self, sourceDescriptor):
         self.sources.append(sourceDescriptor)
diff --git a/Lib/fontTools/fontBuilder.py b/Lib/fontTools/fontBuilder.py
new file mode 100644
index 0000000..7051982
--- /dev/null
+++ b/Lib/fontTools/fontBuilder.py
@@ -0,0 +1,765 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+
+__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.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", "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
+
+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.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", "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
+
+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, {})
+
+metrics = {}
+for gn, advanceWidth in advanceWidths.items():
+    metrics[gn] = (advanceWidth, 100)  # XXX lsb from glyph
+fb.setupHorizontalMetrics(metrics)
+
+fb.setupHorizontalHeader(ascent=824, descent=200)
+fb.setupNameTable(nameStrings)
+fb.setupOS2()
+fb.setupPost()
+
+fb.save("test.otf")
+```
+"""
+
+from .misc.py23 import *
+from .ttLib import TTFont, newTable
+from .ttLib.tables._c_m_a_p import cmap_classes
+from .ttLib.tables._n_a_m_e import NameRecord, makeName
+from .misc.timeTools import timestampNow
+import struct
+
+
+_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 = 2,   # just kerning
+    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, 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.
+        """
+        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)
+
+        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, basestring):
+                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.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
+        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)
+        self.font["CFF2"].cff.topDictIndex[0].VarStore = 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):
+        addFvar(self.font, axes, instances)
+
+    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.
+        """
+        postTable = self._initTableWithValues("post", _postDefaults, values)
+        if self.isTTF 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 a dummy DSIG table to the font to make some MS applications
+        happy. This does not properly sign the font.
+        """
+        from .ttLib.tables.D_S_I_G_ import SignatureRecord
+
+        sig = SignatureRecord()
+        sig.ulLength = 20
+        sig.cbSignature = 12
+        sig.usReserved2 = 0
+        sig.usReserved1 = 0
+        sig.pkcs7 = b'\xd3M4\xd3M5\xd3M4\xd3M4'
+        sig.ulFormat = 1
+        sig.ulOffset = 20
+
+        values = dict(
+            ulVersion = 1,
+            usFlag = 1,
+            usNumSigs = 1,
+            signatureRecords = [sig],
+        )
+        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 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 .misc.py23 import Tag, tounicode
+    from .ttLib.tables._f_v_a_r import Axis, NamedInstance
+
+    assert axes
+
+    fvar = newTable('fvar')
+    nameTable = font['name']
+
+    for tag, minValue, defaultValue, maxValue, name in axes:
+        axis = Axis()
+        axis.axisTag = Tag(tag)
+        axis.minValue, axis.defaultValue, axis.maxValue = minValue, defaultValue, maxValue
+        axis.axisNameID = nameTable.addName(tounicode(name))
+        fvar.axes.append(axis)
+
+    for instance in instances:
+        coordinates = instance['location']
+        name = tounicode(instance['stylename'])
+        psname = instance.get('postscriptfontname')
+
+        inst = NamedInstance()
+        inst.subfamilyNameID = nameTable.addName(name)
+        if psname is not None:
+            psname = tounicode(psname)
+            inst.postscriptNameID = nameTable.addName(psname)
+        inst.coordinates = coordinates
+        fvar.instances.append(inst)
+
+    font['fvar'] = fvar
diff --git a/Lib/fontTools/merge.py b/Lib/fontTools/merge.py
index 7dcb8b5..1005b85 100644
--- a/Lib/fontTools/merge.py
+++ b/Lib/fontTools/merge.py
@@ -498,7 +498,11 @@
 	self = otTables.Script()
 	self.LangSysRecord = lsrecords
 	self.LangSysCount = len(lsrecords)
-	self.DefaultLangSys = mergeLangSyses([s.DefaultLangSys for s in lst if s.DefaultLangSys])
+	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):
diff --git a/Lib/fontTools/misc/dictTools.py b/Lib/fontTools/misc/dictTools.py
new file mode 100644
index 0000000..db5d658
--- /dev/null
+++ b/Lib/fontTools/misc/dictTools.py
@@ -0,0 +1,68 @@
+"""Misc dict tools."""
+
+from __future__ import print_function, absolute_import, division
+from fontTools.misc.py23 import *
+
+__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/intTools.py b/Lib/fontTools/misc/intTools.py
new file mode 100644
index 0000000..9eb2f0f
--- /dev/null
+++ b/Lib/fontTools/misc/intTools.py
@@ -0,0 +1,18 @@
+"""Misc integer tools."""
+
+from __future__ import print_function, absolute_import, division
+from fontTools.misc.py23 import *
+
+__all__ = ['popCount']
+
+
+def popCount(v):
+    """Return number of 1 bits in an integer."""
+
+    if v > 0xFFFFFFFF:
+        return popCount(v >> 32) + popCount(v & 0xFFFFFFFF)
+
+    # HACKMEM 169
+    y = (v >> 1) & 0xDB6DB6DB
+    y = v - y - ((y >> 1) & 0xDB6DB6DB)
+    return (((y + (y >> 3)) & 0xC71C71C7) % 0x3F)
diff --git a/Lib/fontTools/misc/macRes.py b/Lib/fontTools/misc/macRes.py
index 20bfa71..db832ec 100644
--- a/Lib/fontTools/misc/macRes.py
+++ b/Lib/fontTools/misc/macRes.py
@@ -33,6 +33,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)
diff --git a/Lib/fontTools/misc/plistlib.py b/Lib/fontTools/misc/plistlib.py
index fe69593..a0e1003 100644
--- a/Lib/fontTools/misc/plistlib.py
+++ b/Lib/fontTools/misc/plistlib.py
@@ -7,6 +7,11 @@
 from numbers import Integral
 
 try:
+    from collections.abc import Mapping # python >= 3.3
+except ImportError:
+    from collections import Mapping
+
+try:
     from functools import singledispatch
 except ImportError:
     try:
@@ -384,7 +389,7 @@
     _make_element.register(bool)(_bool_element)
     _make_element.register(Integral)(_integer_element)
     _make_element.register(float)(_real_element)
-    _make_element.register(dict)(_dict_element)
+    _make_element.register(Mapping)(_dict_element)
     _make_element.register(list)(_array_element)
     _make_element.register(tuple)(_array_element)
     _make_element.register(datetime)(_date_element)
@@ -404,7 +409,7 @@
             return _integer_element(value, ctx)
         elif isinstance(value, float):
             return _real_element(value, ctx)
-        elif isinstance(value, dict):
+        elif isinstance(value, Mapping):
             return _dict_element(value, ctx)
         elif isinstance(value, (list, tuple)):
             return _array_element(value, ctx)
diff --git a/Lib/fontTools/misc/psCharStrings.py b/Lib/fontTools/misc/psCharStrings.py
index a92802b..6881074 100644
--- a/Lib/fontTools/misc/psCharStrings.py
+++ b/Lib/fontTools/misc/psCharStrings.py
@@ -223,9 +223,16 @@
 	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.":
@@ -234,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:
@@ -267,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()
@@ -311,7 +306,6 @@
 			else:
 				pushToStack(token)
 		if needsDecompilation:
-			self.check_program(program)
 			charString.setProgram(program)
 		del self.callingStack[-1]
 
@@ -424,7 +418,7 @@
 		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()
@@ -463,8 +457,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
 
@@ -477,6 +471,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:
@@ -503,9 +499,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):
@@ -705,12 +701,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
@@ -947,7 +937,6 @@
 	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:
@@ -979,7 +968,8 @@
 	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
 
@@ -988,20 +978,11 @@
 		self.draw(boundsPen)
 		return boundsPen.bounds
 
-	def check_program(self, program, isCFF2=False):
-		if isCFF2:
-			if self.program:
-				assert self.program[-1] not in ("seac",), "illegal CFF2 CharString Termination"
-		else:
-			assert self.program, "illegal CharString: decompiled to empty program"
-			assert self.program[-1] in ("endchar", "return", "callsubr", "callgsubr", "seac"), "illegal CharString"
-
 	def compile(self, isCFF2=False):
 		if self.bytecode is not None:
 			return
 		opcodes = self.opcodes
 		program = self.program
-		self.check_program(program, isCFF2=isCFF2)
 		bytecode = []
 		encodeInt = self.getIntEncoder()
 		encodeFixed = self.getFixedEncoder()
@@ -1148,9 +1129,6 @@
 				program.append(token)
 		self.setProgram(program)
 
-class CFF2Subr(T2CharString):
-	isCFF2 = True
-
 class T1CharString(T2CharString):
 
 	operandEncoding = t1OperandEncoding
diff --git a/Lib/fontTools/mtiLib/__init__.py b/Lib/fontTools/mtiLib/__init__.py
index d4f4880..beec5cb 100644
--- a/Lib/fontTools/mtiLib/__init__.py
+++ b/Lib/fontTools/mtiLib/__init__.py
@@ -1174,7 +1174,8 @@
 		del args[0]
 	for f in args:
 		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=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/pens/pointPen.py b/Lib/fontTools/pens/pointPen.py
index 641eb44..415972f 100644
--- a/Lib/fontTools/pens/pointPen.py
+++ b/Lib/fontTools/pens/pointPen.py
@@ -61,7 +61,7 @@
 	def __init__(self):
 		self.currentPath = None
 
-	def beginPath(self, **kwargs):
+	def beginPath(self, identifier=None, **kwargs):
 		assert self.currentPath is None
 		self.currentPath = []
 
@@ -140,7 +140,8 @@
 
 		self._flushContour(segments)
 
-	def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
+	def addPoint(self, pt, segmentType=None, smooth=False, name=None,
+				 identifier=None, **kwargs):
 		self.currentPath.append((pt, segmentType, smooth, name, kwargs))
 
 
@@ -313,22 +314,29 @@
 		for pt, segmentType, smooth, name, kwargs in points:
 			self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs)
 
-	def beginPath(self):
+	def beginPath(self, identifier=None, **kwargs):
 		assert self._points is None
 		self._points = []
-		self._outPen.beginPath()
+		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, **kwargs):
+	def addPoint(self, pt, segmentType=None, smooth=False, name=None,
+				 identifier=None, **kwargs):
+		if identifier is not None:
+			kwargs["identifier"] = identifier
 		self._points.append((pt, segmentType, False, name, kwargs))
 
-	def addComponent(self, glyphName, transformation):
+	def addComponent(self, glyphName, transformation, identifier=None, **kwargs):
 		assert self._points is None
-		self._outPen.addComponent(glyphName, transformation)
+		if identifier is not None:
+			kwargs["identifier"] = identifier
+		self._outPen.addComponent(glyphName, transformation, **kwargs)
 
 
 class ReverseContourPointPen(AbstractPointPen):
diff --git a/Lib/fontTools/pens/t2CharStringPen.py b/Lib/fontTools/pens/t2CharStringPen.py
index 8cc5e08..2025fd5 100644
--- a/Lib/fontTools/pens/t2CharStringPen.py
+++ b/Lib/fontTools/pens/t2CharStringPen.py
@@ -9,26 +9,26 @@
 from fontTools.cffLib.specializer import specializeCommands, commandsToProgram
 
 
+def t2c_round(number, tolerance=0.5):
+    if tolerance == 0:
+        return number  # no-op
+    rounded = otRound(number)
+    # return rounded integer if the tolerance >= 0.5, or if the absolute
+    # difference between the original float and the rounded integer is
+    # within the tolerance
+    if tolerance >= .5 or abs(rounded - number) <= tolerance:
+        return rounded
+    else:
+        # else return the value un-rounded
+        return number
+
 def makeRoundFunc(tolerance):
     if tolerance < 0:
         raise ValueError("Rounding tolerance must be positive")
 
-    def _round(number):
-        if tolerance == 0:
-            return number  # no-op
-        rounded = otRound(number)
-        # return rounded integer if the tolerance >= 0.5, or if the absolute
-        # difference between the original float and the rounded integer is
-        # within the tolerance
-        if tolerance >= .5 or abs(rounded - number) <= tolerance:
-            return rounded
-        else:
-            # else return the value un-rounded
-            return number
-
     def roundPoint(point):
         x, y = point
-        return _round(x), _round(y)
+        return t2c_round(x, tolerance), t2c_round(y, tolerance)
 
     return roundPoint
 
diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py
index 6ed54e5..2ed17cc 100644
--- a/Lib/fontTools/subset/__init__.py
+++ b/Lib/fontTools/subset/__init__.py
@@ -7,9 +7,9 @@
 from fontTools.misc.fixedTools import otRound
 from fontTools import ttLib
 from fontTools.ttLib.tables import otTables
-from fontTools.misc import psCharStrings
 from fontTools.pens.basePen import NullPen
 from fontTools.misc.loggingTools import Timer
+from fontTools.subset.cff import *
 from fontTools.varLib import varStore
 import sys
 import struct
@@ -378,12 +378,6 @@
 def _uniq_sort(l):
 	return sorted(set(l))
 
-def _set_update(s, *others):
-	# Jython's set.update only takes one other argument.
-	# Emulate real set.update...
-	for other in others:
-		s.update(other)
-
 def _dict_subset(d, glyphs):
 	return {g:d[g] for g in glyphs}
 
@@ -457,7 +451,7 @@
 def closure_glyphs(self, s, cur_glyphs):
 	for glyph, subst in self.mapping.items():
 		if glyph in cur_glyphs:
-			_set_update(s.glyphs, subst)
+			s.glyphs.update(subst)
 
 @_add_method(otTables.MultipleSubst)
 def subset_glyphs(self, s):
@@ -467,8 +461,8 @@
 
 @_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):
@@ -480,10 +474,10 @@
 
 @_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):
@@ -2087,517 +2081,6 @@
 	return True
 
 
-class _ClosureGlyphsT2Decompiler(psCharStrings.SimpleT2Decompiler):
-
-	def __init__(self, components, localSubrs, globalSubrs):
-		psCharStrings.SimpleT2Decompiler.__init__(self,
-							  localSubrs,
-							  globalSubrs)
-		self.components = components
-
-	def op_endchar(self, index):
-		args = self.popall()
-		if len(args) >= 4:
-			from fontTools.encodings.StandardEncoding import StandardEncoding
-			# endchar can do seac accent bulding; The T2 spec says it's deprecated,
-			# but recent software that shall remain nameless does output it.
-			adx, ady, bchar, achar = args[-4:]
-			baseGlyph = StandardEncoding[bchar]
-			accentGlyph = StandardEncoding[achar]
-			self.components.add(baseGlyph)
-			self.components.add(accentGlyph)
-
-@_add_method(ttLib.getTableClass('CFF '))
-def closure_glyphs(self, s):
-	cff = self.cff
-	assert len(cff) == 1
-	font = cff[cff.keys()[0]]
-	glyphSet = font.CharStrings
-
-	decompose = s.glyphs
-	while decompose:
-		components = set()
-		for g in decompose:
-			if g not in glyphSet:
-				continue
-			gl = glyphSet[g]
-
-			subrs = getattr(gl.private, "Subrs", [])
-			decompiler = _ClosureGlyphsT2Decompiler(components, subrs, gl.globalSubrs)
-			decompiler.execute(gl)
-		components -= s.glyphs
-		s.glyphs.update(components)
-		decompose = components
-
-@_add_method(ttLib.getTableClass('CFF '))
-def prune_pre_subset(self, font, options):
-	cff = self.cff
-	# CFF table must have one font only
-	cff.fontNames = cff.fontNames[:1]
-
-	if options.notdef_glyph and not options.notdef_outline:
-		for fontname in cff.keys():
-			font = cff[fontname]
-			c, fdSelectIndex = font.CharStrings.getItemAndSelector('.notdef')
-			if hasattr(font, 'FDArray') and font.FDArray is not None:
-				private = font.FDArray[fdSelectIndex].Private
-			else:
-				private = font.Private
-			dfltWdX = private.defaultWidthX
-			nmnlWdX = private.nominalWidthX
-			pen = NullPen()
-			c.draw(pen)  # this will set the charstring's width
-			if c.width != dfltWdX:
-				c.program = [c.width - nmnlWdX, 'endchar']
-			else:
-				c.program = ['endchar']
-
-	# Clear useless Encoding
-	for fontname in cff.keys():
-		font = cff[fontname]
-		# https://github.com/behdad/fonttools/issues/620
-		font.Encoding = "StandardEncoding"
-
-	return True # bool(cff.fontNames)
-
-@_add_method(ttLib.getTableClass('CFF '))
-def subset_glyphs(self, s):
-	cff = self.cff
-	for fontname in cff.keys():
-		font = cff[fontname]
-		cs = font.CharStrings
-
-		# Load all glyphs
-		for g in font.charset:
-			if g not in s.glyphs: continue
-			c, _ = cs.getItemAndSelector(g)
-
-		if cs.charStringsAreIndexed:
-			indices = [i for i,g in enumerate(font.charset) if g in s.glyphs]
-			csi = cs.charStringsIndex
-			csi.items = [csi.items[i] for i in indices]
-			del csi.file, csi.offsets
-			if hasattr(font, "FDSelect"):
-				sel = font.FDSelect
-				# XXX We want to set sel.format to None, such that the
-				# most compact format is selected. However, OTS was
-				# broken and couldn't parse a FDSelect format 0 that
-				# happened before CharStrings. As such, always force
-				# format 3 until we fix cffLib to always generate
-				# FDSelect after CharStrings.
-				# https://github.com/khaledhosny/ots/pull/31
-				#sel.format = None
-				sel.format = 3
-				sel.gidArray = [sel.gidArray[i] for i in indices]
-			cs.charStrings = {g:indices.index(v)
-					  for g,v in cs.charStrings.items()
-					  if g in s.glyphs}
-		else:
-			cs.charStrings = {g:v
-					  for g,v in cs.charStrings.items()
-					  if g in s.glyphs}
-		font.charset = [g for g in font.charset if g in s.glyphs]
-		font.numGlyphs = len(font.charset)
-
-	return True # any(cff[fontname].numGlyphs for fontname in cff.keys())
-
-@_add_method(psCharStrings.T2CharString)
-def subset_subroutines(self, subrs, gsubrs):
-	p = self.program
-	assert len(p)
-	for i in range(1, len(p)):
-		if p[i] == 'callsubr':
-			assert isinstance(p[i-1], int)
-			p[i-1] = subrs._used.index(p[i-1] + subrs._old_bias) - subrs._new_bias
-		elif p[i] == 'callgsubr':
-			assert isinstance(p[i-1], int)
-			p[i-1] = gsubrs._used.index(p[i-1] + gsubrs._old_bias) - gsubrs._new_bias
-
-@_add_method(psCharStrings.T2CharString)
-def drop_hints(self):
-	hints = self._hints
-
-	if hints.deletions:
-		p = self.program
-		for idx in reversed(hints.deletions):
-			del p[idx-2:idx]
-
-	if hints.has_hint:
-		assert not hints.deletions or hints.last_hint <= hints.deletions[0]
-		self.program = self.program[hints.last_hint:]
-		if hasattr(self, 'width'):
-			# Insert width back if needed
-			if self.width != self.private.defaultWidthX:
-				self.program.insert(0, self.width - self.private.nominalWidthX)
-
-	if hints.has_hintmask:
-		i = 0
-		p = self.program
-		while i < len(p):
-			if p[i] in ['hintmask', 'cntrmask']:
-				assert i + 1 <= len(p)
-				del p[i:i+2]
-				continue
-			i += 1
-
-	assert len(self.program)
-
-	del self._hints
-
-class _MarkingT2Decompiler(psCharStrings.SimpleT2Decompiler):
-
-	def __init__(self, localSubrs, globalSubrs):
-		psCharStrings.SimpleT2Decompiler.__init__(self,
-							  localSubrs,
-							  globalSubrs)
-		for subrs in [localSubrs, globalSubrs]:
-			if subrs and not hasattr(subrs, "_used"):
-				subrs._used = set()
-
-	def op_callsubr(self, index):
-		self.localSubrs._used.add(self.operandStack[-1]+self.localBias)
-		psCharStrings.SimpleT2Decompiler.op_callsubr(self, index)
-
-	def op_callgsubr(self, index):
-		self.globalSubrs._used.add(self.operandStack[-1]+self.globalBias)
-		psCharStrings.SimpleT2Decompiler.op_callgsubr(self, index)
-
-class _DehintingT2Decompiler(psCharStrings.T2WidthExtractor):
-
-	class Hints(object):
-		def __init__(self):
-			# Whether calling this charstring produces any hint stems
-			# Note that if a charstring starts with hintmask, it will
-			# have has_hint set to True, because it *might* produce an
-			# implicit vstem if called under certain conditions.
-			self.has_hint = False
-			# Index to start at to drop all hints
-			self.last_hint = 0
-			# Index up to which we know more hints are possible.
-			# Only relevant if status is 0 or 1.
-			self.last_checked = 0
-			# The status means:
-			# 0: after dropping hints, this charstring is empty
-			# 1: after dropping hints, there may be more hints
-			#	continuing after this
-			# 2: no more hints possible after this charstring
-			self.status = 0
-			# Has hintmask instructions; not recursive
-			self.has_hintmask = False
-			# List of indices of calls to empty subroutines to remove.
-			self.deletions = []
-		pass
-
-	def __init__(self, css, localSubrs, globalSubrs, nominalWidthX, defaultWidthX):
-		self._css = css
-		psCharStrings.T2WidthExtractor.__init__(
-			self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX)
-
-	def execute(self, charString):
-		old_hints = charString._hints if hasattr(charString, '_hints') else None
-		charString._hints = self.Hints()
-
-		psCharStrings.T2WidthExtractor.execute(self, charString)
-
-		hints = charString._hints
-
-		if hints.has_hint or hints.has_hintmask:
-			self._css.add(charString)
-
-		if hints.status != 2:
-			# Check from last_check, make sure we didn't have any operators.
-			for i in range(hints.last_checked, len(charString.program) - 1):
-				if isinstance(charString.program[i], str):
-					hints.status = 2
-					break
-				else:
-					hints.status = 1 # There's *something* here
-			hints.last_checked = len(charString.program)
-
-		if old_hints:
-			assert hints.__dict__ == old_hints.__dict__
-
-	def op_callsubr(self, index):
-		subr = self.localSubrs[self.operandStack[-1]+self.localBias]
-		psCharStrings.T2WidthExtractor.op_callsubr(self, index)
-		self.processSubr(index, subr)
-
-	def op_callgsubr(self, index):
-		subr = self.globalSubrs[self.operandStack[-1]+self.globalBias]
-		psCharStrings.T2WidthExtractor.op_callgsubr(self, index)
-		self.processSubr(index, subr)
-
-	def op_hstem(self, index):
-		psCharStrings.T2WidthExtractor.op_hstem(self, index)
-		self.processHint(index)
-	def op_vstem(self, index):
-		psCharStrings.T2WidthExtractor.op_vstem(self, index)
-		self.processHint(index)
-	def op_hstemhm(self, index):
-		psCharStrings.T2WidthExtractor.op_hstemhm(self, index)
-		self.processHint(index)
-	def op_vstemhm(self, index):
-		psCharStrings.T2WidthExtractor.op_vstemhm(self, index)
-		self.processHint(index)
-	def op_hintmask(self, index):
-		rv = psCharStrings.T2WidthExtractor.op_hintmask(self, index)
-		self.processHintmask(index)
-		return rv
-	def op_cntrmask(self, index):
-		rv = psCharStrings.T2WidthExtractor.op_cntrmask(self, index)
-		self.processHintmask(index)
-		return rv
-
-	def processHintmask(self, index):
-		cs = self.callingStack[-1]
-		hints = cs._hints
-		hints.has_hintmask = True
-		if hints.status != 2:
-			# Check from last_check, see if we may be an implicit vstem
-			for i in range(hints.last_checked, index - 1):
-				if isinstance(cs.program[i], str):
-					hints.status = 2
-					break
-			else:
-				# We are an implicit vstem
-				hints.has_hint = True
-				hints.last_hint = index + 1
-				hints.status = 0
-		hints.last_checked = index + 1
-
-	def processHint(self, index):
-		cs = self.callingStack[-1]
-		hints = cs._hints
-		hints.has_hint = True
-		hints.last_hint = index
-		hints.last_checked = index
-
-	def processSubr(self, index, subr):
-		cs = self.callingStack[-1]
-		hints = cs._hints
-		subr_hints = subr._hints
-
-		# Check from last_check, make sure we didn't have
-		# any operators.
-		if hints.status != 2:
-			for i in range(hints.last_checked, index - 1):
-				if isinstance(cs.program[i], str):
-					hints.status = 2
-					break
-			hints.last_checked = index
-
-		if hints.status != 2:
-			if subr_hints.has_hint:
-				hints.has_hint = True
-
-			# Decide where to chop off from
-			if subr_hints.status == 0:
-				hints.last_hint = index
-			else:
-				hints.last_hint = index - 2 # Leave the subr call in
-		elif subr_hints.status == 0:
-			hints.deletions.append(index)
-
-		hints.status = max(hints.status, subr_hints.status)
-
-class _DesubroutinizingT2Decompiler(psCharStrings.SimpleT2Decompiler):
-
-	def __init__(self, localSubrs, globalSubrs):
-		psCharStrings.SimpleT2Decompiler.__init__(self,
-							  localSubrs,
-							  globalSubrs)
-
-	def execute(self, charString):
-		# Note: Currently we recompute _desubroutinized each time.
-		# This is more robust in some cases, but in other places we assume
-		# that each subroutine always expands to the same code, so
-		# maybe it doesn't matter. To speed up we can just not
-		# recompute _desubroutinized if it's there. For now I just
-		# double-check that it desubroutinized to the same thing.
-		old_desubroutinized = charString._desubroutinized if hasattr(charString, '_desubroutinized') else None
-
-		charString._patches = []
-		psCharStrings.SimpleT2Decompiler.execute(self, charString)
-		desubroutinized = charString.program[:]
-		for idx,expansion in reversed (charString._patches):
-			assert idx >= 2
-			assert desubroutinized[idx - 1] in ['callsubr', 'callgsubr'], desubroutinized[idx - 1]
-			assert type(desubroutinized[idx - 2]) == int
-			if expansion[-1] == 'return':
-				expansion = expansion[:-1]
-			desubroutinized[idx-2:idx] = expansion
-		if 'endchar' in desubroutinized:
-			# Cut off after first endchar
-			desubroutinized = desubroutinized[:desubroutinized.index('endchar') + 1]
-		else:
-			if not len(desubroutinized) or desubroutinized[-1] != 'return':
-				desubroutinized.append('return')
-
-		charString._desubroutinized = desubroutinized
-		del charString._patches
-
-		if old_desubroutinized:
-			assert desubroutinized == old_desubroutinized
-
-	def op_callsubr(self, index):
-		subr = self.localSubrs[self.operandStack[-1]+self.localBias]
-		psCharStrings.SimpleT2Decompiler.op_callsubr(self, index)
-		self.processSubr(index, subr)
-
-	def op_callgsubr(self, index):
-		subr = self.globalSubrs[self.operandStack[-1]+self.globalBias]
-		psCharStrings.SimpleT2Decompiler.op_callgsubr(self, index)
-		self.processSubr(index, subr)
-
-	def processSubr(self, index, subr):
-		cs = self.callingStack[-1]
-		cs._patches.append((index, subr._desubroutinized))
-
-
-@_add_method(ttLib.getTableClass('CFF '))
-def prune_post_subset(self, font, options):
-	cff = self.cff
-	for fontname in cff.keys():
-		font = cff[fontname]
-		cs = font.CharStrings
-
-		# Drop unused FontDictionaries
-		if hasattr(font, "FDSelect"):
-			sel = font.FDSelect
-			indices = _uniq_sort(sel.gidArray)
-			sel.gidArray = [indices.index (ss) for ss in sel.gidArray]
-			arr = font.FDArray
-			arr.items = [arr[i] for i in indices]
-			del arr.file, arr.offsets
-
-		# Desubroutinize if asked for
-		if options.desubroutinize:
-			for g in font.charset:
-				c, _ = cs.getItemAndSelector(g)
-				c.decompile()
-				subrs = getattr(c.private, "Subrs", [])
-				decompiler = _DesubroutinizingT2Decompiler(subrs, c.globalSubrs)
-				decompiler.execute(c)
-				c.program = c._desubroutinized
-
-		# Drop hints if not needed
-		if not options.hinting:
-
-			# This can be tricky, but doesn't have to. What we do is:
-			#
-			# - Run all used glyph charstrings and recurse into subroutines,
-			# - For each charstring (including subroutines), if it has any
-			#   of the hint stem operators, we mark it as such.
-			#   Upon returning, for each charstring we note all the
-			#   subroutine calls it makes that (recursively) contain a stem,
-			# - Dropping hinting then consists of the following two ops:
-			#   * Drop the piece of the program in each charstring before the
-			#     last call to a stem op or a stem-calling subroutine,
-			#   * Drop all hintmask operations.
-			# - It's trickier... A hintmask right after hints and a few numbers
-			#    will act as an implicit vstemhm. As such, we track whether
-			#    we have seen any non-hint operators so far and do the right
-			#    thing, recursively... Good luck understanding that :(
-			css = set()
-			for g in font.charset:
-				c, _ = cs.getItemAndSelector(g)
-				c.decompile()
-				subrs = getattr(c.private, "Subrs", [])
-				decompiler = _DehintingT2Decompiler(css, subrs, c.globalSubrs,
-								    c.private.nominalWidthX,
-								    c.private.defaultWidthX)
-				decompiler.execute(c)
-				c.width = decompiler.width
-			for charstring in css:
-				charstring.drop_hints()
-			del css
-
-			# Drop font-wide hinting values
-			all_privs = []
-			if hasattr(font, 'FDSelect'):
-				all_privs.extend(fd.Private for fd in font.FDArray)
-			else:
-				all_privs.append(font.Private)
-			for priv in all_privs:
-				for k in ['BlueValues', 'OtherBlues',
-					  'FamilyBlues', 'FamilyOtherBlues',
-					  'BlueScale', 'BlueShift', 'BlueFuzz',
-					  'StemSnapH', 'StemSnapV', 'StdHW', 'StdVW',
-					  'ForceBold', 'LanguageGroup', 'ExpansionFactor']:
-					if hasattr(priv, k):
-						setattr(priv, k, None)
-
-		# Renumber subroutines to remove unused ones
-
-		# Mark all used subroutines
-		for g in font.charset:
-			c, _ = cs.getItemAndSelector(g)
-			subrs = getattr(c.private, "Subrs", [])
-			decompiler = _MarkingT2Decompiler(subrs, c.globalSubrs)
-			decompiler.execute(c)
-
-		all_subrs = [font.GlobalSubrs]
-		if hasattr(font, 'FDSelect'):
-			all_subrs.extend(fd.Private.Subrs for fd in font.FDArray if hasattr(fd.Private, 'Subrs') and fd.Private.Subrs)
-		elif hasattr(font.Private, 'Subrs') and font.Private.Subrs:
-			all_subrs.append(font.Private.Subrs)
-
-		subrs = set(subrs) # Remove duplicates
-
-		# Prepare
-		for subrs in all_subrs:
-			if not hasattr(subrs, '_used'):
-				subrs._used = set()
-			subrs._used = _uniq_sort(subrs._used)
-			subrs._old_bias = psCharStrings.calcSubrBias(subrs)
-			subrs._new_bias = psCharStrings.calcSubrBias(subrs._used)
-
-		# Renumber glyph charstrings
-		for g in font.charset:
-			c, _ = cs.getItemAndSelector(g)
-			subrs = getattr(c.private, "Subrs", [])
-			c.subset_subroutines (subrs, font.GlobalSubrs)
-
-		# Renumber subroutines themselves
-		for subrs in all_subrs:
-			if subrs == font.GlobalSubrs:
-				if not hasattr(font, 'FDSelect') and hasattr(font.Private, 'Subrs'):
-					local_subrs = font.Private.Subrs
-				else:
-					local_subrs = []
-			else:
-				local_subrs = subrs
-
-			subrs.items = [subrs.items[i] for i in subrs._used]
-			if hasattr(subrs, 'file'):
-				del subrs.file
-			if hasattr(subrs, 'offsets'):
-				del subrs.offsets
-
-			for subr in subrs.items:
-				subr.subset_subroutines (local_subrs, font.GlobalSubrs)
-
-		# Delete local SubrsIndex if empty
-		if hasattr(font, 'FDSelect'):
-			for fd in font.FDArray:
-				_delete_empty_subrs(fd.Private)
-		else:
-			_delete_empty_subrs(font.Private)
-
-		# Cleanup
-		for subrs in all_subrs:
-			del subrs._used, subrs._old_bias, subrs._new_bias
-
-	return True
-
-
-def _delete_empty_subrs(private_dict):
-	if hasattr(private_dict, 'Subrs') and not private_dict.Subrs:
-		if 'Subrs' in private_dict.rawDict:
-			del private_dict.rawDict['Subrs']
-		del private_dict.Subrs
-
-
 @_add_method(ttLib.getTableClass('cmap'))
 def closure_glyphs(self, s):
 	tables = [t for t in self.tables if t.isUnicode()]
@@ -2691,7 +2174,8 @@
 				if inst.postscriptNameID != 0xFFFF])
 	stat = font.get('STAT')
 	if stat:
-		nameIDs.update([val_rec.ValueNameID for val_rec in stat.table.AxisValueArray.AxisValue])
+		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]
@@ -3257,7 +2741,8 @@
 			text += g[7:]
 			continue
 		if g.startswith('--text-file='):
-			text += open(g[12:], encoding='utf-8').read().replace('\n', '')
+			with open(g[12:], encoding='utf-8') as f:
+				text += f.read().replace('\n', '')
 			continue
 		if g.startswith('--unicodes='):
 			if g[11:] == '*':
@@ -3266,15 +2751,17 @@
 				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]))
+			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='):
-			for line in open(g[12:]).readlines():
-				gids.extend(parse_gids(line.split('#')[0]))
+			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:] == '*':
@@ -3283,8 +2770,9 @@
 				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]))
+			with open(g[14:]) as f:
+				for line in f.readlines():
+					glyphs.extend(parse_glyphs(line.split('#')[0]))
 			continue
 		glyphs.append(g)
 
diff --git a/Lib/fontTools/subset/cff.py b/Lib/fontTools/subset/cff.py
new file mode 100644
index 0000000..9a2b77e
--- /dev/null
+++ b/Lib/fontTools/subset/cff.py
@@ -0,0 +1,579 @@
+from fontTools.misc import psCharStrings
+from fontTools import ttLib
+from fontTools.pens.basePen import NullPen
+from fontTools.misc.fixedTools 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
+
+@_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
+	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 _DesubroutinizingT2Decompiler(psCharStrings.SimpleT2Decompiler):
+
+	def __init__(self, localSubrs, globalSubrs, private=None):
+		psCharStrings.SimpleT2Decompiler.__init__(self,
+							  localSubrs,
+							  globalSubrs, private)
+
+	def execute(self, charString):
+		if hasattr(charString, '_desubroutinized'):
+			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 processSubr(self, index, subr):
+		cs = self.callingStack[-1]
+		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()
+	else:
+		for fontname in cff.keys():
+			font = cff[fontname]
+			self.remove_unused_subroutines()
+
+	# Drop hints if not needed
+	if not options.hinting:
+		self.remove_hints()
+
+
+	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 Subrs!!!
+		if font.GlobalSubrs:
+			del font.GlobalSubrs
+		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']
+	self.remove_unused_subroutines()
+
+
+@_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/t1Lib/__init__.py b/Lib/fontTools/t1Lib/__init__.py
index 5da9cea..db5189a 100644
--- a/Lib/fontTools/t1Lib/__init__.py
+++ b/Lib/fontTools/t1Lib/__init__.py
@@ -108,11 +108,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'
@@ -164,9 +165,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:
@@ -179,35 +179,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:
@@ -244,8 +241,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
@@ -255,13 +251,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:
@@ -275,8 +268,6 @@
 					chunk = chunk[hexlinelen:]
 			else:
 				f.write(chunk)
-	finally:
-		f.close()
 
 
 # decryption tools
diff --git a/Lib/fontTools/ttLib/sfnt.py b/Lib/fontTools/ttLib/sfnt.py
index 6dc48ba..1c11de7 100644
--- a/Lib/fontTools/ttLib/sfnt.py
+++ b/Lib/fontTools/ttLib/sfnt.py
@@ -123,6 +123,30 @@
 	def close(self):
 		self.file.close()
 
+	def __deepcopy__(self, memo):
+		"""Overrides the default deepcopy of SFNTReader object, to make it work
+		in the case when TTFont is loaded with lazy=True, and thus reader holds a
+		reference to a file object which is not pickleable.
+		We work around it by manually copying the data into a in-memory stream.
+		"""
+		from copy import deepcopy
+
+		cls = self.__class__
+		obj = cls.__new__(cls)
+		for k, v in self.__dict__.items():
+			if k == "file":
+				pos = v.tell()
+				v.seek(0)
+				buf = BytesIO(v.read())
+				v.seek(pos)
+				buf.seek(pos)
+				if hasattr(v, "name"):
+					buf.name = v.name
+				obj.file = buf
+			else:
+				obj.__dict__[k] = deepcopy(v, memo)
+		return obj
+
 
 # default compression level for WOFF 1.0 tables and metadata
 ZLIB_COMPRESSION_LEVEL = 6
diff --git a/Lib/fontTools/ttLib/tables/F__e_a_t.py b/Lib/fontTools/ttLib/tables/F__e_a_t.py
index 22be4f6..ec497f2 100644
--- a/Lib/fontTools/ttLib/tables/F__e_a_t.py
+++ b/Lib/fontTools/ttLib/tables/F__e_a_t.py
@@ -58,8 +58,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__l_a_t.py b/Lib/fontTools/ttLib/tables/G__l_a_t.py
index 36ed6df..7d1f735 100644
--- a/Lib/fontTools/ttLib/tables/G__l_a_t.py
+++ b/Lib/fontTools/ttLib/tables/G__l_a_t.py
@@ -139,11 +139,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 +168,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 +176,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 +190,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 +210,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/S__i_l_f.py b/Lib/fontTools/ttLib/tables/S__i_l_f.py
index 44dd69b..e68b9b2 100644
--- a/Lib/fontTools/ttLib/tables/S__i_l_f.py
+++ b/Lib/fontTools/ttLib/tables/S__i_l_f.py
@@ -6,6 +6,7 @@
 from . import DefaultTable
 from . import grUtils
 from array import array
+from functools import reduce
 import struct, operator, warnings, re, sys
 
 Silf_hdr_format = '''
@@ -220,23 +221,23 @@
 
 instre = re.compile("^\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))]
             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)
@@ -334,7 +335,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)
@@ -427,7 +428,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:
@@ -453,8 +454,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 +465,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 +593,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 +602,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 +610,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 +681,7 @@
         self.rulePreContexts = []
         self.ruleSortKeys = []
         self.ruleConstraints = []
-        self.passConstraints = ""
+        self.passConstraints = b""
         self.actions = []
         self.stateTrans = []
         self.startStates = []
@@ -725,7 +726,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,9 +734,9 @@
 
     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:
             if sys.byteorder != "big": t.byteswap()
@@ -761,7 +762,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 +773,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 +840,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 +848,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..4671e13 100644
--- a/Lib/fontTools/ttLib/tables/S__i_l_l.py
+++ b/Lib/fontTools/ttLib/tables/S__i_l_l.py
@@ -42,8 +42,8 @@
                 self.langs[c].append(finfo[i])
 
     def compile(self, ttFont):
-        ldat = ""
-        fdat = ""
+        ldat = b""
+        fdat = b""
         offset = 0
         for c, inf in sorted(self.langs.items()):
             ldat += struct.pack(">4sHH", c.encode('utf8'), len(inf), 8 * (offset + len(self.langs) + 1))
diff --git a/Lib/fontTools/ttLib/tables/_k_e_r_n.py b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
index 98eb709..1e5140b 100644
--- a/Lib/fontTools/ttLib/tables/_k_e_r_n.py
+++ b/Lib/fontTools/ttLib/tables/_k_e_r_n.py
@@ -48,6 +48,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,7 +141,6 @@
 			">HHHH", data[:8])
 		data = data[8:]
 
-		nPairs = min(nPairs, len(data) // 6)
 		datas = array.array("H", data[:6 * nPairs])
 		if sys.byteorder != "big": datas.byteswap()
 		it = iter(datas)
@@ -153,6 +165,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)
 
@@ -175,6 +188,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/_n_a_m_e.py b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
index a30291c..488c4ea 100644
--- a/Lib/fontTools/ttLib/tables/_n_a_m_e.py
+++ b/Lib/fontTools/ttLib/tables/_n_a_m_e.py
@@ -161,7 +161,8 @@
 			raise ValueError("nameID must be less than 32768")
 		return nameID
 
-	def addMultilingualName(self, names, ttFont=None, nameID=None):
+	def addMultilingualName(self, names, ttFont=None, nameID=None,
+	                        windows=True, mac=True):
 		"""Add a multilingual name, returning its name ID
 
 		'names' is a dictionary with the name in multiple languages,
@@ -176,6 +177,9 @@
 
 		'nameID' is the name ID to be used, or None to let the library
 		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 not hasattr(self, 'names'):
 			self.names = []
@@ -184,15 +188,16 @@
 		# 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)
diff --git a/Lib/fontTools/ttLib/tables/grUtils.py b/Lib/fontTools/ttLib/tables/grUtils.py
index 1ce2c9e..d11ac4b 100644
--- a/Lib/fontTools/ttLib/tables/grUtils.py
+++ b/Lib/fontTools/ttLib/tables/grUtils.py
@@ -13,7 +13,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 +27,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 +47,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 +59,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/otData.py b/Lib/fontTools/ttLib/tables/otData.py
old mode 100755
new mode 100644
diff --git a/Lib/fontTools/ttLib/tables/ttProgram.py b/Lib/fontTools/ttLib/tables/ttProgram.py
index 182982f..7ffb37a 100644
--- a/Lib/fontTools/ttLib/tables/ttProgram.py
+++ b/Lib/fontTools/ttLib/tables/ttProgram.py
@@ -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)
diff --git a/Lib/fontTools/ttx.py b/Lib/fontTools/ttx.py
index 0e3eaf3..a785325 100644
--- a/Lib/fontTools/ttx.py
+++ b/Lib/fontTools/ttx.py
@@ -293,11 +293,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)
diff --git a/Lib/fontTools/ufoLib/__init__.py b/Lib/fontTools/ufoLib/__init__.py
old mode 100755
new mode 100644
index b1ece9c..d9a57c5
--- a/Lib/fontTools/ufoLib/__init__.py
+++ b/Lib/fontTools/ufoLib/__init__.py
@@ -5,6 +5,7 @@
 import logging
 import zipfile
 import enum
+from collections import OrderedDict
 import fs
 import fs.base
 import fs.subfs
@@ -57,6 +58,7 @@
 	"UFOLibError",
 	"UFOReader",
 	"UFOWriter",
+	"UFOReaderWriter",
 	"UFOFileStructure",
 	"fontInfoAttributesVersion1",
 	"fontInfoAttributesVersion2",
@@ -105,104 +107,92 @@
 # --------------
 
 
-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 datetimeAsTimestamp(dt)
+class _UFOBaseIO(object):
 
-
-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.getbytes(fsdecode(path))
-	except fs.errors.ResourceNotFound:
-		return None
-
-
-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(
-			"'%s' could not be read on %s: %s" % (fileName, 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:
+	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:
-			data = plistlib.dumps(obj)
+			dt = self.fs.getinfo(fsdecode(path), namespaces=["details"]).modified
+		except (fs.errors.MissingInfoNamespace, fs.errors.ResourceNotFound):
+			return None
+		else:
+			return datetimeAsTimestamp(dt)
+
+	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(
-				"'%s' could not be written on %s because "
-				"the data is not properly formatted: %s"
-				% (fileName, self.fs, e)
+				"'%s' could not be read on %s: %s" % (fileName, self.fs, e)
 			)
-		if self.fs.exists(fileName) and data == self.fs.getbytes(fileName):
-			return
-		self.fs.setbytes(fileName, data)
-	else:
-		with self.fs.openbin(fileName, mode="w") as fp:
+
+	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:
-				plistlib.dump(obj, fp)
+				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.getbytes(fileName):
+				return
+			self.fs.setbytes(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(object):
+class UFOReader(_UFOBaseIO):
 
 	"""
 	Read the various components of the .ufo.
@@ -280,6 +270,18 @@
 
 	# 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):
 		return self._formatVersion
 
@@ -291,7 +293,7 @@
 	fileStructure = property(
 		_get_fileStructure,
 		doc=(
-			"The current file structure of the UFO: "
+			"The file structure of the UFO: "
 			"either UFOFileStructure.ZIP or UFOFileStructure.PACKAGE"
 		)
 	)
@@ -347,9 +349,16 @@
 
 	# support methods
 
-	_getPlist = _getPlist
-	getFileModificationTime = _getFileModificationTime
-	readBytesFromPath = _readBytesFromPath
+	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.getbytes(fsdecode(path))
+		except fs.errors.ResourceNotFound:
+			return None
 
 	def getReadFileForPath(self, path, encoding=None):
 		"""
@@ -796,7 +805,7 @@
 # UFO Writer
 # ----------
 
-class UFOWriter(object):
+class UFOWriter(UFOReader):
 
 	"""
 	Write the various components of the .ufo.
@@ -821,6 +830,8 @@
 			path = path.__fspath__()
 
 		if isinstance(path, basestring):
+			# 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
@@ -946,7 +957,7 @@
 		self.layerContents = {}
 		if previousFormatVersion is not None and previousFormatVersion >= 3:
 			# already exists
-			self._readLayerContents(validate=validate)
+			self.layerContents = OrderedDict(self._readLayerContents(validate))
 		else:
 			# previous < 3
 			# imply the layer contents
@@ -957,46 +968,13 @@
 
 	# 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 the UFO is being written to (DEPRECATED).")
-
-	def _get_formatVersion(self):
-		return self._formatVersion
-
-	formatVersion = property(_get_formatVersion, doc="The format version of the UFO. This is set into metainfo.plist during __init__.")
-
 	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__.")
 
-	def _get_fileStructure(self):
-		return self._fileStructure
-
-	fileStructure = property(
-		_get_fileStructure,
-		doc=(
-			"The file structure of the destination UFO: "
-			"either UFOFileStrucure.ZIP or UFOFileStructure.PACKAGE"
-		)
-	)
-
 	# support methods for file system interaction
 
-	_getPlist = _getPlist
-	_writePlist = _writePlist
-	readBytesFromPath = _readBytesFromPath
-	getFileModificationTime = _getFileModificationTime
-
 	def copyFromReader(self, reader, sourcePath, destPath):
 		"""
 		Copy the sourcePath in the provided UFOReader to destPath
@@ -1332,25 +1310,6 @@
 
 	# glyph sets & layers
 
-	def _readLayerContents(self, validate):
-		"""
-		Rebuild the layer contents list by checking what glyph sets
-		are available on disk.
-
-		``validate`` will validate the data.
-		"""
-		# read the file on disk
-		raw = self._getPlist(LAYERCONTENTS_FILENAME)
-		contents = {}
-		if validate:
-			valid, error = layerContentsValidator(raw, self.fs)
-			if not valid:
-				raise UFOLibError(error)
-		for entry in raw:
-			layerName, directoryName = entry
-			contents[layerName] = directoryName
-		self.layerContents = contents
-
 	def writeLayerContents(self, layerOrder=None, validate=None):
 		"""
 		Write the layercontents.plist file. This method  *must* be called
@@ -1412,7 +1371,7 @@
 			raise UFOLibError("Only the default layer can be writen in UFO %d." % self.formatVersion)
 		# locate a layer name when None has been given
 		if layerName is None and defaultLayer:
-			for existingLayerName, directory in list(self.layerContents.items()):
+			for existingLayerName, directory in self.layerContents.items():
 				if directory == DEFAULT_GLYPHS_DIRNAME:
 					layerName = existingLayerName
 			if layerName is None:
@@ -1460,10 +1419,13 @@
 		# 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 list(self.layerContents.items()):
+			for existingLayerName, directory in self.layerContents.items():
 				if directory == DEFAULT_GLYPHS_DIRNAME:
 					if existingLayerName != layerName:
-						raise UFOLibError("Another layer is already mapped to the default directory.")
+						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
@@ -1476,7 +1438,7 @@
 			else:
 				# not caching this could be slightly expensive,
 				# but caching it will be cumbersome
-				existing = [d.lower() for d in list(self.layerContents.values())]
+				existing = {d.lower() for d in self.layerContents.values()}
 				if not isinstance(layerName, unicode):
 					try:
 						layerName = unicode(layerName)
@@ -1524,14 +1486,14 @@
 			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 list(self.layerContents.values()):
+			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 list(self.layerContents.values())]
+			existing = {name.lower() for name in self.layerContents.values()}
 			newDirectory = userNameToFileName(newLayerName, existing=existing, prefix="glyphs.")
 		# update the internal mapping
 		del self.layerContents[layerName]
@@ -1612,14 +1574,11 @@
 			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))
-		if self._shouldClose:
-			self.fs.close()
+		super(UFOWriter, self).close()
 
-	def __enter__(self):
-		return self
 
-	def __exit__(self, exc_type, exc_value, exc_tb):
-		self.close()
+# just an alias, makes it more explicit
+UFOReaderWriter = UFOWriter
 
 
 # ----------------
diff --git a/Lib/fontTools/ufoLib/glifLib.py b/Lib/fontTools/ufoLib/glifLib.py
old mode 100755
new mode 100644
index eae300b..f2648b8
--- a/Lib/fontTools/ufoLib/glifLib.py
+++ b/Lib/fontTools/ufoLib/glifLib.py
@@ -34,6 +34,7 @@
 	glyphLibValidator,
 )
 from fontTools.misc import etree
+from fontTools.ufoLib import _UFOBaseIO
 from fontTools.ufoLib.utils import integerTypes, numberTypes
 
 
@@ -88,7 +89,7 @@
 # Glyph Set
 # ---------
 
-class GlyphSet(object):
+class GlyphSet(_UFOBaseIO):
 
 	"""
 	GlyphSet manages a set of .glif files inside one directory.
@@ -169,9 +170,6 @@
 
 		self.rebuildContents()
 
-	# here we reuse the same methods from UFOReader/UFOWriter
-	from fontTools.ufoLib import _getPlist, _writePlist, _getFileModificationTime
-
 	def rebuildContents(self, validateRead=None):
 		"""
 		Rebuild the contents dict by loading contents.plist.
@@ -308,7 +306,7 @@
 		Raises KeyError if the glyphName is not in contents.plist.
 		"""
 		fileName = self.contents[glyphName]
-		return self._getFileModificationTime(fileName)
+		return self.getFileModificationTime(fileName)
 
 	# reading/writing API
 
diff --git a/Lib/fontTools/unicode.py b/Lib/fontTools/unicode.py
index 50dfc53..ef3ed69 100644
--- a/Lib/fontTools/unicode.py
+++ b/Lib/fontTools/unicode.py
@@ -18,8 +18,11 @@
 
 	def __init__(self, f):
 		if isinstance(f, basestring):
-			f = open(f)
-		self.codes = _makeunicodes(f)
+			with open(f) as fd:
+				codes = _makeunicodes(fd)
+		else:
+			codes = _makeunicodes(f)
+		self.codes = codes
 
 	def __getitem__(self, charCode):
 		try:
diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py
index 437324e..37d8d33 100644
--- a/Lib/fontTools/varLib/__init__.py
+++ b/Lib/fontTools/varLib/__init__.py
@@ -23,7 +23,7 @@
 from fontTools.misc.py23 import *
 from fontTools.misc.fixedTools import otRound
 from fontTools.misc.arrayTools import Vector
-from fontTools.ttLib import TTFont, newTable
+from fontTools.ttLib import TTFont, newTable, TTLibError
 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
@@ -32,7 +32,7 @@
 from fontTools.ttLib.tables import otTables as ot
 from fontTools.ttLib.tables.otBase import OTTableWriter
 from fontTools.varLib import builder, models, varStore
-from fontTools.varLib.merger import VariationMerger, _all_equal
+from fontTools.varLib.merger import VariationMerger
 from fontTools.varLib.mvar import MVAR_ENTRIES
 from fontTools.varLib.iup import iup_delta_optimize
 from fontTools.varLib.featureVars import addFeatureVariations
@@ -40,6 +40,7 @@
 from collections import OrderedDict, namedtuple
 import os.path
 import logging
+from copy import deepcopy
 from pprint import pformat
 
 log = logging.getLogger("fontTools.varLib")
@@ -184,7 +185,7 @@
 
 	STAT = font["STAT"] = newTable('STAT')
 	stat = STAT.table = ot.STAT()
-	stat.Version = 0x00010002
+	stat.Version = 0x00010001
 
 	axisRecords = []
 	for i, a in enumerate(fvarTable.axes):
@@ -280,7 +281,7 @@
 	# XXX Handle vertical
 	font["hmtx"].metrics[glyphName] = horizontalAdvanceWidth, leftSideBearing
 
-def _add_gvar(font, model, master_ttfs, tolerance=0.5, optimize=True):
+def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
 
 	assert tolerance >= 0
 
@@ -291,13 +292,19 @@
 	gvar.reserved = 0
 	gvar.variations = {}
 
+	glyf = font['glyf']
+
 	for glyph in font.getGlyphOrder():
 
+		isComposite = glyf[glyph].isComposite()
+
 		allData = [_GetCoordinates(m, glyph) for m in master_ttfs]
+		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
@@ -313,13 +320,23 @@
 		endPts = control[1] if control[0] >= 1 else list(range(len(control[1])))
 
 		for i,(delta,support) in enumerate(zip(deltas[1:], supports[1:])):
-			if all(abs(v) <= tolerance for v in delta.array):
+			if all(abs(v) <= tolerance 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)
 
@@ -344,7 +361,7 @@
 	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, tolerance=0.5):
 
 	log.info("Merging TT hinting")
 	assert "cvar" not in font
@@ -372,7 +389,7 @@
 		all_pgms = [
 			m["glyf"][name].program
 			for m in master_ttfs
-			if hasattr(m["glyf"][name], "program")
+			if name in m['glyf'] and hasattr(m["glyf"][name], "program")
 		]
 		if not any(all_pgms):
 			continue
@@ -383,24 +400,21 @@
 			font_pgm = Program()
 		if any(pgm != font_pgm for pgm in all_pgms if pgm):
 			log.warning("Masters have incompatible glyph programs in glyph '%s', hinting is discarded." % name)
+			# 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
@@ -411,8 +425,7 @@
 	cvar.version = 1
 	cvar.variations = []
 
-	deltas = model.getDeltas(all_cvs)
-	supports = model.supports
+	deltas, supports = masterModel.getDeltasAndSupports(all_cvs)
 	for i,(delta,support) in enumerate(zip(deltas[1:], supports[1:])):
 		delta = [otRound(d) for d in delta]
 		if all(abs(v) <= tolerance for v in delta):
@@ -420,54 +433,59 @@
 		var = TupleVariation(support, delta)
 		cvar.variations.append(var)
 
-def _add_HVAR(font, model, master_ttfs, axisTags):
+def _add_HVAR(font, masterModel, master_ttfs, axisTags):
 
 	log.info("Generating HVAR")
 
-	hAdvanceDeltas = {}
+	glyphOrder = font.getGlyphOrder()
+
+	hAdvanceDeltasAndSupports = {}
 	metricses = [m["hmtx"].metrics for m in master_ttfs]
-	for glyph in font.getGlyphOrder():
-		hAdvances = [metrics[glyph][0] for metrics in metricses]
-		# TODO move round somewhere else?
-		hAdvanceDeltas[glyph] = tuple(otRound(d) for d in model.getDeltas(hAdvances)[1:])
+	for glyph in glyphOrder:
+		hAdvances = [metrics[glyph][0] if glyph in metrics else None for metrics in metricses]
+		hAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(hAdvances)
 
-	# Direct mapping
-	supports = model.supports[1:]
-	varTupleList = builder.buildVarRegionList(supports, axisTags)
-	varTupleIndexes = list(range(len(supports)))
-	n = len(supports)
-	items = []
-	for glyphName in font.getGlyphOrder():
-		items.append(hAdvanceDeltas[glyphName])
+	singleModel = models.allEqual(id(v[1]) for v in hAdvanceDeltasAndSupports.values())
 
-	# Build indirect mapping to save on duplicates, compare both sizes
-	uniq = list(set(items))
-	mapper = {v:i for i,v in enumerate(uniq)}
-	mapping = [mapper[item] for item in items]
-	advanceMapping = builder.buildVarIdxMap(mapping, font.getGlyphOrder())
+	directStore = None
+	if singleModel:
+		# Build direct mapping
 
-	# Direct
-	varData = builder.buildVarData(varTupleIndexes, items)
-	directStore = builder.buildVarStore(varTupleList, [varData])
+		supports = next(iter(hAdvanceDeltasAndSupports.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(hAdvanceDeltasAndSupports[glyphName][0])
+		varData.optimize()
+		directStore = builder.buildVarStore(varTupleList, [varData])
 
-	# Indirect
-	varData = builder.buildVarData(varTupleIndexes, uniq)
-	indirectStore = builder.buildVarStore(varTupleList, [varData])
-	mapping = indirectStore.optimize()
-	advanceMapping.mapping = {k:mapping[v] for k,v in advanceMapping.mapping.items()}
+	# Build optimized indirect mapping
+	storeBuilder = varStore.OnlineVarStoreBuilder(axisTags)
+	mapping = {}
+	for glyphName in glyphOrder:
+		deltas,supports = hAdvanceDeltasAndSupports[glyphName]
+		storeBuilder.setSupports(supports)
+		mapping[glyphName] = storeBuilder.storeDeltas(deltas)
+	indirectStore = storeBuilder.finish()
+	mapping2 = indirectStore.optimize()
+	mapping = [mapping2[mapping[g]] for g in glyphOrder]
+	advanceMapping = builder.buildVarIdxMap(mapping, glyphOrder)
 
-	# Compile both, see which is more compact
+	use_direct = False
+	if directStore:
+		# Compile both, see which is more compact
 
-	writer = OTTableWriter()
-	directStore.compile(writer, font)
-	directSize = len(writer.getAllData())
+		writer = OTTableWriter()
+		directStore.compile(writer, font)
+		directSize = len(writer.getAllData())
 
-	writer = OTTableWriter()
-	indirectStore.compile(writer, font)
-	advanceMapping.compile(writer, font)
-	indirectSize = len(writer.getAllData())
+		writer = OTTableWriter()
+		indirectStore.compile(writer, font)
+		advanceMapping.compile(writer, font)
+		indirectSize = len(writer.getAllData())
 
-	use_direct = directSize < indirectSize
+		use_direct = directSize < indirectSize
 
 	# Done; put it all together.
 	assert "HVAR" not in font
@@ -482,12 +500,11 @@
 		hvar.VarStore = indirectStore
 		hvar.AdvWidthMap = advanceMapping
 
-def _add_MVAR(font, model, master_ttfs, axisTags):
+def _add_MVAR(font, masterModel, master_ttfs, axisTags):
 
 	log.info("Generating MVAR")
 
 	store_builder = varStore.OnlineVarStoreBuilder(axisTags)
-	store_builder.setModel(model)
 
 	records = []
 	lastTableTag = None
@@ -497,17 +514,20 @@
 		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 = [master[tableTag] if tableTag in master else None
+					  for master in master_ttfs]
 			lastTableTag = tableTag
 		if tables is None:
 			continue
 
 		# TODO support gasp entries
 
+		model, tables = masterModel.getSubModel(tables)
+		store_builder.setModel(model)
+
 		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)
@@ -545,9 +565,7 @@
 	log.info("Merging OpenType Layout tables")
 	merger = VariationMerger(model, axisTags, font)
 
-	merger.mergeTables(font, master_fonts, ['GPOS'])
-	# TODO Merge GSUB
-	# TODO Merge GDEF itself!
+	merger.mergeTables(font, master_fonts, ['GSUB', 'GDEF', 'GPOS'])
 	store = merger.store_builder.finish()
 	if not store.VarData:
 		return
@@ -587,8 +605,14 @@
 			space = {}
 			for condition in conditions:
 				axis_name = condition["name"]
-				minimum = normalize(axis_name, condition["minimum"])
-				maximum = normalize(axis_name, condition["maximum"])
+				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)
@@ -614,9 +638,24 @@
 )
 
 
-def load_designspace(designspace_filename):
+def _add_CFF2(varFont, model, master_fonts):
+	from .cff import (convertCFFtoCFF2, addCFFVarStore, merge_region_fonts)
+	glyphOrder = varFont.getGlyphOrder()
+	convertCFFtoCFF2(varFont)
+	ordered_fonts_list = model.reorderMasters(master_fonts, model.reverseMapping)
+	# re-ordering the master list simplifies building the CFF2 data item lists.
+	addCFFVarStore(varFont, model)  # Add VarStore to the CFF2 font.
+	merge_region_fonts(varFont, model, ordered_fonts_list, glyphOrder)
 
-	ds = DesignSpaceDocument.fromfile(designspace_filename)
+
+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")
@@ -698,7 +737,7 @@
 	)
 
 
-def build(designspace_filename, master_finder=lambda s:s, exclude=[], optimize=True):
+def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True):
 	"""
 	Build variation font from a designspace file.
 
@@ -706,16 +745,27 @@
 	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)
 
-	ds = 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 ds.masters]
-	master_fonts = [TTFont(ttf_path) for ttf_path in master_ttfs]
-	# Reload base font as target font
-	vf = TTFont(master_ttfs[ds.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, ds.axes, ds.instances)
@@ -748,14 +798,65 @@
 		_merge_TTHinting(vf, model, master_fonts)
 	if 'GSUB' not in exclude and ds.rules:
 		_add_GSUB_feature_variations(vf, ds.axes, ds.internal_axis_supports, ds.rules)
+	if 'CFF2' not in exclude and 'CFF ' in vf:
+		_add_CFF2(vf, model, master_fonts)
 
 	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 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 or to 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 or .otf).
+
+	Return list of master TTFont objects in the same order they are listed in the
+	DesignSpaceDocument.
+	"""
+	master_fonts = []
+
+	for master in designspace.sources:
+		# 1. If the caller already supplies a TTFont for a source, just take it.
+		if master.font:
+			font = master.font
+			master_fonts.append(font)
+		else:
+			# 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:
+				raise AttributeError(
+					"Designspace source '%s' specified a layer name but lacks the "
+					"required TTFont object in the 'font' attribute."
+					% (master.name or "<Unknown>")
+			)
+			else:
+				if master.path is None:
+					raise AttributeError(
+						"Designspace source '%s' has neither 'font' nor 'path' "
+						"attributes" % (master.name or "<Unknown>")
+					)
+				# 2. A SourceDescriptor's path might point to a UFO or an OpenType
+				# binary. Find out the hard way.
+				master_path = os.path.normpath(master.path)
+				try:
+					font = TTFont(master_path)
+				except (IOError, TTLibError):
+					# 3. Not an OpenType binary, fall back to the master finder.
+					master_path = master_finder(master_path)
+					font = TTFont(master_path)
+				master_fonts.append(font)
+
+	return master_fonts
+
+
 class MasterFinder(object):
 
 	def __init__(self, template):
@@ -828,7 +929,7 @@
 	if outfile is None:
 		outfile = os.path.splitext(designspace_filename)[0] + '-VF.ttf'
 
-	vf, model, master_ttfs = build(
+	vf, _, _ = build(
 		designspace_filename,
 		finder,
 		exclude=options.exclude,
diff --git a/Lib/fontTools/varLib/builder.py b/Lib/fontTools/varLib/builder.py
index 90e3337..e923b80 100644
--- a/Lib/fontTools/varLib/builder.py
+++ b/Lib/fontTools/varLib/builder.py
@@ -39,7 +39,7 @@
 			out.append(lst[i])
 	return out
 
-def VarData_CalculateNumShorts(self, optimize=True):
+def VarData_calculateNumShorts(self, optimize=False):
 	count = self.VarRegionCount
 	items = self.Item
 	narrows = set(range(count))
@@ -55,14 +55,29 @@
 		# Reorder columns such that all SHORT columns come before UINT8
 		self.VarRegionIndex = _reorderItem(self.VarRegionIndex, narrows, zeroes)
 		self.VarRegionCount = len(self.VarRegionIndex)
-		for i in range(self.ItemCount):
+		for i in range(len(items)):
 			items[i] = _reorderItem(items[i], narrows, zeroes)
 		self.NumShorts = count - len(narrows)
 	else:
 		wides = set(range(count)) - narrows
 		self.NumShorts = 1+max(wides) if wides else 0
+	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)
@@ -73,7 +88,7 @@
 			assert len(item) == regionCount
 			records.append(list(item))
 	self.ItemCount = len(self.Item)
-	VarData_CalculateNumShorts(self, optimize=optimize)
+	self.calculateNumShorts(optimize=optimize)
 	return self
 
 
diff --git a/Lib/fontTools/varLib/cff.py b/Lib/fontTools/varLib/cff.py
new file mode 100644
index 0000000..a000dd4
--- /dev/null
+++ b/Lib/fontTools/varLib/cff.py
@@ -0,0 +1,502 @@
+import os
+from fontTools.misc.py23 import BytesIO
+from fontTools.misc.psCharStrings import T2CharString, T2OutlineExtractor
+from fontTools.pens.t2CharStringPen import T2CharStringPen, t2c_round
+from fontTools.cffLib import (
+	maxStackLimit,
+	TopDictIndex,
+	buildOrder,
+	topDictOperators,
+	topDictOperators2,
+	privateDictOperators,
+	privateDictOperators2,
+	FDArrayIndex,
+	FontDict,
+	VarStoreData
+)
+from fontTools.cffLib.specializer import (commandsToProgram, specializeCommands)
+from fontTools.ttLib import newTable
+from fontTools import varLib
+from fontTools.varLib.models import allEqual
+
+
+def addCFFVarStore(varFont, varModel):
+	supports = varModel.supports[1:]
+	fvarTable = varFont['fvar']
+	axisKeys = [axis.axisTag for axis in fvarTable.axes]
+	varTupleList = varLib.builder.buildVarRegionList(supports, axisKeys)
+	varTupleIndexes = list(range(len(supports)))
+	varDeltasCFFV = varLib.builder.buildVarData(varTupleIndexes, None, False)
+	varStoreCFFV = varLib.builder.buildVarStore(varTupleList, [varDeltasCFFV])
+
+	topDict = varFont['CFF2'].cff.topDictIndex[0]
+	topDict.VarStore = VarStoreData(otVarStore=varStoreCFFV)
+
+
+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)
+		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 ']
+
+
+class MergeDictError(TypeError):
+	def __init__(self, key, value, values):
+		error_msg = ["For the Private Dict key '{}', ".format(key),
+					 "the default font value list:",
+					 "\t{}".format(value),
+					 "had a different number of values than a region font:"]
+		error_msg += ["\t{}".format(region_value) for region_value in values]
+		error_msg = os.linesep.join(error_msg)
+
+
+def conv_to_int(num):
+	if num % 1 == 0:
+		return int(num)
+	return num
+
+
+pd_blend_fields = ("BlueValues", "OtherBlues", "FamilyBlues",
+				   "FamilyOtherBlues", "BlueScale", "BlueShift",
+				   "BlueFuzz", "StdHW", "StdVW", "StemSnapH",
+				   "StemSnapV")
+
+
+def merge_PrivateDicts(topDict, region_top_dicts, num_masters, var_model):
+	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
+		pds = [private_dict] + [
+			regionFDArray[fd_index].Private for regionFDArray in regionFDArrays
+			]
+		for key, value in private_dict.rawDict.items():
+			if key not in pd_blend_fields:
+				continue
+			if isinstance(value, list):
+				try:
+					values = [pd.rawDict[key] for pd in pds]
+				except KeyError:
+					del private_dict.rawDict[key]
+					print(
+						b"Warning: {key} in default font Private dict is "
+						b"missing from another font, and was "
+						b"discarded.".format(key=key))
+					continue
+				try:
+					values = zip(*values)
+				except IndexError:
+					raise MergeDictError(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]]
+				"""
+				dataList = []
+				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 = var_model.getDeltas(rel_list)
+					# Convert numbers with no decimal part to an int.
+					deltas = [conv_to_int(delta) for delta in deltas]
+					# 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 = var_model.getDeltas(values)
+				else:
+					dataList = values[0]
+			private_dict.rawDict[key] = dataList
+
+
+def merge_region_fonts(varFont, model, ordered_fonts_list, glyphOrder):
+	topDict = varFont['CFF2'].cff.topDictIndex[0]
+	default_charstrings = topDict.CharStrings
+	region_fonts = ordered_fonts_list[1:]
+	region_top_dicts = [
+			ttFont['CFF '].cff.topDictIndex[0] for ttFont in region_fonts
+				]
+	num_masters = len(model.mapping)
+	merge_PrivateDicts(topDict, region_top_dicts, num_masters, model)
+	merge_charstrings(default_charstrings,
+					  glyphOrder,
+					  num_masters,
+					  region_top_dicts, model)
+
+
+def merge_charstrings(default_charstrings,
+					  glyphOrder,
+					  num_masters,
+					  region_top_dicts,
+					  var_model):
+	for gname in glyphOrder:
+		default_charstring = default_charstrings[gname]
+		var_pen = CFF2CharStringMergePen([], gname, num_masters, 0)
+		default_charstring.outlineExtractor = CFFToCFF2OutlineExtractor
+		default_charstring.draw(var_pen)
+		for region_idx, region_td in enumerate(region_top_dicts, start=1):
+			region_charstrings = region_td.CharStrings
+			region_charstring = region_charstrings[gname]
+			var_pen.restart(region_idx)
+			region_charstring.draw(var_pen)
+		new_charstring = var_pen.getCharString(
+			private=default_charstring.private,
+			globalSubrs=default_charstring.globalSubrs,
+			var_model=var_model, optimize=True)
+		default_charstrings[gname] = new_charstring
+
+
+class MergeTypeError(TypeError):
+	def __init__(self, point_type, pt_index, m_index, default_type, glyphName):
+		self.error_msg = [
+					"In glyph '{gname}' "
+					"'{point_type}' at point index {pt_index} in master "
+					"index {m_index} differs from the default font point "
+					"type '{default_type}'"
+					"".format(gname=glyphName,
+							  point_type=point_type, pt_index=pt_index,
+							  m_index=m_index, default_type=default_type)
+					][0]
+		super(MergeTypeError, self).__init__(self.error_msg)
+
+
+def makeRoundNumberFunc(tolerance):
+	if tolerance < 0:
+		raise ValueError("Rounding tolerance must be positive")
+
+	def roundNumber(val):
+		return t2c_round(val, tolerance)
+
+	return roundNumber
+
+
+class CFFToCFF2OutlineExtractor(T2OutlineExtractor):
+	""" This class is used to remove the initial width
+	from the CFF charstring without adding 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 CFF2CharStringMergePen(T2CharStringPen):
+	"""Pen to merge Type 2 CharStrings.
+	"""
+	def __init__(self, default_commands,
+				 glyphName, num_masters, master_idx, roundTolerance=0.5):
+		super(
+			CFF2CharStringMergePen,
+			self).__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.glyphName = glyphName
+		self.roundNumber = makeRoundNumberFunc(roundTolerance)
+
+	def _p(self, pt):
+		""" Unlike T2CharstringPen, this class stores absolute values.
+		This is to allow the logic in check_and_fix_closepath() to work,
+		where the current or previous absolute point has to be compared to
+		the path start-point.
+		"""
+		self._p0 = pt
+		return list(self._p0)
+
+	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:
+				# Fix some issues that show up in some
+				# CFF workflows, even when fonts are
+				# topologically merge compatible.
+				success, pt_coords = self.check_and_fix_flat_curve(
+							cmd, point_type, pt_coords)
+				if not success:
+					success = self.check_and_fix_closepath(
+							cmd, point_type, pt_coords)
+					if success:
+						# We may have incremented self.pt_index
+						cmd = self._commands[self.pt_index]
+						if cmd[0] != point_type:
+							success = False
+					if not success:
+						raise MergeTypeError(point_type,
+											 self.pt_index, len(cmd[1]),
+											 cmd[0], self.glyphName)
+			cmd[1].append(pt_coords)
+		self.pt_index += 1
+
+	def _moveTo(self, pt):
+		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):
+		"""
+		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]
+		]
+		We also make the value relative.
+		If the master values are all the same, we collapse the list to
+		as single value instead of a list.
+		"""
+		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] = m_args
+
+		# Now convert from absolute to relative
+		x0 = [0]*self.num_masters
+		y0 = [0]*self.num_masters
+		for cmd in self._commands:
+			is_x = True
+			coords = cmd[1]
+			rel_coords = []
+			for coord in coords:
+				prev_coord = x0 if is_x else y0
+				rel_coord = [pt[0] - pt[1] for pt in zip(coord, prev_coord)]
+
+				if allEqual(rel_coord):
+					rel_coord = rel_coord[0]
+				rel_coords.append(rel_coord)
+				if is_x:
+					x0 = coord
+				else:
+					y0 = coord
+				is_x = not is_x
+			cmd[1] = rel_coords
+		return commands
+
+	@staticmethod
+	def mergeCommandsToProgram(commands, var_model, round_func):
+		"""
+		Takes a commands list as returned by programToCommands() and
+		converts it back to a T2CharString or CFF2Charstring program list. I
+		need to use this rather than specialize.commandsToProgram, as the
+		commands produced by CFF2CharStringMergePen initially contains a
+		list of coordinate values, one for each master, wherever a single
+		coordinate value is expected by the regular logic. The problem with
+		doing using the specialize.py functions is that a commands list is
+		expected to be a op name with its associated argument list. For the
+		commands list here, some of the arguments may need to be converted
+		to a new argument list and opcode.
+		This version will convert each list of master arguments to a blend
+		op and its arguments, and will also combine successive blend ops up
+		to the stack limit.
+		"""
+		program = []
+		for op, args in commands:
+			num_args = len(args)
+			# some of the args may be blend lists, and some may be
+			# single coordinate values.
+			i = 0
+			stack_use = 0
+			while i < num_args:
+				arg = args[i]
+				if not isinstance(arg, list):
+					program.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,master 1..master n)
+					Combine as many successive tuples as we can,
+					up to the max stack limit.
+					"""
+					num_masters = len(arg)
+					blendlist = [arg]
+					i += 1
+					stack_use += 1 + num_masters  # 1 for the num_blends arg
+					while (i < num_args) and isinstance(args[i], list):
+						blendlist.append(args[i])
+						i += 1
+						stack_use += num_masters
+						if stack_use + num_masters > maxStackLimit:
+							# if we are here, max stack is is the CFF2 max stack.
+							break
+					num_blends = len(blendlist)
+					# append the 'num_blends' default font values
+					for arg in blendlist:
+						if round_func:
+							arg[0] = round_func(arg[0])
+						program.append(arg[0])
+					for arg in blendlist:
+						# for each coordinate tuple, append the region deltas
+						if len(arg) != 3:
+							print(arg)
+							import pdb
+							pdb.set_trace()
+						deltas = var_model.getDeltas(arg)
+						if round_func:
+							deltas = [round_func(delta) for delta in deltas]
+						# First item in 'deltas' is the default master value;
+						# for CFF2 data, that has already been written.
+						program.extend(deltas[1:])
+					program.append(num_blends)
+					program.append('blend')
+					stack_use = prev_stack_use + num_blends
+			if op:
+				program.append(op)
+		return program
+
+
+	def getCharString(self, private=None, globalSubrs=None,
+					  var_model=None, optimize=True):
+		commands = self._commands
+		commands = self.reorder_blend_args(commands)
+		if optimize:
+			commands = specializeCommands(commands, generalizeFirst=False,
+										  maxstack=maxStackLimit)
+		program = self.mergeCommandsToProgram(commands, var_model=var_model,
+									round_func=self.roundNumber)
+		charString = T2CharString(program=program, private=private,
+							  globalSubrs=globalSubrs)
+		return charString
diff --git a/Lib/fontTools/varLib/featureVars.py b/Lib/fontTools/varLib/featureVars.py
index 6033621..de2808d 100644
--- a/Lib/fontTools/varLib/featureVars.py
+++ b/Lib/fontTools/varLib/featureVars.py
@@ -4,11 +4,13 @@
 NOTE: The API is experimental and subject to change.
 """
 from __future__ import print_function, absolute_import, division
-
+from fontTools.misc.py23 import *
+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
-import itertools
+from collections import OrderedDict
 
 
 def addFeatureVariations(font, conditionalSubstitutions):
@@ -17,160 +19,236 @@
     The `conditionalSubstitutions` argument is a list of (Region, Substitutions)
     tuples.
 
-    A Region is a list of Spaces. A Space is a dict mapping axisTags to
-    (minValue, maxValue) tuples. Irrelevant axes may be omitted.
-    A Space represents a 'rectangular' subset of an N-dimensional design space.
+    A Region 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 Spaces in the Region.
-    For efficiency, Spaces within a Region should ideally not overlap, but
+    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)
     """
 
-    # Example:
+    addFeatureVariationsRaw(font,
+                            overlayFeatureVariations(conditionalSubstitutions))
+
+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
+    uniq 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"}),
+    ...     ([{"wdth": (0.5, 1.0)}], {"cent": "cent.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()
     #
-    #     >>> f = TTFont(srcPath)
-    #     >>> condSubst = [
-    #     ...     # A list of (Region, Substitution) tuples.
-    #     ...     ([{"wght": (0.5, 1.0)}], {"dollar": "dollar.rvrn"}),
-    #     ...     ([{"wdth": (0.5, 1.0)}], {"cent": "cent.rvrn"}),
-    #     ... ]
-    #     >>> addFeatureVariations(f, condSubst)
-    #     >>> f.save(dstPath)
+    # 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))
+        if key in merged:
+            merged[key].update(value)
+        else:
+            merged[key] = dict(value)
+    conditionalSubstitutions = list(reversed(merged.items()))
+    del merged
 
-    # Since the FeatureVariations table will only ever match one rule at a time,
-    # we will make new rules for all possible combinations of our input, so we
-    # can indirectly support overlapping rules.
-    explodedConditionalSubstitutions = []
-    for combination in iterAllCombinations(len(conditionalSubstitutions)):
-        regions = []
-        lookups = []
-        for index in combination:
-            regions.append(conditionalSubstitutions[index][0])
-            lookups.append(conditionalSubstitutions[index][1])
-        if not regions:
-            continue
-        intersection = regions[0]
-        for region in regions[1:]:
-            intersection = intersectRegions(intersection, region)
-        for space in intersection:
-            # Remove default values, so we don't generate redundant ConditionSets
-            space = cleanupSpace(space)
-            if space:
-                explodedConditionalSubstitutions.append((space, lookups))
+    # 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
+    del boxMap[hashdict()]
 
-    addFeatureVariationsRaw(font, explodedConditionalSubstitutions)
+    # Generate output
+    items = []
+    for box,rank in sorted(boxMap.items(),
+                           key=(lambda BoxAndRank: -popCount(BoxAndRank[1]))):
+        substsList = []
+        i = 0
+        while rank:
+          if rank & 1:
+              substsList.append(conditionalSubstitutions[i][1])
+          rank >>= 1
+          i += 1
+        items.append((dict(box),substsList))
+    return items
 
 
-def iterAllCombinations(numRules):
-    """Given a number of rules, yield all the combinations of indices, sorted
-    by decreasing length, so we get the most specialized rules first.
-
-        >>> list(iterAllCombinations(0))
-        []
-        >>> list(iterAllCombinations(1))
-        [(0,)]
-        >>> list(iterAllCombinations(2))
-        [(0, 1), (0,), (1,)]
-        >>> list(iterAllCombinations(3))
-        [(0, 1, 2), (0, 1), (0, 2), (1, 2), (0,), (1,), (2,)]
-    """
-    indices = range(numRules)
-    for length in range(numRules, 0, -1):
-        for combinations in itertools.combinations(indices, length):
-            yield combinations
-
-
-#
-# Region and Space support
 #
 # Terminology:
 #
-# A 'Space' is a dict representing a "rectangular" bit of N-dimensional space.
+# 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.
 #
-# A 'Region' is a list of Space dicts, representing the union of the Spaces,
-# therefore representing a more complex subset of design space.
-#
 
-def intersectRegions(region1, region2):
-    """Return the region intersecting `region1` and `region2`.
+def overlayBox(top, bot):
+    """Overlays `top` box on top of `bot` box.
 
-        >>> intersectRegions([], [])
-        []
-        >>> intersectRegions([{'wdth': (0.0, 1.0)}], [])
-        []
-        >>> expected = [{'wdth': (0.0, 1.0), 'wght': (-1.0, 0.0)}]
-        >>> expected == intersectRegions([{'wdth': (0.0, 1.0)}], [{'wght': (-1.0, 0.0)}])
-        True
-        >>> expected = [{'wdth': (0.0, 1.0), 'wght': (-0.5, 0.0)}]
-        >>> expected == intersectRegions([{'wdth': (0.0, 1.0), 'wght': (-0.5, 0.5)}], [{'wght': (-1.0, 0.0)}])
-        True
-        >>> intersectRegions(
-        ...     [{'wdth': (0.0, 1.0), 'wght': (-0.5, 0.5)}],
-        ...     [{'wdth': (-1.0, 0.0), 'wght': (-1.0, 0.0)}])
-        []
-
+    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.
     """
-    region = []
-    for space1 in region1:
-        for space2 in region2:
-            space = intersectSpaces(space1, space2)
-            if space is not None:
-                region.append(space)
-    return region
 
-
-def intersectSpaces(space1, space2):
-    """Return the space intersected by `space1` and `space2`, or None if there
-    is no intersection.
-
-        >>> intersectSpaces({}, {})
-        {}
-        >>> intersectSpaces({'wdth': (-0.5, 0.5)}, {})
-        {'wdth': (-0.5, 0.5)}
-        >>> intersectSpaces({'wdth': (-0.5, 0.5)}, {'wdth': (0.0, 1.0)})
-        {'wdth': (0.0, 0.5)}
-        >>> expected = {'wdth': (0.0, 0.5), 'wght': (0.25, 0.5)}
-        >>> expected == intersectSpaces({'wdth': (-0.5, 0.5), 'wght': (0.0, 0.5)}, {'wdth': (0.0, 1.0), 'wght': (0.25, 0.75)})
-        True
-        >>> expected = {'wdth': (-0.5, 0.5), 'wght': (0.0, 1.0)}
-        >>> expected == intersectSpaces({'wdth': (-0.5, 0.5)}, {'wght': (0.0, 1.0)})
-        True
-        >>> intersectSpaces({'wdth': (-0.5, 0)}, {'wdth': (0.1, 0.5)})
-
-    """
-    space = {}
-    space.update(space1)
-    space.update(space2)
-    for axisTag in set(space1) & set(space2):
-        min1, max1 = space1[axisTag]
-        min2, max2 = space2[axisTag]
+    # 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
-        space[axisTag] = minimum, maximum
-    return space
+            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.
 
-def cleanupSpace(space):
-    """Return a sparse copy of `space`, without redundant (default) values.
+    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
 
-        >>> cleanupSpace({})
+        # 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({})
         {}
-        >>> cleanupSpace({'wdth': (0.0, 1.0)})
+        >>> cleanupBox({'wdth': (0.0, 1.0)})
         {'wdth': (0.0, 1.0)}
-        >>> cleanupSpace({'wdth': (-1.0, 1.0)})
+        >>> cleanupBox({'wdth': (-1.0, 1.0)})
         {}
 
     """
-    return {tag: limit for tag, limit in space.items() if limit != (-1.0, 1.0)}
+    return {tag: limit for tag, limit in box.items() if limit != (-1.0, 1.0)}
 
 
 #
@@ -210,7 +288,8 @@
     rvrnFeatureIndex = gsub.FeatureList.FeatureRecord.index(rvrnFeature)
 
     for scriptRecord in gsub.ScriptList.ScriptRecord:
-        for langSys in [scriptRecord.Script.DefaultLangSys] + scriptRecord.Script.LangSysRecord:
+        langSystems = [lsr.LangSys for lsr in scriptRecord.Script.LangSysRecord]
+        for langSys in [scriptRecord.Script.DefaultLangSys] + langSystems:
             langSys.FeatureIndex.append(rvrnFeatureIndex)
 
     # setup lookups
@@ -327,7 +406,7 @@
     fvr.ConditionSet = ot.ConditionSet()
     fvr.ConditionSet.ConditionTable = conditionTable
     fvr.FeatureTableSubstitution = ot.FeatureTableSubstitution()
-    fvr.FeatureTableSubstitution.Version = 0x00010001
+    fvr.FeatureTableSubstitution.Version = 0x00010000
     fvr.FeatureTableSubstitution.SubstitutionRecord = substitutionRecords
     return fvr
 
@@ -388,5 +467,5 @@
 
 
 if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
+    import doctest, sys
+    sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/varLib/interpolate_layout.py b/Lib/fontTools/varLib/interpolate_layout.py
index 1ce3faf..f252149 100644
--- a/Lib/fontTools/varLib/interpolate_layout.py
+++ b/Lib/fontTools/varLib/interpolate_layout.py
@@ -4,16 +4,17 @@
 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,18 +27,18 @@
 	it is assumed that location is in designspace's internal space and
 	no mapping is performed.
 	"""
-	ds = load_designspace(designspace_filename)
+	if hasattr(designspace, "sources"):  # Assume a DesignspaceDocument
+		pass
+	else:  # Assume a file path
+		from fontTools.designspaceLib import DesignSpaceDocument
+		designspace = DesignSpaceDocument.fromfile(designspace)
 
+	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 ds.masters
-	]
-	master_fonts = [TTFont(ttf_path) for ttf_path in master_ttfs]
 
-	#font = master_fonts[ds.base_idx]
-	font = TTFont(master_ttfs[ds.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:
@@ -53,6 +54,7 @@
 	merger = InstancerMerger(font, model, loc)
 
 	log.info("Building interpolated tables")
+	# TODO GSUB/GDEF
 	merger.mergeTables(font, master_fonts, ['GPOS'])
 	return font
 
diff --git a/Lib/fontTools/varLib/merger.py b/Lib/fontTools/varLib/merger.py
index aaee311..688790c 100644
--- a/Lib/fontTools/varLib/merger.py
+++ b/Lib/fontTools/varLib/merger.py
@@ -8,7 +8,8 @@
 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
 
@@ -75,8 +76,7 @@
 			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])
+		assert allEqualTo(out, lst, len), (len(out), [len(v) for v in lst])
 		for i,(value,values) in enumerate(zip(out, zip(*lst))):
 			try:
 				self.mergeThings(value, values)
@@ -85,9 +85,8 @@
 				raise
 
 	def mergeThings(self, out, lst):
-		clazz = type(out)
 		try:
-			assert all(type(item) == clazz for item in lst), (out, lst)
+			assert allEqualTo(out, lst, type), (out, lst)
 			mergerFunc = self.mergersFor(out).get(None, None)
 			if mergerFunc is not None:
 				mergerFunc(self, out, lst)
@@ -96,16 +95,17 @@
 			elif isinstance(out, list):
 				self.mergeLists(out, lst)
 			else:
-				assert all(out == v for v in lst), (out, lst)
+				assert allEqualTo(out, lst), (out, lst)
 		except Exception as e:
-			e.args = e.args + (clazz.__name__,)
+			e.args = e.args + (type(out).__name__,)
 			raise
 
-	def mergeTables(self, font, master_ttfs, tables):
+	def mergeTables(self, font, master_ttfs, tableTags):
 
-		for tag in tables:
+		for tag in tableTags:
 			if tag not in font: continue
-			self.mergeThings(font[tag], [m[tag] for m in master_ttfs])
+			self.mergeThings(font[tag], [m[tag] if tag in m else None
+						     for m in master_ttfs])
 
 #
 # Aligning merger
@@ -113,6 +113,27 @@
 class AligningMerger(Merger):
 	pass
 
+@AligningMerger.merger(ot.GDEF, "GlyphClassDef")
+def merge(merger, self, lst):
+	if self is None:
+		assert allNone(lst), (lst)
+		return
+
+	self.classDefs = {}
+	# We only care about the .classDefs
+	self = self.classDefs
+	lst = [l.classDefs for l in lst]
+
+	allKeys = set()
+	allKeys.update(*[l.keys() for l in lst])
+	for k in allKeys:
+		allValues = nonNone(l.get(k) for l in lst)
+		assert allEqual(allValues), allValues
+		if not allValues:
+			self[k] = None
+		else:
+			self[k] = allValues[0]
+
 def _SinglePosUpgradeToFormat2(self):
 	if self.Format == 2: return self
 
@@ -201,7 +222,8 @@
 	assert len(lst) == 1 or (valueFormat & ~0xF == 0), valueFormat
 
 	# If all have same coverage table and all are format 1,
-	if all(v.Format == 1 for v in lst) and all(self.Coverage.glyphs == v.Coverage.glyphs for v in lst):
+	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()
@@ -276,7 +298,7 @@
 	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,
@@ -330,19 +352,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):
@@ -353,6 +378,9 @@
 
 	return self, classes
 
+# It's stupid that we need to do this here.  Just need to, to match test
+# expecatation results, since ttx prints out format of ClassDef (and Coverage)
+# even though it should not.
 def _ClassDef_calculate_Format(self, font):
 	fmt = 2
 	ranges = self._getClassRanges(font)
@@ -370,7 +398,7 @@
 	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))
+	self.ClassDef1, classes = _ClassDef_merge_classify([l.ClassDef1 for l in lst], [l.Coverage.glyphs for l in lst])
 	_ClassDef_calculate_Format(self.ClassDef1, font)
 	self.Class1Count = len(classes)
 	new_matrices = []
@@ -382,6 +410,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 = []
@@ -431,7 +464,7 @@
 	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',
@@ -500,6 +533,105 @@
 	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.
+
+		assert allEqual(allClasses), allClasses
+		if not allClasses:
+			rec = None
+		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):
+	assert allEqualTo(self.Format, (l.Format for l in lst))
+	if self.Format == 1:
+		_MarkBasePosFormat1_merge(self, lst, merger)
+	else:
+		assert False
+
+@AligningMerger.merger(ot.MarkMarkPos)
+def merge(merger, self, lst):
+	assert allEqualTo(self.Format, (l.Format for l in lst))
+	if self.Format == 1:
+		_MarkBasePosFormat1_merge(self, lst, merger, 'Mark1', 'Mark2')
+	else:
+		assert False
+
 
 def _PairSet_flatten(lst, font):
 	self = ot.PairSet()
@@ -525,7 +657,7 @@
 	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
@@ -546,7 +678,7 @@
 	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
@@ -603,8 +735,8 @@
 		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])
+			assert allEqual([st.__class__ for st in sts])
+			assert allEqual([st.ExtensionLookupType for st in sts])
 			l.LookupType = sts[0].ExtensionLookupType
 			new_sts = [st.ExtSubTable for st in sts]
 			del sts[:]
@@ -655,8 +787,17 @@
 		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
@@ -688,7 +829,9 @@
 
 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):
 		Merger.__init__(self, font)
@@ -702,59 +845,32 @@
 
 		self.instancer = VarStoreInstancer(store, font['fvar'].axes, location)
 
-	def instantiate(self):
-		font = self.font
+@MutatorMerger.merger(ot.CaretValue)
+def merge(merger, self, lst):
 
-		for tableTag in 'GSUB','GPOS':
-			if not tableTag in font:
-				continue
-			table = font[tableTag].table
-			if not hasattr(table, 'FeatureVariations'):
-				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 = self.font['fvar'].axes[axisIdx].axisTag
-						Min = condition.FilterRangeMinValue
-						Max = condition.FilterRangeMaxValue
-						loc = self.location[axisTag]
-						if not (Min <= loc <= Max):
-							applies = False
-					else:
-						applies = False
-					if not applies:
-						break
+	# Hack till we become selfless.
+	self.__dict__ = lst[0].__dict__.copy()
 
-				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 self.Format != 3:
+		return
 
+	instancer = merger.instancer
+	dev = self.DeviceTable
+	del self.DeviceTable
+	if dev:
+		assert dev.DeltaFormat == 0x8000
+		varidx = (dev.StartSize << 16) + dev.EndSize
+		delta = otRound(instancer[varidx])
+		self.Coordinate  += delta
 
-		self.mergeTables(font, [font], ['GPOS'])
-
-		if 'GDEF' in font:
-			gdef = font['GDEF'].table
-			if gdef.Version >= 0x00010003:
-				del gdef.VarStore
-				gdef.Version = 0x00010002
-				if gdef.MarkGlyphSetsDef is None:
-					del gdef.MarkGlyphSetsDef
-					gdef.Version = 0x00010000
-			if not (gdef.LigCaretList or
-				gdef.MarkAttachClassDef or
-				gdef.GlyphClassDef or
-				gdef.AttachList or
-				(gdef.Version >= 0x00010002 and gdef.MarkGlyphSetsDef)):
-				del font['GDEF']
+	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
 
@@ -780,9 +896,7 @@
 @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
@@ -816,26 +930,43 @@
 
 	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):
+				assert out is None, (out, 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.CaretValue)
+def merge(merger, self, lst):
+	assert self.Format == 1
+	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
diff --git a/Lib/fontTools/varLib/models.py b/Lib/fontTools/varLib/models.py
index 653b75f..207e30f 100644
--- a/Lib/fontTools/varLib/models.py
+++ b/Lib/fontTools/varLib/models.py
@@ -2,9 +2,36 @@
 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']
 
 
+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)
+	else:
+		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)
+	first = next(it)
+	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))
@@ -163,6 +190,9 @@
 	"""
 
 	def __init__(self, locations, axisOrder=[]):
+		self.origLocations = locations
+		self.axisOrder = 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
@@ -172,6 +202,17 @@
 		self.reverseMapping = [locations.index(l) for l in self.locations] # Reverse of above
 
 		self._computeMasterSupports(axisPoints, axisOrder)
+		self._subModels = {}
+
+	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)
 
 	@staticmethod
 	def getMasterLocationsSortKeyFunc(locations, axisOrder=[]):
@@ -223,24 +264,37 @@
 			return min(v for v in lst if v > value)
 		else:
 			return value
+	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.}
+			     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 _computeMasterSupports(self, axisPoints, axisOrder):
 		supports = []
 		deltaWeights = []
 		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))
 		for i,loc in enumerate(locations):
 			box = {}
-
-			# Account for axisPoints first
-			# TODO Use axis min/max instead? Isn't that always -1/+1?
-			for axis,values in axisPoints.items():
-				if not axis in loc:
-					continue
-				locV = loc[axis]
+			for axis,locV in loc.items():
 				if locV > 0:
-					box[axis] = (0, locV, max({locV}|values))
+					box[axis] = (0, locV, maxV[axis])
 				else:
-					box[axis] = (min({locV}|values), locV, 0)
+					box[axis] = (minV[axis], locV, 0)
 
 			locAxes = set(loc.keys())
 			# Walk over previous masters now
@@ -258,12 +312,15 @@
 					continue
 
 				# Split the box for new master; split in whatever direction
-				# that has largest range ratio.  See commit for details.
-				orderedAxes = [axis for axis in axisOrder if axis in m.keys()]
-				orderedAxes.extend([axis for axis in sorted(m.keys()) if axis not in axisOrder])
-				bestAxis = None
+				# 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
+
+				bestAxes = {}
 				bestRatio = -1
-				for axis in orderedAxes:
+				for axis in m.keys():
 					val = m[axis]
 					assert axis in box
 					lower,locV,upper = box[axis]
@@ -278,14 +335,13 @@
 						# Can't split box in this direction.
 						continue
 					if ratio > bestRatio:
+						bestAxes = {}
 						bestRatio = ratio
-						bestAxis = axis
-						bestLower = newLower
-						bestUpper = newUpper
-						bestLocV = locV
+					if ratio == bestRatio:
+						bestAxes[axis] = (newLower, locV, newUpper)
 
-				if bestAxis:
-					box[bestAxis] = (bestLower,bestLocV,bestUpper)
+				for axis,triple in bestAxes.items ():
+					box[axis] = triple
 			supports.append(box)
 
 			deltaWeight = {}
@@ -310,6 +366,10 @@
 			out.append(delta)
 		return out
 
+	def getDeltasAndSupports(self, items):
+		model, items = self.getSubModel(items)
+		return model.getDeltas(items), model.supports
+
 	def getScalars(self, loc):
 		return [supportScalar(loc, support) for support in self.supports]
 
diff --git a/Lib/fontTools/varLib/mutator.py b/Lib/fontTools/varLib/mutator.py
index 7a03e45..1a3b738 100644
--- a/Lib/fontTools/varLib/mutator.py
+++ b/Lib/fontTools/varLib/mutator.py
@@ -5,17 +5,22 @@
 """
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
-from fontTools.misc.fixedTools import floatToFixedToFloat, otRound
-from fontTools.ttLib import TTFont
+from fontTools.misc.fixedTools import floatToFixedToFloat, otRound, floatToFixed
+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
 from fontTools.varLib import _GetCoordinates, _SetCoordinates
 from fontTools.varLib.models import (
-	supportScalar, normalizeLocation, piecewiseLinearMap
+	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
 
@@ -30,6 +35,116 @@
 	OS2_WIDTH_CLASS_VALUES[half] = i
 
 
+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
+		charstring = charstrings[gname]
+		pd = charstring.private
+		vsindex = pd.vsindex if (hasattr(pd, 'vsindex')) else 0
+		num_regions = pd.getNumRegions(vsindex)
+		numMasters = num_regions + 1
+		new_program = []
+		last_i = 0
+		for i, token in enumerate(charstring.program):
+			if token == 'blend':
+				num_args = charstring.program[i - 1]
+				""" The stack 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 = 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):
 	""" Generate a static instance from a variable TTFont and a dictionary
 	defining the desired location along the variable font's axes.
@@ -58,31 +173,34 @@
 	# 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']
+		# 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)
+	else:
+		glyf = None
 
 	if 'cvar' in varfont:
 		log.info("Mutating cvt/cvar tables")
@@ -98,6 +216,20 @@
 		for i, delta in deltas.items():
 			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")
 		mvar = varfont['MVAR'].table
@@ -114,12 +246,98 @@
 			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 hasattr(table, 'FeatureVariations'):
+			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, loc)
+		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 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']
+			if hasattr(maxp, "maxInstructionDefs"):
+				maxp.maxInstructionDefs += 1
+			else:
+				setattr(maxp, "maxInstructionDefs", 1)
+			if hasattr(maxp, "maxStackElements"):
+				maxp.maxStackElements = max(len(loc), maxp.maxStackElements)
+			else:
+				setattr(maxp, "maxInstructionDefs", len(loc))
 
 	if 'name' in varfont:
 		log.info("Pruning name table")
diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py
index a1ca7df..66d0c95 100644
--- a/Lib/fontTools/varLib/varStore.py
+++ b/Lib/fontTools/varLib/varStore.py
@@ -4,8 +4,7 @@
 from fontTools.ttLib.tables import otTables as ot
 from fontTools.varLib.models import supportScalar
 from fontTools.varLib.builder import (buildVarRegionList, buildVarStore,
-				      buildVarRegion, buildVarData,
-				      VarData_CalculateNumShorts)
+				      buildVarRegion, buildVarData)
 from functools import partial
 from collections import defaultdict
 from array import array
@@ -24,25 +23,36 @@
 		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
-		self._cache = {} # Empty cached items
+
+	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)
-			VarData_CalculateNumShorts(data, optimize)
+			data.calculateNumShorts(optimize=optimize)
 		return self._store
 
 	def _add_VarData(self):
 		regionMap = self._regionMap
 		regionList = self._regionList
 
-		regions = self._model.supports[1:]
+		regions = self._supports
 		regionIndices = []
 		for region in regions:
 			key = _getLocationKey(region)
@@ -53,17 +63,46 @@
 				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) == 0xFFF:
+				# This is full.  Need new one.
+				varDataIdx = None
+
+		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 = [otRound(d) for d in self._model.getDeltas(master_values)]
-		base = deltas.pop(0)
-		deltas = tuple(deltas)
+		deltas = self._model.getDeltas(master_values)
+		base = otRound(deltas.pop(0))
+		return base, self.storeDeltas(deltas)
+
+	def storeDeltas(self, deltas):
+		# Pity that this exists here, since VarData_addItem
+		# does the same.  But to look into our cache, it's
+		# good to adjust deltas here as well...
+		deltas = [otRound(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 base, varIdx
+			return varIdx
 
 		if not self._data:
 			self._add_VarData()
@@ -71,18 +110,34 @@
 		if inner == 0xFFFF:
 			# Full array. Start new one.
 			self._add_VarData()
-			return self.storeMasters(master_values)
-		self._data.Item.append(deltas)
+			return self.storeDeltas(deltas)
+		self._data.addItem(deltas)
 
 		varIdx = (self._outer << 16) + inner
 		self._cache[deltas] = varIdx
-		return base, varIdx
+		return varIdx
 
+def VarData_addItem(self, deltas):
+	deltas = [otRound(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)}
 
+ot.VarRegion.get_support = VarRegion_get_support
+
 class VarStoreInstancer(object):
 
 	def __init__(self, varstore, fvar_axes, location={}):
@@ -102,24 +157,32 @@
 	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
@@ -149,7 +212,7 @@
 		usedMinors = used.get(major)
 		if usedMinors is None:
 			continue
-		newMajor = varDataMap[major] = len(newVarData)
+		newMajor = len(newVarData)
 		newVarData.append(data)
 
 		items = data.Item
@@ -162,8 +225,7 @@
 		data.Item = newItems
 		data.ItemCount = len(data.Item)
 
-		if optimize:
-			VarData_CalculateNumShorts(data)
+		data.calculateNumShorts(optimize=optimize)
 
 	self.VarData = newVarData
 	self.VarDataCount = len(self.VarData)
@@ -201,27 +263,26 @@
 ot.VarStore.prune_regions = VarStore_prune_regions
 
 
-def _visit(self, objType, func):
-	"""Recurse down from self, if type of an object is objType,
-	call func() on it.  Only works for otData-style classes."""
+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) == objType:
+	if type(self) == ot.Device:
 		func(self)
-		return # We don't recurse down; don't need to.
 
-	if isinstance(self, list):
+	elif isinstance(self, list):
 		for that in self:
-			_visit(that, objType, func)
+			_visit(that, func)
 
-	if hasattr(self, 'getConverters'):
+	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, objType, func)
+				_visit(that, func)
 
-	if isinstance(self, ot.ValueRecord):
+	elif isinstance(self, ot.ValueRecord):
 		for that in self.__dict__.values():
-			_visit(that, objType, func)
+			_visit(that, func)
 
 def _Device_recordVarIdx(self, s):
 	"""Add VarIdx in this Device table (if any) to the set s."""
@@ -230,13 +291,13 @@
 
 def Object_collect_device_varidxes(self, varidxes):
 	adder = partial(_Device_recordVarIdx, s=varidxes)
-	_visit(self, ot.Device, adder)
+	_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):
-	"""Add VarIdx in this Device table (if any) to the set s."""
+	"""Map VarIdx in this Device table (if any) through mapping."""
 	if id(self) in done:
 		return
 	done.add(id(self))
@@ -247,7 +308,7 @@
 
 def Object_remap_device_varidxes(self, varidxes_map):
 	mapper = partial(_Device_mapVarIdx, mapping=varidxes_map, done=set())
-	_visit(self, ot.Device, mapper)
+	_visit(self, mapper)
 
 ot.GDEF.remap_device_varidxes = Object_remap_device_varidxes
 ot.GPOS.remap_device_varidxes = Object_remap_device_varidxes
@@ -464,7 +525,7 @@
 	self.VarDataCount = len(self.VarData)
 	for data in self.VarData:
 		data.ItemCount = len(data.Item)
-		VarData_CalculateNumShorts(data)
+		data.optimize()
 
 	return varidx_map
 
diff --git a/Lib/fonttools.egg-info/PKG-INFO b/Lib/fonttools.egg-info/PKG-INFO
index ee3a377..32f9568 100644
--- a/Lib/fonttools.egg-info/PKG-INFO
+++ b/Lib/fonttools.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: fonttools
-Version: 3.31.0
+Version: 3.35.0
 Summary: Tools to manipulate font files
 Home-page: http://github.com/fonttools/fonttools
 Author: Just van Rossum
@@ -28,6 +28,13 @@
         FontTools requires `Python <http://www.python.org/download/>`__ 2.7, 3.4
         or later.
         
+        **NOTE** After January 1 2019, until no later than June 30 2019, the support
+        for *Python 2.7* will be limited to only bug fixes, and no new features will
+        be added to the ``py27`` branch. The upcoming FontTools 4.x series will require
+        *Python 3.5* or above. 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>`__:
         
@@ -255,6 +262,14 @@
         
           *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:
@@ -314,7 +329,7 @@
         installed ``fontTools`` package, or the first one found in the
         ``PYTHONPATH``.
         
-        You can also use `tox <https://testrun.org/tox/latest/>`__ to
+        You can also use `tox <https://tox.readthedocs.io/en/latest/>`__ to
         automatically run tests on different Python versions in isolated virtual
         environments.
         
@@ -415,6 +430,82 @@
         Changelog
         ~~~~~~~~~
         
+        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)
         ----------------------------
         
@@ -1491,11 +1582,13 @@
 Classifier: Topic :: Text Processing :: Fonts
 Classifier: Topic :: Multimedia :: Graphics
 Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
-Provides-Extra: type1
+Provides-Extra: all
+Provides-Extra: interpolatable
+Provides-Extra: woff
 Provides-Extra: lxml
 Provides-Extra: unicode
-Provides-Extra: symfont
-Provides-Extra: all
+Provides-Extra: graphite
 Provides-Extra: ufo
-Provides-Extra: woff
-Provides-Extra: interpolatable
+Provides-Extra: type1
+Provides-Extra: plot
+Provides-Extra: symfont
diff --git a/Lib/fonttools.egg-info/SOURCES.txt b/Lib/fonttools.egg-info/SOURCES.txt
index ef8491d..04db095 100644
--- a/Lib/fonttools.egg-info/SOURCES.txt
+++ b/Lib/fonttools.egg-info/SOURCES.txt
@@ -87,6 +87,7 @@
 Lib/fontTools/__main__.py
 Lib/fontTools/afmLib.py
 Lib/fontTools/agl.py
+Lib/fontTools/fontBuilder.py
 Lib/fontTools/inspect.py
 Lib/fontTools/merge.py
 Lib/fontTools/ttx.py
@@ -111,11 +112,13 @@
 Lib/fontTools/misc/bezierTools.py
 Lib/fontTools/misc/classifyTools.py
 Lib/fontTools/misc/cliTools.py
+Lib/fontTools/misc/dictTools.py
 Lib/fontTools/misc/eexec.py
 Lib/fontTools/misc/encodingTools.py
 Lib/fontTools/misc/etree.py
 Lib/fontTools/misc/filenames.py
 Lib/fontTools/misc/fixedTools.py
+Lib/fontTools/misc/intTools.py
 Lib/fontTools/misc/loggingTools.py
 Lib/fontTools/misc/macCreatorType.py
 Lib/fontTools/misc/macRes.py
@@ -159,6 +162,7 @@
 Lib/fontTools/pens/wxPen.py
 Lib/fontTools/subset/__init__.py
 Lib/fontTools/subset/__main__.py
+Lib/fontTools/subset/cff.py
 Lib/fontTools/svgLib/__init__.py
 Lib/fontTools/svgLib/path/__init__.py
 Lib/fontTools/svgLib/path/parser.py
@@ -285,6 +289,7 @@
 Lib/fontTools/varLib/__init__.py
 Lib/fontTools/varLib/__main__.py
 Lib/fontTools/varLib/builder.py
+Lib/fontTools/varLib/cff.py
 Lib/fontTools/varLib/featureVars.py
 Lib/fontTools/varLib/interpolatable.py
 Lib/fontTools/varLib/interpolate_layout.py
@@ -520,6 +525,11 @@
 Tests/feaLib/data/include/includemissingfile.fea
 Tests/feaLib/data/include/includeself.fea
 Tests/feaLib/data/include/subdir/include2.fea
+Tests/fontBuilder/fontBuilder_test.py
+Tests/fontBuilder/data/test.otf.ttx
+Tests/fontBuilder/data/test.ttf.ttx
+Tests/fontBuilder/data/test_var.otf.ttx
+Tests/fontBuilder/data/test_var.ttf.ttx
 Tests/misc/arrayTools_test.py
 Tests/misc/bezierTools_test.py
 Tests/misc/classifyTools_test.py
@@ -609,6 +619,7 @@
 Tests/pens/boundsPen_test.py
 Tests/pens/perimeterPen_test.py
 Tests/pens/pointInsidePen_test.py
+Tests/pens/pointPen_test.py
 Tests/pens/recordingPen_test.py
 Tests/pens/reverseContourPen_test.py
 Tests/pens/t2CharStringPen_test.py
@@ -1383,6 +1394,7 @@
 Tests/ufoLib/testdata/UFO3-Read Data.ufo/data/org.unifiedfontobject.directory/bar/lol.txt
 Tests/varLib/__init__.py
 Tests/varLib/builder_test.py
+Tests/varLib/featureVars_test.py
 Tests/varLib/interpolatable_test.py
 Tests/varLib/interpolate_layout_test.py
 Tests/varLib/models_test.py
@@ -1392,10 +1404,17 @@
 Tests/varLib/data/BuildAvarEmptyAxis.designspace
 Tests/varLib/data/BuildAvarIdentityMaps.designspace
 Tests/varLib/data/BuildAvarSingleAxis.designspace
+Tests/varLib/data/BuildGvarCompositeExplicitDelta.designspace
 Tests/varLib/data/FeatureVars.designspace
 Tests/varLib/data/InterpolateLayout.designspace
 Tests/varLib/data/InterpolateLayout2.designspace
 Tests/varLib/data/InterpolateLayout3.designspace
+Tests/varLib/data/TestCFF2.designspace
+Tests/varLib/data/TestCFF2VF.otf
+Tests/varLib/data/master_cff2/TestCFF2_Black.otf
+Tests/varLib/data/master_cff2/TestCFF2_ExtraLight.otf
+Tests/varLib/data/master_cff2/TestCFF2_Regular.otf
+Tests/varLib/data/master_ttx_getvar_ttf/Mutator_Getvar.ttx
 Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master0.ttx
 Tests/varLib/data/master_ttx_interpolatable_otf/TestFamily2-Master1.ttx
 Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master0.ttx
@@ -1413,6 +1432,8 @@
 Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Light.ttx
 Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-Regular.ttx
 Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily3-SemiBold.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily4-Italic15.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily4-Regular.ttx
 Tests/varLib/data/master_ttx_varfont_ttf/Mutator_IUP.ttx
 Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/features.fea
 Tests/varLib/data/master_ufo/TestFamily-Master0.ufo/fontinfo.plist
@@ -1635,11 +1656,53 @@
 Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/o.glif
 Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/s.glif
 Tests/varLib/data/master_ufo/TestFamily3-SemiBold.ufo/glyphs/t.glif
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/features.fea
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/groups.plist
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/layercontents.plist
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/N_.glif
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/O_.glif
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/O_dieresis.glif
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/dieresiscomb.glif
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/n.glif
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/o.glif
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs/odieresis.glif
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/N_.glif
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/O_.glif
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/contents.plist
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/dieresiscomb.glif
+Tests/varLib/data/master_ufo/TestFamily4-Italic15.ufo/glyphs.public.background/o.glif
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/features.fea
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/fontinfo.plist
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/groups.plist
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/kerning.plist
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/layercontents.plist
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/lib.plist
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/metainfo.plist
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/N_.glif
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/O_.glif
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/O_dieresis.glif
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/contents.plist
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/dieresiscomb.glif
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/n.glif
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/o.glif
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs/odieresis.glif
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/N_.glif
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/O_.glif
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/contents.plist
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/dieresiscomb.glif
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/n.glif
+Tests/varLib/data/master_ufo/TestFamily4-Regular.ufo/glyphs.public.background/o.glif
 Tests/varLib/data/test_results/Build.ttx
 Tests/varLib/data/test_results/BuildAvarEmptyAxis.ttx
 Tests/varLib/data/test_results/BuildAvarIdentityMaps.ttx
 Tests/varLib/data/test_results/BuildAvarSingleAxis.ttx
+Tests/varLib/data/test_results/BuildGvarCompositeExplicitDelta.ttx
 Tests/varLib/data/test_results/BuildMain.ttx
+Tests/varLib/data/test_results/BuildTestCFF2.ttx
 Tests/varLib/data/test_results/FeatureVars.ttx
 Tests/varLib/data/test_results/InterpolateLayout.ttx
 Tests/varLib/data/test_results/InterpolateLayout2.ttx
@@ -1664,7 +1727,9 @@
 Tests/varLib/data/test_results/InterpolateLayoutGPOS_8_same.ttx
 Tests/varLib/data/test_results/InterpolateLayoutGPOS_size_feat_same.ttx
 Tests/varLib/data/test_results/InterpolateLayoutMain.ttx
+Tests/varLib/data/test_results/InterpolateTestCFF2VF.ttx
 Tests/varLib/data/test_results/Mutator.ttx
+Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx
 Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
 Tests/voltLib/lexer_test.py
 Tests/voltLib/parser_test.py
\ No newline at end of file
diff --git a/Lib/fonttools.egg-info/requires.txt b/Lib/fonttools.egg-info/requires.txt
index c7eb2e1..9f92764 100644
--- a/Lib/fonttools.egg-info/requires.txt
+++ b/Lib/fonttools.egg-info/requires.txt
@@ -3,6 +3,8 @@
 fs<3,>=2.1.1
 lxml<5,>=4.0
 zopfli>=0.1.4
+lz4>=1.7.4.2
+matplotlib
 sympy
 
 [all:platform_python_implementation != "PyPy"]
@@ -16,6 +18,7 @@
 [all:python_version < "3.4"]
 enum34>=1.1.6
 singledispatch>=3.4.0.3
+typing>=3.6.4
 
 [all:python_version < "3.7" and platform_python_implementation != "PyPy"]
 unicodedata2>=11.0.0
@@ -23,6 +26,9 @@
 [all:sys_platform == "darwin"]
 xattr
 
+[graphite]
+lz4>=1.7.4.2
+
 [interpolatable]
 
 [interpolatable:platform_python_implementation != "PyPy"]
@@ -36,6 +42,10 @@
 
 [lxml:python_version < "3.4"]
 singledispatch>=3.4.0.3
+typing>=3.6.4
+
+[plot]
+matplotlib
 
 [symfont]
 sympy
diff --git a/METADATA b/METADATA
index f017a66..ba000aa 100644
--- a/METADATA
+++ b/METADATA
@@ -7,12 +7,12 @@
   }
   url {
     type: ARCHIVE
-    value: "https://github.com/fonttools/fonttools/releases/download/3.31.0/fonttools-3.31.0.zip"
+    value: "https://github.com/fonttools/fonttools/releases/download/3.35.0/fonttools-3.35.0.zip"
   }
-  version: "3.31.0"
+  version: "3.35.0"
   last_upgrade_date {
-    year: 2018
-    month: 10
-    day: 30
+    year: 2019
+    month: 1
+    day: 8
   }
 }
diff --git a/MetaTools/buildTableList.py b/MetaTools/buildTableList.py
old mode 100755
new mode 100644
index de8a039..eb9fb85
--- a/MetaTools/buildTableList.py
+++ b/MetaTools/buildTableList.py
@@ -30,9 +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('''
+	file.write('''
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 
@@ -45,21 +45,20 @@
 	"""
 ''')
 
-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"
 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
@@ -70,4 +69,5 @@
 
 doc = doc[:beginPos] + blockquote + 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
old mode 100755
new mode 100644
diff --git a/MetaTools/roundTrip.py b/MetaTools/roundTrip.py
old mode 100755
new mode 100644
index 122b39b..648bc9d
--- a/MetaTools/roundTrip.py
+++ b/MetaTools/roundTrip.py
@@ -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 16c2a90..59bd94f 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,3 +1,79 @@
+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)
 ----------------------------
 
diff --git a/PKG-INFO b/PKG-INFO
index ee3a377..32f9568 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: fonttools
-Version: 3.31.0
+Version: 3.35.0
 Summary: Tools to manipulate font files
 Home-page: http://github.com/fonttools/fonttools
 Author: Just van Rossum
@@ -28,6 +28,13 @@
         FontTools requires `Python <http://www.python.org/download/>`__ 2.7, 3.4
         or later.
         
+        **NOTE** After January 1 2019, until no later than June 30 2019, the support
+        for *Python 2.7* will be limited to only bug fixes, and no new features will
+        be added to the ``py27`` branch. The upcoming FontTools 4.x series will require
+        *Python 3.5* or above. 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>`__:
         
@@ -255,6 +262,14 @@
         
           *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:
@@ -314,7 +329,7 @@
         installed ``fontTools`` package, or the first one found in the
         ``PYTHONPATH``.
         
-        You can also use `tox <https://testrun.org/tox/latest/>`__ to
+        You can also use `tox <https://tox.readthedocs.io/en/latest/>`__ to
         automatically run tests on different Python versions in isolated virtual
         environments.
         
@@ -415,6 +430,82 @@
         Changelog
         ~~~~~~~~~
         
+        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)
         ----------------------------
         
@@ -1491,11 +1582,13 @@
 Classifier: Topic :: Text Processing :: Fonts
 Classifier: Topic :: Multimedia :: Graphics
 Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
-Provides-Extra: type1
+Provides-Extra: all
+Provides-Extra: interpolatable
+Provides-Extra: woff
 Provides-Extra: lxml
 Provides-Extra: unicode
-Provides-Extra: symfont
-Provides-Extra: all
+Provides-Extra: graphite
 Provides-Extra: ufo
-Provides-Extra: woff
-Provides-Extra: interpolatable
+Provides-Extra: type1
+Provides-Extra: plot
+Provides-Extra: symfont
diff --git a/README.rst b/README.rst
index 5f39ed8..f36084a 100644
--- a/README.rst
+++ b/README.rst
@@ -18,6 +18,13 @@
 FontTools requires `Python <http://www.python.org/download/>`__ 2.7, 3.4
 or later.
 
+**NOTE** After January 1 2019, until no later than June 30 2019, the support
+for *Python 2.7* will be limited to only bug fixes, and no new features will
+be added to the ``py27`` branch. The upcoming FontTools 4.x series will require
+*Python 3.5* or above. 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>`__:
 
@@ -245,6 +252,14 @@
 
   *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:
@@ -304,7 +319,7 @@
 installed ``fontTools`` package, or the first one found in the
 ``PYTHONPATH``.
 
-You can also use `tox <https://testrun.org/tox/latest/>`__ to
+You can also use `tox <https://tox.readthedocs.io/en/latest/>`__ to
 automatically run tests on different Python versions in isolated virtual
 environments.
 
diff --git a/Snippets/cmap-format.py b/Snippets/cmap-format.py
old mode 100755
new mode 100644
diff --git a/Snippets/interpolate.py b/Snippets/interpolate.py
old mode 100755
new mode 100644
diff --git a/Snippets/layout-features.py b/Snippets/layout-features.py
old mode 100755
new mode 100644
diff --git a/Snippets/otf2ttf.py b/Snippets/otf2ttf.py
old mode 100755
new mode 100644
diff --git a/Snippets/rename-fonts.py b/Snippets/rename-fonts.py
old mode 100755
new mode 100644
diff --git a/Snippets/subset-fpgm.py b/Snippets/subset-fpgm.py
old mode 100755
new mode 100644
diff --git a/Snippets/svg2glif.py b/Snippets/svg2glif.py
old mode 100755
new mode 100644
diff --git a/Snippets/woff2_compress.py b/Snippets/woff2_compress.py
old mode 100755
new mode 100644
diff --git a/Snippets/woff2_decompress.py b/Snippets/woff2_decompress.py
old mode 100755
new mode 100644
diff --git a/Tests/designspaceLib/designspace_test.py b/Tests/designspaceLib/designspace_test.py
index b5d0b70..2044f00 100644
--- a/Tests/designspaceLib/designspace_test.py
+++ b/Tests/designspaceLib/designspace_test.py
@@ -4,6 +4,7 @@
                         unicode_literals)
 
 import os
+import sys
 import pytest
 import warnings
 
@@ -237,12 +238,10 @@
     new.read(testDocPath)
     new.write(testDocPath2)
     # compare the file contents
-    f1 = open(testDocPath, 'r', encoding='utf-8')
-    t1 = f1.read()
-    f1.close()
-    f2 = open(testDocPath2, 'r', encoding='utf-8')
-    t2 = f2.read()
-    f2.close()
+    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]
@@ -337,12 +336,10 @@
     new = DesignSpaceDocument()
     new.read(testDocPath)
     new.write(testDocPath2)
-    f1 = open(testDocPath, 'r', encoding='utf-8')
-    t1 = f1.read()
-    f1.close()
-    f2 = open(testDocPath2, 'r', encoding='utf-8')
-    t2 = f2.read()
-    f2.close()
+    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
 
 
@@ -761,14 +758,12 @@
     # only for testing, so we can make an invalid designspace file
     # older designspace files may have conditions that are not wrapped in a conditionset
     # These can be read into a new conditionset.
-    f = open(path, 'r', encoding='utf-8')
-    d = f.read()
+    with open(path, 'r', encoding='utf-8') as f:
+        d = f.read()
     print(d)
-    f.close()
     d = d.replace('<rule name="named.rule.1">', '<rule name="named.rule.1">\n\t<condition maximum="22" minimum="33" name="axisName_a" />')
-    f = open(path, 'w', encoding='utf-8')
-    f.write(d)
-    f.close()
+    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
@@ -791,3 +786,64 @@
     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()
diff --git a/Tests/fontBuilder/data/test.otf.ttx b/Tests/fontBuilder/data/test.otf.ttx
new file mode 100644
index 0000000..4c9a2a7
--- /dev/null
+++ b/Tests/fontBuilder/data/test.otf.ttx
@@ -0,0 +1,253 @@
+<?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>
+
+  <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="0x1" numSigs="1" version="1"/>
+    <SignatureRecord format="1">
+-----BEGIN PKCS7-----
+0000000100000000
+-----END PKCS7-----
+    </SignatureRecord>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/fontBuilder/data/test.ttf.ttx b/Tests/fontBuilder/data/test.ttf.ttx
new file mode 100644
index 0000000..b2804cc
--- /dev/null
+++ b/Tests/fontBuilder/data/test.ttf.ttx
@@ -0,0 +1,270 @@
+<?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="2"/>
+  </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>
+
+  <DSIG>
+    <!-- note that the Digital Signature will be invalid after recompilation! -->
+    <tableHeader flag="0x1" numSigs="1" version="1"/>
+    <SignatureRecord format="1">
+-----BEGIN PKCS7-----
+0000000100000000
+-----END PKCS7-----
+    </SignatureRecord>
+  </DSIG>
+
+</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..c9465d2
--- /dev/null
+++ b/Tests/fontBuilder/data/test_var.otf.ttx
@@ -0,0 +1,291 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.33">
+
+  <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="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="0xed07360f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Dec  5 11:55:26 2018"/>
+    <modified value="Wed Dec  5 11:55:26 2018"/>
+    <xMin value="0"/>
+    <yMin value="0"/>
+    <xMax value="0"/>
+    <yMax value="0"/>
+    <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="0"/>
+    <minLeftSideBearing value="0"/>
+    <minRightSideBearing value="0"/>
+    <xMaxExtent value="0"/>
+    <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="2"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="600" lsb="0"/>
+    <mtx name="A" width="600" lsb="0"/>
+    <mtx name="a" width="600" lsb="0"/>
+    <mtx name="glyph00001" 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">
+      TotallyNormal
+    </namerecord>
+    <namerecord nameID="258" 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">
+      TotallyNormal
+    </namerecord>
+    <namerecord nameID="258" 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="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>
+
+  <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"/>
+          </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="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>
+        <CharString name="glyph00001">
+          100 100 rmoveto
+          900 vlineto
+          -67 67 66 -33 67 hhcurveto
+          67 66 33 67 67 hvcurveto
+          -900 vlineto
+        </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="257">
+      <coord axis="TEST" value="0.0"/>
+    </NamedInstance>
+
+    <!-- TotallyTested -->
+    <NamedInstance flags="0x0" subfamilyNameID="258">
+      <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..760e65a
--- /dev/null
+++ b/Tests/fontBuilder/data/test_var.ttf.ttx
@@ -0,0 +1,376 @@
+<?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="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="100"/>
+    <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="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="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="2"/>
+  </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="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"/><!-- 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="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>
+
+  </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">
+      TotallyNormal
+    </namerecord>
+    <namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Right Up
+    </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">
+      TotallyNormal
+    </namerecord>
+    <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
+      Right Up
+    </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>
+
+  <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="260">
+      <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="261">
+      <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="0x1" numSigs="1" version="1"/>
+    <SignatureRecord format="1">
+-----BEGIN PKCS7-----
+0000000100000000
+-----END PKCS7-----
+    </SignatureRecord>
+  </DSIG>
+
+</ttFont>
diff --git a/Tests/fontBuilder/fontBuilder_test.py b/Tests/fontBuilder/fontBuilder_test.py
new file mode 100644
index 0000000..d23f0f1
--- /dev/null
+++ b/Tests/fontBuilder/fontBuilder_test.py
@@ -0,0 +1,246 @@
+from __future__ import print_function, division, absolute_import
+from __future__ import unicode_literals
+
+import os
+import shutil
+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.ttLib.tables._g_l_y_f import GlyphCoordinates
+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 _verifyOutput(outPath):
+    f = TTFont(outPath)
+    f.saveXML(outPath + ".ttx")
+    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.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, {})
+    metrics = {}
+    for gn, advanceWidth in advanceWidths.items():
+        metrics[gn] = (advanceWidth, 100)  # XXX lsb from glyph
+    fb.setupHorizontalMetrics(metrics)
+
+    fb.setupHorizontalHeader(ascent=824, descent=200)
+    fb.setupNameTable(nameStrings)
+    fb.setupOS2()
+    fb.setupPost()
+    fb.setupDummyDSIG()
+
+    fb.save(outPath)
+
+    _verifyOutput(outPath)
+
+
+def test_build_var(tmpdir):
+    outPath = os.path.join(str(tmpdir), "test_var.ttf")
+
+    fb = FontBuilder(1024, isTTF=True)
+    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
+
+    pen = TTGlyphPen(None)
+    pen.moveTo((100, 0))
+    pen.lineTo((100, 400))
+    pen.lineTo((500, 400))
+    pen.lineTo((500, 000))
+    pen.closePath()
+
+    glyph = pen.glyph()
+
+    pen = TTGlyphPen(None)
+    emptyGlyph = pen.glyph()
+
+    glyphs = {".notdef": emptyGlyph, "A": glyph, "a": glyph, ".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.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)
+
+    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)
+
+    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)}])
+
+    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_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)
diff --git a/Tests/misc/plistlib_test.py b/Tests/misc/plistlib_test.py
index 041f332..c866529 100644
--- a/Tests/misc/plistlib_test.py
+++ b/Tests/misc/plistlib_test.py
@@ -14,6 +14,10 @@
 )
 import pytest
 
+try:
+    from collections.abc import Mapping # python >= 3.3
+except ImportError:
+    from collections import Mapping
 
 PY2 = sys.version_info < (3,)
 if PY2:
@@ -530,6 +534,25 @@
         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
 
diff --git a/Tests/misc/psCharStrings_test.py b/Tests/misc/psCharStrings_test.py
index 3f35ac8..f69f748 100644
--- a/Tests/misc/psCharStrings_test.py
+++ b/Tests/misc/psCharStrings_test.py
@@ -1,7 +1,7 @@
 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.psCharStrings import T2CharString, encodeFloat, read_realNumber
 import unittest
 
 
@@ -47,6 +47,47 @@
             cs2.program, [100, 'rmoveto', -50, -150, 200.5, 0, -50, 150,
                           'rrcurveto'])
 
+    def test_encodeFloat(self):
+        import sys
+        def hexenc(s):
+            return ' '.join('%02x' % ord(x) for x in s)
+        if sys.version_info[0] >= 3:
+            def hexenc_py3(s):
+                return ' '.join('%02x' % x for x in s)
+            hexenc = hexenc_py3
+
+        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.
+
 
 if __name__ == "__main__":
     import sys
diff --git a/Tests/pens/pointPen_test.py b/Tests/pens/pointPen_test.py
new file mode 100644
index 0000000..9c71c5e
--- /dev/null
+++ b/Tests/pens/pointPen_test.py
@@ -0,0 +1,412 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+from fontTools.misc.loggingTools import CapturingLogHandler
+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, basestring):
+            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))
+
+
+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_quad(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))
diff --git a/Tests/t1Lib/t1Lib_test.py b/Tests/t1Lib/t1Lib_test.py
index f5e934d..385dad0 100644
--- a/Tests/t1Lib/t1Lib_test.py
+++ b/Tests/t1Lib/t1Lib_test.py
@@ -2,6 +2,7 @@
 from fontTools.misc.py23 import *
 import unittest
 import os
+import sys
 from fontTools import t1Lib
 from fontTools.pens.basePen import NullPen
 import random
@@ -50,6 +51,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())
diff --git a/Tests/ttLib/tables/_k_e_r_n_test.py b/Tests/ttLib/tables/_k_e_r_n_test.py
index 8ab1b1c..b37748a 100644
--- a/Tests/ttLib/tables/_k_e_r_n_test.py
+++ b/Tests/ttLib/tables/_k_e_r_n_test.py
@@ -5,6 +5,7 @@
     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 +121,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 +385,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/_n_a_m_e_test.py b/Tests/ttLib/tables/_n_a_m_e_test.py
index a27e3c1..fde14bc 100644
--- a/Tests/ttLib/tables/_n_a_m_e_test.py
+++ b/Tests/ttLib/tables/_n_a_m_e_test.py
@@ -93,12 +93,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), [
diff --git a/Tests/ufoLib/testSupport.py b/Tests/ufoLib/testSupport.py
old mode 100755
new mode 100644
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/FeatureVars.designspace b/Tests/varLib/data/FeatureVars.designspace
index d641ba2..9c958a5 100644
--- a/Tests/varLib/data/FeatureVars.designspace
+++ b/Tests/varLib/data/FeatureVars.designspace
@@ -9,7 +9,7 @@
     <rules>
         <rule name="dollar-stroke">
             <conditionset>
-                <condition name="weight" minimum="500" maximum="1000" />
+                <condition name="weight" minimum="500" /> <!-- intentionally omitted maximum -->
             </conditionset>
             <sub name="uni0024" with="uni0024.nostroke" />
         </rule>
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/TestCFF2VF.otf b/Tests/varLib/data/TestCFF2VF.otf
new file mode 100644
index 0000000..590ad27
--- /dev/null
+++ b/Tests/varLib/data/TestCFF2VF.otf
Binary files differ
diff --git a/Tests/varLib/data/master_cff2/TestCFF2_Black.otf b/Tests/varLib/data/master_cff2/TestCFF2_Black.otf
new file mode 100644
index 0000000..b4249e1
--- /dev/null
+++ b/Tests/varLib/data/master_cff2/TestCFF2_Black.otf
Binary files differ
diff --git a/Tests/varLib/data/master_cff2/TestCFF2_ExtraLight.otf b/Tests/varLib/data/master_cff2/TestCFF2_ExtraLight.otf
new file mode 100644
index 0000000..4464791
--- /dev/null
+++ b/Tests/varLib/data/master_cff2/TestCFF2_ExtraLight.otf
Binary files differ
diff --git a/Tests/varLib/data/master_cff2/TestCFF2_Regular.otf b/Tests/varLib/data/master_cff2/TestCFF2_Regular.otf
new file mode 100644
index 0000000..c87342e
--- /dev/null
+++ b/Tests/varLib/data/master_cff2/TestCFF2_Regular.otf
Binary files differ
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 100644
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_ttf/TestFamily4-Italic15.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily4-Italic15.ttx
new file mode 100644
index 0000000..f7aa15f
--- /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 Format="2">
+      <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" Format="1">
+        <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 Format="1">
+            <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 Format="1">
+            <Glyph value="uni0308"/>
+          </MarkCoverage>
+          <BaseCoverage Format="2">
+            <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"/>
+        <!-- SubTableCount=1 -->
+        <MarkMarkPos index="0" Format="1">
+          <Mark1Coverage Format="1">
+            <Glyph value="uni0308"/>
+          </Mark1Coverage>
+          <Mark2Coverage Format="1">
+            <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..2e354b0
--- /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 Format="2">
+      <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" Format="1">
+        <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 Format="1">
+            <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 Format="1">
+            <Glyph value="uni0308"/>
+          </MarkCoverage>
+          <BaseCoverage Format="2">
+            <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"/>
+        <!-- SubTableCount=1 -->
+        <MarkMarkPos index="0" Format="1">
+          <Mark1Coverage Format="1">
+            <Glyph value="uni0308"/>
+          </Mark1Coverage>
+          <Mark2Coverage Format="1">
+            <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_ttf/Mutator_IUP.ttx b/Tests/varLib/data/master_ttx_varfont_ttf/Mutator_IUP.ttx
old mode 100755
new mode 100644
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/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 33ebbd4..66c8032 100644
--- a/Tests/varLib/data/test_results/BuildMain.ttx
+++ b/Tests/varLib/data/test_results/BuildMain.ttx
@@ -782,7 +782,7 @@
   </MVAR>
 
   <STAT>
-    <Version value="0x00010002"/>
+    <Version value="0x00010001"/>
     <DesignAxisRecordSize value="8"/>
     <!-- DesignAxisCount=2 -->
     <DesignAxisRecord>
diff --git a/Tests/varLib/data/test_results/BuildTestCFF2.ttx b/Tests/varLib/data/test_results/BuildTestCFF2.ttx
new file mode 100644
index 0000000..a4e859f
--- /dev/null
+++ b/Tests/varLib/data/test_results/BuildTestCFF2.ttx
@@ -0,0 +1,265 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="3.32">
+
+  <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>
+
+  <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.0 67.0"/>
+            </StdHW>
+            <StdVW>
+                <blend value="85 -51.0 87.0"/>
+            </StdVW>
+          </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="glyph00003">
+          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
index 0764bb8..f2f6b05 100644
--- a/Tests/varLib/data/test_results/FeatureVars.ttx
+++ b/Tests/varLib/data/test_results/FeatureVars.ttx
@@ -95,7 +95,7 @@
           </ConditionTable>
         </ConditionSet>
         <FeatureTableSubstitution>
-          <Version value="0x00010001"/>
+          <Version value="0x00010000"/>
           <!-- SubstitutionCount=1 -->
           <SubstitutionRecord index="0">
             <FeatureIndex value="0"/>
@@ -109,48 +109,6 @@
       </FeatureVariationRecord>
       <FeatureVariationRecord index="1">
         <ConditionSet>
-          <!-- ConditionCount=1 -->
-          <ConditionTable index="0" Format="1">
-            <AxisIndex value="0"/>
-            <FilterRangeMinValue value="0.20886"/>
-            <FilterRangeMaxValue value="1.0"/>
-          </ConditionTable>
-        </ConditionSet>
-        <FeatureTableSubstitution>
-          <Version value="0x00010001"/>
-          <!-- SubstitutionCount=1 -->
-          <SubstitutionRecord index="0">
-            <FeatureIndex value="0"/>
-            <Feature>
-              <!-- LookupCount=1 -->
-              <LookupListIndex index="0" value="0"/>
-            </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="0x00010001"/>
-          <!-- 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=2 -->
           <ConditionTable index="0" Format="1">
             <AxisIndex value="1"/>
@@ -164,7 +122,7 @@
           </ConditionTable>
         </ConditionSet>
         <FeatureTableSubstitution>
-          <Version value="0x00010001"/>
+          <Version value="0x00010000"/>
           <!-- SubstitutionCount=1 -->
           <SubstitutionRecord index="0">
             <FeatureIndex value="0"/>
@@ -175,6 +133,48 @@
           </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>
 
diff --git a/Tests/varLib/data/test_results/InterpolateTestCFF2VF.ttx b/Tests/varLib/data/test_results/InterpolateTestCFF2VF.ttx
new file mode 100644
index 0000000..e2d0f71
--- /dev/null
+++ b/Tests/varLib/data/test_results/InterpolateTestCFF2VF.ttx
@@ -0,0 +1,107 @@
+<?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"/>
+          </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_Getvar-instance.ttx b/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx
new file mode 100644
index 0000000..a20f0e4
--- /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"/>
+        <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"/>
+        <pt x="284" y="468" on="1"/>
+        <pt x="426" y="316" on="1"/>
+        <pt x="426" y="140" on="1"/>
+        <pt x="284" y="-12" on="1"/>
+        <pt x="220" y="-12" on="1"/>
+        <pt x="152" y="64" on="1"/>
+        <pt x="152" y="0" on="1"/>
+        <pt x="76" y="0" on="1"/>
+        <pt x="76" y="628" on="1"/>
+        <pt x="152" y="628" on="1"/>
+        <pt x="152" y="392" on="1"/>
+      </contour>
+      <contour>
+        <pt x="152" y="316" on="1"/>
+        <pt x="152" y="140" on="1"/>
+        <pt x="218" y="64" on="1"/>
+        <pt x="284" y="64" on="1"/>
+        <pt x="350" y="140" on="1"/>
+        <pt x="350" y="316" on="1"/>
+        <pt x="284" y="392" on="1"/>
+        <pt x="218" y="392" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="nonmarkingreturn"/><!-- contains no outline data -->
+
+    <TTGlyph name="q" xMin="38" yMin="-172" xMax="388" yMax="468">
+      <component glyphName="b" x="464" y="456" scale="-0.99994" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="space"/><!-- contains no outline data -->
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      VarFont
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      VarFont Regular: 2017
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      VarFont Regular
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      VarFont-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      VarFont
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      VarFont Regular: 2017
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      VarFont Regular
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      VarFont-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="NULL"/>
+    </extraNames>
+  </post>
+
+</ttFont>
diff --git a/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx b/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
old mode 100755
new mode 100644
diff --git a/Tests/varLib/featureVars_test.py b/Tests/varLib/featureVars_test.py
new file mode 100644
index 0000000..4db2e62
--- /dev/null
+++ b/Tests/varLib/featureVars_test.py
@@ -0,0 +1,123 @@
+from __future__ import print_function, division, absolute_import
+from fontTools.misc.py23 import *
+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/mutator_test.py b/Tests/varLib/mutator_test.py
index de794f0..8625de3 100644
--- a/Tests/varLib/mutator_test.py
+++ b/Tests/varLib/mutator_test.py
@@ -3,6 +3,7 @@
 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
@@ -117,6 +118,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 +161,18 @@
         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):
+
+        otf_vf_path = self.get_test_input('TestCFF2VF.otf')
+        expected_ttx_name = 'InterpolateTestCFF2VF'
+        tables = ["hmtx", "CFF2"]
+        loc = {'wght':float(200)}
+
+        varfont = TTFont(otf_vf_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 2bfe0f2..6638ea3 100644
--- a/Tests/varLib/varLib_test.py
+++ b/Tests/varLib/varLib_test.py
@@ -2,14 +2,25 @@
 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.designspaceLib import DesignSpaceDocumentError
+from fontTools.varLib import main as varLib_main, load_masters
+from fontTools.designspaceLib import (
+    DesignSpaceDocumentError, DesignSpaceDocument, SourceDescriptor,
+)
 import difflib
 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)
+    buf.seek(0)
+    return TTFont(buf)
 
 
 class BuildTest(unittest.TestCase):
@@ -113,10 +124,7 @@
             # 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
-            buf = BytesIO()
-            varfont.save(buf)
-            buf.seek(0)
-            varfont = TTFont(buf)
+            varfont = reload_font(varfont)
 
         expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx')
         self.expect_ttx(varfont, expected_ttx_path, tables)
@@ -160,7 +168,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
         """
@@ -180,7 +188,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
         """
@@ -204,6 +212,38 @@
             save_before_dump=True,
         )
 
+    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_build_CFF2(self):
+        ds_path = self.get_test_input('TestCFF2.designspace')
+        suffix = '.otf'
+        expected_ttx_name = 'BuildTestCFF2'
+        tables = ["fvar", "CFF2"]
+
+        finder = lambda s: s.replace('.ufo', suffix)
+        varfont, model, _ = build(ds_path, finder)
+        # 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)
+
     def test_varlib_main_ttf(self):
         """Mostly for testing varLib.main()
         """
@@ -250,6 +290,44 @@
         expected_ttx_path = self.get_test_output('BuildMain.ttx')
         self.expect_ttx(varfont, expected_ttx_path, tables)
 
+    def test_varlib_build_from_ds_object(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_load_masters_layerName_without_required_font():
+    ds = DesignSpaceDocument()
+    s = SourceDescriptor()
+    s.font = None
+    s.layerName = "Medium"
+    ds.addSource(s)
+
+    with pytest.raises(
+        AttributeError,
+        match="specified a layer name but lacks the required TTFont object",
+    ):
+        load_masters(ds)
+
 
 if __name__ == "__main__":
     sys.exit(unittest.main())
diff --git a/fonttools b/fonttools
old mode 100755
new mode 100644
diff --git a/post_update.sh b/post_update.sh
new file mode 100755
index 0000000..7ea7d90
--- /dev/null
+++ b/post_update.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# $1 Path to the new version.
+# $2 Path to the old version.
+
+cp -a -n $2/Lib/fontTools/Android.bp $1/Lib/fontTools/
diff --git a/requirements.txt b/requirements.txt
index 0e5f5ba..c9ac4f3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,9 +1,9 @@
 # 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.7; platform_python_implementation != "PyPy"
 brotlipy==0.7.0; platform_python_implementation == "PyPy"
 unicodedata2==11.0.0; python_version < '3.7' and platform_python_implementation != "PyPy"
-scipy==1.1.0; platform_python_implementation != "PyPy"
+scipy==1.2.0; platform_python_implementation != "PyPy"
 munkres==1.0.12; platform_python_implementation == "PyPy"
-zopfli==0.1.4
-fs==2.1.1
+zopfli==0.1.6
+fs==2.1.3
diff --git a/run-tests.sh b/run-tests.sh
old mode 100755
new mode 100644
diff --git a/setup.cfg b/setup.cfg
index 0d36f4c..6ee2542 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 3.31.0
+current_version = 3.35.0
 commit = True
 tag = False
 tag_name = {new_version}
@@ -37,7 +37,6 @@
 minversion = 3.0
 testpaths = 
 	Tests
-	fontTools
 python_files = 
 	*_test.py
 python_classes = 
@@ -47,6 +46,9 @@
 	--doctest-modules
 	--doctest-ignore-import-errors
 	--pyargs
+doctest_optionflags = 
+	ALLOW_UNICODE
+	ELLIPSIS
 filterwarnings = 
 	ignore:tostring:DeprecationWarning
 	ignore:fromstring:DeprecationWarning
diff --git a/setup.py b/setup.py
old mode 100755
new mode 100644
index e975f63..25093cd
--- a/setup.py
+++ b/setup.py
@@ -39,6 +39,11 @@
 	"lxml": [
 		"lxml >= 4.0, < 5",
 		"singledispatch >= 3.4.0.3; python_version < '3.4'",
+		# typing >= 3.6.4 is required when using ABC collections with the
+		# singledispatch backport, see:
+		# https://github.com/fonttools/fonttools/issues/1423
+		# https://github.com/python/typing/issues/484
+		"typing >= 3.6.4; python_version < '3.4'",
 	],
 	# for fontTools.sfnt and fontTools.woff2: to compress/uncompress
 	# WOFF 1.0 and WOFF 2.0 webfonts.
@@ -58,6 +63,10 @@
 			"python_version < '3.7' 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": [
@@ -65,6 +74,12 @@
 		"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",
@@ -337,7 +352,7 @@
 
 setup(
 	name="fonttools",
-	version="3.31.0",
+	version="3.35.0",
 	description="Tools to manipulate font files",
 	author="Just van Rossum",
 	author_email="just@letterror.com",
diff --git a/tox.ini b/tox.ini
index 63619e3..2e8d9ee 100644
--- a/tox.ini
+++ b/tox.ini
@@ -15,8 +15,8 @@
     !nolxml: lxml
 commands =
     # test with or without coverage, passing extra positonal args to pytest
-    cov: coverage run --parallel-mode -m pytest {posargs}
-    !cov: pytest {posargs}
+    cov: coverage run --parallel-mode -m pytest {posargs:Tests fontTools}
+    !cov: pytest {posargs:Tests fontTools}
 
 [testenv:htmlcov]
 deps =
@@ -37,10 +37,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]
 deps =
-    pygments
-    docutils
     setuptools
     wheel
 skip_install = true
@@ -50,8 +57,6 @@
 whitelist_externals =
     rm
 commands =
-    # check metadata and rst long_description
-    python setup.py check --restructuredtext --strict
     # clean up build/ and dist/ folders
     python -c 'import shutil; shutil.rmtree("dist", ignore_errors=True)'
     python setup.py clean --all