Upgrade fonttools to 3.37.0
am: f08648c1c4

Change-Id: Ifcff9ecf8cc4213d6d5a4360124e181fdfa2cfd4
diff --git a/.travis/after_success.sh b/.travis/after_success.sh
old mode 100644
new mode 100755
diff --git a/.travis/before_install.sh b/.travis/before_install.sh
old mode 100644
new mode 100755
diff --git a/.travis/install.sh b/.travis/install.sh
old mode 100644
new mode 100755
diff --git a/.travis/run.sh b/.travis/run.sh
old mode 100644
new mode 100755
diff --git a/Doc/source/designspaceLib/readme.rst b/Doc/source/designspaceLib/readme.rst
index 06bf46f..68a7009 100644
--- a/Doc/source/designspaceLib/readme.rst
+++ b/Doc/source/designspaceLib/readme.rst
@@ -216,8 +216,6 @@
 -  ``glyphs``: dict for special master definitions for glyphs. If glyphs
    need special masters (to record the results of executed rules for
    example). MutatorMath.
--  ``mutedGlyphNames``: list of glyphnames that should be suppressed in
-   the generation of this instance.
 -  ``kerning``: bool. Indicates if this instance needs its kerning
    calculated. MutatorMath.
 -  ``info``: bool. Indicated if this instance needs the interpolating
@@ -277,7 +275,8 @@
    dicts. MutatorMath + Varlib.
 -  ``labelNames``: dict. When defining a non-registered axis, it will be
    necessary to define user-facing readable names for the axis. Keyed by
-   xml:lang code. Varlib.
+   xml:lang code. Values are required to be ``unicode`` strings, even if
+   they only contain ASCII characters.
 -  ``minimum``: number. The minimum value for this axis in user space.
    MutatorMath + Varlib.
 -  ``maximum``: number. The maximum value for this axis in user space.
diff --git a/Doc/source/designspaceLib/scripting.rst b/Doc/source/designspaceLib/scripting.rst
index 2bd4a0a..5a17816 100644
--- a/Doc/source/designspaceLib/scripting.rst
+++ b/Doc/source/designspaceLib/scripting.rst
@@ -70,7 +70,7 @@
 by OpenType. Think "The label next to the slider". The attribute is a
 dictionary. The key is the `xml language
 tag <https://www.w3.org/International/articles/language-tags/>`__, the
-value is a utf-8 string with the name. Whether or not this attribute is
+value is a ``unicode`` string with the name. Whether or not this attribute is
 used depends on the font building tool, the operating system and the
 authoring software. This, at least, is the place to record it.
 
diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py
index e922c48..f3759f1 100644
--- a/Lib/fontTools/__init__.py
+++ b/Lib/fontTools/__init__.py
@@ -5,6 +5,6 @@
 
 log = logging.getLogger(__name__)
 
-version = __version__ = "3.35.0"
+version = __version__ = "3.37.0"
 
 __all__ = ["version", "log", "configLogger"]
diff --git a/Lib/fontTools/designspaceLib/__init__.py b/Lib/fontTools/designspaceLib/__init__.py
index d9da48c..b8b962f 100644
--- a/Lib/fontTools/designspaceLib/__init__.py
+++ b/Lib/fontTools/designspaceLib/__init__.py
@@ -235,7 +235,6 @@
         self.localisedStyleMapStyleName = {}
         self.localisedStyleMapFamilyName = {}
         self.glyphs = {}
-        self.mutedGlyphNames = []
         self.kerning = True
         self.info = True
 
@@ -246,25 +245,25 @@
     filename = posixpath_property("_filename")
 
     def setStyleName(self, styleName, languageCode="en"):
-        self.localisedStyleName[languageCode] = styleName
+        self.localisedStyleName[languageCode] = tounicode(styleName)
 
     def getStyleName(self, languageCode="en"):
         return self.localisedStyleName.get(languageCode)
 
     def setFamilyName(self, familyName, languageCode="en"):
-        self.localisedFamilyName[languageCode] = familyName
+        self.localisedFamilyName[languageCode] = tounicode(familyName)
 
     def getFamilyName(self, languageCode="en"):
         return self.localisedFamilyName.get(languageCode)
 
     def setStyleMapStyleName(self, styleMapStyleName, languageCode="en"):
-        self.localisedStyleMapStyleName[languageCode] = styleMapStyleName
+        self.localisedStyleMapStyleName[languageCode] = tounicode(styleMapStyleName)
 
     def getStyleMapStyleName(self, languageCode="en"):
         return self.localisedStyleMapStyleName.get(languageCode)
 
     def setStyleMapFamilyName(self, styleMapFamilyName, languageCode="en"):
-        self.localisedStyleMapFamilyName[languageCode] = styleMapFamilyName
+        self.localisedStyleMapFamilyName[languageCode] = tounicode(styleMapFamilyName)
 
     def getStyleMapFamilyName(self, languageCode="en"):
         return self.localisedStyleMapFamilyName.get(languageCode)
@@ -753,8 +752,7 @@
                 # '{http://www.w3.org/XML/1998/namespace}lang'
                 for key, lang in labelNameElement.items():
                     if key == XML_LANG:
-                        labelName = labelNameElement.text
-                        axisObject.labelNames[lang] = labelName
+                        axisObject.labelNames[lang] = tounicode(labelNameElement.text)
             self.documentObject.axes.append(axisObject)
             self.axisDefaults[axisObject.name] = axisObject.default
         self.documentObject.defaultLoc = self.axisDefaults
diff --git a/Lib/fontTools/feaLib/ast.py b/Lib/fontTools/feaLib/ast.py
index 7f33ca4..b7d2066 100644
--- a/Lib/fontTools/feaLib/ast.py
+++ b/Lib/fontTools/feaLib/ast.py
@@ -293,7 +293,10 @@
         builder.end_feature()
 
     def asFea(self, indent=""):
-        res = indent + "feature %s {\n" % self.name.strip()
+        res = indent + "feature %s " % self.name.strip()
+        if self.use_extension:
+            res += "useExtension "
+        res += "{\n"
         res += Block.asFea(self, indent=indent)
         res += indent + "} %s;\n" % self.name.strip()
         return res
@@ -329,7 +332,10 @@
         builder.end_lookup_block()
 
     def asFea(self, indent=""):
-        res = "lookup {} {{\n".format(self.name)
+        res = "lookup {} ".format(self.name)
+        if self.use_extension:
+            res += "useExtension "
+        res += "{\n"
         res += Block.asFea(self, indent=indent)
         res += "{}}} {};\n".format(indent, self.name)
         return res
@@ -957,12 +963,12 @@
         res = "enum " if self.enumerated else ""
         if self.valuerecord2:
             res += "pos {} {} {} {};".format(
-                self.glyphs1.asFea(), self.valuerecord1.makeString(),
-                self.glyphs2.asFea(), self.valuerecord2.makeString())
+                self.glyphs1.asFea(), self.valuerecord1.asFea(),
+                self.glyphs2.asFea(), self.valuerecord2.asFea())
         else:
             res += "pos {} {} {};".format(
                 self.glyphs1.asFea(), self.glyphs2.asFea(),
-                self.valuerecord1.makeString())
+                self.valuerecord1.asFea())
         return res
 
 
@@ -1063,12 +1069,12 @@
             if len(self.prefix):
                 res += " ".join(map(asFea, self.prefix)) + " "
             res += " ".join([asFea(x[0]) + "'" + (
-                (" " + x[1].makeString()) if x[1] else "") for x in self.pos])
+                (" " + x[1].asFea()) if x[1] else "") for x in self.pos])
             if len(self.suffix):
                 res += " " + " ".join(map(asFea, self.suffix))
         else:
             res += " ".join([asFea(x[0]) + " " +
-                             (x[1].makeString() if x[1] else "") for x in self.pos])
+                             (x[1].asFea() if x[1] else "") for x in self.pos])
         res += ";"
         return res
 
@@ -1114,13 +1120,15 @@
                 hash(self.xPlaDevice) ^ hash(self.yPlaDevice) ^
                 hash(self.xAdvDevice) ^ hash(self.yAdvDevice))
 
-    def makeString(self, vertical=None):
+    def asFea(self, indent=""):
+        if not self:
+            return "<NULL>"
+
         x, y = self.xPlacement, self.yPlacement
         xAdvance, yAdvance = self.xAdvance, self.yAdvance
         xPlaDevice, yPlaDevice = self.xPlaDevice, self.yPlaDevice
         xAdvDevice, yAdvDevice = self.xAdvDevice, self.yAdvDevice
-        if vertical is None:
-            vertical = self.vertical
+        vertical = self.vertical
 
         # Try format A, if possible.
         if x is None and y is None:
@@ -1140,6 +1148,23 @@
             deviceToString(xPlaDevice), deviceToString(yPlaDevice),
             deviceToString(xAdvDevice), deviceToString(yAdvDevice))
 
+    def __bool__(self):
+        return any(
+            getattr(self, v) is not None
+            for v in [
+                "xPlacement",
+                "yPlacement",
+                "xAdvance",
+                "yAdvance",
+                "xPlaDevice",
+                "yPlaDevice",
+                "xAdvDevice",
+                "yAdvDevice",
+            ]
+        )
+
+    __nonzero__ = __bool__
+
 
 class ValueRecordDefinition(Statement):
     def __init__(self, name, value, location=None):
diff --git a/Lib/fontTools/feaLib/builder.py b/Lib/fontTools/feaLib/builder.py
index 957b01d..e521a0e 100644
--- a/Lib/fontTools/feaLib/builder.py
+++ b/Lib/fontTools/feaLib/builder.py
@@ -1114,7 +1114,7 @@
 
 def makeOpenTypeValueRecord(v, pairPosContext):
     """ast.ValueRecord --> (otBase.ValueRecord, int ValueFormat)"""
-    if v is None:
+    if not v:
         return None, 0
 
     vr = {}
diff --git a/Lib/fontTools/feaLib/parser.py b/Lib/fontTools/feaLib/parser.py
index 3042698..61d3711 100644
--- a/Lib/fontTools/feaLib/parser.py
+++ b/Lib/fontTools/feaLib/parser.py
@@ -1153,7 +1153,7 @@
             name = self.expect_name_()
             if name == "NULL":
                 self.expect_symbol_(">")
-                return None
+                return self.ast.ValueRecord()
             vrd = self.valuerecords_.resolve(name)
             if vrd is None:
                 raise FeatureLibError("Unknown valueRecordDef \"%s\"" % name,
diff --git a/Lib/fontTools/fontBuilder.py b/Lib/fontTools/fontBuilder.py
index 7051982..8854c16 100644
--- a/Lib/fontTools/fontBuilder.py
+++ b/Lib/fontTools/fontBuilder.py
@@ -364,9 +364,18 @@
         """Set the glyph order for the font."""
         self.font.setGlyphOrder(glyphOrder)
 
-    def setupCharacterMap(self, cmapping, allowFallback=False):
+    def setupCharacterMap(self, cmapping, uvs=None, allowFallback=False):
         """Build the `cmap` table for the font. The `cmapping` argument should
         be a dict mapping unicode code points as integers to glyph names.
+
+        The `uvs` argument, when passed, must be a list of tuples, describing
+        Unicode Variation Sequences. These tuples have three elements:
+            (unicodeValue, variationSelector, glyphName)
+        `unicodeValue` and `variationSelector` are integer code points.
+        `glyphName` may be None, to indicate this is the default variation.
+        Text processors will then use the cmap to find the glyph name.
+        Each Unicode Variation Sequence should be an officially supported
+        sequence, but this is not policed.
         """
         subTables = []
         highestUnicode = max(cmapping)
@@ -390,6 +399,19 @@
         subTable_0_3 = buildCmapSubTable(cmapping_3_1, format, 0, 3)
         subTables.append(subTable_0_3)
 
+        if uvs is not None:
+            uvsDict = {}
+            for unicodeValue, variationSelector, glyphName in uvs:
+                if cmapping.get(unicodeValue) == glyphName:
+                    # this is a default variation
+                    glyphName = None
+                if variationSelector not in uvsDict:
+                    uvsDict[variationSelector] = []
+                uvsDict[variationSelector].append((unicodeValue, glyphName))
+            uvsSubTable = buildCmapSubTable({}, 14, 0, 5)
+            uvsSubTable.uvsDict = uvsDict
+            subTables.append(uvsSubTable)
+
         self.font["cmap"] = newTable("cmap")
         self.font["cmap"].tableVersion = 0
         self.font["cmap"].tables = subTables
diff --git a/Lib/fontTools/misc/psCharStrings.py b/Lib/fontTools/misc/psCharStrings.py
index 6881074..34f21bb 100644
--- a/Lib/fontTools/misc/psCharStrings.py
+++ b/Lib/fontTools/misc/psCharStrings.py
@@ -983,6 +983,16 @@
 			return
 		opcodes = self.opcodes
 		program = self.program
+
+		if isCFF2:
+			# If present, remove return and endchar operators.
+			if program and program[-1] in ("return", "endchar"):
+				program = program[:-1]
+		elif program and not isinstance(program[-1], basestring):
+			raise CharStringCompileError(
+				"T2CharString or Subr has items on the stack after last operator."
+			)
+
 		bytecode = []
 		encodeInt = self.getIntEncoder()
 		encodeFixed = self.getFixedEncoder()
@@ -1012,11 +1022,6 @@
 			raise
 		self.setBytecode(bytecode)
 
-		if isCFF2:
-			# If present, remove return and endchar operators.
-			if self.bytecode and (byteord(self.bytecode[-1]) in (11, 14)):
-				self.bytecode = self.bytecode[:-1]
-
 	def needsDecompilation(self):
 		return self.bytecode is not None
 
@@ -1088,13 +1093,12 @@
 				else:
 					args.append(token)
 			if args:
-				if self.isCFF2:
-					# CFF2Subr's can have numeric arguments on the stack after the last operator.
-					args = [str(arg) for arg in args]
-					line = ' '.join(args)
-					xmlWriter.write(line)
-				else:
-					assert 0, "T2Charstring or Subr has items on the stack after last operator."
+				# NOTE: only CFF2 charstrings/subrs can have numeric arguments on
+				# the stack after the last operator. Compiling this would fail if
+				# this is part of CFF 1.0 table.
+				args = [str(arg) for arg in args]
+				line = ' '.join(args)
+				xmlWriter.write(line)
 
 	def fromXML(self, name, attrs, content):
 		from fontTools.misc.textTools import binary2num, readHex
diff --git a/Lib/fontTools/misc/py23.py b/Lib/fontTools/misc/py23.py
index 0a13b6f..3702055 100644
--- a/Lib/fontTools/misc/py23.py
+++ b/Lib/fontTools/misc/py23.py
@@ -308,6 +308,35 @@
 		return result
 
 
+try:
+	_isfinite = _math.isfinite  # Python >= 3.2
+except AttributeError:
+	_isfinite = None
+	_isnan = _math.isnan
+	_isinf = _math.isinf
+
+
+def isfinite(f):
+	"""
+	>>> isfinite(0.0)
+	True
+	>>> isfinite(-0.1)
+	True
+	>>> isfinite(1e10)
+	True
+	>>> isfinite(float("nan"))
+	False
+	>>> isfinite(float("+inf"))
+	False
+	>>> isfinite(float("-inf"))
+	False
+	"""
+	if _isfinite is not None:
+		return _isfinite(f)
+	else:
+		return not (_isnan(f) or _isinf(f))
+
+
 import decimal as _decimal
 
 if PY3:
diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py
index 2ed17cc..9403fc1 100644
--- a/Lib/fontTools/subset/__init__.py
+++ b/Lib/fontTools/subset/__init__.py
@@ -123,6 +123,8 @@
 
 Glyph set expansion:
   These options control how additional glyphs are added to the subset.
+  --retain-gids
+      Retain glyph indices; just empty glyphs not needed in-place.
   --notdef-glyph
       Add the '.notdef' glyph to the subset (ie, keep it). [default]
   --no-notdef-glyph
@@ -1714,16 +1716,23 @@
 @_add_method(ttLib.getTableClass('vmtx'))
 def subset_glyphs(self, s):
 	self.metrics = _dict_subset(self.metrics, s.glyphs)
+	for g in s.glyphs_emptied:
+		self.metrics[g] = (0,0)
 	return bool(self.metrics)
 
 @_add_method(ttLib.getTableClass('hmtx'))
 def subset_glyphs(self, s):
 	self.metrics = _dict_subset(self.metrics, s.glyphs)
+	for g in s.glyphs_emptied:
+		self.metrics[g] = (0,0)
 	return True # Required table
 
 @_add_method(ttLib.getTableClass('hdmx'))
 def subset_glyphs(self, s):
 	self.hdmx = {sz:_dict_subset(l, s.glyphs) for sz,l in self.hdmx.items()}
+	for sz in self.hdmx:
+		for g in s.glyphs_emptied:
+			self.hdmx[sz][g] = 0
 	return bool(self.hdmx)
 
 @_add_method(ttLib.getTableClass('ankr'))
@@ -1784,6 +1793,8 @@
 def subset_glyphs(self, s):
 	table = self.table
 
+	# TODO Update for retain_gids
+
 	used = set()
 
 	if table.AdvWidthMap:
@@ -2010,7 +2021,7 @@
 	return True
 
 @_add_method(ttLib.getTableModule('glyf').Glyph)
-def remapComponentsFast(self, indices):
+def remapComponentsFast(self, glyphidmap):
 	if not self.data or struct.unpack(">h", self.data[:2])[0] >= 0:
 		return	# Not composite
 	data = array.array("B", self.data)
@@ -2020,7 +2031,7 @@
 		flags =(data[i] << 8) | data[i+1]
 		glyphID =(data[i+2] << 8) | data[i+3]
 		# Remap
-		glyphID = indices.index(glyphID)
+		glyphID = glyphidmap[glyphID]
 		data[i+2] = glyphID >> 8
 		data[i+3] = glyphID & 0xFF
 		i += 4
@@ -2063,13 +2074,17 @@
 @_add_method(ttLib.getTableClass('glyf'))
 def subset_glyphs(self, s):
 	self.glyphs = _dict_subset(self.glyphs, s.glyphs)
-	indices = [i for i,g in enumerate(self.glyphOrder) if g in s.glyphs]
-	for v in self.glyphs.values():
-		if hasattr(v, "data"):
-			v.remapComponentsFast(indices)
-		else:
-			pass	# No need
-	self.glyphOrder = [g for g in self.glyphOrder if g in s.glyphs]
+	if not s.options.retain_gids:
+		indices = [i for i,g in enumerate(self.glyphOrder) if g in s.glyphs]
+		glyphmap = {o:n for n,o in enumerate(indices)}
+		for v in self.glyphs.values():
+			if hasattr(v, "data"):
+				v.remapComponentsFast(glyphmap)
+	Glyph = ttLib.getTableModule('glyf').Glyph
+	for g in s.glyphs_emptied:
+		self.glyphs[g] = Glyph()
+		self.glyphs[g].data = ''
+	self.glyphOrder = [g for g in self.glyphOrder if g in s.glyphs or g in s.glyphs_emptied]
 	# Don't drop empty 'glyf' tables, otherwise 'loca' doesn't get subset.
 	return True
 
@@ -2275,6 +2290,7 @@
 		self.name_legacy = False
 		self.name_languages = [0x0409] # English
 		self.obfuscate_names = False # to make webfont unusable as a system font
+		self.retain_gids = False
 		self.notdef_glyph = True # gid0 for TrueType / .notdef for CFF
 		self.notdef_outline = False # No need for notdef to have an outline really
 		self.recommended_glyphs = False # gid1, gid2, gid3 for TrueType
@@ -2533,12 +2549,17 @@
 				log.glyphs(self.glyphs, font=font)
 		self.glyphs_cffed = frozenset(self.glyphs)
 
-		self.glyphs_all = frozenset(self.glyphs)
+		self.glyphs_retained = frozenset(self.glyphs)
+
+		self.glyphs_emptied = frozenset()
+		if self.options.retain_gids:
+			self.glyphs_emptied = realGlyphs - self.glyphs_retained
+			# TODO Drop empty glyphs at the end of GlyphOrder vector.
 
 		order = font.getReverseGlyphMap()
-		self.reverseOrigGlyphMap = {g:order[g] for g in self.glyphs_all}
+		self.reverseOrigGlyphMap = {g:order[g] for g in self.glyphs_retained}
 
-		log.info("Retaining %d glyphs", len(self.glyphs_all))
+		log.info("Retaining %d glyphs", len(self.glyphs_retained))
 
 		del self.glyphs
 
@@ -2551,7 +2572,7 @@
 			elif hasattr(clazz, 'subset_glyphs'):
 				with timer("subset '%s'" % tag):
 					table = font[tag]
-					self.glyphs = self.glyphs_all
+					self.glyphs = self.glyphs_retained
 					retain = table.subset_glyphs(self)
 					del self.glyphs
 				if not retain:
@@ -2565,11 +2586,12 @@
 				log.warning("%s NOT subset; don't know how to subset; dropped", tag)
 				del font[tag]
 
-		with timer("subset GlyphOrder"):
-			glyphOrder = font.getGlyphOrder()
-			glyphOrder = [g for g in glyphOrder if g in self.glyphs_all]
-			font.setGlyphOrder(glyphOrder)
-			font._buildReverseGlyphOrderDict()
+		if not self.options.retain_gids:
+			with timer("subset GlyphOrder"):
+				glyphOrder = font.getGlyphOrder()
+				glyphOrder = [g for g in glyphOrder if g in self.glyphs_retained]
+				font.setGlyphOrder(glyphOrder)
+				font._buildReverseGlyphOrderDict()
 
 	def _prune_post_subset(self, font):
 		for tag in font.keys():
diff --git a/Lib/fontTools/subset/cff.py b/Lib/fontTools/subset/cff.py
index 9a2b77e..96dc321 100644
--- a/Lib/fontTools/subset/cff.py
+++ b/Lib/fontTools/subset/cff.py
@@ -66,6 +66,26 @@
 		s.glyphs.update(components)
 		decompose = components
 
+def _empty_charstring(font, glyphName, isCFF2, ignoreWidth=False):
+	c, fdSelectIndex = font.CharStrings.getItemAndSelector(glyphName)
+	if isCFF2 or ignoreWidth:
+		# CFF2 charstrings have no widths nor 'endchar' operators
+		c.decompile()
+		c.program = [] if isCFF2 else ['endchar']
+	else:
+		if hasattr(font, 'FDArray') and font.FDArray is not None:
+			private = font.FDArray[fdSelectIndex].Private
+		else:
+			private = font.Private
+		dfltWdX = private.defaultWidthX
+		nmnlWdX = private.nominalWidthX
+		pen = NullPen()
+		c.draw(pen)  # this will set the charstring's width
+		if c.width != dfltWdX:
+			c.program = [c.width - nmnlWdX, 'endchar']
+		else:
+			c.program = ['endchar']
+
 @_add_method(ttLib.getTableClass('CFF '))
 def prune_pre_subset(self, font, options):
 	cff = self.cff
@@ -73,21 +93,10 @@
 	cff.fontNames = cff.fontNames[:1]
 
 	if options.notdef_glyph and not options.notdef_outline:
+		isCFF2 = cff.major > 1
 		for fontname in cff.keys():
 			font = cff[fontname]
-			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']
+			_empty_charstring(font, ".notdef", isCFF2=isCFF2)
 
 	# Clear useless Encoding
 	for fontname in cff.keys():
@@ -104,37 +113,42 @@
 		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}
+		if s.options.retain_gids:
+			isCFF2 = cff.major > 1
+			for g in s.glyphs_emptied:
+				_empty_charstring(font, g, isCFF2=isCFF2, ignoreWidth=True)
 		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)
+			# 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())
 
diff --git a/Lib/fontTools/svgLib/path/arc.py b/Lib/fontTools/svgLib/path/arc.py
new file mode 100644
index 0000000..38d1ea9
--- /dev/null
+++ b/Lib/fontTools/svgLib/path/arc.py
@@ -0,0 +1,157 @@
+"""Convert SVG Path's elliptical arcs to Bezier curves.
+
+The code is mostly adapted from Blink's SVGPathNormalizer::DecomposeArcToCubic
+https://github.com/chromium/chromium/blob/93831f2/third_party/
+blink/renderer/core/svg/svg_path_parser.cc#L169-L278
+"""
+from __future__ import print_function, division, absolute_import, unicode_literals
+from fontTools.misc.py23 import *
+from fontTools.misc.py23 import isfinite
+from fontTools.misc.transform import Identity, Scale
+from math import atan2, ceil, cos, fabs, pi, radians, sin, sqrt, tan
+
+
+TWO_PI = 2 * pi
+PI_OVER_TWO = 0.5 * pi
+
+
+def _map_point(matrix, pt):
+    # apply Transform matrix to a point represented as a complex number
+    r = matrix.transformPoint((pt.real, pt.imag))
+    return r[0] + r[1] * 1j
+
+
+class EllipticalArc(object):
+
+    def __init__(self, current_point, rx, ry, rotation, large, sweep, target_point):
+        self.current_point = current_point
+        self.rx = rx
+        self.ry = ry
+        self.rotation = rotation
+        self.large = large
+        self.sweep = sweep
+        self.target_point = target_point
+
+        # SVG arc's rotation angle is expressed in degrees, whereas Transform.rotate
+        # uses radians
+        self.angle = radians(rotation)
+
+        # these derived attributes are computed by the _parametrize method
+        self.center_point = self.theta1 = self.theta2 = self.theta_arc = None
+
+    def _parametrize(self):
+        # convert from endopoint to center parametrization:
+        # https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
+
+        # If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a
+        # "lineto") joining the endpoints.
+        # http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
+        rx = fabs(self.rx)
+        ry = fabs(self.ry)
+        if not (rx and ry):
+            return False
+
+        # If the current point and target point for the arc are identical, it should
+        # be treated as a zero length path. This ensures continuity in animations.
+        if self.target_point == self.current_point:
+            return False
+
+        mid_point_distance = (self.current_point - self.target_point) * 0.5
+
+        point_transform = Identity.rotate(-self.angle)
+
+        transformed_mid_point = _map_point(point_transform, mid_point_distance)
+        square_rx = rx * rx
+        square_ry = ry * ry
+        square_x = transformed_mid_point.real * transformed_mid_point.real
+        square_y = transformed_mid_point.imag * transformed_mid_point.imag
+
+        # Check if the radii are big enough to draw the arc, scale radii if not.
+        # http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
+        radii_scale = square_x / square_rx + square_y / square_ry
+        if radii_scale > 1:
+            rx *= sqrt(radii_scale)
+            ry *= sqrt(radii_scale)
+            self.rx, self.ry = rx, ry
+
+        point_transform = Scale(1 / rx, 1 / ry).rotate(-self.angle)
+
+        point1 = _map_point(point_transform, self.current_point)
+        point2 = _map_point(point_transform, self.target_point)
+        delta = point2 - point1
+
+        d = delta.real * delta.real + delta.imag * delta.imag
+        scale_factor_squared = max(1 / d - 0.25, 0.0)
+
+        scale_factor = sqrt(scale_factor_squared)
+        if self.sweep == self.large:
+            scale_factor = -scale_factor
+
+        delta *= scale_factor
+        center_point = (point1 + point2) * 0.5
+        center_point += complex(-delta.imag, delta.real)
+        point1 -= center_point
+        point2 -= center_point
+
+        theta1 = atan2(point1.imag, point1.real)
+        theta2 = atan2(point2.imag, point2.real)
+
+        theta_arc = theta2 - theta1
+        if theta_arc < 0 and self.sweep:
+            theta_arc += TWO_PI
+        elif theta_arc > 0 and not self.sweep:
+            theta_arc -= TWO_PI
+
+        self.theta1 = theta1
+        self.theta2 = theta1 + theta_arc
+        self.theta_arc = theta_arc
+        self.center_point = center_point
+
+        return True
+
+    def _decompose_to_cubic_curves(self):
+        if self.center_point is None and not self._parametrize():
+            return
+
+        point_transform = Identity.rotate(self.angle).scale(self.rx, self.ry)
+
+        # Some results of atan2 on some platform implementations are not exact
+        # enough. So that we get more cubic curves than expected here. Adding 0.001f
+        # reduces the count of sgements to the correct count.
+        num_segments = int(ceil(fabs(self.theta_arc / (PI_OVER_TWO + 0.001))))
+        for i in range(num_segments):
+            start_theta = self.theta1 + i * self.theta_arc / num_segments
+            end_theta = self.theta1 + (i + 1) * self.theta_arc / num_segments
+
+            t = (4 / 3) * tan(0.25 * (end_theta - start_theta))
+            if not isfinite(t):
+                return
+
+            sin_start_theta = sin(start_theta)
+            cos_start_theta = cos(start_theta)
+            sin_end_theta = sin(end_theta)
+            cos_end_theta = cos(end_theta)
+
+            point1 = complex(
+                cos_start_theta - t * sin_start_theta,
+                sin_start_theta + t * cos_start_theta,
+            )
+            point1 += self.center_point
+            target_point = complex(cos_end_theta, sin_end_theta)
+            target_point += self.center_point
+            point2 = target_point
+            point2 += complex(t * sin_end_theta, -t * cos_end_theta)
+
+            point1 = _map_point(point_transform, point1)
+            point2 = _map_point(point_transform, point2)
+            target_point = _map_point(point_transform, target_point)
+
+            yield point1, point2, target_point
+
+    def draw(self, pen):
+        for point1, point2, target_point in self._decompose_to_cubic_curves():
+            pen.curveTo(
+                (point1.real, point1.imag),
+                (point2.real, point2.imag),
+                (target_point.real, target_point.imag),
+            )
diff --git a/Lib/fontTools/svgLib/path/parser.py b/Lib/fontTools/svgLib/path/parser.py
index 4daefca..ae0aba3 100644
--- a/Lib/fontTools/svgLib/path/parser.py
+++ b/Lib/fontTools/svgLib/path/parser.py
@@ -10,8 +10,10 @@
 from __future__ import (
     print_function, division, absolute_import, unicode_literals)
 from fontTools.misc.py23 import *
+from .arc import EllipticalArc
 import re
 
+
 COMMANDS = set('MmZzLlHhVvCcSsQqTtAa')
 UPPERCASE = set('MZLHVCSQTA')
 
@@ -27,7 +29,7 @@
             yield token
 
 
-def parse_path(pathdef, pen, current_pos=(0, 0)):
+def parse_path(pathdef, pen, current_pos=(0, 0), arc_class=EllipticalArc):
     """ Parse SVG path definition (i.e. "d" attribute of <path> elements)
     and call a 'pen' object's moveTo, lineTo, curveTo, qCurveTo and closePath
     methods.
@@ -35,8 +37,13 @@
     If 'current_pos' (2-float tuple) is provided, the initial moveTo will
     be relative to that instead being absolute.
 
-    Arc segments (commands "A" or "a") are not currently supported, and raise
-    NotImplementedError.
+    If the pen has an "arcTo" method, it is called with the original values
+    of the elliptical arc curve commands:
+
+        pen.arcTo(rx, ry, rotation, arc_large, arc_sweep, (x, y))
+
+    Otherwise, the arcs are approximated by series of cubic Bezier segments
+    ("curveTo"), one every 90 degrees.
     """
     # In the SVG specs, initial movetos are absolute, even if
     # specified as 'm'. This is the default behavior here as well.
@@ -52,6 +59,8 @@
     command = None
     last_control = None
 
+    have_arcTo = hasattr(pen, "arcTo")
+
     while elements:
 
         if elements[-1] in COMMANDS:
@@ -209,7 +218,34 @@
             last_control = control
 
         elif command == 'A':
-            raise NotImplementedError('arcs are not supported')
+            rx = float(elements.pop())
+            ry = float(elements.pop())
+            rotation = float(elements.pop())
+            arc_large = bool(int(elements.pop()))
+            arc_sweep = bool(int(elements.pop()))
+            end = float(elements.pop()) + float(elements.pop()) * 1j
+
+            if not absolute:
+                end += current_pos
+
+            # if the pen supports arcs, pass the values unchanged, otherwise
+            # approximate the arc with a series of cubic bezier curves
+            if have_arcTo:
+                pen.arcTo(
+                    rx,
+                    ry,
+                    rotation,
+                    arc_large,
+                    arc_sweep,
+                    (end.real, end.imag),
+                )
+            else:
+                arc = arc_class(
+                    current_pos, rx, ry, rotation, arc_large, arc_sweep, end
+                )
+                arc.draw(pen)
+
+            current_pos = end
 
     # no final Z command, it's an open path
     if start_pos is not None:
diff --git a/Lib/fontTools/ttLib/tables/_c_m_a_p.py b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
index 5bc9354..1767ad4 100644
--- a/Lib/fontTools/ttLib/tables/_c_m_a_p.py
+++ b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
@@ -104,7 +104,7 @@
 			tables.append(table)
 
 	def compile(self, ttFont):
-		self.tables.sort()    # sort according to the spec; see CmapSubtable.__lt__()
+		self.tables.sort()  # sort according to the spec; see CmapSubtable.__lt__()
 		numSubTables = len(self.tables)
 		totalOffset = 4 + 8 * numSubTables
 		data = struct.pack(">HH", self.tableVersion, numSubTables)
@@ -246,7 +246,7 @@
 
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
-		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
+		# If not, someone is calling the subtable decompile() directly, and must provide both args.
 		if data is not None and ttFont is not None:
 			self.decompileHeader(data, ttFont)
 		else:
@@ -310,15 +310,15 @@
 		# so that we are more likely to be able to combine glypharray GID subranges.
 		# This means that we have a problem when minGI is > 32K
 		# Since the final gi is reconstructed from the glyphArray GID by:
-		#    (short)finalGID = (gid +  idDelta) % 0x10000),
+		#    (short)finalGID = (gid + idDelta) % 0x10000),
 		# we can get from a glypharray GID of 1 to a final GID of 65K by subtracting 2, and casting the
 		# negative number to an unsigned short.
 
-		if  (minGI > 1):
-			if  minGI > 0x7FFF:
+		if (minGI > 1):
+			if minGI > 0x7FFF:
 				subHeader.idDelta = -(0x10000 - minGI) -1
 			else:
-				subHeader.idDelta =  minGI -1
+				subHeader.idDelta = minGI -1
 			idDelta = subHeader.idDelta
 			for i in range(subHeader.entryCount):
 				gid = subHeader.glyphIndexArray[i]
@@ -327,7 +327,7 @@
 
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
-		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
+		# If not, someone is calling the subtable decompile() directly, and must provide both args.
 		if data is not None and ttFont is not None:
 			self.decompileHeader(data, ttFont)
 		else:
@@ -360,7 +360,7 @@
 			subHeaderList.append(subHeader)
 		# How this gets processed.
 		# Charcodes may be one or two bytes.
-		# The first byte of a charcode is mapped through the  subHeaderKeys, to select
+		# The first byte of a charcode is mapped through the subHeaderKeys, to select
 		# a subHeader. For any subheader but 0, the next byte is then mapped through the
 		# selected subheader. If subheader Index 0 is selected, then the byte itself is
 		# mapped through the subheader, and there is no second byte.
@@ -466,17 +466,17 @@
 
 					gids.append(gid)
 
-		# Process the (char code to gid) item list  in char code order.
+		# Process the (char code to gid) item list in char code order.
 		# By definition, all one byte char codes map to subheader 0.
 		# For all the two byte char codes, we assume that the first byte maps maps to the empty subhead (with an entry count of 0,
 		# which defines all char codes in its range to map to notdef) unless proven otherwise.
 		# Note that since the char code items are processed in char code order, all the char codes with the
 		# same first byte are in sequential order.
 
-		subHeaderKeys = [ kEmptyTwoCharCodeRange for x in  range(256)] # list of indices into subHeaderList.
+		subHeaderKeys = [kEmptyTwoCharCodeRange for x in range(256)] # list of indices into subHeaderList.
 		subHeaderList = []
 
-		# We force this subheader entry 0  to exist in the subHeaderList in the case where some one comes up
+		# We force this subheader entry 0 to exist in the subHeaderList in the case where some one comes up
 		# with a cmap where all the one byte char codes map to notdef,
 		# with the result that the subhead 0 would not get created just by processing the item list.
 		charCode = charCodes[0]
@@ -549,7 +549,7 @@
 		for index in range(subheadRangeLen):
 			subHeader = subHeaderList[index]
 			subHeader.idRangeOffset = 0
-			for j  in range(index):
+			for j in range(index):
 				prevSubhead = subHeaderList[j]
 				if prevSubhead.glyphIndexArray == subHeader.glyphIndexArray: # use the glyphIndexArray subarray
 					subHeader.idRangeOffset = prevSubhead.idRangeOffset - (index-j)*8
@@ -684,7 +684,7 @@
 
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
-		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
+		# If not, someone is calling the subtable decompile() directly, and must provide both args.
 		if data is not None and ttFont is not None:
 			self.decompileHeader(data, ttFont)
 		else:
@@ -730,7 +730,7 @@
 			else:
 				for charCode in rangeCharCodes:
 					index = charCode + partial
-					assert (index < lenGIArray), "In format 4 cmap, range (%d), the calculated index (%d) into the glyph index array  is not less than the length of the array (%d) !" % (i, index, lenGIArray)
+					assert (index < lenGIArray), "In format 4 cmap, range (%d), the calculated index (%d) into the glyph index array is not less than the length of the array (%d) !" % (i, index, lenGIArray)
 					if glyphIndexArray[index] != 0:  # if not missing glyph
 						glyphID = glyphIndexArray[index] + delta
 					else:
@@ -807,7 +807,7 @@
 			indices = []
 			for charCode in range(startCode[i], endCode[i] + 1):
 				indices.append(cmap[charCode])
-			if  (indices == list(range(indices[0], indices[0] + len(indices)))):
+			if (indices == list(range(indices[0], indices[0] + len(indices)))):
 				idDelta.append((indices[0] - startCode[i]) % 0x10000)
 				idRangeOffset.append(0)
 			else:
@@ -855,7 +855,7 @@
 
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
-		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
+		# If not, someone is calling the subtable decompile() directly, and must provide both args.
 		if data is not None and ttFont is not None:
 			self.decompileHeader(data, ttFont)
 		else:
@@ -932,7 +932,7 @@
 
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
-		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
+		# If not, someone is calling the subtable decompile() directly, and must provide both args.
 		if data is not None and ttFont is not None:
 			self.decompileHeader(data, ttFont)
 		else:
@@ -991,7 +991,7 @@
 		lastGlyphID = startGlyphID - self._format_step
 		lastCharCode = startCharCode - 1
 		nGroups = 0
-		dataList =  []
+		dataList = []
 		maxIndex = len(charCodes)
 		for index in range(maxIndex):
 			charCode = charCodes[index]
@@ -1073,12 +1073,12 @@
 		return (glyphID == lastGlyphID) and (charCode == 1 + lastCharCode)
 
 
-def  cvtToUVS(threeByteString):
+def cvtToUVS(threeByteString):
 	data = b"\0" + threeByteString
 	val, = struct.unpack(">L", data)
 	return val
 
-def  cvtFromUVS(val):
+def cvtFromUVS(val):
 	assert 0 <= val < 0x1000000
 	fourByteString = struct.pack(">L", val)
 	return fourByteString[1:]
@@ -1105,7 +1105,7 @@
 		uvsDict = {}
 		recOffset = 0
 		for n in range(self.numVarSelectorRecords):
-			uvs, defOVSOffset, nonDefUVSOffset =  struct.unpack(">3sLL", data[recOffset:recOffset +11])
+			uvs, defOVSOffset, nonDefUVSOffset = struct.unpack(">3sLL", data[recOffset:recOffset +11])
 			recOffset += 11
 			varUVS = cvtToUVS(uvs)
 			if defOVSOffset:
@@ -1135,7 +1135,7 @@
 					startOffset += 5
 					uv = cvtToUVS(uv)
 					glyphName = self.ttFont.getGlyphName(gid)
-					localUVList.append( [uv, glyphName] )
+					localUVList.append((uv, glyphName))
 				try:
 					uvsDict[varUVS].extend(localUVList)
 				except KeyError:
@@ -1147,9 +1147,6 @@
 		writer.begintag(self.__class__.__name__, [
 				("platformID", self.platformID),
 				("platEncID", self.platEncID),
-				("format", self.format),
-				("length", self.length),
-				("numVarSelectorRecords", self.numVarSelectorRecords),
 				])
 		writer.newline()
 		uvsDict = self.uvsDict
@@ -1158,25 +1155,27 @@
 			uvList = uvsDict[uvs]
 			uvList.sort(key=lambda item: (item[1] is not None, item[0], item[1]))
 			for uv, gname in uvList:
-				if gname is None:
-					gname = "None"
-				# I use the arg rather than th keyword syntax in order to preserve the attribute order.
-				writer.simpletag("map", [ ("uvs",hex(uvs)), ("uv",hex(uv)), ("name", gname)]  )
+				attrs = [("uv", hex(uv)), ("uvs", hex(uvs))]
+				if gname is not None:
+					attrs.append(("name", gname))
+				writer.simpletag("map", attrs)
 				writer.newline()
 		writer.endtag(self.__class__.__name__)
 		writer.newline()
 
 	def fromXML(self, name, attrs, content, ttFont):
-		self.format = safeEval(attrs["format"])
-		self.length = safeEval(attrs["length"])
-		self.numVarSelectorRecords = safeEval(attrs["numVarSelectorRecords"])
-		self.language = 0xFF # provide a value so that  CmapSubtable.__lt__() won't fail
+		self.language = 0xFF # provide a value so that CmapSubtable.__lt__() won't fail
 		if not hasattr(self, "cmap"):
 			self.cmap = {} # so that clients that expect this to exist in a cmap table won't fail.
 		if not hasattr(self, "uvsDict"):
 			self.uvsDict = {}
 			uvsDict = self.uvsDict
 
+		# For backwards compatibility reasons we accept "None" as an indicator
+		# for "default mapping", unless the font actually has a glyph named
+		# "None".
+		_hasGlyphNamedNone = None
+
 		for element in content:
 			if not isinstance(element, tuple):
 				continue
@@ -1185,13 +1184,16 @@
 				continue
 			uvs = safeEval(attrs["uvs"])
 			uv = safeEval(attrs["uv"])
-			gname = attrs["name"]
+			gname = attrs.get("name")
 			if gname == "None":
-				gname = None
+				if _hasGlyphNamedNone is None:
+					_hasGlyphNamedNone = "None" in ttFont.getGlyphOrder()
+				if not _hasGlyphNamedNone:
+					gname = None
 			try:
-				uvsDict[uvs].append( [uv, gname])
+				uvsDict[uvs].append((uv, gname))
 			except KeyError:
-				uvsDict[uvs] = [ [uv, gname] ]
+				uvsDict[uvs] = [(uv, gname)]
 
 	def compile(self, ttFont):
 		if self.data:
@@ -1281,7 +1283,7 @@
 
 	def decompile(self, data, ttFont):
 		# we usually get here indirectly from the subtable __getattr__ function, in which case both args must be None.
-		# If not, someone is calling  the subtable decompile() directly, and must provide both args.
+		# If not, someone is calling the subtable decompile() directly, and must provide both args.
 		if data is not None and ttFont is not None:
 			self.decompileHeader(data, ttFont)
 		else:
diff --git a/Lib/fontTools/ttLib/tables/_h_m_t_x.py b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
index 6f8bb97..24cad4f 100644
--- a/Lib/fontTools/ttLib/tables/_h_m_t_x.py
+++ b/Lib/fontTools/ttLib/tables/_h_m_t_x.py
@@ -23,7 +23,11 @@
 
 	def decompile(self, data, ttFont):
 		numGlyphs = ttFont['maxp'].numGlyphs
-		numberOfMetrics = int(getattr(ttFont[self.headerTag], self.numberOfMetricsName))
+		headerTable = ttFont.get(self.headerTag)
+		if headerTable is not None:
+			numberOfMetrics = int(getattr(headerTable, self.numberOfMetricsName))
+		else:
+			numberOfMetrics = numGlyphs
 		if numberOfMetrics > numGlyphs:
 			log.warning("The %s.%s exceeds the maxp.numGlyphs" % (
 				self.headerTag, self.numberOfMetricsName))
@@ -69,19 +73,26 @@
 					glyphName, self.advanceName))
 				hasNegativeAdvances = True
 			metrics.append([advanceWidth, sideBearing])
-		lastAdvance = metrics[-1][0]
-		lastIndex = len(metrics)
-		while metrics[lastIndex-2][0] == lastAdvance:
-			lastIndex -= 1
-			if lastIndex <= 1:
-				# all advances are equal
-				lastIndex = 1
-				break
-		additionalMetrics = metrics[lastIndex:]
-		additionalMetrics = [otRound(sb) for _, sb in additionalMetrics]
-		metrics = metrics[:lastIndex]
-		numberOfMetrics = len(metrics)
-		setattr(ttFont[self.headerTag], self.numberOfMetricsName, numberOfMetrics)
+
+		headerTable = ttFont.get(self.headerTag)
+		if headerTable is not None:
+			lastAdvance = metrics[-1][0]
+			lastIndex = len(metrics)
+			while metrics[lastIndex-2][0] == lastAdvance:
+				lastIndex -= 1
+				if lastIndex <= 1:
+					# all advances are equal
+					lastIndex = 1
+					break
+			additionalMetrics = metrics[lastIndex:]
+			additionalMetrics = [otRound(sb) for _, sb in additionalMetrics]
+			metrics = metrics[:lastIndex]
+			numberOfMetrics = len(metrics)
+			setattr(headerTable, self.numberOfMetricsName, numberOfMetrics)
+		else:
+			# no hhea/vhea, can't store numberOfMetrics; assume == numGlyphs
+			numberOfMetrics = ttFont["maxp"].numGlyphs
+			additionalMetrics = []
 
 		allMetrics = []
 		for advance, sb in metrics:
diff --git a/Lib/fontTools/ttLib/tables/_m_a_x_p.py b/Lib/fontTools/ttLib/tables/_m_a_x_p.py
index a94a9cf..7da30b4 100644
--- a/Lib/fontTools/ttLib/tables/_m_a_x_p.py
+++ b/Lib/fontTools/ttLib/tables/_m_a_x_p.py
@@ -107,6 +107,7 @@
 		self.maxContours = maxContours
 		self.maxCompositePoints = maxCompositePoints
 		self.maxCompositeContours = maxCompositeContours
+		self.maxComponentElements = maxComponentElements
 		self.maxComponentDepth = maxComponentDepth
 		if allXMinIsLsb:
 			headTable.flags = headTable.flags | 0x2
diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py
old mode 100644
new mode 100755
diff --git a/Lib/fontTools/ttx.py b/Lib/fontTools/ttx.py
index a785325..8f598f8 100644
--- a/Lib/fontTools/ttx.py
+++ b/Lib/fontTools/ttx.py
@@ -77,6 +77,7 @@
        file as-is.
     --recalc-timestamp Set font 'modified' timestamp to current time.
        By default, the modification time of the TTX file will be used.
+    --no-recalc-timestamp Keep the original font 'modified' timestamp.
     --flavor <type> Specify flavor of output font file. May be 'woff'
       or 'woff2'. Note that WOFF2 requires the Brotli Python extension,
       available at https://github.com/google/brotli
@@ -123,7 +124,7 @@
 	bitmapGlyphDataFormat = 'raw'
 	unicodedata = None
 	newlinestr = None
-	recalcTimestamp = False
+	recalcTimestamp = None
 	flavor = None
 	useZopfli = False
 
@@ -204,6 +205,8 @@
 						% (value, ", ".join(map(repr, validOptions))))
 			elif option == "--recalc-timestamp":
 				self.recalcTimestamp = True
+			elif option == "--no-recalc-timestamp":
+				self.recalcTimestamp = False
 			elif option == "--flavor":
 				self.flavor = value
 			elif option == "--with-zopfli":
@@ -282,7 +285,7 @@
 			allowVID=options.allowVID)
 	ttf.importXML(input)
 
-	if not options.recalcTimestamp and 'head' in ttf:
+	if options.recalcTimestamp is None and 'head' in ttf:
 		# use TTX file modification time for head "modified" timestamp
 		mtime = os.path.getmtime(input)
 		ttf['head'].modified = timestampSinceEpoch(mtime)
@@ -328,8 +331,8 @@
 
 def parseOptions(args):
 	rawOptions, files = getopt.getopt(args, "ld:o:fvqht:x:sgim:z:baey:",
-			['unicodedata=', "recalc-timestamp", 'flavor=', 'version',
-			 'with-zopfli', 'newline='])
+			['unicodedata=', "recalc-timestamp", "no-recalc-timestamp",
+			 'flavor=', 'version', 'with-zopfli', 'newline='])
 
 	options = Options(rawOptions, len(files))
 	jobs = []
diff --git a/Lib/fontTools/ufoLib/__init__.py b/Lib/fontTools/ufoLib/__init__.py
old mode 100644
new mode 100755
index d9a57c5..26b7ddd
--- a/Lib/fontTools/ufoLib/__init__.py
+++ b/Lib/fontTools/ufoLib/__init__.py
@@ -173,9 +173,9 @@
 					"the data is not properly formatted: %s"
 					% (fileName, self.fs, e)
 				)
-			if self.fs.exists(fileName) and data == self.fs.getbytes(fileName):
+			if self.fs.exists(fileName) and data == self.fs.readbytes(fileName):
 				return
-			self.fs.setbytes(fileName, data)
+			self.fs.writebytes(fileName, data)
 		else:
 			with self.fs.openbin(fileName, mode="w") as fp:
 				try:
@@ -356,7 +356,7 @@
 		Returns None if the file does not exist.
 		"""
 		try:
-			return self.fs.getbytes(fsdecode(path))
+			return self.fs.readbytes(fsdecode(path))
 		except fs.errors.ResourceNotFound:
 			return None
 
@@ -758,7 +758,7 @@
 			except AttributeError:
 				# in case readData is called before getDataDirectoryListing
 				dataFS = self.fs.opendir(DATA_DIRNAME)
-			data = dataFS.getbytes(fileName)
+			data = dataFS.readbytes(fileName)
 		except fs.errors.ResourceNotFound:
 			raise UFOLibError("No data file named '%s' on %s" % (fileName, self.fs))
 		return data
@@ -781,7 +781,7 @@
 			except AttributeError:
 				# in case readImage is called before getImageDirectoryListing
 				imagesFS = self.fs.opendir(IMAGES_DIRNAME)
-			data = imagesFS.getbytes(fileName)
+			data = imagesFS.readbytes(fileName)
 		except fs.errors.ResourceNotFound:
 			raise UFOLibError("No image file named '%s' on %s" % (fileName, self.fs))
 		if validate:
@@ -1006,15 +1006,15 @@
 		"""
 		path = fsdecode(path)
 		if self._havePreviousFile:
-			if self.fs.isfile(path) and data == self.fs.getbytes(path):
+			if self.fs.isfile(path) and data == self.fs.readbytes(path):
 				return
 		try:
-			self.fs.setbytes(path, data)
+			self.fs.writebytes(path, data)
 		except fs.errors.FileExpected:
 			raise UFOLibError("A directory exists at '%s'" % path)
 		except fs.errors.ResourceNotFound:
 			self.fs.makedirs(fs.path.dirname(path), recreate=True)
-			self.fs.setbytes(path, data)
+			self.fs.writebytes(path, data)
 
 	def getFileObjectForPath(self, path, mode="w", encoding=None):
 		"""
diff --git a/Lib/fontTools/ufoLib/glifLib.py b/Lib/fontTools/ufoLib/glifLib.py
old mode 100644
new mode 100755
index f2648b8..e36c3c7
--- a/Lib/fontTools/ufoLib/glifLib.py
+++ b/Lib/fontTools/ufoLib/glifLib.py
@@ -290,7 +290,7 @@
 		"""
 		fileName = self.contents[glyphName]
 		try:
-			return self.fs.getbytes(fileName)
+			return self.fs.readbytes(fileName)
 		except fs.errors.ResourceNotFound:
 			raise GlifLibError(
 				"The file '%s' associated with glyph '%s' in contents.plist "
@@ -316,7 +316,7 @@
 		'glyphObject' argument can be any kind of object (even None);
 		the readGlyph() method will attempt to set the following
 		attributes on it:
-			"width"      the advance with of the glyph
+			"width"      the advance width of the glyph
 			"height"     the advance height of the glyph
 			"unicodes"   a list of unicode values for this glyph
 			"note"       a string
@@ -420,10 +420,10 @@
 		if (
 			self._havePreviousFile
 			and self.fs.exists(fileName)
-			and data == self.fs.getbytes(fileName)
+			and data == self.fs.readbytes(fileName)
 		):
 			return
-		self.fs.setbytes(fileName, data)
+		self.fs.writebytes(fileName, data)
 
 	def deleteGlyph(self, glyphName):
 		"""Permanently delete the glyph from the glyph set on disk. Will
diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py
index 37d8d33..2dd5718 100644
--- a/Lib/fontTools/varLib/__init__.py
+++ b/Lib/fontTools/varLib/__init__.py
@@ -76,21 +76,24 @@
 		axis.axisTag = Tag(a.tag)
 		# TODO Skip axes that have no variation.
 		axis.minValue, axis.defaultValue, axis.maxValue = a.minimum, a.default, a.maximum
-		axis.axisNameID = nameTable.addName(tounicode(a.labelNames['en']))
-		# TODO:
-		# Replace previous line with the following when the following issues are resolved:
-		# https://github.com/fonttools/fonttools/issues/930
-		# https://github.com/fonttools/fonttools/issues/931
-		# axis.axisNameID = nameTable.addMultilingualName(a.labelname, font)
+		axis.axisNameID = nameTable.addMultilingualName(a.labelNames, font)
+		axis.flags = int(a.hidden)
 		fvar.axes.append(axis)
 
 	for instance in instances:
 		coordinates = instance.location
-		name = tounicode(instance.styleName)
+
+		if "en" not in instance.localisedStyleName:
+			assert instance.styleName
+			localisedStyleName = dict(instance.localisedStyleName)
+			localisedStyleName["en"] = tounicode(instance.styleName)
+		else:
+			localisedStyleName = instance.localisedStyleName
+
 		psname = instance.postScriptFontName
 
 		inst = NamedInstance()
-		inst.subfamilyNameID = nameTable.addName(name)
+		inst.subfamilyNameID = nameTable.addMultilingualName(localisedStyleName)
 		if psname is not None:
 			psname = tounicode(psname)
 			inst.postscriptNameID = nameTable.addName(psname)
@@ -510,13 +513,28 @@
 	lastTableTag = None
 	fontTable = None
 	tables = None
+	# HACK: we need to special-case post.underlineThickness and .underlinePosition
+	# and unilaterally/arbitrarily define a sentinel value to distinguish the case
+	# when a post table is present in a given master simply because that's where
+	# the glyph names in TrueType must be stored, but the underline values are not
+	# meant to be used for building MVAR's deltas. The value of -0x8000 (-36768)
+	# the minimum FWord (int16) value, was chosen for its unlikelyhood to appear
+	# in real-world underline position/thickness values.
+	specialTags = {"unds": -0x8000, "undo": -0x8000}
 	for tag, (tableTag, itemName) in sorted(MVAR_ENTRIES.items(), key=lambda kv: kv[1]):
 		if tableTag != lastTableTag:
 			tables = fontTable = None
 			if tableTag in font:
 				fontTable = font[tableTag]
-				tables = [master[tableTag] if tableTag in master else None
-					  for master in master_ttfs]
+				tables = []
+				for master in master_ttfs:
+					if tableTag not in master or (
+						tag in specialTags
+						and getattr(master[tableTag], itemName) == specialTags[tag]
+					):
+						tables.append(None)
+					else:
+						tables.append(master[tableTag])
 			lastTableTag = tableTag
 		if tables is None:
 			continue
@@ -662,10 +680,10 @@
 	instances = ds.instances
 
 	standard_axis_map = OrderedDict([
-		('weight',  ('wght', {'en':'Weight'})),
-		('width',   ('wdth', {'en':'Width'})),
-		('slant',   ('slnt', {'en':'Slant'})),
-		('optical', ('opsz', {'en':'Optical Size'})),
+		('weight',  ('wght', {'en': u'Weight'})),
+		('width',   ('wdth', {'en': u'Width'})),
+		('slant',   ('slnt', {'en': u'Slant'})),
+		('optical', ('opsz', {'en': u'Optical Size'})),
 		])
 
 	# Setup axes
@@ -684,7 +702,7 @@
 		else:
 			assert axis.tag is not None
 			if not axis.labelNames:
-				axis.labelNames["en"] = axis_name
+				axis.labelNames["en"] = tounicode(axis_name)
 
 		axes[axis_name] = axis
 	log.info("Axes:\n%s", pformat([axis.asdict() for axis in axes.values()]))
@@ -809,14 +827,36 @@
 	return vf, model, master_ttfs
 
 
+def _open_font(path, master_finder):
+	# load TTFont masters from given 'path': this can be either a .TTX or an
+	# OpenType binary font; or if neither of these, try use the 'master_finder'
+	# callable to resolve the path to a valid .TTX or OpenType font binary.
+	from fontTools.ttx import guessFileType
+
+	master_path = os.path.normpath(path)
+	tp = guessFileType(master_path)
+	if tp is None:
+		# not an OpenType binary/ttx, fall back to the master finder.
+		master_path = master_finder(master_path)
+		tp = guessFileType(master_path)
+	if tp in ("TTX", "OTX"):
+		font = TTFont()
+		font.importXML(master_path)
+	elif tp in ("TTF", "OTF", "WOFF", "WOFF2"):
+		font = TTFont(master_path)
+	else:
+		raise VarLibError("Invalid master path: %r" % master_path)
+	return font
+
+
 def load_masters(designspace, master_finder=lambda s: s):
 	"""Ensure that all SourceDescriptor.font attributes have an appropriate TTFont
 	object loaded, or else open TTFont objects from the SourceDescriptor.path
 	attributes.
 
-	The paths can point to either an OpenType font 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).
+	The paths can point to either an OpenType font, a TTX file, or a UFO. In the
+	latter case, use the provided master_finder callable to map from UFO paths to
+	the respective master font binaries (e.g. .ttf, .otf or .ttx).
 
 	Return list of master TTFont objects in the same order they are listed in the
 	DesignSpaceDocument.
@@ -843,15 +883,10 @@
 						"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)
+				# 2. A SourceDescriptor's path might point an OpenType binary, a
+				# TTX file, or another source file (e.g. UFO), in which case we
+				# resolve the path using 'master_finder' function
+				font = _open_font(master.path, master_finder)
 				master_fonts.append(font)
 
 	return master_fonts
diff --git a/Lib/fontTools/varLib/mutator.py b/Lib/fontTools/varLib/mutator.py
index 1a3b738..79a6f3d 100644
--- a/Lib/fontTools/varLib/mutator.py
+++ b/Lib/fontTools/varLib/mutator.py
@@ -345,6 +345,16 @@
 		for i in fvar.instances:
 			exclude.add(i.subfamilyNameID)
 			exclude.add(i.postscriptNameID)
+		if 'ltag' in varfont:
+			# Drop the whole 'ltag' table if all its language tags are referenced by
+			# name records to be pruned.
+			# TODO: prune unused ltag tags and re-enumerate langIDs accordingly
+			excludedUnicodeLangIDs = [
+				n.langID for n in varfont['name'].names
+				if n.nameID in exclude and n.platformID == 0 and n.langID != 0xFFFF
+			]
+			if set(excludedUnicodeLangIDs) == set(range(len((varfont['ltag'].tags)))):
+				del varfont['ltag']
 		varfont['name'].names[:] = [
 			n for n in varfont['name'].names
 			if n.nameID not in exclude
diff --git a/Lib/fontTools/voltLib/ast.py b/Lib/fontTools/voltLib/ast.py
index 4e78600..de626ba 100644
--- a/Lib/fontTools/voltLib/ast.py
+++ b/Lib/fontTools/voltLib/ast.py
@@ -83,7 +83,7 @@
         self.glyph = glyph
 
     def glyphSet(self):
-        return frozenset((self.glyph,))
+        return (self.glyph,)
 
 
 class Enum(Expression):
@@ -96,14 +96,23 @@
         for e in self.glyphSet():
             yield e
 
+    def __len__(self):
+        return len(self.enum)
+
+    def __eq__(self, other):
+        return self.glyphSet() == other.glyphSet()
+
+    def __hash__(self):
+        return hash(self.glyphSet())
+
     def glyphSet(self, groups=None):
-        glyphs = set()
+        glyphs = []
         for element in self.enum:
             if isinstance(element, (GroupName, Enum)):
-                glyphs = glyphs.union(element.glyphSet(groups))
+                glyphs.extend(element.glyphSet(groups))
             else:
-                glyphs = glyphs.union(element.glyphSet())
-        return frozenset(glyphs)
+                glyphs.extend(element.glyphSet())
+        return tuple(glyphs)
 
 
 class GroupName(Expression):
@@ -133,8 +142,7 @@
         self.parser = parser
 
     def glyphSet(self):
-        glyphs = self.parser.glyph_range(self.start, self.end)
-        return frozenset(glyphs)
+        return tuple(self.parser.glyph_range(self.start, self.end))
 
 
 class ScriptDefinition(Statement):
@@ -162,12 +170,14 @@
 
 
 class LookupDefinition(Statement):
-    def __init__(self, name, process_base, process_marks, direction,
-                 reversal, comments, context, sub, pos, location=None):
+    def __init__(self, name, process_base, process_marks, mark_glyph_set,
+                 direction, reversal, comments, context, sub, pos,
+                 location=None):
         Statement.__init__(self, location)
         self.name = name
         self.process_base = process_base
         self.process_marks = process_marks
+        self.mark_glyph_set = mark_glyph_set
         self.direction = direction
         self.reversal = reversal
         self.comments = comments
diff --git a/Lib/fontTools/voltLib/parser.py b/Lib/fontTools/voltLib/parser.py
index db3ccf3..a452b9a 100644
--- a/Lib/fontTools/voltLib/parser.py
+++ b/Lib/fontTools/voltLib/parser.py
@@ -44,10 +44,7 @@
                 func = getattr(self, PARSE_FUNCS[self.cur_token_])
                 statements.append(func())
             elif self.is_cur_keyword_("END"):
-                if self.next_token_type_ is not None:
-                    raise VoltLibError("Expected the end of the file",
-                                       self.cur_token_location_)
-                return self.doc_
+                break
             else:
                 raise VoltLibError(
                     "Expected " + ", ".join(sorted(PARSE_FUNCS.keys())),
@@ -76,7 +73,7 @@
         if self.next_token_ == "TYPE":
             self.expect_keyword_("TYPE")
             gtype = self.expect_name_()
-            assert gtype in ("BASE", "LIGATURE", "MARK")
+            assert gtype in ("BASE", "LIGATURE", "MARK", "COMPONENT")
         components = None
         if self.next_token_ == "COMPONENTS":
             self.expect_keyword_("COMPONENTS")
@@ -206,11 +203,12 @@
             self.advance_lexer_()
             process_base = False
         process_marks = True
+        mark_glyph_set = None
         if self.next_token_ == "PROCESS_MARKS":
             self.advance_lexer_()
             if self.next_token_ == "MARK_GLYPH_SET":
                 self.advance_lexer_()
-                process_marks = self.expect_string_()
+                mark_glyph_set = self.expect_string_()
             elif self.next_token_type_ == Lexer.STRING:
                 process_marks = self.expect_string_()
             elif self.next_token_ == "ALL":
@@ -252,8 +250,8 @@
                 "Got %s" % (as_pos_or_sub),
                 location)
         def_lookup = ast.LookupDefinition(
-            name, process_base, process_marks, direction, reversal,
-            comments, context, sub, pos, location=location)
+            name, process_base, process_marks, mark_glyph_set, direction,
+            reversal, comments, context, sub, pos, location=location)
         self.lookups_.define(name, def_lookup)
         return def_lookup
 
@@ -422,16 +420,17 @@
         gid = self.expect_number_()
         self.expect_keyword_("GLYPH")
         glyph_name = self.expect_name_()
-        # check for duplicate anchor names on this glyph
-        if (glyph_name in self.anchors_
-                and self.anchors_[glyph_name].resolve(name) is not None):
-            raise VoltLibError(
-                'Anchor "%s" already defined, '
-                'anchor names are case insensitive' % name,
-                location
-            )
         self.expect_keyword_("COMPONENT")
         component = self.expect_number_()
+        # check for duplicate anchor names on this glyph
+        if glyph_name in self.anchors_:
+            anchor = self.anchors_[glyph_name].resolve(name)
+            if anchor is not None and anchor.component == component:
+                raise VoltLibError(
+                    'Anchor "%s" already defined, '
+                    'anchor names are case insensitive' % name,
+                    location
+                )
         if self.next_token_ == "LOCKED":
             locked = True
             self.advance_lexer_()
@@ -516,36 +515,24 @@
             elif self.next_token_ == "GLYPH":
                 self.expect_keyword_("GLYPH")
                 name = self.expect_string_()
-                coverage.append(name)
+                coverage.append(ast.GlyphName(name, location=location))
             elif self.next_token_ == "GROUP":
                 self.expect_keyword_("GROUP")
                 name = self.expect_string_()
-                # resolved_group = self.groups_.resolve(name)
-                group = (name,)
-                coverage.append(group)
-                # if resolved_group is not None:
-                #     coverage.extend(resolved_group.enum)
-                # # TODO: check that group exists after all groups are defined
-                # else:
-                #     group = (name,)
-                #     coverage.append(group)
-                #     # raise VoltLibError(
-                #     #     'Glyph group "%s" is not defined' % name,
-                #     #     location)
+                coverage.append(ast.GroupName(name, self, location=location))
             elif self.next_token_ == "RANGE":
                 self.expect_keyword_("RANGE")
                 start = self.expect_string_()
                 self.expect_keyword_("TO")
                 end = self.expect_string_()
-                coverage.append((start, end))
-        return tuple(coverage)
+                coverage.append(ast.Range(start, end, self, location=location))
+        return ast.Enum(coverage, location=location)
 
     def resolve_group(self, group_name):
         return self.groups_.resolve(group_name)
 
     def glyph_range(self, start, end):
-        rng = self.glyphs_.range(start, end)
-        return frozenset(rng)
+        return self.glyphs_.range(start, end)
 
     def parse_ppem_(self):
         location = self.cur_token_location_
@@ -601,6 +588,8 @@
         self.cur_token_type_, self.cur_token_, self.cur_token_location_ = (
             self.next_token_type_, self.next_token_, self.next_token_location_)
         try:
+            if self.is_cur_keyword_("END"):
+                raise StopIteration
             (self.next_token_type_, self.next_token_,
              self.next_token_location_) = self.lexer_.next()
         except StopIteration:
diff --git a/Lib/fonttools.egg-info/PKG-INFO b/Lib/fonttools.egg-info/PKG-INFO
index 32f9568..28c4c03 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.35.0
+Version: 3.37.0
 Summary: Tools to manipulate font files
 Home-page: http://github.com/fonttools/fonttools
 Author: Just van Rossum
@@ -430,6 +430,54 @@
         Changelog
         ~~~~~~~~~
         
+        3.37.0 (released 2019-01-28)
+        ----------------------------
+        
+        - [svgLib] Added support for converting elliptical arcs to cubic bezier curves
+          (#1464).
+        - [py23] Added backport for ``math.isfinite``.
+        - [varLib] Apply HIDDEN flag to fvar axis if designspace axis has attribute
+          ``hidden=1``.
+        - Fixed "DeprecationWarning: invalid escape sequence" in Python 3.7.
+        - [voltLib] Fixed parsing glyph groups. Distinguish different PROCESS_MARKS.
+          Accept COMPONENT glyph type.
+        - [feaLib] Distinguish missing value and explicit ``<NULL>`` for PairPos2
+          format A (#1459). Round-trip ``useExtension`` keyword. Implemented
+          ``ValueRecord.asFea`` method.
+        - [subset] Insert empty widths into hdmx when retaining gids (#1458).
+        
+        3.36.0 (released 2019-01-17)
+        ----------------------------
+        
+        - [ttx] Added ``--no-recalc-timestamp`` option to keep the original font's
+          ``head.modified`` timestamp (#1455, #46).
+        - [ttx/psCharStrings] Fixed issues while dumping and round-tripping CFF2 table
+          with ttx (#1451, #1452, #1456).
+        - [voltLib] Fixed check for duplicate anchors (#1450). Don't try to read past
+          the ``END`` operator in .vtp file (#1453).
+        - [varLib] Use sentinel value -0x8000 (-32768) to ignore post.underlineThickness
+          and post.underlinePosition when generating MVAR deltas (#1449,
+          googlei18n/ufo2ft#308).
+        - [subset] Added ``--retain-gids`` option to subset font without modifying the
+          current glyph indices (#1443, #1447).
+        - [ufoLib] Replace deprecated calls to ``getbytes`` and ``setbytes`` with new
+          equivalent ``readbytes`` and ``writebytes`` calls. ``fs`` >= 2.2 no required.
+        - [varLib] Allow loading masters from TTX files as well (#1441).
+        
+        3.35.2 (released 2019-01-14)
+        ----------------------------
+        
+        - [hmtx/vmtx]: Allow to compile/decompile ``hmtx`` and ``vmtx`` tables even
+          without the corresponding (required) metrics header tables, ``hhea`` and
+          ``vhea`` (#1439).
+        - [varLib] Added support for localized axes' ``labelname`` and named instances'
+          ``stylename`` (#1438).
+        
+        3.35.1 (released 2019-01-09)
+        ----------------------------
+        
+        - [_m_a_x_p] Include ``maxComponentElements`` in ``maxp`` table's recalculation.
+        
         3.35.0 (released 2019-01-07)
         ----------------------------
         
@@ -1582,13 +1630,13 @@
 Classifier: Topic :: Text Processing :: Fonts
 Classifier: Topic :: Multimedia :: Graphics
 Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
-Provides-Extra: all
-Provides-Extra: interpolatable
-Provides-Extra: woff
-Provides-Extra: lxml
 Provides-Extra: unicode
-Provides-Extra: graphite
-Provides-Extra: ufo
-Provides-Extra: type1
+Provides-Extra: interpolatable
 Provides-Extra: plot
+Provides-Extra: type1
 Provides-Extra: symfont
+Provides-Extra: ufo
+Provides-Extra: all
+Provides-Extra: graphite
+Provides-Extra: lxml
+Provides-Extra: woff
diff --git a/Lib/fonttools.egg-info/SOURCES.txt b/Lib/fonttools.egg-info/SOURCES.txt
index 04db095..c71e28f 100644
--- a/Lib/fonttools.egg-info/SOURCES.txt
+++ b/Lib/fonttools.egg-info/SOURCES.txt
@@ -165,6 +165,7 @@
 Lib/fontTools/subset/cff.py
 Lib/fontTools/svgLib/__init__.py
 Lib/fontTools/svgLib/path/__init__.py
+Lib/fontTools/svgLib/path/arc.py
 Lib/fontTools/svgLib/path/parser.py
 Lib/fontTools/t1Lib/__init__.py
 Lib/fontTools/ttLib/__init__.py
@@ -397,6 +398,8 @@
 Tests/feaLib/data/baseClass.feax
 Tests/feaLib/data/bug1307.fea
 Tests/feaLib/data/bug1307.ttx
+Tests/feaLib/data/bug1459.fea
+Tests/feaLib/data/bug1459.ttx
 Tests/feaLib/data/bug453.fea
 Tests/feaLib/data/bug453.ttx
 Tests/feaLib/data/bug457.fea
@@ -528,6 +531,7 @@
 Tests/fontBuilder/fontBuilder_test.py
 Tests/fontBuilder/data/test.otf.ttx
 Tests/fontBuilder/data/test.ttf.ttx
+Tests/fontBuilder/data/test_uvs.ttf.ttx
 Tests/fontBuilder/data/test_var.otf.ttx
 Tests/fontBuilder/data/test_var.ttf.ttx
 Tests/misc/arrayTools_test.py
@@ -724,6 +728,8 @@
 Tests/ttLib/tables/data/C_F_F_.ttx
 Tests/ttLib/tables/data/C_F_F__2.bin
 Tests/ttLib/tables/data/C_F_F__2.ttx
+Tests/ttLib/tables/data/_c_m_a_p_format_14.ttx
+Tests/ttLib/tables/data/_c_m_a_p_format_14_bw_compat.ttx
 Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.glyf.bin
 Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.head.bin
 Tests/ttLib/tables/data/_g_l_y_f_outline_flag_bit6.loca.bin
@@ -1409,6 +1415,7 @@
 Tests/varLib/data/InterpolateLayout.designspace
 Tests/varLib/data/InterpolateLayout2.designspace
 Tests/varLib/data/InterpolateLayout3.designspace
+Tests/varLib/data/SparseMasters.designspace
 Tests/varLib/data/TestCFF2.designspace
 Tests/varLib/data/TestCFF2VF.otf
 Tests/varLib/data/master_cff2/TestCFF2_Black.otf
@@ -1417,6 +1424,9 @@
 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/SparseMasters-Bold.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Medium.ttx
+Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Regular.ttx
 Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master0.ttx
 Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master1.ttx
 Tests/varLib/data/master_ttx_interpolatable_ttf/TestFamily-Master2.ttx
@@ -1731,5 +1741,6 @@
 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/varLib/data/test_results/SparseMasters.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 9f92764..c94f139 100644
--- a/Lib/fonttools.egg-info/requires.txt
+++ b/Lib/fonttools.egg-info/requires.txt
@@ -1,6 +1,6 @@
 
 [all]
-fs<3,>=2.1.1
+fs<3,>=2.2.0
 lxml<5,>=4.0
 zopfli>=0.1.4
 lz4>=1.7.4.2
@@ -56,7 +56,7 @@
 xattr
 
 [ufo]
-fs<3,>=2.1.1
+fs<3,>=2.2.0
 
 [ufo:python_version < "3.4"]
 enum34>=1.1.6
diff --git a/METADATA b/METADATA
index ba000aa..4da9b16 100644
--- a/METADATA
+++ b/METADATA
@@ -7,12 +7,12 @@
   }
   url {
     type: ARCHIVE
-    value: "https://github.com/fonttools/fonttools/releases/download/3.35.0/fonttools-3.35.0.zip"
+    value: "https://github.com/fonttools/fonttools/releases/download/3.37.0/fonttools-3.37.0.zip"
   }
-  version: "3.35.0"
+  version: "3.37.0"
   last_upgrade_date {
     year: 2019
-    month: 1
-    day: 8
+    month: 2
+    day: 1
   }
 }
diff --git a/MetaTools/buildTableList.py b/MetaTools/buildTableList.py
old mode 100644
new mode 100755
diff --git a/MetaTools/buildUCD.py b/MetaTools/buildUCD.py
old mode 100644
new mode 100755
diff --git a/MetaTools/roundTrip.py b/MetaTools/roundTrip.py
old mode 100644
new mode 100755
diff --git a/NEWS.rst b/NEWS.rst
index 59bd94f..8ebf4be 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,3 +1,51 @@
+3.37.0 (released 2019-01-28)
+----------------------------
+
+- [svgLib] Added support for converting elliptical arcs to cubic bezier curves
+  (#1464).
+- [py23] Added backport for ``math.isfinite``.
+- [varLib] Apply HIDDEN flag to fvar axis if designspace axis has attribute
+  ``hidden=1``.
+- Fixed "DeprecationWarning: invalid escape sequence" in Python 3.7.
+- [voltLib] Fixed parsing glyph groups. Distinguish different PROCESS_MARKS.
+  Accept COMPONENT glyph type.
+- [feaLib] Distinguish missing value and explicit ``<NULL>`` for PairPos2
+  format A (#1459). Round-trip ``useExtension`` keyword. Implemented
+  ``ValueRecord.asFea`` method.
+- [subset] Insert empty widths into hdmx when retaining gids (#1458).
+
+3.36.0 (released 2019-01-17)
+----------------------------
+
+- [ttx] Added ``--no-recalc-timestamp`` option to keep the original font's
+  ``head.modified`` timestamp (#1455, #46).
+- [ttx/psCharStrings] Fixed issues while dumping and round-tripping CFF2 table
+  with ttx (#1451, #1452, #1456).
+- [voltLib] Fixed check for duplicate anchors (#1450). Don't try to read past
+  the ``END`` operator in .vtp file (#1453).
+- [varLib] Use sentinel value -0x8000 (-32768) to ignore post.underlineThickness
+  and post.underlinePosition when generating MVAR deltas (#1449,
+  googlei18n/ufo2ft#308).
+- [subset] Added ``--retain-gids`` option to subset font without modifying the
+  current glyph indices (#1443, #1447).
+- [ufoLib] Replace deprecated calls to ``getbytes`` and ``setbytes`` with new
+  equivalent ``readbytes`` and ``writebytes`` calls. ``fs`` >= 2.2 no required.
+- [varLib] Allow loading masters from TTX files as well (#1441).
+
+3.35.2 (released 2019-01-14)
+----------------------------
+
+- [hmtx/vmtx]: Allow to compile/decompile ``hmtx`` and ``vmtx`` tables even
+  without the corresponding (required) metrics header tables, ``hhea`` and
+  ``vhea`` (#1439).
+- [varLib] Added support for localized axes' ``labelname`` and named instances'
+  ``stylename`` (#1438).
+
+3.35.1 (released 2019-01-09)
+----------------------------
+
+- [_m_a_x_p] Include ``maxComponentElements`` in ``maxp`` table's recalculation.
+
 3.35.0 (released 2019-01-07)
 ----------------------------
 
diff --git a/PKG-INFO b/PKG-INFO
index 32f9568..28c4c03 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: fonttools
-Version: 3.35.0
+Version: 3.37.0
 Summary: Tools to manipulate font files
 Home-page: http://github.com/fonttools/fonttools
 Author: Just van Rossum
@@ -430,6 +430,54 @@
         Changelog
         ~~~~~~~~~
         
+        3.37.0 (released 2019-01-28)
+        ----------------------------
+        
+        - [svgLib] Added support for converting elliptical arcs to cubic bezier curves
+          (#1464).
+        - [py23] Added backport for ``math.isfinite``.
+        - [varLib] Apply HIDDEN flag to fvar axis if designspace axis has attribute
+          ``hidden=1``.
+        - Fixed "DeprecationWarning: invalid escape sequence" in Python 3.7.
+        - [voltLib] Fixed parsing glyph groups. Distinguish different PROCESS_MARKS.
+          Accept COMPONENT glyph type.
+        - [feaLib] Distinguish missing value and explicit ``<NULL>`` for PairPos2
+          format A (#1459). Round-trip ``useExtension`` keyword. Implemented
+          ``ValueRecord.asFea`` method.
+        - [subset] Insert empty widths into hdmx when retaining gids (#1458).
+        
+        3.36.0 (released 2019-01-17)
+        ----------------------------
+        
+        - [ttx] Added ``--no-recalc-timestamp`` option to keep the original font's
+          ``head.modified`` timestamp (#1455, #46).
+        - [ttx/psCharStrings] Fixed issues while dumping and round-tripping CFF2 table
+          with ttx (#1451, #1452, #1456).
+        - [voltLib] Fixed check for duplicate anchors (#1450). Don't try to read past
+          the ``END`` operator in .vtp file (#1453).
+        - [varLib] Use sentinel value -0x8000 (-32768) to ignore post.underlineThickness
+          and post.underlinePosition when generating MVAR deltas (#1449,
+          googlei18n/ufo2ft#308).
+        - [subset] Added ``--retain-gids`` option to subset font without modifying the
+          current glyph indices (#1443, #1447).
+        - [ufoLib] Replace deprecated calls to ``getbytes`` and ``setbytes`` with new
+          equivalent ``readbytes`` and ``writebytes`` calls. ``fs`` >= 2.2 no required.
+        - [varLib] Allow loading masters from TTX files as well (#1441).
+        
+        3.35.2 (released 2019-01-14)
+        ----------------------------
+        
+        - [hmtx/vmtx]: Allow to compile/decompile ``hmtx`` and ``vmtx`` tables even
+          without the corresponding (required) metrics header tables, ``hhea`` and
+          ``vhea`` (#1439).
+        - [varLib] Added support for localized axes' ``labelname`` and named instances'
+          ``stylename`` (#1438).
+        
+        3.35.1 (released 2019-01-09)
+        ----------------------------
+        
+        - [_m_a_x_p] Include ``maxComponentElements`` in ``maxp`` table's recalculation.
+        
         3.35.0 (released 2019-01-07)
         ----------------------------
         
@@ -1582,13 +1630,13 @@
 Classifier: Topic :: Text Processing :: Fonts
 Classifier: Topic :: Multimedia :: Graphics
 Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
-Provides-Extra: all
-Provides-Extra: interpolatable
-Provides-Extra: woff
-Provides-Extra: lxml
 Provides-Extra: unicode
-Provides-Extra: graphite
-Provides-Extra: ufo
-Provides-Extra: type1
+Provides-Extra: interpolatable
 Provides-Extra: plot
+Provides-Extra: type1
 Provides-Extra: symfont
+Provides-Extra: ufo
+Provides-Extra: all
+Provides-Extra: graphite
+Provides-Extra: lxml
+Provides-Extra: woff
diff --git a/Snippets/cmap-format.py b/Snippets/cmap-format.py
old mode 100644
new mode 100755
diff --git a/Snippets/interpolate.py b/Snippets/interpolate.py
old mode 100644
new mode 100755
diff --git a/Snippets/layout-features.py b/Snippets/layout-features.py
old mode 100644
new mode 100755
diff --git a/Snippets/otf2ttf.py b/Snippets/otf2ttf.py
old mode 100644
new mode 100755
diff --git a/Snippets/rename-fonts.py b/Snippets/rename-fonts.py
old mode 100644
new mode 100755
diff --git a/Snippets/subset-fpgm.py b/Snippets/subset-fpgm.py
old mode 100644
new mode 100755
diff --git a/Snippets/svg2glif.py b/Snippets/svg2glif.py
old mode 100644
new mode 100755
diff --git a/Snippets/woff2_compress.py b/Snippets/woff2_compress.py
old mode 100644
new mode 100755
diff --git a/Snippets/woff2_decompress.py b/Snippets/woff2_decompress.py
old mode 100644
new mode 100755
diff --git a/Tests/feaLib/builder_test.py b/Tests/feaLib/builder_test.py
index 18ece3b..fc1141a 100644
--- a/Tests/feaLib/builder_test.py
+++ b/Tests/feaLib/builder_test.py
@@ -65,7 +65,7 @@
         spec9a spec9b spec9c1 spec9c2 spec9c3 spec9d spec9e spec9f spec9g
         spec10
         bug453 bug457 bug463 bug501 bug502 bug504 bug505 bug506 bug509
-        bug512 bug514 bug568 bug633 bug1307
+        bug512 bug514 bug568 bug633 bug1307 bug1459
         name size size2 multiple_feature_blocks omitted_GlyphClassDef
         ZeroValue_SinglePos_horizontal ZeroValue_SinglePos_vertical
         ZeroValue_PairPos_horizontal ZeroValue_PairPos_vertical
diff --git a/Tests/feaLib/data/bug1459.fea b/Tests/feaLib/data/bug1459.fea
new file mode 100644
index 0000000..1ad688e
--- /dev/null
+++ b/Tests/feaLib/data/bug1459.fea
@@ -0,0 +1,7 @@
+# A pair position lookup where only the second glyph has a non-empty valuerecord
+# while the first glyph has a NULL valuerecord. The ValueFormat1 for the first
+# glyph is expected to be 0.
+# https://github.com/fonttools/fonttools/issues/1459
+feature kern {
+    pos A <NULL> V <-180 0 -90 0>;
+} kern;
diff --git a/Tests/feaLib/data/bug1459.ttx b/Tests/feaLib/data/bug1459.ttx
new file mode 100644
index 0000000..8a7c096
--- /dev/null
+++ b/Tests/feaLib/data/bug1459.ttx
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="kern"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <PairPos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat1 value="0"/>
+          <ValueFormat2 value="5"/>
+          <!-- PairSetCount=1 -->
+          <PairSet index="0">
+            <!-- PairValueCount=1 -->
+            <PairValueRecord index="0">
+              <SecondGlyph value="V"/>
+              <Value2 XPlacement="-180" XAdvance="-90"/>
+            </PairValueRecord>
+          </PairSet>
+        </PairPos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/parser_test.py b/Tests/feaLib/parser_test.py
index f9b1063..6636b41 100644
--- a/Tests/feaLib/parser_test.py
+++ b/Tests/feaLib/parser_test.py
@@ -215,6 +215,8 @@
         [liga] = self.parse("feature liga useExtension {} liga;").statements
         self.assertEqual(liga.name, "liga")
         self.assertTrue(liga.use_extension)
+        self.assertEqual(liga.asFea(),
+                         "feature liga useExtension {\n    \n} liga;\n")
 
     def test_feature_comment(self):
         [liga] = self.parse("feature liga { # Comment\n } liga;").statements
@@ -608,6 +610,8 @@
         [lookup] = self.parse("lookup Foo useExtension {} Foo;").statements
         self.assertEqual(lookup.name, "Foo")
         self.assertTrue(lookup.use_extension)
+        self.assertEqual(lookup.asFea(),
+                         "lookup Foo useExtension {\n    \n} Foo;\n")
 
     def test_lookup_block_name_mismatch(self):
         self.assertRaisesRegex(
@@ -722,7 +726,7 @@
         self.assertIsInstance(pos, ast.SinglePosStatement)
         [(glyphs, value)] = pos.pos
         self.assertEqual(glyphstr([glyphs]), "one")
-        self.assertEqual(value.makeString(vertical=False), "<1 2 3 4>")
+        self.assertEqual(value.asFea(), "<1 2 3 4>")
 
     def test_gpos_type_1_glyphclass_horizontal(self):
         doc = self.parse("feature kern {pos [one two] -300;} kern;")
@@ -730,7 +734,7 @@
         self.assertIsInstance(pos, ast.SinglePosStatement)
         [(glyphs, value)] = pos.pos
         self.assertEqual(glyphstr([glyphs]), "[one two]")
-        self.assertEqual(value.makeString(vertical=False), "-300")
+        self.assertEqual(value.asFea(), "-300")
 
     def test_gpos_type_1_glyphclass_vertical(self):
         doc = self.parse("feature vkrn {pos [one two] -300;} vkrn;")
@@ -738,7 +742,7 @@
         self.assertIsInstance(pos, ast.SinglePosStatement)
         [(glyphs, value)] = pos.pos
         self.assertEqual(glyphstr([glyphs]), "[one two]")
-        self.assertEqual(value.makeString(vertical=True), "-300")
+        self.assertEqual(value.asFea(), "-300")
 
     def test_gpos_type_1_multiple(self):
         doc = self.parse("feature f {pos one'1 two'2 [five six]'56;} f;")
@@ -746,11 +750,11 @@
         self.assertIsInstance(pos, ast.SinglePosStatement)
         [(glyphs1, val1), (glyphs2, val2), (glyphs3, val3)] = pos.pos
         self.assertEqual(glyphstr([glyphs1]), "one")
-        self.assertEqual(val1.makeString(vertical=False), "1")
+        self.assertEqual(val1.asFea(), "1")
         self.assertEqual(glyphstr([glyphs2]), "two")
-        self.assertEqual(val2.makeString(vertical=False), "2")
+        self.assertEqual(val2.asFea(), "2")
         self.assertEqual(glyphstr([glyphs3]), "[five six]")
-        self.assertEqual(val3.makeString(vertical=False), "56")
+        self.assertEqual(val3.asFea(), "56")
         self.assertEqual(pos.prefix, [])
         self.assertEqual(pos.suffix, [])
 
@@ -770,7 +774,7 @@
         self.assertIsInstance(pos, ast.SinglePosStatement)
         [(glyphs, value)] = pos.pos
         self.assertEqual(glyphstr([glyphs]), "[T Y]")
-        self.assertEqual(value.makeString(vertical=False), "20")
+        self.assertEqual(value.asFea(), "20")
         self.assertEqual(glyphstr(pos.prefix), "[A B]")
         self.assertEqual(glyphstr(pos.suffix), "comma")
 
@@ -782,10 +786,9 @@
         self.assertEqual(type(pos), ast.PairPosStatement)
         self.assertFalse(pos.enumerated)
         self.assertEqual(glyphstr([pos.glyphs1]), "[T V]")
-        self.assertEqual(pos.valuerecord1.makeString(vertical=False), "-60")
+        self.assertEqual(pos.valuerecord1.asFea(), "-60")
         self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
-        self.assertEqual(pos.valuerecord2.makeString(vertical=False),
-                         "<1 2 3 4>")
+        self.assertEqual(pos.valuerecord2.asFea(), "<1 2 3 4>")
 
     def test_gpos_type_2_format_a_enumerated(self):
         doc = self.parse("feature kern {"
@@ -795,12 +798,25 @@
         self.assertEqual(type(pos), ast.PairPosStatement)
         self.assertTrue(pos.enumerated)
         self.assertEqual(glyphstr([pos.glyphs1]), "[T V]")
-        self.assertEqual(pos.valuerecord1.makeString(vertical=False), "-60")
+        self.assertEqual(pos.valuerecord1.asFea(), "-60")
         self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
-        self.assertEqual(pos.valuerecord2.makeString(vertical=False),
-                         "<1 2 3 4>")
+        self.assertEqual(pos.valuerecord2.asFea(), "<1 2 3 4>")
 
-    def test_gpos_type_2_format_a_with_null(self):
+    def test_gpos_type_2_format_a_with_null_first(self):
+        doc = self.parse("feature kern {"
+                         "    pos [T V] <NULL> [a b c] <1 2 3 4>;"
+                         "} kern;")
+        pos = doc.statements[0].statements[0]
+        self.assertEqual(type(pos), ast.PairPosStatement)
+        self.assertFalse(pos.enumerated)
+        self.assertEqual(glyphstr([pos.glyphs1]), "[T V]")
+        self.assertFalse(pos.valuerecord1)
+        self.assertEqual(pos.valuerecord1.asFea(), "<NULL>")
+        self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
+        self.assertEqual(pos.valuerecord2.asFea(), "<1 2 3 4>")
+        self.assertEqual(pos.asFea(), "pos [T V] <NULL> [a b c] <1 2 3 4>;")
+
+    def test_gpos_type_2_format_a_with_null_second(self):
         doc = self.parse("feature kern {"
                          "    pos [T V] <1 2 3 4> [a b c] <NULL>;"
                          "} kern;")
@@ -808,10 +824,10 @@
         self.assertEqual(type(pos), ast.PairPosStatement)
         self.assertFalse(pos.enumerated)
         self.assertEqual(glyphstr([pos.glyphs1]), "[T V]")
-        self.assertEqual(pos.valuerecord1.makeString(vertical=False),
-                         "<1 2 3 4>")
+        self.assertEqual(pos.valuerecord1.asFea(), "<1 2 3 4>")
         self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
-        self.assertIsNone(pos.valuerecord2)
+        self.assertFalse(pos.valuerecord2)
+        self.assertEqual(pos.asFea(), "pos [T V] [a b c] <1 2 3 4>;")
 
     def test_gpos_type_2_format_b(self):
         doc = self.parse("feature kern {"
@@ -821,8 +837,7 @@
         self.assertEqual(type(pos), ast.PairPosStatement)
         self.assertFalse(pos.enumerated)
         self.assertEqual(glyphstr([pos.glyphs1]), "[T V]")
-        self.assertEqual(pos.valuerecord1.makeString(vertical=False),
-                         "<1 2 3 4>")
+        self.assertEqual(pos.valuerecord1.asFea(), "<1 2 3 4>")
         self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
         self.assertIsNone(pos.valuerecord2)
 
@@ -834,8 +849,7 @@
         self.assertEqual(type(pos), ast.PairPosStatement)
         self.assertTrue(pos.enumerated)
         self.assertEqual(glyphstr([pos.glyphs1]), "[T V]")
-        self.assertEqual(pos.valuerecord1.makeString(vertical=False),
-                         "<1 2 3 4>")
+        self.assertEqual(pos.valuerecord1.asFea(), "<1 2 3 4>")
         self.assertEqual(glyphstr([pos.glyphs2]), "[a b c]")
         self.assertIsNone(pos.valuerecord2)
 
@@ -1411,7 +1425,8 @@
 
     def test_valuerecord_format_a_horizontal(self):
         doc = self.parse("feature liga {valueRecordDef 123 foo;} liga;")
-        value = doc.statements[0].statements[0].value
+        valuedef = doc.statements[0].statements[0]
+        value = valuedef.value
         self.assertIsNone(value.xPlacement)
         self.assertIsNone(value.yPlacement)
         self.assertEqual(value.xAdvance, 123)
@@ -1420,11 +1435,13 @@
         self.assertIsNone(value.yPlaDevice)
         self.assertIsNone(value.xAdvDevice)
         self.assertIsNone(value.yAdvDevice)
-        self.assertEqual(value.makeString(vertical=False), "123")
+        self.assertEqual(valuedef.asFea(), "valueRecordDef 123 foo;")
+        self.assertEqual(value.asFea(), "123")
 
     def test_valuerecord_format_a_vertical(self):
         doc = self.parse("feature vkrn {valueRecordDef 123 foo;} vkrn;")
-        value = doc.statements[0].statements[0].value
+        valuedef = doc.statements[0].statements[0]
+        value = valuedef.value
         self.assertIsNone(value.xPlacement)
         self.assertIsNone(value.yPlacement)
         self.assertIsNone(value.xAdvance)
@@ -1433,11 +1450,13 @@
         self.assertIsNone(value.yPlaDevice)
         self.assertIsNone(value.xAdvDevice)
         self.assertIsNone(value.yAdvDevice)
-        self.assertEqual(value.makeString(vertical=True), "123")
+        self.assertEqual(valuedef.asFea(), "valueRecordDef 123 foo;")
+        self.assertEqual(value.asFea(), "123")
 
     def test_valuerecord_format_a_zero_horizontal(self):
         doc = self.parse("feature liga {valueRecordDef 0 foo;} liga;")
-        value = doc.statements[0].statements[0].value
+        valuedef = doc.statements[0].statements[0]
+        value = valuedef.value
         self.assertIsNone(value.xPlacement)
         self.assertIsNone(value.yPlacement)
         self.assertEqual(value.xAdvance, 0)
@@ -1446,11 +1465,13 @@
         self.assertIsNone(value.yPlaDevice)
         self.assertIsNone(value.xAdvDevice)
         self.assertIsNone(value.yAdvDevice)
-        self.assertEqual(value.makeString(vertical=False), "0")
+        self.assertEqual(valuedef.asFea(), "valueRecordDef 0 foo;")
+        self.assertEqual(value.asFea(), "0")
 
     def test_valuerecord_format_a_zero_vertical(self):
         doc = self.parse("feature vkrn {valueRecordDef 0 foo;} vkrn;")
-        value = doc.statements[0].statements[0].value
+        valuedef = doc.statements[0].statements[0]
+        value = valuedef.value
         self.assertIsNone(value.xPlacement)
         self.assertIsNone(value.yPlacement)
         self.assertIsNone(value.xAdvance)
@@ -1459,7 +1480,8 @@
         self.assertIsNone(value.yPlaDevice)
         self.assertIsNone(value.xAdvDevice)
         self.assertIsNone(value.yAdvDevice)
-        self.assertEqual(value.makeString(vertical=True), "0")
+        self.assertEqual(valuedef.asFea(), "valueRecordDef 0 foo;")
+        self.assertEqual(value.asFea(), "0")
 
     def test_valuerecord_format_a_vertical_contexts_(self):
         for tag in "vkrn vpal vhal valt".split():
@@ -1472,7 +1494,8 @@
 
     def test_valuerecord_format_b(self):
         doc = self.parse("feature liga {valueRecordDef <1 2 3 4> foo;} liga;")
-        value = doc.statements[0].statements[0].value
+        valuedef = doc.statements[0].statements[0]
+        value = valuedef.value
         self.assertEqual(value.xPlacement, 1)
         self.assertEqual(value.yPlacement, 2)
         self.assertEqual(value.xAdvance, 3)
@@ -1481,11 +1504,13 @@
         self.assertIsNone(value.yPlaDevice)
         self.assertIsNone(value.xAdvDevice)
         self.assertIsNone(value.yAdvDevice)
-        self.assertEqual(value.makeString(vertical=False), "<1 2 3 4>")
+        self.assertEqual(valuedef.asFea(), "valueRecordDef <1 2 3 4> foo;")
+        self.assertEqual(value.asFea(), "<1 2 3 4>")
 
     def test_valuerecord_format_b_zero(self):
         doc = self.parse("feature liga {valueRecordDef <0 0 0 0> foo;} liga;")
-        value = doc.statements[0].statements[0].value
+        valuedef = doc.statements[0].statements[0]
+        value = valuedef.value
         self.assertEqual(value.xPlacement, 0)
         self.assertEqual(value.yPlacement, 0)
         self.assertEqual(value.xAdvance, 0)
@@ -1494,7 +1519,8 @@
         self.assertIsNone(value.yPlaDevice)
         self.assertIsNone(value.xAdvDevice)
         self.assertIsNone(value.yAdvDevice)
-        self.assertEqual(value.makeString(vertical=False), "<0 0 0 0>")
+        self.assertEqual(valuedef.asFea(), "valueRecordDef <0 0 0 0> foo;")
+        self.assertEqual(value.asFea(), "<0 0 0 0>")
 
     def test_valuerecord_format_c(self):
         doc = self.parse(
@@ -1516,14 +1542,15 @@
         self.assertEqual(value.yPlaDevice, ((11, 111), (12, 112)))
         self.assertIsNone(value.xAdvDevice)
         self.assertEqual(value.yAdvDevice, ((33, -113), (44, -114), (55, 115)))
-        self.assertEqual(value.makeString(vertical=False),
+        self.assertEqual(value.asFea(),
                          "<1 2 3 4 <device 8 88> <device 11 111, 12 112>"
                          " <device NULL> <device 33 -113, 44 -114, 55 115>>")
 
     def test_valuerecord_format_d(self):
         doc = self.parse("feature test {valueRecordDef <NULL> foo;} test;")
         value = doc.statements[0].statements[0].value
-        self.assertIsNone(value)
+        self.assertFalse(value)
+        self.assertEqual(value.asFea(), "<NULL>")
 
     def test_valuerecord_named(self):
         doc = self.parse("valueRecordDef <1 2 3 4> foo;"
diff --git a/Tests/fontBuilder/data/test_uvs.ttf.ttx b/Tests/fontBuilder/data/test_uvs.ttf.ttx
new file mode 100644
index 0000000..55b0a80
--- /dev/null
+++ b/Tests/fontBuilder/data/test_uvs.ttf.ttx
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+    </cmap_format_4>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uv="0x30" uvs="0xfe00" name="zero.slash"/>
+      <map uv="0x30" uvs="0xfe01"/>
+    </cmap_format_14>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x20" name="space"/><!-- SPACE -->
+      <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
+    </cmap_format_4>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/fontBuilder/fontBuilder_test.py b/Tests/fontBuilder/fontBuilder_test.py
index d23f0f1..97d7cb6 100644
--- a/Tests/fontBuilder/fontBuilder_test.py
+++ b/Tests/fontBuilder/fontBuilder_test.py
@@ -53,9 +53,9 @@
     return fb, advanceWidths, nameStrings
 
 
-def _verifyOutput(outPath):
+def _verifyOutput(outPath, tables=None):
     f = TTFont(outPath)
-    f.saveXML(outPath + ".ttx")
+    f.saveXML(outPath + ".ttx", tables=tables)
     with open(outPath + ".ttx") as f:
         testData = strip_VariableItems(f.read())
     refData = strip_VariableItems(getTestData(os.path.basename(outPath) + ".ttx"))
@@ -244,3 +244,42 @@
 
     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)
+
+
+def test_unicodeVariationSequences(tmpdir):
+    familyName = "UVSTestFont"
+    styleName = "Regular"
+    nameStrings = dict(familyName=familyName, styleName=styleName)
+    nameStrings['psName'] = familyName + "-" + styleName
+    glyphOrder = [".notdef", "space", "zero", "zero.slash"]
+    cmap = {ord(" "): "space", ord("0"): "zero"}
+    uvs = [
+        (0x0030, 0xFE00, "zero.slash"),
+        (0x0030, 0xFE01, None),  # not an official sequence, just testing
+    ]
+    metrics = {gn: (600, 0) for gn in glyphOrder}
+    pen = TTGlyphPen(None)
+    glyph = pen.glyph()  # empty placeholder
+    glyphs = {gn: glyph for gn in glyphOrder}
+
+    fb = FontBuilder(1024, isTTF=True)
+    fb.setupGlyphOrder(glyphOrder)
+    fb.setupCharacterMap(cmap, uvs)
+    fb.setupGlyf(glyphs)
+    fb.setupHorizontalMetrics(metrics)
+    fb.setupHorizontalHeader(ascent=824, descent=200)
+    fb.setupNameTable(nameStrings)
+    fb.setupOS2()
+    fb.setupPost()
+
+    outPath = os.path.join(str(tmpdir), "test_uvs.ttf")
+    fb.save(outPath)
+    _verifyOutput(outPath, tables=["cmap"])
+
+    uvs = [
+        (0x0030, 0xFE00, "zero.slash"),
+        (0x0030, 0xFE01, "zero"),  # should result in the exact same subtable data, due to cmap[0x0030] == "zero"
+    ]
+    fb.setupCharacterMap(cmap, uvs)
+    fb.save(outPath)
+    _verifyOutput(outPath, tables=["cmap"])
diff --git a/Tests/misc/loggingTools_test.py b/Tests/misc/loggingTools_test.py
index 694c109..b402efa 100644
--- a/Tests/misc/loggingTools_test.py
+++ b/Tests/misc/loggingTools_test.py
@@ -122,13 +122,13 @@
         test1()
 
         assert re.match(
-            "Took [0-9]\.[0-9]{3}s to run 'test1'",
+            r"Took [0-9]\.[0-9]{3}s to run 'test1'",
             logger.handlers[0].stream.getvalue())
 
         test2()
 
         assert re.search(
-            "Took [0-9]\.[0-9]{3}s to run test 2",
+            r"Took [0-9]\.[0-9]{3}s to run test 2",
             logger.handlers[0].stream.getvalue())
 
 
diff --git a/Tests/subset/subset_test.py b/Tests/subset/subset_test.py
index 76d89c2..72fee41 100644
--- a/Tests/subset/subset_test.py
+++ b/Tests/subset/subset_test.py
@@ -475,6 +475,107 @@
         subset.main([fontpath, "--recalc-timestamp", "--output-file=%s" % subsetpath, "*"])
         self.assertLess(modified, TTFont(subsetpath)['head'].modified)
 
+    def test_retain_gids_ttf(self):
+        _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf")
+        font = TTFont(fontpath)
+
+        self.assertEqual(font["hmtx"]["A"], (500, 132))
+        self.assertEqual(font["hmtx"]["B"], (400, 132))
+
+        self.assertGreater(font["glyf"]["A"].numberOfContours, 0)
+        self.assertGreater(font["glyf"]["B"].numberOfContours, 0)
+
+        subsetpath = self.temp_path(".ttf")
+        subset.main(
+            [
+                fontpath,
+                "--retain-gids",
+                "--output-file=%s" % subsetpath,
+                "--glyph-names",
+                "A",
+            ]
+        )
+        subsetfont = TTFont(subsetpath)
+
+        self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder())
+
+        hmtx = subsetfont["hmtx"]
+        self.assertEqual(hmtx["A"], (500, 132))
+        self.assertEqual(hmtx["B"], (0, 0))
+
+        glyf = subsetfont["glyf"]
+        self.assertGreater(glyf["A"].numberOfContours, 0)
+        self.assertEqual(glyf["B"].numberOfContours, 0)
+
+    def test_retain_gids_cff(self):
+        _, fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf")
+        font = TTFont(fontpath)
+
+        self.assertEqual(font["hmtx"]["A"], (500, 132))
+        self.assertEqual(font["hmtx"]["B"], (400, 132))
+
+        font["CFF "].cff[0].decompileAllCharStrings()
+        cs = font["CFF "].cff[0].CharStrings
+        self.assertGreater(len(cs["A"].program), 0)
+        self.assertGreater(len(cs["B"].program), 0)
+
+        subsetpath = self.temp_path(".otf")
+        subset.main(
+            [
+                fontpath,
+                "--retain-gids",
+                "--output-file=%s" % subsetpath,
+                "--glyph-names",
+                "A",
+            ]
+        )
+        subsetfont = TTFont(subsetpath)
+
+        self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder())
+
+        hmtx = subsetfont["hmtx"]
+        self.assertEqual(hmtx["A"], (500, 132))
+        self.assertEqual(hmtx["B"], (0, 0))
+
+        subsetfont["CFF "].cff[0].decompileAllCharStrings()
+        cs = subsetfont["CFF "].cff[0].CharStrings
+        self.assertGreater(len(cs["A"].program), 0)
+        self.assertEqual(cs["B"].program, ["endchar"])
+
+    def test_retain_gids_cff2(self):
+        fontpath = self.getpath("../../varLib/data/TestCFF2VF.otf")
+        font = TTFont(fontpath)
+
+        self.assertEqual(font["hmtx"]["A"], (600, 31))
+        self.assertEqual(font["hmtx"]["T"], (600, 41))
+
+        font["CFF2"].cff[0].decompileAllCharStrings()
+        cs = font["CFF2"].cff[0].CharStrings
+        self.assertGreater(len(cs["A"].program), 0)
+        self.assertGreater(len(cs["T"].program), 0)
+
+        subsetpath = self.temp_path(".otf")
+        subset.main(
+            [
+                fontpath,
+                "--retain-gids",
+                "--output-file=%s" % subsetpath,
+                "A",
+            ]
+        )
+        subsetfont = TTFont(subsetpath)
+
+        self.assertEqual(len(subsetfont.getGlyphOrder()), len(font.getGlyphOrder()))
+
+        hmtx = subsetfont["hmtx"]
+        self.assertEqual(hmtx["A"], (600, 31))
+        self.assertEqual(hmtx["glyph00002"], (0, 0))
+
+        subsetfont["CFF2"].cff[0].decompileAllCharStrings()
+        cs = subsetfont["CFF2"].cff[0].CharStrings
+        self.assertGreater(len(cs["A"].program), 0)
+        self.assertEqual(cs["glyph00002"].program, [])
+
 
 if __name__ == "__main__":
     sys.exit(unittest.main())
diff --git a/Tests/svgLib/path/parser_test.py b/Tests/svgLib/path/parser_test.py
index 7865870..eada14e 100644
--- a/Tests/svgLib/path/parser_test.py
+++ b/Tests/svgLib/path/parser_test.py
@@ -290,8 +290,67 @@
     assert exc_info.match("Unallowed implicit command")
 
 
-def test_arc_not_implemented():
-    pathdef = "M300,200 h-150 a150,150 0 1,0 150,-150 z"
-    with pytest.raises(NotImplementedError) as exc_info:
-        parse_path(pathdef, RecordingPen())
-    assert exc_info.match("arcs are not supported")
+def test_arc_to_cubic_bezier():
+    pen = RecordingPen()
+    parse_path("M300,200 h-150 a150,150 0 1,0 150,-150 z", pen)
+    expected = [
+        ('moveTo', ((300.0, 200.0),)),
+        ('lineTo', ((150.0, 200.0),)),
+        (
+            'curveTo',
+            (
+                (150.0, 282.842),
+                (217.157, 350.0),
+                (300.0, 350.0)
+            )
+        ),
+        (
+            'curveTo',
+            (
+                (382.842, 350.0),
+                (450.0, 282.842),
+                (450.0, 200.0)
+            )
+        ),
+        (
+            'curveTo',
+            (
+                (450.0, 117.157),
+                (382.842, 50.0),
+                (300.0, 50.0)
+            )
+        ),
+        ('lineTo', ((300.0, 200.0),)),
+        ('closePath', ())
+    ]
+
+    result = list(pen.value)
+    assert len(result) == len(expected)
+    for (cmd1, points1), (cmd2, points2) in zip(result, expected):
+        assert cmd1 == cmd2
+        assert len(points1) == len(points2)
+        for pt1, pt2 in zip(points1, points2):
+            assert pt1 == pytest.approx(pt2, rel=1e-5)
+
+
+
+class ArcRecordingPen(RecordingPen):
+
+    def arcTo(self, rx, ry, rotation, arc_large, arc_sweep, end_point):
+        self.value.append(
+            ("arcTo", (rx, ry, rotation, arc_large, arc_sweep, end_point))
+        )
+
+
+def test_arc_pen_with_arcTo():
+    pen = ArcRecordingPen()
+    parse_path("M300,200 h-150 a150,150 0 1,0 150,-150 z", pen)
+    expected = [
+        ('moveTo', ((300.0, 200.0),)),
+        ('lineTo', ((150.0, 200.0),)),
+        ('arcTo', (150.0, 150.0, 0.0, True, False, (300.0, 50.0))),
+        ('lineTo', ((300.0, 200.0),)),
+        ('closePath', ())
+    ]
+
+    assert pen.value == expected
diff --git a/Tests/ttLib/tables/_c_m_a_p_test.py b/Tests/ttLib/tables/_c_m_a_p_test.py
index 6647345..306d048 100644
--- a/Tests/ttLib/tables/_c_m_a_p_test.py
+++ b/Tests/ttLib/tables/_c_m_a_p_test.py
@@ -1,9 +1,23 @@
 from __future__ import print_function, division, absolute_import, unicode_literals
+import io
+import os
+import re
 from fontTools.misc.py23 import *
 from fontTools import ttLib
+from fontTools.fontBuilder import FontBuilder
 import unittest
 from fontTools.ttLib.tables._c_m_a_p import CmapSubtable, table__c_m_a_p
 
+CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
+DATA_DIR = os.path.join(CURR_DIR, 'data')
+CMAP_FORMAT_14_TTX = os.path.join(DATA_DIR, "_c_m_a_p_format_14.ttx")
+CMAP_FORMAT_14_BW_COMPAT_TTX = os.path.join(DATA_DIR, "_c_m_a_p_format_14_bw_compat.ttx")
+
+def strip_VariableItems(string):
+    # ttlib changes with the fontTools version
+    string = re.sub(' ttLibVersion=".*"', '', string)
+    return string
+
 class CmapSubtableTest(unittest.TestCase):
 
 	def makeSubtable(self, cmapFormat, platformID, platEncID, langID):
@@ -82,6 +96,37 @@
 		self.assertEqual(font.getBestCmap(cmapPreferences=[(3, 1)]), {0x0041:'A', 0x0391:'A'})
 		self.assertEqual(font.getBestCmap(cmapPreferences=[(0, 4)]), None)
 
+	def test_format_14(self):
+		subtable = self.makeSubtable(14, 0, 5, 0)
+		subtable.cmap = {}  # dummy
+		subtable.uvsDict = {
+			0xFE00: [(0x0030, "zero.slash")],
+			0xFE01: [(0x0030, None)],
+		}
+		fb = FontBuilder(1024, isTTF=True)
+		font = fb.font
+		fb.setupGlyphOrder([".notdef", "zero.slash"])
+		fb.setupMaxp()
+		fb.setupPost()
+		cmap = table__c_m_a_p()
+		cmap.tableVersion = 0
+		cmap.tables = [subtable]
+		font["cmap"] = cmap
+		f = io.BytesIO()
+		font.save(f)
+		f.seek(0)
+		font = ttLib.TTFont(f)
+		self.assertEqual(font["cmap"].getcmap(0, 5).uvsDict, subtable.uvsDict)
+		f = io.StringIO(newline=None)
+		font.saveXML(f, tables=["cmap"])
+		ttx = strip_VariableItems(f.getvalue())
+		with open(CMAP_FORMAT_14_TTX) as f:
+			expected = strip_VariableItems(f.read())
+		self.assertEqual(ttx, expected)
+		with open(CMAP_FORMAT_14_BW_COMPAT_TTX) as f:
+			font.importXML(f)
+		self.assertEqual(font["cmap"].getcmap(0, 5).uvsDict, subtable.uvsDict)
+
 
 if __name__ == "__main__":
 	import sys
diff --git a/Tests/ttLib/tables/_h_m_t_x_test.py b/Tests/ttLib/tables/_h_m_t_x_test.py
index 007f3cd..5c79fb8 100644
--- a/Tests/ttLib/tables/_h_m_t_x_test.py
+++ b/Tests/ttLib/tables/_h_m_t_x_test.py
@@ -107,6 +107,27 @@
             len([r for r in captor.records
                 if "has a huge advance" in r.msg]) == 1)
 
+    def test_decompile_no_header_table(self):
+        font = TTFont()
+        maxp = font['maxp'] = newTable('maxp')
+        maxp.numGlyphs = 3
+        font.glyphOrder = ["A", "B", "C"]
+
+        self.assertNotIn(self.tableClass.headerTag, font)
+
+        data = deHexStr("0190 001E 0190 0028 0190 0032")
+        mtxTable = newTable(self.tag)
+        mtxTable.decompile(data, font)
+
+        self.assertEqual(
+            mtxTable.metrics,
+            {
+                "A": (400, 30),
+                "B": (400, 40),
+                "C": (400, 50),
+            }
+        )
+
     def test_compile(self):
         # we set the wrong 'numberOfMetrics' to check it gets adjusted
         font = self.makeFont(numGlyphs=3, numberOfMetrics=4)
@@ -173,6 +194,24 @@
 
         self.assertEqual(data, deHexStr("0001 0001 0000 0001 0000"))
 
+    def test_compile_no_header_table(self):
+        font = TTFont()
+        maxp = font['maxp'] = newTable('maxp')
+        maxp.numGlyphs = 3
+        font.glyphOrder = [chr(i) for i in range(65, 68)]
+        mtxTable = font[self.tag] = newTable(self.tag)
+        mtxTable.metrics = {
+            "A": (400, 30),
+            "B": (400, 40),
+            "C": (400, 50),
+        }
+
+        self.assertNotIn(self.tableClass.headerTag, font)
+
+        data = mtxTable.compile(font)
+
+        self.assertEqual(data, deHexStr("0190 001E 0190 0028 0190 0032"))
+
     def test_toXML(self):
         font = self.makeFont(numGlyphs=2, numberOfMetrics=2)
         mtxTable = font[self.tag] = newTable(self.tag)
diff --git a/Tests/ttLib/tables/data/_c_m_a_p_format_14.ttx b/Tests/ttLib/tables/data/_c_m_a_p_format_14.ttx
new file mode 100644
index 0000000..73bc6bf
--- /dev/null
+++ b/Tests/ttLib/tables/data/_c_m_a_p_format_14.ttx
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uv="0x30" uvs="0xfe00" name="zero.slash"/>
+      <map uv="0x30" uvs="0xfe01"/>
+    </cmap_format_14>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/_c_m_a_p_format_14_bw_compat.ttx b/Tests/ttLib/tables/data/_c_m_a_p_format_14_bw_compat.ttx
new file mode 100644
index 0000000..00be8ca
--- /dev/null
+++ b/Tests/ttLib/tables/data/_c_m_a_p_format_14_bw_compat.ttx
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uvs="0xfe00" uv="0x30" name="zero.slash"/>
+      <map uvs="0xfe01" uv="0x30" name="None"/><!-- testing whether the old format that uses name="None" is still accepted -->
+    </cmap_format_14>
+  </cmap>
+
+</ttFont>
diff --git a/Tests/ttLib/tables/data/aots/cmap14_font1.ttx.cmap b/Tests/ttLib/tables/data/aots/cmap14_font1.ttx.cmap
index ced8c8a..acead7b 100644
--- a/Tests/ttLib/tables/data/aots/cmap14_font1.ttx.cmap
+++ b/Tests/ttLib/tables/data/aots/cmap14_font1.ttx.cmap
@@ -3,14 +3,14 @@
 
   <cmap>
     <tableVersion version="0"/>
-    <cmap_format_14 platformID="0" platEncID="5" format="14" length="47" numVarSelectorRecords="1">
-      <map uvs="0xe0100" uv="0x4e00" name="None"/>
-      <map uvs="0xe0100" uv="0x4e03" name="None"/>
-      <map uvs="0xe0100" uv="0x4e04" name="None"/>
-      <map uvs="0xe0100" uv="0x4e05" name="None"/>
-      <map uvs="0xe0100" uv="0x4e06" name="None"/>
-      <map uvs="0xe0100" uv="0x4e10" name="g25"/>
-      <map uvs="0xe0100" uv="0x4e11" name="g26"/>
+    <cmap_format_14 platformID="0" platEncID="5">
+      <map uv="0x4e00" uvs="0xe0100"/>
+      <map uv="0x4e03" uvs="0xe0100"/>
+      <map uv="0x4e04" uvs="0xe0100"/>
+      <map uv="0x4e05" uvs="0xe0100"/>
+      <map uv="0x4e06" uvs="0xe0100"/>
+      <map uv="0x4e10" uvs="0xe0100" name="g25"/>
+      <map uv="0x4e11" uvs="0xe0100" name="g26"/>
     </cmap_format_14>
     <cmap_format_4 platformID="3" platEncID="1" language="0">
       <map code="0x4e00" name="g10"/><!-- CJK UNIFIED IDEOGRAPH-4E00 -->
diff --git a/Tests/ttLib/woff2_test.py b/Tests/ttLib/woff2_test.py
index b3ec6b2..55c4b77 100644
--- a/Tests/ttLib/woff2_test.py
+++ b/Tests/ttLib/woff2_test.py
@@ -765,7 +765,7 @@
 
 		self.assertRaisesRegex(
 			ttLib.TTLibError,
-			"UIntBase128 value exceeds 2\*\*32-1",
+			r"UIntBase128 value exceeds 2\*\*32-1",
 			unpackBase128, b'\x90\x80\x80\x80\x00')
 
 		self.assertRaisesRegex(
@@ -783,11 +783,11 @@
 		self.assertEqual(packBase128(2**32-1), b'\x8f\xff\xff\xff\x7f')
 		self.assertRaisesRegex(
 			ttLib.TTLibError,
-			"UIntBase128 format requires 0 <= integer <= 2\*\*32-1",
+			r"UIntBase128 format requires 0 <= integer <= 2\*\*32-1",
 			packBase128, 2**32+1)
 		self.assertRaisesRegex(
 			ttLib.TTLibError,
-			"UIntBase128 format requires 0 <= integer <= 2\*\*32-1",
+			r"UIntBase128 format requires 0 <= integer <= 2\*\*32-1",
 			packBase128, -1)
 
 
diff --git a/Tests/ttx/ttx_test.py b/Tests/ttx/ttx_test.py
index ceb7abd..eb5816b 100644
--- a/Tests/ttx/ttx_test.py
+++ b/Tests/ttx/ttx_test.py
@@ -476,6 +476,11 @@
     assert tto.recalcTimestamp is True
 
 
+def test_options_recalc_timestamp():
+    tto = ttx.Options([("--no-recalc-timestamp", "")], 1)
+    assert tto.recalcTimestamp is False
+
+
 def test_options_flavor():
     tto = ttx.Options([("--flavor", "woff")], 1)
     assert tto.flavor == "woff"
@@ -789,6 +794,14 @@
     ttf = TTFont(str(outttf2))
     assert ttf["head"].modified > epochtime
 
+    # --no-recalc-timestamp will keep original timestamp
+    options.recalcTimestamp = False
+    ttx.ttCompile(inttx, str(outttf2), options)
+    assert outttf2.check(file=True)
+    inttf = TTFont()
+    inttf.importXML(inttx)
+    assert inttf["head"].modified == TTFont(str(outttf2))["head"].modified
+
 
 # -------------------------
 # ttx.ttList function tests
diff --git a/Tests/ufoLib/testSupport.py b/Tests/ufoLib/testSupport.py
old mode 100644
new mode 100755
diff --git a/Tests/varLib/data/Build.designspace b/Tests/varLib/data/Build.designspace
index e0bf58d..29a43ce 100644
--- a/Tests/varLib/data/Build.designspace
+++ b/Tests/varLib/data/Build.designspace
@@ -4,6 +4,8 @@
         <axis default="368.0" maximum="1000.0" minimum="0.0" name="weight" tag="wght" />
         <axis default="0.0" maximum="100.0" minimum="0.0" name="contrast" tag="cntr">
             <labelname xml:lang="en">Contrast</labelname>
+            <labelname xml:lang="de">Kontrast</labelname>
+            <labelname xml:lang="fa">کنتراست</labelname>
         </axis>
     </axes>
     <sources>
diff --git a/Tests/varLib/data/SparseMasters.designspace b/Tests/varLib/data/SparseMasters.designspace
new file mode 100644
index 0000000..327fb43
--- /dev/null
+++ b/Tests/varLib/data/SparseMasters.designspace
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<designspace format="4.0">
+  <axes>
+    <axis tag="wght" name="Weight" minimum="350" maximum="625" default="350"/>
+  </axes>
+  <sources>
+      <source filename="master_ttx_interpolatable_ttf/SparseMasters-Regular.ttx" name="Sparse Masters Regular">
+      <location>
+        <dimension name="Weight" xvalue="350"/>
+      </location>
+    </source>
+    <source filename="master_ttx_interpolatable_ttf/SparseMasters-Medium.ttx" name="Sparse Masters Medium">
+      <location>
+        <dimension name="Weight" xvalue="450"/>
+      </location>
+    </source>
+    <source filename="master_ttx_interpolatable_ttf/SparseMasters-Bold.ttx" name="Sparse Masters Bold">
+      <location>
+        <dimension name="Weight" xvalue="625"/>
+      </location>
+    </source>
+  </sources>
+</designspace>
diff --git a/Tests/varLib/data/master_ttx_getvar_ttf/Mutator_Getvar.ttx b/Tests/varLib/data/master_ttx_getvar_ttf/Mutator_Getvar.ttx
old mode 100644
new mode 100755
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Bold.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Bold.ttx
new file mode 100644
index 0000000..55d686e
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Bold.ttx
@@ -0,0 +1,419 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+    <GlyphID id="2" name="e"/>
+    <GlyphID id="3" name="s"/>
+    <GlyphID id="4" name="dotabovecomb"/>
+    <GlyphID id="5" name="edotabove"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="0.0"/>
+    <checkSumAdjustment value="0x778ee739"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Nov 21 11:49:03 2018"/>
+    <modified value="Tue Jan 15 18:40:09 2019"/>
+    <xMin value="-64"/>
+    <yMin value="-250"/>
+    <xMax value="608"/>
+    <yMax value="812"/>
+    <macStyle value="00000000 00000001"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="950"/>
+    <descent value="-250"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="-64"/>
+    <minRightSideBearing value="-63"/>
+    <xMaxExtent value="608"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="6"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="18"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="17"/>
+    <maxCompositeContours value="2"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="2"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="580"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 01000101"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 00100000"/>
+    <usFirstCharIndex value="97"/>
+    <usLastCharIndex value="775"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="950"/>
+    <usWinDescent value="250"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="a" width="600" lsb="9"/>
+    <mtx name="dotabovecomb" width="0" lsb="-64"/>
+    <mtx name="e" width="600" lsb="9"/>
+    <mtx name="edotabove" width="600" lsb="9"/>
+    <mtx name="s" width="600" lsb="7"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x65" name="e"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x117" name="edotabove"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x307" name="dotabovecomb"/><!-- COMBINING DOT ABOVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x65" name="e"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x117" name="edotabove"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x307" name="dotabovecomb"/><!-- COMBINING DOT ABOVE -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-250" xMax="450" yMax="750">
+      <contour>
+        <pt x="50" y="-250" on="1"/>
+        <pt x="450" y="-250" on="1"/>
+        <pt x="450" y="750" on="1"/>
+        <pt x="50" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-200" on="1"/>
+        <pt x="100" y="700" on="1"/>
+        <pt x="400" y="700" on="1"/>
+        <pt x="400" y="-200" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="9" yMin="-12" xMax="468" yMax="504">
+      <contour>
+        <pt x="468" y="-1" on="1"/>
+        <pt x="307" y="-1" on="1"/>
+        <pt x="304" y="303" on="1"/>
+        <pt x="208" y="341" on="1"/>
+        <pt x="36" y="281" on="1"/>
+        <pt x="9" y="428" on="1"/>
+        <pt x="214" y="504" on="1"/>
+        <pt x="447" y="434" on="1"/>
+      </contour>
+      <contour>
+        <pt x="378" y="263" on="1"/>
+        <pt x="381" y="184" on="1"/>
+        <pt x="165" y="179" on="1"/>
+        <pt x="163" y="133" on="1"/>
+        <pt x="201" y="102" on="1"/>
+        <pt x="383" y="149" on="1"/>
+        <pt x="389" y="71" on="1"/>
+        <pt x="168" y="-12" on="1"/>
+        <pt x="29" y="22" on="1"/>
+        <pt x="26" y="240" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dotabovecomb" xMin="-64" yMin="483" xMax="63" yMax="625">
+      <contour>
+        <pt x="-29" y="625" on="1"/>
+        <pt x="63" y="605" on="1"/>
+        <pt x="58" y="488" on="1"/>
+        <pt x="-64" y="483" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="e" xMin="9" yMin="-18" xMax="601" yMax="548">
+      <contour>
+        <pt x="197" y="229" on="1"/>
+        <pt x="195" y="299" on="1"/>
+        <pt x="404" y="293" on="1"/>
+        <pt x="301" y="360" on="1"/>
+        <pt x="217" y="264" on="1"/>
+        <pt x="244" y="130" on="1"/>
+        <pt x="524" y="184" on="1"/>
+        <pt x="528" y="0" on="1"/>
+        <pt x="188" y="-18" on="1"/>
+        <pt x="9" y="262" on="1"/>
+        <pt x="314" y="548" on="1"/>
+        <pt x="596" y="304" on="1"/>
+        <pt x="601" y="225" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="edotabove" xMin="9" yMin="-18" xMax="601" yMax="812">
+      <component glyphName="e" x="0" y="0" flags="0x204"/>
+      <component glyphName="dotabovecomb" x="307" y="187" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="7" yMin="-58" xMax="608" yMax="530">
+      <contour>
+        <pt x="559" y="459" on="1"/>
+        <pt x="537" y="336" on="1"/>
+        <pt x="324" y="402" on="1"/>
+        <pt x="268" y="357" on="1"/>
+        <pt x="608" y="141" on="1"/>
+        <pt x="284" y="-58" on="1"/>
+        <pt x="7" y="79" on="1"/>
+        <pt x="26" y="226" on="1"/>
+        <pt x="221" y="119" on="1"/>
+        <pt x="347" y="149" on="1"/>
+        <pt x="16" y="398" on="1"/>
+        <pt x="324" y="530" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Layer Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      0.000;NONE;LayerFont-Bold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Layer Font Bold
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 0.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      LayerFont-Bold
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-100"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dotabovecomb"/>
+      <psName name="edotabove"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="1">
+      <ClassDef glyph="dotabovecomb" class="3"/>
+      <ClassDef glyph="e" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="mark"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="dotabovecomb"/>
+          </MarkCoverage>
+          <BaseCoverage Format="1">
+            <Glyph value="e"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="-2"/>
+                <YCoordinate value="465"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="315"/>
+                <YCoordinate value="644"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="a">
+            <Ligature components="e,s,s" glyph="s"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Medium.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Medium.ttx
new file mode 100644
index 0000000..eb3c745
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Medium.ttx
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="e"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="0.0"/>
+    <checkSumAdjustment value="0x62ddba7b"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Nov 21 11:49:03 2018"/>
+    <modified value="Tue Jan 15 18:40:09 2019"/>
+    <xMin value="40"/>
+    <yMin value="-250"/>
+    <xMax value="576"/>
+    <yMax value="750"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="2"/>
+    <maxPoints value="13"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="0"/>
+    <maxCompositeContours value="0"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+    <maxComponentDepth value="0"/>
+  </maxp>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="e" width="600" lsb="40"/>
+  </hmtx>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-250" xMax="450" yMax="750">
+      <contour>
+        <pt x="50" y="-250" on="1"/>
+        <pt x="450" y="-250" on="1"/>
+        <pt x="450" y="750" on="1"/>
+        <pt x="50" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-200" on="1"/>
+        <pt x="100" y="700" on="1"/>
+        <pt x="400" y="700" on="1"/>
+        <pt x="400" y="-200" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="e" xMin="40" yMin="-18" xMax="576" yMax="513">
+      <contour>
+        <pt x="126" y="203" on="1"/>
+        <pt x="125" y="298" on="1"/>
+        <pt x="396" y="297" on="1"/>
+        <pt x="318" y="387" on="1"/>
+        <pt x="180" y="264" on="1"/>
+        <pt x="264" y="116" on="1"/>
+        <pt x="507" y="157" on="1"/>
+        <pt x="526" y="45" on="1"/>
+        <pt x="188" y="-18" on="1"/>
+        <pt x="40" y="261" on="1"/>
+        <pt x="316" y="513" on="1"/>
+        <pt x="571" y="305" on="1"/>
+        <pt x="576" y="199" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-32768"/>
+    <underlineThickness value="-32768"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+    </extraNames>
+  </post>
+
+</ttFont>
diff --git a/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Regular.ttx b/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Regular.ttx
new file mode 100644
index 0000000..e013e0b
--- /dev/null
+++ b/Tests/varLib/data/master_ttx_interpolatable_ttf/SparseMasters-Regular.ttx
@@ -0,0 +1,419 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+    <GlyphID id="2" name="e"/>
+    <GlyphID id="3" name="s"/>
+    <GlyphID id="4" name="dotabovecomb"/>
+    <GlyphID id="5" name="edotabove"/>
+  </GlyphOrder>
+
+  <head>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="1.0"/>
+    <fontRevision value="0.0"/>
+    <checkSumAdjustment value="0x5cdfc4c1"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Nov 21 11:49:03 2018"/>
+    <modified value="Tue Jan 15 18:40:09 2019"/>
+    <xMin value="-37"/>
+    <yMin value="-250"/>
+    <xMax value="582"/>
+    <yMax value="750"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="6"/>
+    <fontDirectionHint value="2"/>
+    <indexToLocFormat value="0"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="950"/>
+    <descent value="-250"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="-37"/>
+    <minRightSideBearing value="-50"/>
+    <xMaxExtent value="582"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="6"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="18"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="17"/>
+    <maxCompositeContours value="2"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="2"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="580"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 01000101"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="97"/>
+    <usLastCharIndex value="775"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="950"/>
+    <usWinDescent value="250"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="a" width="600" lsb="9"/>
+    <mtx name="dotabovecomb" width="0" lsb="-37"/>
+    <mtx name="e" width="600" lsb="40"/>
+    <mtx name="edotabove" width="600" lsb="40"/>
+    <mtx name="s" width="600" lsb="25"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x65" name="e"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x117" name="edotabove"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x307" name="dotabovecomb"/><!-- COMBINING DOT ABOVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x65" name="e"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x117" name="edotabove"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x307" name="dotabovecomb"/><!-- COMBINING DOT ABOVE -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-250" xMax="450" yMax="750">
+      <contour>
+        <pt x="50" y="-250" on="1"/>
+        <pt x="450" y="-250" on="1"/>
+        <pt x="450" y="750" on="1"/>
+        <pt x="50" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-200" on="1"/>
+        <pt x="100" y="700" on="1"/>
+        <pt x="400" y="700" on="1"/>
+        <pt x="400" y="-200" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="9" yMin="-12" xMax="468" yMax="504">
+      <contour>
+        <pt x="468" y="-1" on="1"/>
+        <pt x="366" y="-3" on="1"/>
+        <pt x="363" y="357" on="1"/>
+        <pt x="208" y="397" on="1"/>
+        <pt x="36" y="337" on="1"/>
+        <pt x="9" y="428" on="1"/>
+        <pt x="214" y="504" on="1"/>
+        <pt x="447" y="434" on="1"/>
+      </contour>
+      <contour>
+        <pt x="378" y="263" on="1"/>
+        <pt x="382" y="207" on="1"/>
+        <pt x="88" y="172" on="1"/>
+        <pt x="86" y="126" on="1"/>
+        <pt x="161" y="74" on="1"/>
+        <pt x="383" y="134" on="1"/>
+        <pt x="389" y="71" on="1"/>
+        <pt x="168" y="-12" on="1"/>
+        <pt x="29" y="22" on="1"/>
+        <pt x="26" y="240" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dotabovecomb" xMin="-37" yMin="501" xMax="50" yMax="597">
+      <contour>
+        <pt x="-21" y="597" on="1"/>
+        <pt x="50" y="589" on="1"/>
+        <pt x="41" y="501" on="1"/>
+        <pt x="-37" y="503" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="e" xMin="40" yMin="-18" xMax="576" yMax="513">
+      <contour>
+        <pt x="127" y="228" on="1"/>
+        <pt x="125" y="298" on="1"/>
+        <pt x="480" y="292" on="1"/>
+        <pt x="317" y="416" on="1"/>
+        <pt x="147" y="263" on="1"/>
+        <pt x="229" y="75" on="1"/>
+        <pt x="509" y="129" on="1"/>
+        <pt x="526" y="45" on="1"/>
+        <pt x="188" y="-18" on="1"/>
+        <pt x="40" y="261" on="1"/>
+        <pt x="316" y="513" on="1"/>
+        <pt x="571" y="305" on="1"/>
+        <pt x="576" y="226" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="edotabove" xMin="40" yMin="-18" xMax="576" yMax="693">
+      <component glyphName="e" x="0" y="0" flags="0x204"/>
+      <component glyphName="dotabovecomb" x="313" y="96" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="25" yMin="-13" xMax="582" yMax="530">
+      <contour>
+        <pt x="559" y="459" on="1"/>
+        <pt x="539" y="376" on="1"/>
+        <pt x="326" y="442" on="1"/>
+        <pt x="213" y="366" on="1"/>
+        <pt x="582" y="174" on="1"/>
+        <pt x="304" y="-13" on="1"/>
+        <pt x="25" y="83" on="1"/>
+        <pt x="53" y="174" on="1"/>
+        <pt x="282" y="76" on="1"/>
+        <pt x="427" y="155" on="1"/>
+        <pt x="38" y="343" on="1"/>
+        <pt x="324" y="530" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Layer Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      0.000;NONE;LayerFont-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Layer Font Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 0.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      LayerFont-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dotabovecomb"/>
+      <psName name="edotabove"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010000"/>
+    <GlyphClassDef Format="1">
+      <ClassDef glyph="dotabovecomb" class="3"/>
+      <ClassDef glyph="e" class="1"/>
+    </GlyphClassDef>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="mark"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="dotabovecomb"/>
+          </MarkCoverage>
+          <BaseCoverage Format="1">
+            <Glyph value="e"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="-2"/>
+                <YCoordinate value="465"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="1">
+                <XCoordinate value="314"/>
+                <YCoordinate value="556"/>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="a">
+            <Ligature components="e,s,s" glyph="s"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</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 100644
new mode 100755
diff --git a/Tests/varLib/data/test_results/BuildMain.ttx b/Tests/varLib/data/test_results/BuildMain.ttx
index 66c8032..84a7573 100644
--- a/Tests/varLib/data/test_results/BuildMain.ttx
+++ b/Tests/varLib/data/test_results/BuildMain.ttx
@@ -440,6 +440,9 @@
   </glyf>
 
   <name>
+    <namerecord nameID="257" platformID="0" platEncID="4" langID="0x0">
+      کنتراست
+    </namerecord>
     <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
       Weight
     </namerecord>
@@ -494,6 +497,12 @@
     <namerecord nameID="273" platformID="1" platEncID="0" langID="0x0" unicode="True">
       TestFamily-BlackHighContrast
     </namerecord>
+    <namerecord nameID="257" platformID="1" platEncID="0" langID="0x2" unicode="True">
+      Kontrast
+    </namerecord>
+    <namerecord nameID="257" platformID="3" platEncID="1" langID="0x407">
+      Kontrast
+    </namerecord>
     <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
       Test Family
     </namerecord>
@@ -2235,4 +2244,10 @@
     </glyphVariations>
   </gvar>
 
+  <ltag>
+    <version value="1"/>
+    <flags value="0"/>
+    <LanguageTag tag="fa"/>
+  </ltag>
+
 </ttFont>
diff --git a/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx b/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx
old mode 100644
new mode 100755
diff --git a/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx b/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
old mode 100644
new mode 100755
diff --git a/Tests/varLib/data/test_results/SparseMasters.ttx b/Tests/varLib/data/test_results/SparseMasters.ttx
new file mode 100644
index 0000000..99a80bf
--- /dev/null
+++ b/Tests/varLib/data/test_results/SparseMasters.ttx
@@ -0,0 +1,660 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
+
+  <GlyphOrder>
+    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+    <GlyphID id="2" name="e"/>
+    <GlyphID id="3" name="s"/>
+    <GlyphID id="4" name="dotabovecomb"/>
+    <GlyphID id="5" name="edotabove"/>
+  </GlyphOrder>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="950"/>
+    <descent value="-250"/>
+    <lineGap value="0"/>
+    <advanceWidthMax value="600"/>
+    <minLeftSideBearing value="-37"/>
+    <minRightSideBearing value="-50"/>
+    <xMaxExtent value="582"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+    <numberOfHMetrics value="6"/>
+  </hhea>
+
+  <maxp>
+    <!-- Most of this table will be recalculated by the compiler -->
+    <tableVersion value="0x10000"/>
+    <numGlyphs value="6"/>
+    <maxPoints value="18"/>
+    <maxContours value="2"/>
+    <maxCompositePoints value="17"/>
+    <maxCompositeContours value="2"/>
+    <maxZones value="1"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="2"/>
+    <maxComponentDepth value="1"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="4"/>
+    <xAvgCharWidth value="580"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00000100"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="0"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 01000101"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="NONE"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="97"/>
+    <usLastCharIndex value="775"/>
+    <sTypoAscender value="750"/>
+    <sTypoDescender value="-250"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="950"/>
+    <usWinDescent value="250"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="4"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="50"/>
+    <mtx name="a" width="600" lsb="9"/>
+    <mtx name="dotabovecomb" width="0" lsb="-37"/>
+    <mtx name="e" width="600" lsb="40"/>
+    <mtx name="edotabove" width="600" lsb="40"/>
+    <mtx name="s" width="600" lsb="25"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="0" platEncID="3" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x65" name="e"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x117" name="edotabove"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x307" name="dotabovecomb"/><!-- COMBINING DOT ABOVE -->
+    </cmap_format_4>
+    <cmap_format_4 platformID="3" platEncID="1" language="0">
+      <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
+      <map code="0x65" name="e"/><!-- LATIN SMALL LETTER E -->
+      <map code="0x73" name="s"/><!-- LATIN SMALL LETTER S -->
+      <map code="0x117" name="edotabove"/><!-- LATIN SMALL LETTER E WITH DOT ABOVE -->
+      <map code="0x307" name="dotabovecomb"/><!-- COMBINING DOT ABOVE -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+
+    <!-- The xMin, yMin, xMax and yMax values
+         will be recalculated by the compiler. -->
+
+    <TTGlyph name=".notdef" xMin="50" yMin="-250" xMax="450" yMax="750">
+      <contour>
+        <pt x="50" y="-250" on="1"/>
+        <pt x="450" y="-250" on="1"/>
+        <pt x="450" y="750" on="1"/>
+        <pt x="50" y="750" on="1"/>
+      </contour>
+      <contour>
+        <pt x="100" y="-200" on="1"/>
+        <pt x="100" y="700" on="1"/>
+        <pt x="400" y="700" on="1"/>
+        <pt x="400" y="-200" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="a" xMin="9" yMin="-12" xMax="468" yMax="504">
+      <contour>
+        <pt x="468" y="-1" on="1"/>
+        <pt x="366" y="-3" on="1"/>
+        <pt x="363" y="357" on="1"/>
+        <pt x="208" y="397" on="1"/>
+        <pt x="36" y="337" on="1"/>
+        <pt x="9" y="428" on="1"/>
+        <pt x="214" y="504" on="1"/>
+        <pt x="447" y="434" on="1"/>
+      </contour>
+      <contour>
+        <pt x="378" y="263" on="1"/>
+        <pt x="382" y="207" on="1"/>
+        <pt x="88" y="172" on="1"/>
+        <pt x="86" y="126" on="1"/>
+        <pt x="161" y="74" on="1"/>
+        <pt x="383" y="134" on="1"/>
+        <pt x="389" y="71" on="1"/>
+        <pt x="168" y="-12" on="1"/>
+        <pt x="29" y="22" on="1"/>
+        <pt x="26" y="240" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="dotabovecomb" xMin="-37" yMin="501" xMax="50" yMax="597">
+      <contour>
+        <pt x="-21" y="597" on="1"/>
+        <pt x="50" y="589" on="1"/>
+        <pt x="41" y="501" on="1"/>
+        <pt x="-37" y="503" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="e" xMin="40" yMin="-18" xMax="576" yMax="513">
+      <contour>
+        <pt x="127" y="228" on="1"/>
+        <pt x="125" y="298" on="1"/>
+        <pt x="480" y="292" on="1"/>
+        <pt x="317" y="416" on="1"/>
+        <pt x="147" y="263" on="1"/>
+        <pt x="229" y="75" on="1"/>
+        <pt x="509" y="129" on="1"/>
+        <pt x="526" y="45" on="1"/>
+        <pt x="188" y="-18" on="1"/>
+        <pt x="40" y="261" on="1"/>
+        <pt x="316" y="513" on="1"/>
+        <pt x="571" y="305" on="1"/>
+        <pt x="576" y="226" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+    <TTGlyph name="edotabove" xMin="40" yMin="-18" xMax="576" yMax="693">
+      <component glyphName="e" x="0" y="0" flags="0x204"/>
+      <component glyphName="dotabovecomb" x="313" y="96" flags="0x4"/>
+    </TTGlyph>
+
+    <TTGlyph name="s" xMin="25" yMin="-13" xMax="582" yMax="530">
+      <contour>
+        <pt x="559" y="459" on="1"/>
+        <pt x="539" y="376" on="1"/>
+        <pt x="326" y="442" on="1"/>
+        <pt x="213" y="366" on="1"/>
+        <pt x="582" y="174" on="1"/>
+        <pt x="304" y="-13" on="1"/>
+        <pt x="25" y="83" on="1"/>
+        <pt x="53" y="174" on="1"/>
+        <pt x="282" y="76" on="1"/>
+        <pt x="427" y="155" on="1"/>
+        <pt x="38" y="343" on="1"/>
+        <pt x="324" y="530" on="1"/>
+      </contour>
+      <instructions/>
+    </TTGlyph>
+
+  </glyf>
+
+  <name>
+    <namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Weight
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Layer Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
+      0.000;NONE;LayerFont-Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Layer Font Regular
+    </namerecord>
+    <namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
+      Version 0.000
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      LayerFont-Regular
+    </namerecord>
+    <namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
+      Weight
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="2.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+    <psNames>
+      <!-- This file uses unique glyph names based on the information
+           found in the 'post' table. Since these names might not be unique,
+           we have to invent artificial names in case of clashes. In order to
+           be able to retain the original information, we need a name to
+           ps name mapping for those cases where they differ. That's what
+           you see below.
+            -->
+    </psNames>
+    <extraNames>
+      <!-- following are the name that are not taken from the standard Mac glyph order -->
+      <psName name="dotabovecomb"/>
+      <psName name="edotabove"/>
+    </extraNames>
+  </post>
+
+  <GDEF>
+    <Version value="0x00010003"/>
+    <GlyphClassDef Format="1">
+      <ClassDef glyph="dotabovecomb" class="3"/>
+      <ClassDef glyph="e" class="1"/>
+    </GlyphClassDef>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=1 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=2 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="0"/>
+        <Item index="0" value="[1]"/>
+        <Item index="1" value="[88]"/>
+      </VarData>
+    </VarStore>
+  </GDEF>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="mark"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <MarkBasePos index="0" Format="1">
+          <MarkCoverage Format="1">
+            <Glyph value="dotabovecomb"/>
+          </MarkCoverage>
+          <BaseCoverage Format="1">
+            <Glyph value="e"/>
+          </BaseCoverage>
+          <!-- ClassCount=1 -->
+          <MarkArray>
+            <!-- MarkCount=1 -->
+            <MarkRecord index="0">
+              <Class value="0"/>
+              <MarkAnchor Format="1">
+                <XCoordinate value="-2"/>
+                <YCoordinate value="465"/>
+              </MarkAnchor>
+            </MarkRecord>
+          </MarkArray>
+          <BaseArray>
+            <!-- BaseCount=1 -->
+            <BaseRecord index="0">
+              <BaseAnchor index="0" Format="3">
+                <XCoordinate value="314"/>
+                <YCoordinate value="556"/>
+                <XDeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="0"/>
+                  <DeltaFormat value="32768"/>
+                </XDeviceTable>
+                <YDeviceTable>
+                  <StartSize value="0"/>
+                  <EndSize value="1"/>
+                  <DeltaFormat value="32768"/>
+                </YDeviceTable>
+              </BaseAnchor>
+            </BaseRecord>
+          </BaseArray>
+        </MarkBasePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="liga"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <LigatureSubst index="0" Format="1">
+          <LigatureSet glyph="a">
+            <Ligature components="e,s,s" glyph="s"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+  <HVAR>
+    <Version value="0x00010000"/>
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=3 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="0.36365"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="1">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.36365"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+        <Region index="2">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=1 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=0 -->
+        <Item index="0" value="[]"/>
+      </VarData>
+    </VarStore>
+    <AdvWidthMap>
+      <Map glyph=".notdef" outer="0" inner="0"/>
+      <Map glyph="a" outer="0" inner="0"/>
+      <Map glyph="dotabovecomb" outer="0" inner="0"/>
+      <Map glyph="e" outer="0" inner="0"/>
+      <Map glyph="edotabove" outer="0" inner="0"/>
+      <Map glyph="s" outer="0" inner="0"/>
+    </AdvWidthMap>
+  </HVAR>
+
+  <MVAR>
+    <Version value="0x00010000"/>
+    <Reserved value="0"/>
+    <ValueRecordSize value="8"/>
+    <!-- ValueRecordCount=1 -->
+    <VarStore Format="1">
+      <Format value="1"/>
+      <VarRegionList>
+        <!-- RegionAxisCount=1 -->
+        <!-- RegionCount=1 -->
+        <Region index="0">
+          <VarRegionAxis index="0">
+            <StartCoord value="0.0"/>
+            <PeakCoord value="1.0"/>
+            <EndCoord value="1.0"/>
+          </VarRegionAxis>
+        </Region>
+      </VarRegionList>
+      <!-- VarDataCount=1 -->
+      <VarData index="0">
+        <!-- ItemCount=1 -->
+        <NumShorts value="0"/>
+        <!-- VarRegionCount=1 -->
+        <VarRegionIndex index="0" value="0"/>
+        <Item index="0" value="[-25]"/>
+      </VarData>
+    </VarStore>
+    <ValueRecord index="0">
+      <ValueTag value="undo"/>
+      <VarIdx value="0"/>
+    </ValueRecord>
+  </MVAR>
+
+  <STAT>
+    <Version value="0x00010001"/>
+    <DesignAxisRecordSize value="8"/>
+    <!-- DesignAxisCount=1 -->
+    <DesignAxisRecord>
+      <Axis index="0">
+        <AxisTag value="wght"/>
+        <AxisNameID value="256"/>  <!-- Weight -->
+        <AxisOrdering value="0"/>
+      </Axis>
+    </DesignAxisRecord>
+    <!-- AxisValueCount=0 -->
+    <ElidedFallbackNameID value="2"/>  <!-- Regular -->
+  </STAT>
+
+  <fvar>
+
+    <!-- Weight -->
+    <Axis>
+      <AxisTag>wght</AxisTag>
+      <Flags>0x0</Flags>
+      <MinValue>350.0</MinValue>
+      <DefaultValue>350.0</DefaultValue>
+      <MaxValue>625.0</MaxValue>
+      <AxisNameID>256</AxisNameID>
+    </Axis>
+  </fvar>
+
+  <gvar>
+    <version value="1"/>
+    <reserved value="0"/>
+    <glyphVariations glyph="a">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="-59" y="2"/>
+        <delta pt="2" x="-59" y="-54"/>
+        <delta pt="3" x="0" y="-56"/>
+        <delta pt="4" x="0" y="-56"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="-1" y="-23"/>
+        <delta pt="10" x="77" y="7"/>
+        <delta pt="11" x="77" y="7"/>
+        <delta pt="12" x="40" y="28"/>
+        <delta pt="13" x="0" y="15"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+        <delta pt="17" x="0" y="0"/>
+        <delta pt="18" x="0" y="0"/>
+        <delta pt="19" x="0" y="0"/>
+        <delta pt="20" x="0" y="0"/>
+        <delta pt="21" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="e">
+      <tuple>
+        <coord axis="wght" max="1.0" min="0.0" value="0.36365"/>
+        <delta pt="0" x="-1" y="-25"/>
+        <delta pt="1" x="0" y="0"/>
+        <delta pt="2" x="-84" y="5"/>
+        <delta pt="3" x="1" y="-29"/>
+        <delta pt="4" x="33" y="1"/>
+        <delta pt="5" x="35" y="41"/>
+        <delta pt="6" x="-2" y="28"/>
+        <delta pt="7" x="0" y="0"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="0" y="0"/>
+        <delta pt="10" x="0" y="0"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="-27"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="0"/>
+        <delta pt="16" x="0" y="0"/>
+      </tuple>
+      <tuple>
+        <coord axis="wght" max="1.0" min="0.36365" value="1.0"/>
+        <delta pt="0" x="70" y="1"/>
+        <delta pt="1" x="70" y="1"/>
+        <delta pt="2" x="-76" y="1"/>
+        <delta pt="3" x="-16" y="-56"/>
+        <delta pt="4" x="70" y="1"/>
+        <delta pt="5" x="15" y="55"/>
+        <delta pt="6" x="15" y="55"/>
+        <delta pt="7" x="2" y="-45"/>
+        <delta pt="8" x="0" y="0"/>
+        <delta pt="9" x="-31" y="1"/>
+        <delta pt="10" x="-2" y="35"/>
+        <delta pt="11" x="25" y="-1"/>
+        <delta pt="12" x="25" y="-1"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="35"/>
+        <delta pt="16" x="0" y="0"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="s">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="0" y="0"/>
+        <delta pt="1" x="-2" y="-40"/>
+        <delta pt="2" x="-2" y="-40"/>
+        <delta pt="3" x="55" y="-9"/>
+        <delta pt="4" x="26" y="-33"/>
+        <delta pt="5" x="-20" y="-45"/>
+        <delta pt="6" x="-18" y="-4"/>
+        <delta pt="7" x="-27" y="52"/>
+        <delta pt="8" x="-61" y="43"/>
+        <delta pt="9" x="-80" y="-6"/>
+        <delta pt="10" x="-22" y="55"/>
+        <delta pt="11" x="0" y="0"/>
+        <delta pt="12" x="0" y="0"/>
+        <delta pt="13" x="0" y="0"/>
+        <delta pt="14" x="0" y="0"/>
+        <delta pt="15" x="0" y="45"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="dotabovecomb">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="0" x="-8" y="28"/>
+        <delta pt="1" x="13" y="16"/>
+        <delta pt="2" x="17" y="-13"/>
+        <delta pt="3" x="-27" y="-20"/>
+        <delta pt="4" x="0" y="0"/>
+        <delta pt="5" x="0" y="0"/>
+        <delta pt="6" x="0" y="28"/>
+        <delta pt="7" x="0" y="18"/>
+      </tuple>
+    </glyphVariations>
+    <glyphVariations glyph="edotabove">
+      <tuple>
+        <coord axis="wght" value="1.0"/>
+        <delta pt="1" x="-6" y="91"/>
+        <delta pt="4" x="0" y="119"/>
+      </tuple>
+    </glyphVariations>
+  </gvar>
+
+</ttFont>
diff --git a/Tests/varLib/varLib_test.py b/Tests/varLib/varLib_test.py
index 6638ea3..831d8b8 100644
--- a/Tests/varLib/varLib_test.py
+++ b/Tests/varLib/varLib_test.py
@@ -290,7 +290,7 @@
         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):
+    def test_varlib_build_from_ds_object_in_memory_ttfonts(self):
         ds_path = self.get_test_input("Build.designspace")
         ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf")
         expected_ttx_path = self.get_test_output("BuildMain.ttx")
@@ -314,6 +314,53 @@
         tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"]
         self.expect_ttx(varfont, expected_ttx_path, tables)
 
+    def test_varlib_build_from_ttf_paths(self):
+        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:
+            source.path = os.path.join(
+                self.tempdir, os.path.basename(source.filename).replace(".ufo", ".ttf")
+            )
+        ds.updatePaths()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+        tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
+    def test_varlib_build_from_ttx_paths(self):
+        ds_path = self.get_test_input("Build.designspace")
+        ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf")
+        expected_ttx_path = self.get_test_output("BuildMain.ttx")
+
+        ds = DesignSpaceDocument.fromfile(ds_path)
+        for source in ds.sources:
+            source.path = os.path.join(
+                ttx_dir, os.path.basename(source.filename).replace(".ufo", ".ttx")
+            )
+        ds.updatePaths()
+
+        varfont, _, _ = build(ds)
+        varfont = reload_font(varfont)
+        tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
+    def test_varlib_build_sparse_masters(self):
+        ds_path = self.get_test_input("SparseMasters.designspace")
+        expected_ttx_path = self.get_test_output("SparseMasters.ttx")
+
+        varfont, _, _ = build(ds_path)
+        varfont = reload_font(varfont)
+        tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"]
+        self.expect_ttx(varfont, expected_ttx_path, tables)
+
 
 def test_load_masters_layerName_without_required_font():
     ds = DesignSpaceDocument()
diff --git a/Tests/voltLib/parser_test.py b/Tests/voltLib/parser_test.py
index 6baf900..51a65fc 100644
--- a/Tests/voltLib/parser_test.py
+++ b/Tests/voltLib/parser_test.py
@@ -1,5 +1,6 @@
 from __future__ import print_function, division, absolute_import
 from __future__ import unicode_literals
+from fontTools.voltLib import ast
 from fontTools.voltLib.error import VoltLibError
 from fontTools.voltLib.parser import Parser
 from io import open
@@ -76,6 +77,22 @@
                           def_glyph.type, def_glyph.components),
                          ("f_f", 320, None, "LIGATURE", 2))
 
+    def test_def_glyph_mark(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH "brevecomb" ID 320 TYPE MARK END_GLYPH'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         ("brevecomb", 320, None, "MARK", None))
+
+    def test_def_glyph_component(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH "f.f_f" ID 320 TYPE COMPONENT END_GLYPH'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         ("f.f_f", 320, None, "COMPONENT", None))
+
     def test_def_glyph_no_type(self):
         [def_glyph] = self.parse(
             'DEF_GLYPH "glyph20" ID 20 END_GLYPH'
@@ -106,14 +123,14 @@
             'GLYPH "aogonek" GLYPH "aring" GLYPH "atilde" END_ENUM\n'
             'END_GROUP\n'
         ).statements
-        self.assertEqual((def_group.name, def_group.enum),
+        self.assertEqual((def_group.name, def_group.enum.glyphSet()),
                          ("aaccented",
                           ("aacute", "abreve", "acircumflex", "adieresis",
                            "ae", "agrave", "amacron", "aogonek", "aring",
                            "atilde")))
 
     def test_def_group_groups(self):
-        [group1, group2, test_group] = self.parse(
+        parser = self.parser(
             'DEF_GROUP "Group1"\n'
             'ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n'
             'END_GROUP\n'
@@ -123,14 +140,16 @@
             'DEF_GROUP "TestGroup"\n'
             'ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n'
             'END_GROUP\n'
-        ).statements
+        )
+        [group1, group2, test_group] = parser.parse().statements
         self.assertEqual(
             (test_group.name, test_group.enum),
             ("TestGroup",
-             (("Group1",), ("Group2",))))
+             ast.Enum([ast.GroupName("Group1", parser),
+                       ast.GroupName("Group2", parser)])))
 
     def test_def_group_groups_not_yet_defined(self):
-        [group1, test_group1, test_group2, test_group3, group2] = self.parse(
+        parser = self.parser(
             'DEF_GROUP "Group1"\n'
             'ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n'
             'END_GROUP\n'
@@ -146,19 +165,23 @@
             'DEF_GROUP "Group2"\n'
             'ENUM GLYPH "e" GLYPH "f" GLYPH "g" GLYPH "h" END_ENUM\n'
             'END_GROUP\n'
-        ).statements
+        )
+        [group1, test_group1, test_group2, test_group3, group2] = \
+                parser.parse().statements
         self.assertEqual(
             (test_group1.name, test_group1.enum),
             ("TestGroup1",
-             (("Group1", ), ("Group2", ))))
+             ast.Enum([ast.GroupName("Group1", parser),
+                       ast.GroupName("Group2", parser)])))
         self.assertEqual(
             (test_group2.name, test_group2.enum),
             ("TestGroup2",
-             (("Group2", ), )))
+             ast.Enum([ast.GroupName("Group2", parser)])))
         self.assertEqual(
             (test_group3.name, test_group3.enum),
             ("TestGroup3",
-             (("Group2", ), ("Group1", ))))
+             ast.Enum([ast.GroupName("Group2", parser),
+                       ast.GroupName("Group1", parser)])))
 
     # def test_def_group_groups_undefined(self):
     #     with self.assertRaisesRegex(
@@ -184,20 +207,30 @@
             'ENUM GLYPH "a" GROUP "aaccented" END_ENUM\n'
             'END_GROUP'
         ).statements
-        self.assertEqual((def_group2.name, def_group2.enum),
-                         ("KERN_lc_a_2ND",
-                          ("a", ("aaccented", ))))
+        items = def_group2.enum.enum
+        self.assertEqual((def_group2.name, items[0].glyphSet(), items[1].group),
+                         ("KERN_lc_a_2ND", ("a",), "aaccented"))
 
     def test_def_group_range(self):
-        [def_group] = self.parse(
+        def_group = self.parse(
+            'DEF_GLYPH "a" ID 163 UNICODE 97 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "agrave" ID 194 UNICODE 224 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "aacute" ID 195 UNICODE 225 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "acircumflex" ID 196 UNICODE 226 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "atilde" ID 197 UNICODE 227 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "c" ID 165 UNICODE 99 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "ccaron" ID 209 UNICODE 269 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "ccedilla" ID 210 UNICODE 231 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "cdotaccent" ID 210 UNICODE 267 TYPE BASE END_GLYPH\n'
             'DEF_GROUP "KERN_lc_a_2ND"\n'
             'ENUM RANGE "a" TO "atilde" GLYPH "b" RANGE "c" TO "cdotaccent" '
             'END_ENUM\n'
             'END_GROUP'
-        ).statements
-        self.assertEqual((def_group.name, def_group.enum),
+        ).statements[-1]
+        self.assertEqual((def_group.name, def_group.enum.glyphSet()),
                          ("KERN_lc_a_2ND",
-                          (("a", "atilde"), "b", ("c", "cdotaccent"))))
+                          ("a", "agrave", "aacute", "acircumflex", "atilde",
+                           "b", "c", "ccaron", "ccedilla", "cdotaccent")))
 
     def test_group_duplicate(self):
         self.assertRaisesRegex(
@@ -433,10 +466,10 @@
     def test_lookup_name_starts_with_letter(self):
         with self.assertRaisesRegex(
             VoltLibError,
-            'Lookup name "\\\lookupname" must start with a letter'
+            r'Lookup name "\\lookupname" must start with a letter'
         ):
             [lookup] = self.parse(
-                'DEF_LOOKUP "\lookupname"\n'
+                'DEF_LOOKUP "\\lookupname"\n'
                 'AS_SUBSTITUTION\n'
                 'SUB GLYPH "a"\n'
                 'WITH GLYPH "a.alt"\n'
@@ -524,10 +557,11 @@
             'END_SUBSTITUTION'
         ).statements
         self.assertEqual((lookup.name, list(lookup.sub.mapping.items())),
-                         ("smcp", [(("a",), ("a.sc",)), (("b",), ("b.sc",))]))
+                         ("smcp", [(self.enum(["a"]), self.enum(["a.sc"])),
+                                   (self.enum(["b"]), self.enum(["b.sc"]))]))
 
     def test_substitution_single_in_context(self):
-        [group, lookup] = self.parse(
+        parser = self.parser(
             'DEF_GROUP "Denominators" ENUM GLYPH "one.dnom" GLYPH "two.dnom" '
             'END_ENUM END_GROUP\n'
             'DEF_LOOKUP "fracdnom" PROCESS_BASE PROCESS_MARKS ALL '
@@ -543,17 +577,22 @@
             'WITH GLYPH "two.dnom"\n'
             'END_SUB\n'
             'END_SUBSTITUTION'
-        ).statements
+        )
+        [group, lookup] = parser.parse().statements
         context = lookup.context[0]
         self.assertEqual(
             (lookup.name, list(lookup.sub.mapping.items()),
              context.ex_or_in, context.left, context.right),
-            ("fracdnom", [(("one",), ("one.dnom",)), (("two",), ("two.dnom",))],
-             "IN_CONTEXT", [((("Denominators",), "fraction"),)], [])
+            ("fracdnom",
+            [(self.enum(["one"]), self.enum(["one.dnom"])),
+             (self.enum(["two"]), self.enum(["two.dnom"]))],
+             "IN_CONTEXT", [ast.Enum([
+                            ast.GroupName("Denominators", parser=parser),
+                            ast.GlyphName("fraction")])], [])
         )
 
     def test_substitution_single_in_contexts(self):
-        [group, lookup] = self.parse(
+        parser = self.parser(
             'DEF_GROUP "Hebrew" ENUM GLYPH "uni05D0" GLYPH "uni05D1" '
             'END_ENUM END_GROUP\n'
             'DEF_LOOKUP "HebrewCurrency" PROCESS_BASE PROCESS_MARKS ALL '
@@ -571,7 +610,8 @@
             'WITH GLYPH "dollar.Hebr"\n'
             'END_SUB\n'
             'END_SUBSTITUTION'
-        ).statements
+        )
+        [group, lookup] = parser.parse().statements
         context1 = lookup.context[0]
         context2 = lookup.context[1]
         self.assertEqual(
@@ -579,8 +619,10 @@
              context1.right, context2.ex_or_in,
              context2.left, context2.right),
             ("HebrewCurrency", "IN_CONTEXT", [],
-             [(("Hebrew",),), ("one.Hebr",)], "IN_CONTEXT",
-             [(("Hebrew",),), ("one.Hebr",)], []))
+             [ast.Enum([ast.GroupName("Hebrew", parser)]),
+              self.enum(["one.Hebr"])], "IN_CONTEXT",
+             [ast.Enum([ast.GroupName("Hebrew", parser)]),
+              self.enum(["one.Hebr"])], []))
 
     def test_substitution_skip_base(self):
         [group, lookup] = self.parse(
@@ -596,9 +638,8 @@
             'END_SUB\n'
             'END_SUBSTITUTION'
         ).statements
-        process_base = lookup.process_base
         self.assertEqual(
-            (lookup.name, process_base),
+            (lookup.name, lookup.process_base),
             ("SomeSub", False))
 
     def test_substitution_process_base(self):
@@ -615,9 +656,8 @@
             'END_SUB\n'
             'END_SUBSTITUTION'
         ).statements
-        process_base = lookup.process_base
         self.assertEqual(
-            (lookup.name, process_base),
+            (lookup.name, lookup.process_base),
             ("SomeSub", True))
 
     def test_substitution_skip_marks(self):
@@ -634,12 +674,11 @@
             'END_SUB\n'
             'END_SUBSTITUTION'
         ).statements
-        process_marks = lookup.process_marks
         self.assertEqual(
-            (lookup.name, process_marks),
+            (lookup.name, lookup.process_marks),
             ("SomeSub", False))
 
-    def test_substitution_process_marks(self):
+    def test_substitution_mark_attachment(self):
         [group, lookup] = self.parse(
             'DEF_GROUP "SomeMarks" ENUM GLYPH "acutecmb" GLYPH "gravecmb" '
             'END_ENUM END_GROUP\n'
@@ -652,9 +691,25 @@
             'END_SUB\n'
             'END_SUBSTITUTION'
         ).statements
-        process_marks = lookup.process_marks
         self.assertEqual(
-            (lookup.name, process_marks),
+            (lookup.name, lookup.process_marks),
+            ("SomeSub", "SomeMarks"))
+
+    def test_substitution_mark_glyph_set(self):
+        [group, lookup] = self.parse(
+            'DEF_GROUP "SomeMarks" ENUM GLYPH "acutecmb" GLYPH "gravecmb" '
+            'END_ENUM END_GROUP\n'
+            'DEF_LOOKUP "SomeSub" PROCESS_BASE '
+            'PROCESS_MARKS MARK_GLYPH_SET "SomeMarks" \n'
+            'DIRECTION RTL\n'
+            'AS_SUBSTITUTION\n'
+            'SUB GLYPH "A"\n'
+            'WITH GLYPH "A.c2sc"\n'
+            'END_SUB\n'
+            'END_SUBSTITUTION'
+        ).statements
+        self.assertEqual(
+            (lookup.name, lookup.mark_glyph_set),
             ("SomeSub", "SomeMarks"))
 
     def test_substitution_process_all_marks(self):
@@ -670,9 +725,8 @@
             'END_SUB\n'
             'END_SUBSTITUTION'
         ).statements
-        process_marks = lookup.process_marks
         self.assertEqual(
-            (lookup.name, process_marks),
+            (lookup.name, lookup.process_marks),
             ("SomeSub", True))
 
     def test_substitution_no_reversal(self):
@@ -695,7 +749,13 @@
         )
 
     def test_substitution_reversal(self):
-        [lookup] = self.parse(
+        lookup = self.parse(
+            'DEF_GROUP "DFLT_Num_standardFigures"\n'
+            'ENUM GLYPH "zero" GLYPH "one" GLYPH "two" END_ENUM\n'
+            'END_GROUP\n'
+            'DEF_GROUP "DFLT_Num_numerators"\n'
+            'ENUM GLYPH "zero.numr" GLYPH "one.numr" GLYPH "two.numr" END_ENUM\n'
+            'END_GROUP\n'
             'DEF_LOOKUP "RevLookup" PROCESS_BASE PROCESS_MARKS ALL '
             'DIRECTION LTR REVERSAL\n'
             'IN_CONTEXT\n'
@@ -706,7 +766,7 @@
             'WITH GROUP "DFLT_Num_numerators"\n'
             'END_SUB\n'
             'END_SUBSTITUTION'
-        ).statements
+        ).statements[-1]
         self.assertEqual(
             (lookup.name, lookup.reversal),
             ("RevLookup", True)
@@ -729,8 +789,8 @@
         ).statements
         self.assertEqual((lookup.name, list(lookup.sub.mapping.items())),
                          ("ccmp",
-                          [(("aacute",), ("a", "acutecomb")),
-                           (("agrave",), ("a", "gravecomb"))]
+                          [(self.enum(["aacute"]), self.enum(["a", "acutecomb"])),
+                           (self.enum(["agrave"]), self.enum(["a", "gravecomb"]))]
                           ))
 
     def test_substitution_multiple_to_single(self):
@@ -750,11 +810,31 @@
         ).statements
         self.assertEqual((lookup.name, list(lookup.sub.mapping.items())),
                          ("liga",
-                          [(("f", "i"), ("f_i",)),
-                           (("f", "t"), ("f_t",))]))
+                          [(self.enum(["f", "i"]), self.enum(["f_i"])),
+                           (self.enum(["f", "t"]), self.enum(["f_t"]))]))
 
     def test_substitution_reverse_chaining_single(self):
-        [lookup] = self.parse(
+        parser = self.parser(
+            'DEF_GLYPH "zero" ID 1 UNICODE 48 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "one" ID 2 UNICODE 49 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "two" ID 3 UNICODE 50 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "three" ID 4 UNICODE 51 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "four" ID 5 UNICODE 52 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "five" ID 6 UNICODE 53 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "six" ID 7 UNICODE 54 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "seven" ID 8 UNICODE 55 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "eight" ID 9 UNICODE 56 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "nine" ID 10 UNICODE 57 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "zero.numr" ID 11 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "one.numr" ID 12 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "two.numr" ID 13 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "three.numr" ID 14 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "four.numr" ID 15 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "five.numr" ID 16 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "six.numr" ID 17 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "seven.numr" ID 18 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "eight.numr" ID 19 TYPE BASE END_GLYPH\n'
+            'DEF_GLYPH "nine.numr" ID 20 TYPE BASE END_GLYPH\n'
             'DEF_LOOKUP "numr" PROCESS_BASE PROCESS_MARKS ALL '
             'DIRECTION LTR REVERSAL\n'
             'IN_CONTEXT\n'
@@ -768,12 +848,16 @@
             'WITH RANGE "zero.numr" TO "nine.numr"\n'
             'END_SUB\n'
             'END_SUBSTITUTION'
-        ).statements
+        )
+        lookup = parser.parse().statements[-1]
         self.assertEqual(
             (lookup.name, lookup.context[0].right,
              list(lookup.sub.mapping.items())),
-            ("numr", [(("fraction", ("zero.numr", "nine.numr")),)],
-             [((("zero", "nine"),), (("zero.numr", "nine.numr"),))]))
+            ("numr",
+             [(ast.Enum([ast.GlyphName("fraction"),
+                         ast.Range("zero.numr", "nine.numr", parser)]))],
+             [(ast.Enum([ast.Range("zero", "nine", parser)]),
+               ast.Enum([ast.Range("zero.numr", "nine.numr", parser)]))]))
 
     # GPOS
     #  ATTACH_CURSIVE
@@ -817,8 +901,9 @@
         ).statements
         self.assertEqual(
             (lookup.name, lookup.pos.coverage, lookup.pos.coverage_to),
-            ("anchor_top", ("a", "e"), [(("acutecomb",), "top"),
-                                        (("gravecomb",), "top")])
+            ("anchor_top", self.enum(["a", "e"]),
+             [(self.enum(["acutecomb"]), "top"),
+              (self.enum(["gravecomb"]), "top")])
         )
         self.assertEqual(
             (anchor1.name, anchor1.gid, anchor1.glyph_name, anchor1.component,
@@ -858,7 +943,7 @@
             (lookup.name,
              lookup.pos.coverages_exit, lookup.pos.coverages_enter),
             ("SomeLookup",
-             [("a", "b")], [("c",)])
+             [self.enum(["a", "b"])], [self.enum(["c"])])
         )
 
     def test_position_adjust_pair(self):
@@ -879,7 +964,7 @@
         self.assertEqual(
             (lookup.name, lookup.pos.coverages_1, lookup.pos.coverages_2,
              lookup.pos.adjust_pair),
-            ("kern1", [("A",)], [("V",)],
+            ("kern1", [self.enum(["A"])], [self.enum(["V"])],
              {(1, 2): ((-30, None, None, {}, {}, {}),
                        (None, None, None, {}, {}, {})),
               (2, 1): ((-30, None, None, {}, {}, {}),
@@ -904,8 +989,8 @@
         self.assertEqual(
             (lookup.name, lookup.pos.adjust_single),
             ("TestLookup",
-             [(("glyph1",), (0, 123, None, {}, {}, {})),
-              (("glyph2",), (0, 456, None, {}, {}, {}))])
+             [(self.enum(["glyph1"]), (0, 123, None, {}, {}, {})),
+              (self.enum(["glyph2"]), (0, 456, None, {}, {}, {}))])
         )
 
     def test_def_anchor(self):
@@ -936,6 +1021,22 @@
              False, (None, 250, 0, {}, {}, {}))
         )
 
+    def test_def_anchor_multi_component(self):
+        [anchor1, anchor2] = self.parse(
+            'DEF_ANCHOR "top" ON 120 GLYPH a '
+            'COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
+            'DEF_ANCHOR "top" ON 120 GLYPH a '
+            'COMPONENT 2 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
+        ).statements
+        self.assertEqual(
+            (anchor1.name, anchor1.gid, anchor1.glyph_name, anchor1.component),
+            ("top", 120, "a", 1)
+        )
+        self.assertEqual(
+            (anchor2.name, anchor2.gid, anchor2.glyph_name, anchor2.component),
+            ("top", 120, "a", 2)
+        )
+
     def test_def_anchor_duplicate(self):
         self.assertRaisesRegex(
             VoltLibError,
@@ -1012,6 +1113,14 @@
              ("CMAP_FORMAT", (3, 1, 4)))
         )
 
+    def test_stop_at_end(self):
+        [def_glyph] = self.parse(
+            'DEF_GLYPH ".notdef" ID 0 TYPE BASE END_GLYPH END\0\0\0\0'
+        ).statements
+        self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
+                          def_glyph.type, def_glyph.components),
+                         (".notdef", 0, None, "BASE", None))
+
     def setUp(self):
         self.tempdir = None
         self.num_tempfiles = 0
@@ -1020,14 +1129,20 @@
         if self.tempdir:
             shutil.rmtree(self.tempdir)
 
-    def parse(self, text):
+    def parser(self, text):
         if not self.tempdir:
             self.tempdir = tempfile.mkdtemp()
         self.num_tempfiles += 1
         path = os.path.join(self.tempdir, "tmp%d.vtp" % self.num_tempfiles)
         with open(path, "w") as outfile:
             outfile.write(text)
-        return Parser(path).parse()
+        return Parser(path)
+
+    def parse(self, text):
+        return self.parser(text).parse()
+
+    def enum(self, glyphs):
+        return ast.Enum([ast.GlyphName(g) for g in glyphs])
 
 if __name__ == "__main__":
     import sys
diff --git a/fonttools b/fonttools
old mode 100644
new mode 100755
diff --git a/requirements.txt b/requirements.txt
index c9ac4f3..b910fea 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,4 +6,4 @@
 scipy==1.2.0; platform_python_implementation != "PyPy"
 munkres==1.0.12; platform_python_implementation == "PyPy"
 zopfli==0.1.6
-fs==2.1.3
+fs==2.2.1
diff --git a/run-tests.sh b/run-tests.sh
old mode 100644
new mode 100755
diff --git a/setup.cfg b/setup.cfg
index 6ee2542..97d51ae 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 3.35.0
+current_version = 3.37.0
 commit = True
 tag = False
 tag_name = {new_version}
diff --git a/setup.py b/setup.py
old mode 100644
new mode 100755
index 25093cd..f22309d
--- a/setup.py
+++ b/setup.py
@@ -31,7 +31,7 @@
 extras_require = {
 	# for fontTools.ufoLib: to read/write UFO fonts
 	"ufo": [
-		"fs >= 2.1.1, < 3",
+		"fs >= 2.2.0, < 3",
 		"enum34 >= 1.1.6; python_version < '3.4'",
 	],
 	# for fontTools.misc.etree and fontTools.misc.plistlib: use lxml to
@@ -352,7 +352,7 @@
 
 setup(
 	name="fonttools",
-	version="3.35.0",
+	version="3.37.0",
 	description="Tools to manipulate font files",
 	author="Just van Rossum",
 	author_email="just@letterror.com",