Upgrade fonttools to 3.39.0

Release log:
https://github.com/fonttools/fonttools/releases/tag/3.39.0

Test: None
Change-Id: I1f44c46f9b97e91ad013d294a1b806b2e5980057
diff --git a/Doc/source/designspaceLib/readme.rst b/Doc/source/designspaceLib/readme.rst
index 68a7009..2594084 100644
--- a/Doc/source/designspaceLib/readme.rst
+++ b/Doc/source/designspaceLib/readme.rst
@@ -1044,9 +1044,24 @@
 -  Use ``documentObject.updateFilenameFromPath()`` to explicitly set the
    **filename** attributes for all instance and source descriptors.
 
-.. 7-this-document:
+.. 7-common-lib-key-registry:
 
-7 This document
+7 Common Lib Key Registry
+=========================
+
+public.skipExportGlyphs
+-----------------------
+
+This lib key works the same as the UFO lib key with the same name. The
+difference is that applications using a Designspace as the corner stone of the
+font compilation process should use the lib key in that Designspace instead of
+any of the UFOs. If the lib key is empty or not present in the Designspace, all
+glyphs should be exported, regardless of what the same lib key in any of the
+UFOs says.
+
+.. 8-this-document:
+
+8 This document
 ===============
 
 -  The package is rather new and changes are to be expected.
diff --git a/Doc/source/index.rst b/Doc/source/index.rst
index 67ec88e..b3d44cf 100644
--- a/Doc/source/index.rst
+++ b/Doc/source/index.rst
@@ -8,7 +8,6 @@
    agl
    cffLib
    designspaceLib/index
-   inspect
    encodings
    feaLib
    merge
diff --git a/Doc/source/inspect.rst b/Doc/source/inspect.rst
deleted file mode 100644
index e0f65c2..0000000
--- a/Doc/source/inspect.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-#######
-inspect
-#######
-
-.. automodule:: fontTools.inspect
-   :members:
-   :undoc-members:
diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py
index 99dc4e1..b0b9973 100644
--- a/Lib/fontTools/__init__.py
+++ b/Lib/fontTools/__init__.py
@@ -5,6 +5,6 @@
 
 log = logging.getLogger(__name__)
 
-version = __version__ = "3.38.0"
+version = __version__ = "3.39.0"
 
 __all__ = ["version", "log", "configLogger"]
diff --git a/Lib/fontTools/designspaceLib/__init__.py b/Lib/fontTools/designspaceLib/__init__.py
index b8b962f..4c5be8b 100644
--- a/Lib/fontTools/designspaceLib/__init__.py
+++ b/Lib/fontTools/designspaceLib/__init__.py
@@ -1128,11 +1128,14 @@
         self.rules.append(ruleDescriptor)
 
     def newDefaultLocation(self):
+        """Return default location in design space."""
         # Without OrderedDict, output XML would be non-deterministic.
         # https://github.com/LettError/designSpaceDocument/issues/10
         loc = collections.OrderedDict()
         for axisDescriptor in self.axes:
-            loc[axisDescriptor.name] = axisDescriptor.default
+            loc[axisDescriptor.name] = axisDescriptor.map_forward(
+                axisDescriptor.default
+            )
         return loc
 
     def updateFilenameFromPath(self, masters=True, instances=True, force=False):
@@ -1176,43 +1179,43 @@
         return None
 
     def findDefault(self):
-        # new default finder
-        # take the sourcedescriptor with the location at all the defaults
-        # if we can't find it, return None, let someone else figure it out
+        """Set and return SourceDescriptor at the default location or None.
+
+        The default location is the set of all `default` values in user space
+        of all axes.
+        """
         self.default = None
+
+        # Convert the default location from user space to design space before comparing
+        # it against the SourceDescriptor locations (always in design space).
+        default_location_design = {
+            axis.name: axis.map_forward(self.defaultLoc[axis.name])
+            for axis in self.axes
+        }
+
         for sourceDescriptor in self.sources:
-            if sourceDescriptor.location == self.defaultLoc:
-                # we choose you!
+            if sourceDescriptor.location == default_location_design:
                 self.default = sourceDescriptor
                 return sourceDescriptor
+
         return None
 
     def normalizeLocation(self, location):
-        # adapted from fontTools.varlib.models.normalizeLocation because:
-        #   - this needs to work with axis names, not tags
-        #   - this needs to accomodate anisotropic locations
-        #   - the axes are stored differently here, it's just math
+        from fontTools.varLib.models import normalizeValue
+
         new = {}
         for axis in self.axes:
             if axis.name not in location:
                 # skipping this dimension it seems
                 continue
-            v = location.get(axis.name, axis.default)
-            if type(v) == tuple:
-                v = v[0]
-            if v == axis.default:
-                v = 0.0
-            elif v < axis.default:
-                if axis.default == axis.minimum:
-                    v = 0.0
-                else:
-                    v = (max(v, axis.minimum) - axis.default) / (axis.default - axis.minimum)
-            else:
-                if axis.default == axis.maximum:
-                    v = 0.0
-                else:
-                    v = (min(v, axis.maximum) - axis.default) / (axis.maximum - axis.default)
-            new[axis.name] = v
+            value = location[axis.name]
+            # 'anisotropic' location, take first coord only
+            if isinstance(value, tuple):
+                value = value[0]
+            triple = [
+                axis.map_forward(v) for v in (axis.minimum, axis.default, axis.maximum)
+            ]
+            new[axis.name] = normalizeValue(value, triple)
         return new
 
     def normalize(self):
diff --git a/Lib/fontTools/encodings/codecs.py b/Lib/fontTools/encodings/codecs.py
index 30e4691..5e401f0 100644
--- a/Lib/fontTools/encodings/codecs.py
+++ b/Lib/fontTools/encodings/codecs.py
@@ -1,5 +1,5 @@
 """Extend the Python codecs module with a few encodings that are used in OpenType (name table)
-but missing from Python.  See https://github.com/behdad/fonttools/issues/236 for details."""
+but missing from Python.  See https://github.com/fonttools/fonttools/issues/236 for details."""
 
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
diff --git a/Lib/fontTools/feaLib/ast.py b/Lib/fontTools/feaLib/ast.py
index 7bf6498..39dc4bc 100644
--- a/Lib/fontTools/feaLib/ast.py
+++ b/Lib/fontTools/feaLib/ast.py
@@ -821,19 +821,20 @@
                                 markAttach, markFilter)
 
     def asFea(self, indent=""):
-        res = "lookupflag"
+        res = []
         flags = ["RightToLeft", "IgnoreBaseGlyphs", "IgnoreLigatures", "IgnoreMarks"]
         curr = 1
         for i in range(len(flags)):
             if self.value & curr != 0:
-                res += " " + flags[i]
+                res.append(flags[i])
             curr = curr << 1
         if self.markAttachment is not None:
-            res += " MarkAttachmentType {}".format(self.markAttachment.asFea())
+            res.append("MarkAttachmentType {}".format(self.markAttachment.asFea()))
         if self.markFilteringSet is not None:
-            res += " UseMarkFilteringSet {}".format(self.markFilteringSet.asFea())
-        res += ";"
-        return res
+            res.append("UseMarkFilteringSet {}".format(self.markFilteringSet.asFea()))
+        if not res:
+            res = ["0"]
+        return "lookupflag {};".format(" ".join(res))
 
 
 class LookupReferenceStatement(Statement):
@@ -905,20 +906,24 @@
 
 
 class MultipleSubstStatement(Statement):
-    def __init__(self, prefix, glyph, suffix, replacement, location=None):
+    def __init__(
+        self, prefix, glyph, suffix, replacement, forceChain=False, location=None
+    ):
         Statement.__init__(self, location)
         self.prefix, self.glyph, self.suffix = prefix, glyph, suffix
         self.replacement = replacement
+        self.forceChain = forceChain
 
     def build(self, builder):
         prefix = [p.glyphSet() for p in self.prefix]
         suffix = [s.glyphSet() for s in self.suffix]
         builder.add_multiple_subst(
-            self.location, prefix, self.glyph, suffix, self.replacement)
+            self.location, prefix, self.glyph, suffix, self.replacement,
+            self.forceChain)
 
     def asFea(self, indent=""):
         res = "sub "
-        if len(self.prefix) or len(self.suffix):
+        if len(self.prefix) or len(self.suffix) or self.forceChain:
             if len(self.prefix):
                 res += " ".join(map(asFea, self.prefix)) + " "
             res += asFea(self.glyph) + "'"
@@ -1088,7 +1093,7 @@
         builder.add_subtable_break(self.location)
 
     def asFea(self, indent=""):
-        return indent + "subtable;"
+        return "subtable;"
 
 
 class ValueRecord(Expression):
diff --git a/Lib/fontTools/feaLib/builder.py b/Lib/fontTools/feaLib/builder.py
index e521a0e..456ae3c 100644
--- a/Lib/fontTools/feaLib/builder.py
+++ b/Lib/fontTools/feaLib/builder.py
@@ -9,7 +9,7 @@
 from fontTools.otlLib import builder as otl
 from fontTools.ttLib import newTable, getTableModule
 from fontTools.ttLib.tables import otBase, otTables
-from collections import defaultdict
+from collections import defaultdict, OrderedDict
 import itertools
 import logging
 
@@ -545,7 +545,7 @@
         required_feature_indices = {}  # ('latn', 'DEU') --> 23
         scripts = {}  # 'latn' --> {'DEU': [23, 24]} for feature #23,24
         # Sort the feature table by feature tag:
-        # https://github.com/behdad/fonttools/issues/568
+        # https://github.com/fonttools/fonttools/issues/568
         sortFeatureTag = lambda f: (f[0][2], f[0][1], f[0][0], f[1])
         for key, lookups in sorted(self.features_.items(), key=sortFeatureTag):
             script, lang, feature_tag = key
@@ -881,8 +881,8 @@
             lookup.ligatures[g] = replacement
 
     def add_multiple_subst(self, location,
-                           prefix, glyph, suffix, replacements):
-        if prefix or suffix:
+                           prefix, glyph, suffix, replacements, forceChain=False):
+        if prefix or suffix or forceChain:
             chain = self.get_lookup_(location, ChainContextSubstBuilder)
             sub = self.get_chained_lookup_(location, MultipleSubstBuilder)
             sub.mapping[glyph] = replacements
@@ -918,19 +918,10 @@
                     location)
             lookup.mapping[from_glyph] = to_glyph
 
-    def find_chainable_SingleSubst_(self, chain, glyphs):
-        """Helper for add_single_subst_chained_()"""
-        for _, _, _, substitutions in chain.substitutions:
-            for sub in substitutions:
-                if (isinstance(sub, SingleSubstBuilder) and
-                        not any(g in glyphs for g in sub.mapping.keys())):
-                    return sub
-        return None
-
     def add_single_subst_chained_(self, location, prefix, suffix, mapping):
-        # https://github.com/behdad/fonttools/issues/512
+        # https://github.com/fonttools/fonttools/issues/512
         chain = self.get_lookup_(location, ChainContextSubstBuilder)
-        sub = self.find_chainable_SingleSubst_(chain, set(mapping.keys()))
+        sub = chain.find_chainable_single_subst(set(mapping.keys()))
         if sub is None:
             sub = self.get_chained_lookup_(location, SingleSubstBuilder)
         sub.mapping.update(mapping)
@@ -996,14 +987,7 @@
         lookup.addClassPair(location, glyphclass1, value1, glyphclass2, value2)
 
     def add_subtable_break(self, location):
-        if type(self.cur_lookup_) is not PairPosBuilder:
-            raise FeatureLibError(
-                'explicit "subtable" statement is intended for use with only '
-                "Pair Adjustment Positioning Format 2 (i.e. pair class kerning)",
-                location
-            )
-        lookup = self.get_lookup_(location, PairPosBuilder)
-        lookup.add_subtable_break(location)
+        self.cur_lookup_.add_subtable_break(location)
 
     def add_specific_pair_pos(self, location, glyph1, value1, glyph2, value2):
         lookup = self.get_lookup_(location, PairPosBuilder)
@@ -1018,28 +1002,19 @@
                 for glyph in glyphs:
                     lookup.add_pos(location, glyph, value)
 
-    def find_chainable_SinglePos_(self, lookups, glyphs, value):
-        """Helper for add_single_pos_chained_()"""
-        for look in lookups:
-            if all(look.can_add(glyph, value) for glyph in glyphs):
-                return look
-        return None
-
     def add_single_pos_chained_(self, location, prefix, suffix, pos):
         # https://github.com/fonttools/fonttools/issues/514
         chain = self.get_lookup_(location, ChainContextPosBuilder)
         targets = []
         for _, _, _, lookups in chain.rules:
-            for lookup in lookups:
-                if isinstance(lookup, SinglePosBuilder):
-                    targets.append(lookup)
+            targets.extend(lookups)
         subs = []
         for glyphs, value in pos:
             if value is None:
                 subs.append(None)
                 continue
             otValue, _ = makeOpenTypeValueRecord(value, pairPosContext=False)
-            sub = self.find_chainable_SinglePos_(targets, glyphs, otValue)
+            sub = chain.find_chainable_single_pos(targets, glyphs, otValue)
             if sub is None:
                 sub = self.get_chained_lookup_(location, SinglePosBuilder)
                 targets.append(sub)
@@ -1129,6 +1104,8 @@
 
 
 class LookupBuilder(object):
+    SUBTABLE_BREAK_ = "SUBTABLE_BREAK"
+
     def __init__(self, font, location, table, lookup_type):
         self.font = font
         self.glyphMap = font.getReverseGlyphMap()
@@ -1191,23 +1168,43 @@
             coverage = otl.buildCoverage(g, self.glyphMap)
             subtable.InputCoverage.append(coverage)
 
+    def build_subst_subtables(self, mapping, klass):
+        substitutions = [{}]
+        for key in mapping:
+            if key[0] == self.SUBTABLE_BREAK_:
+                substitutions.append({})
+            else:
+                substitutions[-1][key] = mapping[key]
+        subtables = [klass(s) for s in substitutions]
+        return subtables
+
+    def add_subtable_break(self, location):
+        log.warning(FeatureLibError(
+            'unsupported "subtable" statement for lookup type',
+            location
+        ))
+
 
 class AlternateSubstBuilder(LookupBuilder):
     def __init__(self, font, location):
         LookupBuilder.__init__(self, font, location, 'GSUB', 3)
-        self.alternates = {}
+        self.alternates = OrderedDict()
 
     def equals(self, other):
         return (LookupBuilder.equals(self, other) and
                 self.alternates == other.alternates)
 
     def build(self):
-        subtable = otl.buildAlternateSubstSubtable(self.alternates)
-        return self.buildLookup_([subtable])
+        subtables = self.build_subst_subtables(self.alternates,
+                otl.buildAlternateSubstSubtable)
+        return self.buildLookup_(subtables)
 
     def getAlternateGlyphs(self):
         return self.alternates
 
+    def add_subtable_break(self, location):
+        self.alternates[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
+
 
 class ChainContextPosBuilder(LookupBuilder):
     def __init__(self, font, location):
@@ -1221,6 +1218,8 @@
     def build(self):
         subtables = []
         for (prefix, glyphs, suffix, lookups) in self.rules:
+            if prefix == self.SUBTABLE_BREAK_:
+                continue
             st = otTables.ChainContextPos()
             subtables.append(st)
             st.Format = 3
@@ -1238,6 +1237,21 @@
                     st.PosLookupRecord.append(rec)
         return self.buildLookup_(subtables)
 
+    def find_chainable_single_pos(self, lookups, glyphs, value):
+        """Helper for add_single_pos_chained_()"""
+        res = None
+        for lookup in lookups[::-1]:
+            if lookup == self.SUBTABLE_BREAK_:
+                return res
+            if isinstance(lookup, SinglePosBuilder) and \
+                    all(lookup.can_add(glyph, value) for glyph in glyphs):
+                res = lookup
+        return res
+
+    def add_subtable_break(self, location):
+        self.rules.append((self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_,
+                           self.SUBTABLE_BREAK_, [self.SUBTABLE_BREAK_]))
+
 
 class ChainContextSubstBuilder(LookupBuilder):
     def __init__(self, font, location):
@@ -1251,6 +1265,8 @@
     def build(self):
         subtables = []
         for (prefix, input, suffix, lookups) in self.substitutions:
+            if prefix == self.SUBTABLE_BREAK_:
+                continue
             st = otTables.ChainContextSubst()
             subtables.append(st)
             st.Format = 3
@@ -1270,40 +1286,66 @@
 
     def getAlternateGlyphs(self):
         result = {}
-        for (_prefix, _input, _suffix, lookups) in self.substitutions:
+        for (_, _, _, lookups) in self.substitutions:
+            if lookups == self.SUBTABLE_BREAK_:
+                continue
             for lookup in lookups:
                 alts = lookup.getAlternateGlyphs()
                 for glyph, replacements in alts.items():
                     result.setdefault(glyph, set()).update(replacements)
         return result
 
+    def find_chainable_single_subst(self, glyphs):
+        """Helper for add_single_subst_chained_()"""
+        res = None
+        for _, _, _, substitutions in self.substitutions[::-1]:
+            if substitutions == self.SUBTABLE_BREAK_:
+                return res
+            for sub in substitutions:
+                if (isinstance(sub, SingleSubstBuilder) and
+                        not any(g in glyphs for g in sub.mapping.keys())):
+                    res = sub
+        return res
+
+    def add_subtable_break(self, location):
+        self.substitutions.append((self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_,
+                                   self.SUBTABLE_BREAK_, self.SUBTABLE_BREAK_))
+
 
 class LigatureSubstBuilder(LookupBuilder):
     def __init__(self, font, location):
         LookupBuilder.__init__(self, font, location, 'GSUB', 4)
-        self.ligatures = {}  # {('f','f','i'): 'f_f_i'}
+        self.ligatures = OrderedDict()  # {('f','f','i'): 'f_f_i'}
 
     def equals(self, other):
         return (LookupBuilder.equals(self, other) and
                 self.ligatures == other.ligatures)
 
     def build(self):
-        subtable = otl.buildLigatureSubstSubtable(self.ligatures)
-        return self.buildLookup_([subtable])
+        subtables = self.build_subst_subtables(self.ligatures,
+                otl.buildLigatureSubstSubtable)
+        return self.buildLookup_(subtables)
+
+    def add_subtable_break(self, location):
+        self.ligatures[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
 
 
 class MultipleSubstBuilder(LookupBuilder):
     def __init__(self, font, location):
         LookupBuilder.__init__(self, font, location, 'GSUB', 2)
-        self.mapping = {}
+        self.mapping = OrderedDict()
 
     def equals(self, other):
         return (LookupBuilder.equals(self, other) and
                 self.mapping == other.mapping)
 
     def build(self):
-        subtable = otl.buildMultipleSubstSubtable(self.mapping)
-        return self.buildLookup_([subtable])
+        subtables = self.build_subst_subtables(self.mapping,
+                otl.buildMultipleSubstSubtable)
+        return self.buildLookup_(subtables)
+
+    def add_subtable_break(self, location):
+        self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
 
 
 class CursivePosBuilder(LookupBuilder):
@@ -1440,23 +1482,31 @@
             subtables.append(st)
         return self.buildLookup_(subtables)
 
+    def add_subtable_break(self, location):
+        # Nothing to do here, each substitution is in its own subtable.
+        pass
+
 
 class SingleSubstBuilder(LookupBuilder):
     def __init__(self, font, location):
         LookupBuilder.__init__(self, font, location, 'GSUB', 1)
-        self.mapping = {}
+        self.mapping = OrderedDict()
 
     def equals(self, other):
         return (LookupBuilder.equals(self, other) and
                 self.mapping == other.mapping)
 
     def build(self):
-        subtable = otl.buildSingleSubstSubtable(self.mapping)
-        return self.buildLookup_([subtable])
+        subtables = self.build_subst_subtables(self.mapping,
+                otl.buildSingleSubstSubtable)
+        return self.buildLookup_(subtables)
 
     def getAlternateGlyphs(self):
         return {glyph: set([repl]) for glyph, repl in self.mapping.items()}
 
+    def add_subtable_break(self, location):
+        self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
+
 
 class ClassPairPosSubtableBuilder(object):
     def __init__(self, builder, valueFormat1, valueFormat2):
@@ -1501,8 +1551,6 @@
 
 
 class PairPosBuilder(LookupBuilder):
-    SUBTABLE_BREAK_ = "SUBTABLE_BREAK"
-
     def __init__(self, font, location):
         LookupBuilder.__init__(self, font, location, 'GPOS', 2)
         self.pairs = []  # [(gc1, value1, gc2, value2)*]
diff --git a/Lib/fontTools/feaLib/parser.py b/Lib/fontTools/feaLib/parser.py
index e497171..9edfc46 100644
--- a/Lib/fontTools/feaLib/parser.py
+++ b/Lib/fontTools/feaLib/parser.py
@@ -526,6 +526,7 @@
             return self.ast.LookupFlagStatement(value, location=location)
 
         # format A: "lookupflag RightToLeft MarkAttachmentType @M;"
+        value_seen = False
         value, markAttachment, markFilteringSet = 0, None, None
         flags = {
             "RightToLeft": 1, "IgnoreBaseGlyphs": 2,
@@ -545,12 +546,18 @@
                 self.expect_keyword_("UseMarkFilteringSet")
                 markFilteringSet = self.parse_class_name_()
             elif self.next_token_ in flags:
+                value_seen = True
                 value = value | flags[self.expect_name_()]
             else:
                 raise FeatureLibError(
                     '"%s" is not a recognized lookupflag' % self.next_token_,
                     self.next_token_location_)
         self.expect_symbol_(";")
+
+        if not any([value_seen, markAttachment, markFilteringSet]):
+            raise FeatureLibError(
+                'lookupflag must have a value', self.next_token_location_)
+
         return self.ast.LookupFlagStatement(value,
                                             markAttachment=markAttachment,
                                             markFilteringSet=markFilteringSet,
@@ -755,7 +762,8 @@
                 num_lookups == 0):
             return self.ast.MultipleSubstStatement(
                 old_prefix, tuple(old[0].glyphSet())[0], old_suffix,
-                tuple([list(n.glyphSet())[0] for n in new]), location=location)
+                tuple([list(n.glyphSet())[0] for n in new]),
+                forceChain=hasMarks, location=location)
 
         # GSUB lookup type 4: Ligature substitution.
         # Format: "substitute f f i by f_f_i;"
@@ -1442,7 +1450,7 @@
             if isinstance(s, self.ast.SingleSubstStatement):
                 has_single = not any([s.prefix, s.suffix, s.forceChain])
             elif isinstance(s, self.ast.MultipleSubstStatement):
-                has_multiple = not any([s.prefix, s.suffix])
+                has_multiple = not any([s.prefix, s.suffix, s.forceChain])
 
         # Upgrade all single substitutions to multiple substitutions.
         if has_single and has_multiple:
@@ -1451,7 +1459,7 @@
                     statements[i] = self.ast.MultipleSubstStatement(
                         s.prefix, s.glyphs[0].glyphSet()[0], s.suffix,
                         [r.glyphSet()[0] for r in s.replacements],
-                        location=s.location)
+                        s.forceChain, location=s.location)
 
     def is_cur_keyword_(self, k):
         if self.cur_token_type_ is Lexer.NAME:
diff --git a/Lib/fontTools/inspect.py b/Lib/fontTools/inspect.py
deleted file mode 100644
index bdacadf..0000000
--- a/Lib/fontTools/inspect.py
+++ /dev/null
@@ -1,271 +0,0 @@
-# Copyright 2013 Google, Inc. All Rights Reserved.
-#
-# Google Author(s): Behdad Esfahbod
-
-"""GUI font inspector.
-"""
-
-from __future__ import print_function, division, absolute_import
-from fontTools.misc.py23 import *
-from fontTools import misc, ttLib, cffLib
-try:
-    from gi import pygtkcompat
-except ImportError:
-    pygtkcompat = None
-
-if pygtkcompat is not None:
-    pygtkcompat.enable()
-    pygtkcompat.enable_gtk(version='3.0')
-import gtk
-import sys
-
-
-class Row(object):
-	def __init__(self, parent, index, key, value, font):
-		self._parent = parent
-		self._index = index
-		self._key = key
-		self._value = value
-		self._font = font
-
-		if isinstance(value, ttLib.TTFont):
-			self._add_font(value)
-			return
-
-		if not isinstance(value, basestring):
-			# Try sequences
-			is_sequence = True
-			try:
-				len(value)
-				iter(value)
-				# It's hard to differentiate list-type sequences
-				# from dict-type ones.  Try fetching item 0.
-				value[0]
-			except (TypeError, AttributeError, KeyError, IndexError):
-				is_sequence = False
-			if is_sequence:
-				self._add_list(key, value)
-				return
-		if hasattr(value, '__dict__'):
-			self._add_object(key, value)
-			return
-		if hasattr(value, 'items'):
-			self._add_dict(key, value)
-			return
-
-		if isinstance(value, basestring):
-			self._value_str = '"'+value+'"'
-			self._children = []
-			return
-
-		# Everything else
-		self._children = []
-
-	def _filter_items(self):
-		items = []
-		for k,v in self._items:
-			if isinstance(v, ttLib.TTFont):
-				continue
-			if k in ['reader', 'file', 'tableTag', 'compileStatus', 'recurse']:
-				continue
-			if isinstance(k, basestring) and k[0] == '_':
-				continue
-			items.append((k,v))
-		self._items = items
-
-	def _add_font(self, font):
-		self._items = [(tag,font[tag]) for tag in font.keys()]
-
-	def _add_object(self, key, value):
-		# Make sure item is decompiled
-		try:
-			value.asdf # Any better way?!
-		except (AttributeError, KeyError, TypeError, ttLib.TTLibError):
-			pass
-		if isinstance(value, ttLib.getTableModule('glyf').Glyph):
-			# Glyph type needs explicit expanding to be useful
-			value.expand(self._font['glyf'])
-		if isinstance(value, misc.psCharStrings.T2CharString):
-			try:
-				value.decompile()
-			except TypeError:  # Subroutines can't be decompiled
-				pass
-		if isinstance(value, cffLib.BaseDict):
-			for k in value.rawDict.keys():
-				getattr(value, k)
-		if isinstance(value, cffLib.Index):
-			# Load all items
-			for i in range(len(value)):
-				value[i]
-			# Discard offsets as should not be needed anymore
-			if hasattr(value, 'offsets'):
-				del value.offsets
-
-		self._value_str = value.__class__.__name__
-		if isinstance(value, ttLib.tables.DefaultTable.DefaultTable):
-			self._value_str += ' (%d Bytes)' % self._font.reader.tables[key].length
-		self._items = sorted(value.__dict__.items())
-		self._filter_items()
-
-	def _add_dict(self, key, value):
-		self._value_str = '%s of %d items' % (value.__class__.__name__, len(value))
-		self._items = sorted(value.items())
-
-	def _add_list(self, key, value):
-		if len(value) and len(value) <= 32:
-			self._value_str = str(value)
-		else:
-			self._value_str = '%s of %d items' % (value.__class__.__name__, len(value))
-		self._items = list(enumerate(value))
-
-	def __len__(self):
-		if hasattr(self, '_children'):
-			return len(self._children)
-		if hasattr(self, '_items'):
-			return len(self._items)
-		assert False
-
-	def _ensure_children(self):
-		if hasattr(self, '_children'):
-			return
-		children = []
-		for i,(k,v) in enumerate(self._items):
-			children.append(Row(self, i, k, v, self._font))
-		self._children = children
-		del self._items
-
-	def __getitem__(self, n):
-		if n >= len(self):
-			return None
-		if not hasattr(self, '_children'):
-			self._children = [None] * len(self)
-		c = self._children[n]
-		if c is None:
-			k,v = self._items[n]
-			c = self._children[n] = Row(self, n, k, v, self._font)
-			self._items[n] = None
-		return c
-
-	def get_parent(self):
-		return self._parent
-
-	def get_index(self):
-		return self._index
-
-	def get_key(self):
-		return self._key
-
-	def get_value(self):
-		return self._value
-
-	def get_value_str(self):
-		if hasattr(self,'_value_str'):
-			return self._value_str
-		return str(self._value)
-
-class FontTreeModel(gtk.GenericTreeModel):
-
-	__gtype_name__ = 'FontTreeModel'
-
-	def __init__(self, font):
-		super(FontTreeModel, self).__init__()
-		self._columns = (str, str)
-		self.font = font
-		self._root = Row(None, 0, "font", font, font)
-
-	def on_get_flags(self):
-		return 0
-
-	def on_get_n_columns(self):
-		return len(self._columns)
-
-	def on_get_column_type(self, index):
-		return self._columns[index]
-
-	def on_get_iter(self, path):
-		rowref = self._root
-		while path:
-			rowref = rowref[path[0]]
-			path = path[1:]
-		return rowref
-
-	def on_get_path(self, rowref):
-		path = []
-		while rowref != self._root:
-			path.append(rowref.get_index())
-			rowref = rowref.get_parent()
-		path.reverse()
-		return tuple(path)
-
-	def on_get_value(self, rowref, column):
-		if column == 0:
-			return rowref.get_key()
-		else:
-			return rowref.get_value_str()
-
-	def on_iter_next(self, rowref):
-		return rowref.get_parent()[rowref.get_index() + 1]
-
-	def on_iter_children(self, rowref):
-		return rowref[0]
-
-	def on_iter_has_child(self, rowref):
-		return bool(len(rowref))
-
-	def on_iter_n_children(self, rowref):
-		return len(rowref)
-
-	def on_iter_nth_child(self, rowref, n):
-		if not rowref: rowref = self._root
-		return rowref[n]
-
-	def on_iter_parent(self, rowref):
-		return rowref.get_parent()
-
-class Inspect(object):
-
-	def _delete_event(self, widget, event, data=None):
-		gtk.main_quit()
-		return False
-
-	def __init__(self, fontfile):
-
-		self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
-		self.window.set_title("%s - pyftinspect" % fontfile)
-		self.window.connect("delete_event", self._delete_event)
-		self.window.set_size_request(400, 600)
-
-		self.scrolled_window = gtk.ScrolledWindow()
-		self.window.add(self.scrolled_window)
-
-		self.font = ttLib.TTFont(fontfile, lazy=True)
-		self.treemodel = FontTreeModel(self.font)
-		self.treeview = gtk.TreeView(self.treemodel)
-		#self.treeview.set_reorderable(True)
-
-		for i in range(2):
-			col_name = ('Key', 'Value')[i]
-			col = gtk.TreeViewColumn(col_name)
-			col.set_sort_column_id(-1)
-			self.treeview.append_column(col)
-
-			cell = gtk.CellRendererText()
-			col.pack_start(cell, True)
-			col.add_attribute(cell, 'text', i)
-
-		self.treeview.set_search_column(1)
-		self.scrolled_window.add(self.treeview)
-		self.window.show_all()
-
-def main(args=None):
-	if args is None:
-		args = sys.argv[1:]
-	if len(args) < 1:
-		print("usage: pyftinspect font...", file=sys.stderr)
-		return 1
-	for arg in args:
-		Inspect(arg)
-	gtk.main()
-
-if __name__ == "__main__":
-	sys.exit(main())
diff --git a/Lib/fontTools/merge.py b/Lib/fontTools/merge.py
index 1005b85..36ac756 100644
--- a/Lib/fontTools/merge.py
+++ b/Lib/fontTools/merge.py
@@ -226,6 +226,23 @@
 	'numberOfHMetrics': recalculate,
 }
 
+ttLib.getTableClass('vhea').mergeMap = {
+	'*': equal,
+	'tableTag': equal,
+	'tableVersion': max,
+	'ascent': max,
+	'descent': min,
+	'lineGap': max,
+	'advanceHeightMax': max,
+	'minTopSideBearing': min,
+	'minBottomSideBearing': min,
+	'yMaxExtent': max,
+	'caretSlopeRise': first,
+	'caretSlopeRun': first,
+	'caretOffset': first,
+	'numberOfVMetrics': recalculate,
+}
+
 os2FsTypeMergeBitMap = {
 	'size': 16,
 	'*': lambda bit: 0,
@@ -363,10 +380,18 @@
 		g1.width == g2.width and
 		(not hasattr(g1, 'height') or g1.height == g2.height))
 
+# Valid (format, platformID, platEncID) triplets for cmap subtables containing
+# Unicode BMP-only and Unicode Full Repertoire semantics.
+# Cf. OpenType spec for "Platform specific encodings":
+# https://docs.microsoft.com/en-us/typography/opentype/spec/name
+class CmapUnicodePlatEncodings:
+	BMP = {(4, 3, 1), (4, 0, 3), (4, 0, 4), (4, 0, 6)}
+	FullRepertoire = {(12, 3, 10), (12, 0, 4), (12, 0, 6)}
+
 @_add_method(ttLib.getTableClass('cmap'))
 def merge(self, m, tables):
 	# TODO Handle format=14.
-	# Only merges 4/3/1 and 12/3/10 subtables, ignores all other subtables
+	# Only merge format 4 and 12 Unicode subtables, ignores all other subtables
 	# If there is a format 12 table for the same font, ignore the format 4 table
 	cmapTables = []
 	for fontIdx,table in enumerate(tables):
@@ -374,10 +399,16 @@
 		format12 = None
 		for subtable in table.tables:
 			properties = (subtable.format, subtable.platformID, subtable.platEncID)
-			if properties == (4,3,1):
+			if properties in CmapUnicodePlatEncodings.BMP:
 				format4 = subtable
-			elif properties == (12,3,10):
+			elif properties in CmapUnicodePlatEncodings.FullRepertoire:
 				format12 = subtable
+			else:
+				log.warning(
+					"Dropped cmap subtable from font [%s]:\t"
+					"format %2s, platformID %2s, platEncID %2s",
+					fontIdx, subtable.format, subtable.platformID, subtable.platEncID
+				)
 		if format12 is not None:
 			cmapTables.append((format12, fontIdx))
 		elif format4 is not None:
diff --git a/Lib/fontTools/misc/encodingTools.py b/Lib/fontTools/misc/encodingTools.py
index 275ae9f..454ec9a 100644
--- a/Lib/fontTools/misc/encodingTools.py
+++ b/Lib/fontTools/misc/encodingTools.py
@@ -18,7 +18,7 @@
 	},
 	1: { # Macintosh
 		# See
-		# https://github.com/behdad/fonttools/issues/236
+		# https://github.com/fonttools/fonttools/issues/236
 		0: { # Macintosh, platEncID==0, keyed by langID
 			15: "mac_iceland",
 			17: "mac_turkish",
diff --git a/Lib/fontTools/misc/psCharStrings.py b/Lib/fontTools/misc/psCharStrings.py
index 34f21bb..7fc7a26 100644
--- a/Lib/fontTools/misc/psCharStrings.py
+++ b/Lib/fontTools/misc/psCharStrings.py
@@ -199,8 +199,7 @@
 				# distinguish anymore between small ints that were supposed to
 				# be small fixed numbers and small ints that were just small
 				# ints. Hence the warning.
-				import sys
-				sys.stderr.write("Warning: 4-byte T2 number got passed to the "
+				log.warning("4-byte T2 number got passed to the "
 					"IntType handler. This should happen only when reading in "
 					"old XML files.\n")
 				code = bytechr(255) + pack(">l", value)
diff --git a/Lib/fontTools/misc/py23.py b/Lib/fontTools/misc/py23.py
index 3702055..5cddfeb 100644
--- a/Lib/fontTools/misc/py23.py
+++ b/Lib/fontTools/misc/py23.py
@@ -7,7 +7,7 @@
 __all__ = ['basestring', 'unicode', 'unichr', 'byteord', 'bytechr', 'BytesIO',
 		'StringIO', 'UnicodeIO', 'strjoin', 'bytesjoin', 'tobytes', 'tostr',
 		'tounicode', 'Tag', 'open', 'range', 'xrange', 'round', 'Py23Error',
-		'SimpleNamespace', 'zip']
+		'SimpleNamespace', 'zip', 'RecursionError']
 
 
 class Py23Error(NotImplementedError):
@@ -514,6 +514,12 @@
 		_stream = "stderr"
 
 
+try:
+	RecursionError = RecursionError
+except NameError:
+	RecursionError = RuntimeError
+
+
 if __name__ == "__main__":
 	import doctest, sys
 	sys.exit(doctest.testmod().failed)
diff --git a/Lib/fontTools/misc/timeTools.py b/Lib/fontTools/misc/timeTools.py
index c30a377..657d77f 100644
--- a/Lib/fontTools/misc/timeTools.py
+++ b/Lib/fontTools/misc/timeTools.py
@@ -31,7 +31,7 @@
 	In Python 3.x, the day of the month is right-justified, whereas on Windows
 	Python 2.7 it is padded with zeros.
 
-	See https://github.com/behdad/fonttools/issues/455
+	See https://github.com/fonttools/fonttools/issues/455
 	"""
 	if t is None:
 		t = time.localtime()
diff --git a/Lib/fontTools/misc/xmlWriter.py b/Lib/fontTools/misc/xmlWriter.py
index d625d3a..ef51a79 100644
--- a/Lib/fontTools/misc/xmlWriter.py
+++ b/Lib/fontTools/misc/xmlWriter.py
@@ -30,7 +30,7 @@
 		# Figure out if writer expects bytes or unicodes
 		try:
 			# The bytes check should be first.  See:
-			# https://github.com/behdad/fonttools/pull/233
+			# https://github.com/fonttools/fonttools/pull/233
 			self.file.write(b'')
 			self.totype = tobytes
 		except TypeError:
diff --git a/Lib/fontTools/pens/pointPen.py b/Lib/fontTools/pens/pointPen.py
index 415972f..6299e86 100644
--- a/Lib/fontTools/pens/pointPen.py
+++ b/Lib/fontTools/pens/pointPen.py
@@ -200,7 +200,8 @@
 		else:
 			pen.endPath()
 
-	def addComponent(self, glyphName, transform, **kwargs):
+	def addComponent(self, glyphName, transform, identifier=None, **kwargs):
+		del identifier  # unused
 		self.pen.addComponent(glyphName, transform)
 
 
diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py
index 9403fc1..6e17cc3 100644
--- a/Lib/fontTools/subset/__init__.py
+++ b/Lib/fontTools/subset/__init__.py
@@ -208,7 +208,7 @@
       By default, the following tables are dropped:
       'BASE', 'JSTF', 'DSIG', 'EBDT', 'EBLC', 'EBSC', 'SVG ', 'PCLT', 'LTSH'
       and Graphite tables: 'Feat', 'Glat', 'Gloc', 'Silf', 'Sill'
-      and color tables: 'CBLC', 'CBDT', 'sbix'.
+      and color tables: 'sbix'.
       The tool will attempt to subset the remaining tables.
       Examples:
         --drop-tables-='SVG '
@@ -266,7 +266,7 @@
   --name-legacy
       Keep legacy (non-Unicode) 'name' table entries (0.x, 1.x etc.).
       XXX Note: This might be needed for some fonts that have no Unicode name
-      entires for English. See: https://github.com/behdad/fonttools/issues/146
+      entires for English. See: https://github.com/fonttools/fonttools/issues/146
   --no-name-legacy
       Drop legacy (non-Unicode) 'name' table entries [default]
   --name-languages[+|-]=<langID>[,<langID>]
@@ -1377,7 +1377,7 @@
 
 	return True
 
-# CBDC will inherit it
+# CBDT will inherit it
 @_add_method(ttLib.getTableClass('EBDT'))
 def subset_glyphs(self, s):
   self.strikeData = [{g: strike[g] for g in s.glyphs if g in strike}
@@ -1425,7 +1425,7 @@
 @_add_method(ttLib.getTableClass('GSUB'),
 	     ttLib.getTableClass('GPOS'))
 def retain_empty_scripts(self):
-	# https://github.com/behdad/fonttools/issues/518
+	# https://github.com/fonttools/fonttools/issues/518
 	# https://bugzilla.mozilla.org/show_bug.cgi?id=1080739#c15
 	return self.__class__ == ttLib.getTableClass('GSUB')
 
@@ -1798,17 +1798,20 @@
 	used = set()
 
 	if table.AdvWidthMap:
-		table.AdvWidthMap.mapping = _dict_subset(table.AdvWidthMap.mapping, s.glyphs)
+		if not s.options.retain_gids:
+			table.AdvWidthMap.mapping = _dict_subset(table.AdvWidthMap.mapping, s.glyphs)
 		used.update(table.AdvWidthMap.mapping.values())
 	else:
 		assert table.LsbMap is None and table.RsbMap is None, "File a bug."
 		used.update(s.reverseOrigGlyphMap.values())
 
 	if table.LsbMap:
-		table.LsbMap.mapping = _dict_subset(table.LsbMap.mapping, s.glyphs)
+		if not s.options.retain_gids:
+			table.LsbMap.mapping = _dict_subset(table.LsbMap.mapping, s.glyphs)
 		used.update(table.LsbMap.mapping.values())
 	if table.RsbMap:
-		table.RsbMap.mapping = _dict_subset(table.RsbMap.mapping, s.glyphs)
+		if not s.options.retain_gids:
+			table.RsbMap.mapping = _dict_subset(table.RsbMap.mapping, s.glyphs)
 		used.update(table.RsbMap.mapping.values())
 
 	varidx_map = varStore.VarStore_subset_varidxes(table.VarStore, used)
@@ -1852,9 +1855,9 @@
 	if table.TsbMap:
 		table.TsbMap.mapping = {k:varidx_map[v] for k,v in table.TsbMap.mapping.items()}
 	if table.BsbMap:
-		table.RsbMap.mapping = {k:varidx_map[v] for k,v in table.RsbMap.mapping.items()}
+		table.BsbMap.mapping = {k:varidx_map[v] for k,v in table.BsbMap.mapping.items()}
 	if table.VOrgMap:
-		table.RsbMap.mapping = {k:varidx_map[v] for k,v in table.RsbMap.mapping.items()}
+		table.VOrgMap.mapping = {k:varidx_map[v] for k,v in table.VOrgMap.mapping.items()}
 
 	# TODO Return emptiness...
 	return True
diff --git a/Lib/fontTools/subset/cff.py b/Lib/fontTools/subset/cff.py
index f01dfe6..274071e 100644
--- a/Lib/fontTools/subset/cff.py
+++ b/Lib/fontTools/subset/cff.py
@@ -101,7 +101,7 @@
 	# Clear useless Encoding
 	for fontname in cff.keys():
 		font = cff[fontname]
-		# https://github.com/behdad/fonttools/issues/620
+		# https://github.com/fonttools/fonttools/issues/620
 		font.Encoding = "StandardEncoding"
 
 	return True # bool(cff.fontNames)
diff --git a/Lib/fontTools/ttLib/tables/C_O_L_R_.py b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
index 743fa91..441242e 100644
--- a/Lib/fontTools/ttLib/tables/C_O_L_R_.py
+++ b/Lib/fontTools/ttLib/tables/C_O_L_R_.py
@@ -6,7 +6,6 @@
 from fontTools.misc.py23 import *
 from fontTools.misc.textTools import safeEval
 from . import DefaultTable
-import operator
 import struct
 
 
@@ -44,12 +43,13 @@
 
 		self.ColorLayers = colorLayerLists = {}
 		try:
-			names = list(map(operator.getitem, [glyphOrder]*numBaseGlyphRecords, gids))
+			names = [glyphOrder[gid] for gid in gids]
 		except IndexError:
 			getGlyphName = self.getGlyphName
-			names = list(map(getGlyphName, gids ))
+			names = map(getGlyphName, gids)
 
-		list(map(operator.setitem, [colorLayerLists]*numBaseGlyphRecords, names, layerLists))
+		for name, layerList in zip(names, layerLists):
+			colorLayerLists[name] = layerList
 
 	def compile(self, ttFont):
 		ordered = []
@@ -112,7 +112,7 @@
 				layer = LayerRecord()
 				layer.fromXML(element[0], element[1], element[2], ttFont)
 				layers.append (layer)
-			operator.setitem(self, glyphName, layers)
+			self[glyphName] = layers
 		elif "value" in attrs:
 			setattr(self, name, safeEval(attrs["value"]))
 
diff --git a/Lib/fontTools/ttLib/tables/E_B_L_C_.py b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
index 0c53e7d..8cf6600 100644
--- a/Lib/fontTools/ttLib/tables/E_B_L_C_.py
+++ b/Lib/fontTools/ttLib/tables/E_B_L_C_.py
@@ -115,7 +115,7 @@
 				indexSubTable.indexFormat = indexFormat
 				indexSubTable.imageFormat = imageFormat
 				indexSubTable.imageDataOffset = imageDataOffset
-				indexSubTable.decompile() # https://github.com/behdad/fonttools/issues/317
+				indexSubTable.decompile() # https://github.com/fonttools/fonttools/issues/317
 				curStrike.indexSubTables.append(indexSubTable)
 
 	def compile(self, ttFont):
diff --git a/Lib/fontTools/ttLib/tables/G__l_a_t.py b/Lib/fontTools/ttLib/tables/G__l_a_t.py
index 7d1f735..b7e8281 100644
--- a/Lib/fontTools/ttLib/tables/G__l_a_t.py
+++ b/Lib/fontTools/ttLib/tables/G__l_a_t.py
@@ -7,10 +7,6 @@
 from . import DefaultTable
 from . import grUtils
 import struct, operator, warnings
-try:
-    import lz4
-except: 
-    lz4 = None
 
 
 Glat_format_0 = """
diff --git a/Lib/fontTools/ttLib/tables/TupleVariation.py b/Lib/fontTools/ttLib/tables/TupleVariation.py
index 92d07a1..e2ceba4 100644
--- a/Lib/fontTools/ttLib/tables/TupleVariation.py
+++ b/Lib/fontTools/ttLib/tables/TupleVariation.py
@@ -71,7 +71,13 @@
 				if minValue == defaultMinValue and maxValue == defaultMaxValue:
 					writer.simpletag("coord", axis=axis, value=value)
 				else:
-					writer.simpletag("coord", axis=axis, value=value, min=minValue, max=maxValue)
+					attrs = [
+						("axis", axis),
+						("min", minValue),
+						("value", value),
+						("max", maxValue),
+				        ]
+					writer.simpletag("coord", attrs)
 				writer.newline()
 		wrote_any_deltas = False
 		for i, delta in enumerate(self.coordinates):
diff --git a/Lib/fontTools/ttLib/tables/V_O_R_G_.py b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
index 8b2c317..1b75294 100644
--- a/Lib/fontTools/ttLib/tables/V_O_R_G_.py
+++ b/Lib/fontTools/ttLib/tables/V_O_R_G_.py
@@ -30,27 +30,27 @@
 		self.VOriginRecords = vOrig = {}
 		glyphOrder = ttFont.getGlyphOrder()
 		try:
-			names = map(operator.getitem, [glyphOrder]*self.numVertOriginYMetrics, gids)
+			names = [glyphOrder[gid] for gid in gids]
 		except IndexError:
 			getGlyphName = self.getGlyphName
-			names = map(getGlyphName, gids )
+			names = map(getGlyphName, gids)
 
-		list(map(operator.setitem, [vOrig]*self.numVertOriginYMetrics, names, vids))
+		for name, vid in zip(names, vids):
+			vOrig[name] = vid
 
 	def compile(self, ttFont):
 		vorgs = list(self.VOriginRecords.values())
 		names = list(self.VOriginRecords.keys())
 		nameMap = ttFont.getReverseGlyphMap()
-		lenRecords = len(vorgs)
 		try:
-			gids = map(operator.getitem, [nameMap]*lenRecords, names)
+			gids = [nameMap[name] for name in names]
 		except KeyError:
 			nameMap = ttFont.getReverseGlyphMap(rebuild=True)
-			gids = map(operator.getitem, [nameMap]*lenRecords, names)
+			gids = [nameMap[name] for name in names]
 		vOriginTable = list(zip(gids, vorgs))
-		self.numVertOriginYMetrics = lenRecords
+		self.numVertOriginYMetrics = len(vorgs)
 		vOriginTable.sort() # must be in ascending GID order
-		dataList = [ struct.pack(">Hh", rec[0], rec[1]) for rec in vOriginTable]
+		dataList = [struct.pack(">Hh", rec[0], rec[1]) for rec in vOriginTable]
 		header = struct.pack(">HHhH", self.majorVersion, self.minorVersion, self.defaultVertOriginY, self.numVertOriginYMetrics)
 		dataList.insert(0, header)
 		data = bytesjoin(dataList)
diff --git a/Lib/fontTools/ttLib/tables/_c_m_a_p.py b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
index 1767ad4..971a316 100644
--- a/Lib/fontTools/ttLib/tables/_c_m_a_p.py
+++ b/Lib/fontTools/ttLib/tables/_c_m_a_p.py
@@ -8,7 +8,6 @@
 import sys
 import struct
 import array
-import operator
 import logging
 
 
@@ -442,13 +441,12 @@
 		charCodes = [item[0] for item in items]
 		names = [item[1] for item in items]
 		nameMap = ttFont.getReverseGlyphMap()
-		lenCharCodes = len(charCodes)
 		try:
-			gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+			gids = [nameMap[name] for name in names]
 		except KeyError:
 			nameMap = ttFont.getReverseGlyphMap(rebuild=True)
 			try:
-				gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+				gids = [nameMap[name] for name in names]
 			except KeyError:
 				# allow virtual GIDs in format 2 tables
 				gids = []
@@ -458,7 +456,7 @@
 					except KeyError:
 						try:
 							if (name[:3] == 'gid'):
-								gid = eval(name[3:])
+								gid = int(name[3:])
 							else:
 								gid = ttFont.getGlyphID(name)
 						except:
@@ -744,20 +742,19 @@
 			return struct.pack(">HHH", self.format, self.length, self.language) + self.data
 
 		charCodes = list(self.cmap.keys())
-		lenCharCodes = len(charCodes)
-		if lenCharCodes == 0:
+		if not charCodes:
 			startCode = [0xffff]
 			endCode = [0xffff]
 		else:
 			charCodes.sort()
-			names = list(map(operator.getitem, [self.cmap]*lenCharCodes, charCodes))
+			names = [self.cmap[code] for code in charCodes]
 			nameMap = ttFont.getReverseGlyphMap()
 			try:
-				gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+				gids = [nameMap[name] for name in names]
 			except KeyError:
 				nameMap = ttFont.getReverseGlyphMap(rebuild=True)
 				try:
-					gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+					gids = [nameMap[name] for name in names]
 				except KeyError:
 					# allow virtual GIDs in format 4 tables
 					gids = []
@@ -767,7 +764,7 @@
 						except KeyError:
 							try:
 								if (name[:3] == 'gid'):
-									gid = eval(name[3:])
+									gid = int(name[3:])
 								else:
 									gid = ttFont.getGlyphID(name)
 							except:
@@ -775,7 +772,8 @@
 
 						gids.append(gid)
 			cmap = {}  # code:glyphID mapping
-			list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
+			for code, gid in zip(charCodes, gids):
+				cmap[code] = gid
 
 			# Build startCode and endCode lists.
 			# Split the char codes in ranges of consecutive char codes, then split
@@ -955,15 +953,14 @@
 		if self.data:
 			return struct.pack(">HHLLL", self.format, self.reserved, self.length, self.language, self.nGroups) + self.data
 		charCodes = list(self.cmap.keys())
-		lenCharCodes = len(charCodes)
 		names = list(self.cmap.values())
 		nameMap = ttFont.getReverseGlyphMap()
 		try:
-			gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+			gids = [nameMap[name] for name in names]
 		except KeyError:
 			nameMap = ttFont.getReverseGlyphMap(rebuild=True)
 			try:
-				gids = list(map(operator.getitem, [nameMap]*lenCharCodes, names))
+				gids = [nameMap[name] for name in names]
 			except KeyError:
 				# allow virtual GIDs in format 12 tables
 				gids = []
@@ -973,7 +970,7 @@
 					except KeyError:
 						try:
 							if (name[:3] == 'gid'):
-								gid = eval(name[3:])
+								gid = int(name[3:])
 							else:
 								gid = ttFont.getGlyphID(name)
 						except:
@@ -982,7 +979,8 @@
 					gids.append(gid)
 
 		cmap = {}  # code:glyphID mapping
-		list(map(operator.setitem, [cmap]*len(charCodes), charCodes, gids))
+		for code, gid in zip(charCodes, gids):
+			cmap[code] = gid
 
 		charCodes.sort()
 		index = 0
diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
index 8b36050..b8020ca 100644
--- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -841,7 +841,10 @@
 			allEndPts = []
 			for compo in self.components:
 				g = glyfTable[compo.glyphName]
-				coordinates, endPts, flags = g.getCoordinates(glyfTable)
+				try:
+					coordinates, endPts, flags = g.getCoordinates(glyfTable)
+				except RecursionError:
+					raise ttLib.TTLibError("glyph '%s' contains a recursive component reference" % compo.glyphName)
 				if hasattr(compo, "firstPt"):
 					# move according to two reference points
 					x1,y1 = allCoords[compo.firstPt]
diff --git a/Lib/fontTools/ttLib/tables/_h_e_a_d.py b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
index 9275d41..4235acf 100644
--- a/Lib/fontTools/ttLib/tables/_h_e_a_d.py
+++ b/Lib/fontTools/ttLib/tables/_h_e_a_d.py
@@ -46,7 +46,7 @@
 		# bogus values there.  Since till 2038 those bytes only can be zero,
 		# ignore them.
 		#
-		# https://github.com/behdad/fonttools/issues/99#issuecomment-66776810
+		# https://github.com/fonttools/fonttools/issues/99#issuecomment-66776810
 		for stamp in 'created', 'modified':
 			value = getattr(self, stamp)
 			if value > 0xFFFFFFFF:
diff --git a/Lib/fontTools/ttLib/tables/grUtils.py b/Lib/fontTools/ttLib/tables/grUtils.py
index d11ac4b..a60df23 100644
--- a/Lib/fontTools/ttLib/tables/grUtils.py
+++ b/Lib/fontTools/ttLib/tables/grUtils.py
@@ -1,8 +1,10 @@
 import struct, warnings
 try:
     import lz4
-except: 
+except ImportError:
     lz4 = None
+else:
+    import lz4.block
 
 #old scheme for VERSION < 0.9 otherwise use lz4.block
 
diff --git a/Lib/fontTools/ttLib/tables/otBase.py b/Lib/fontTools/ttLib/tables/otBase.py
index c71cc8d..81ab354 100644
--- a/Lib/fontTools/ttLib/tables/otBase.py
+++ b/Lib/fontTools/ttLib/tables/otBase.py
@@ -303,7 +303,7 @@
 		# Certain versions of Uniscribe reject the font if the GSUB/GPOS top-level
 		# arrays (ScriptList, FeatureList, LookupList) point to the same, possibly
 		# empty, array.  So, we don't share those.
-		# See: https://github.com/behdad/fonttools/issues/518
+		# See: https://github.com/fonttools/fonttools/issues/518
 		dontShare = hasattr(self, 'DontShare')
 
 		if isExtension:
diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py
index 4d2bc57..af2a7c6 100644
--- a/Lib/fontTools/ttLib/tables/otTables.py
+++ b/Lib/fontTools/ttLib/tables/otTables.py
@@ -9,7 +9,6 @@
 from fontTools.misc.py23 import *
 from fontTools.misc.textTools import pad, safeEval
 from .otBase import BaseTable, FormatSwitchingBaseTable, ValueRecord
-import operator
 import logging
 import struct
 
@@ -698,18 +697,19 @@
 	def postRead(self, rawTable, font):
 		mapping = {}
 		input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
-		lenMapping = len(input)
 		if self.Format == 1:
 			delta = rawTable["DeltaGlyphID"]
 			inputGIDS =  [ font.getGlyphID(name) for name in input ]
 			outGIDS = [ (glyphID + delta) % 65536 for glyphID in inputGIDS ]
 			outNames = [ font.getGlyphName(glyphID) for glyphID in outGIDS ]
-			list(map(operator.setitem, [mapping]*lenMapping, input, outNames))
+			for inp, out in zip(input, outNames):
+				mapping[inp] = out
 		elif self.Format == 2:
 			assert len(input) == rawTable["GlyphCount"], \
 					"invalid SingleSubstFormat2 table"
 			subst = rawTable["Substitute"]
-			list(map(operator.setitem, [mapping]*lenMapping, input, subst))
+			for inp, sub in zip(input, subst):
+				mapping[inp] = sub
 		else:
 			assert 0, "unknown format: %s" % self.Format
 		self.mapping = mapping
diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py
index 6ec4113..0543ee3 100644
--- a/Lib/fontTools/varLib/__init__.py
+++ b/Lib/fontTools/varLib/__init__.py
@@ -209,8 +209,40 @@
 	# TODO make this user-configurable via designspace document
 	stat.ElidedFallbackNameID = 2
 
+
+def _get_phantom_points(font, glyphName, defaultVerticalOrigin=None):
+	glyf = font["glyf"]
+	glyph = glyf[glyphName]
+	horizontalAdvanceWidth, leftSideBearing = font["hmtx"].metrics[glyphName]
+	if not hasattr(glyph, 'xMin'):
+		glyph.recalcBounds(glyf)
+	leftSideX = glyph.xMin - leftSideBearing
+	rightSideX = leftSideX + horizontalAdvanceWidth
+	if "vmtx" in font:
+		verticalAdvanceWidth, topSideBearing = font["vmtx"].metrics[glyphName]
+		topSideY = topSideBearing + glyph.yMax
+	else:
+		# without vmtx, use ascent as vertical origin and UPEM as vertical advance
+		# like HarfBuzz does
+		verticalAdvanceWidth = font["head"].unitsPerEm
+		try:
+			topSideY = font["hhea"].ascent
+		except KeyError:
+			# sparse masters may not contain an hhea table; use the ascent
+			# of the default master as the vertical origin
+			assert defaultVerticalOrigin is not None
+			topSideY = defaultVerticalOrigin
+	bottomSideY = topSideY - verticalAdvanceWidth
+	return [
+		(leftSideX, 0),
+		(rightSideX, 0),
+		(0, topSideY),
+		(0, bottomSideY),
+	]
+
+
 # TODO Move to glyf or gvar table proper
-def _GetCoordinates(font, glyphName):
+def _GetCoordinates(font, glyphName, defaultVerticalOrigin=None):
 	"""font, glyphName --> glyph coordinates as expected by "gvar" table
 
 	The result includes four "phantom points" for the glyph metrics,
@@ -228,19 +260,9 @@
 		control = (glyph.numberOfContours,)+allData[1:]
 
 	# Add phantom points for (left, right, top, bottom) positions.
-	horizontalAdvanceWidth, leftSideBearing = font["hmtx"].metrics[glyphName]
-	if not hasattr(glyph, 'xMin'):
-		glyph.recalcBounds(glyf)
-	leftSideX = glyph.xMin - leftSideBearing
-	rightSideX = leftSideX + horizontalAdvanceWidth
-	# XXX these are incorrect.  Load vmtx and fix.
-	topSideY = glyph.yMax
-	bottomSideY = -glyph.yMin
+	phantomPoints = _get_phantom_points(font, glyphName, defaultVerticalOrigin)
 	coord = coord.copy()
-	coord.extend([(leftSideX, 0),
-	              (rightSideX, 0),
-	              (0, topSideY),
-	              (0, bottomSideY)])
+	coord.extend(phantomPoints)
 
 	return coord, control
 
@@ -297,11 +319,16 @@
 
 	glyf = font['glyf']
 
+	# use hhea.ascent of base master as default vertical origin when vmtx is missing
+	defaultVerticalOrigin = font['hhea'].ascent
 	for glyph in font.getGlyphOrder():
 
 		isComposite = glyf[glyph].isComposite()
 
-		allData = [_GetCoordinates(m, glyph) for m in master_ttfs]
+		allData = [
+			_GetCoordinates(m, glyph, defaultVerticalOrigin=defaultVerticalOrigin)
+			for m in master_ttfs
+		]
 		model, allData = masterModel.getSubModel(allData)
 
 		allCoords = [d[0] for d in allData]
@@ -688,6 +715,7 @@
 		('width',   ('wdth', {'en': u'Width'})),
 		('slant',   ('slnt', {'en': u'Slant'})),
 		('optical', ('opsz', {'en': u'Optical Size'})),
+		('italic',  ('ital', {'en': u'Italic'})),
 		])
 
 	# Setup axes
diff --git a/Lib/fontTools/varLib/models.py b/Lib/fontTools/varLib/models.py
index 207e30f..9a990b7 100644
--- a/Lib/fontTools/varLib/models.py
+++ b/Lib/fontTools/varLib/models.py
@@ -189,19 +189,22 @@
 	  7: 0.6666666666666667}]
 	"""
 
-	def __init__(self, locations, axisOrder=[]):
+	def __init__(self, locations, axisOrder=None):
+		if len(set(tuple(sorted(l.items())) for l in locations)) != len(locations):
+			raise ValueError("locations must be unique")
+
 		self.origLocations = locations
-		self.axisOrder = axisOrder
+		self.axisOrder = axisOrder if axisOrder is not None else []
 
 		locations = [{k:v for k,v in loc.items() if v != 0.} for loc in locations]
-		keyFunc = self.getMasterLocationsSortKeyFunc(locations, axisOrder=axisOrder)
-		axisPoints = keyFunc.axisPoints
+		keyFunc = self.getMasterLocationsSortKeyFunc(locations, axisOrder=self.axisOrder)
 		self.locations = sorted(locations, key=keyFunc)
-		# TODO Assert that locations are unique.
-		self.mapping = [self.locations.index(l) for l in locations] # Mapping from user's master order to our master order
-		self.reverseMapping = [locations.index(l) for l in self.locations] # Reverse of above
 
-		self._computeMasterSupports(axisPoints, axisOrder)
+		# Mapping from user's master order to our master order
+		self.mapping = [self.locations.index(l) for l in locations]
+		self.reverseMapping = [locations.index(l) for l in self.locations]
+
+		self._computeMasterSupports(keyFunc.axisPoints)
 		self._subModels = {}
 
 	def getSubModel(self, items):
@@ -252,18 +255,6 @@
 		ret.axisPoints = axisPoints
 		return ret
 
-	@staticmethod
-	def lowerBound(value, lst):
-		if any(v < value for v in lst):
-			return max(v for v in lst if v < value)
-		else:
-			return value
-	@staticmethod
-	def upperBound(value, lst):
-		if any(v > value for v in lst):
-			return min(v for v in lst if v > value)
-		else:
-			return value
 	def reorderMasters(self, master_list, mapping):
 		# For changing the master data order without
 		# recomputing supports and deltaWeights.
@@ -276,7 +267,7 @@
 		self._subModels = {}
 		return new_list
 
-	def _computeMasterSupports(self, axisPoints, axisOrder):
+	def _computeMasterSupports(self, axisPoints):
 		supports = []
 		deltaWeights = []
 		locations = self.locations
@@ -444,6 +435,7 @@
 		pprint(locs)
 		doc.normalize()
 		print("Normalized locations:")
+		locs = [s.location for s in doc.sources]
 		pprint(locs)
 	else:
 		axes = [chr(c) for c in range(ord('A'), ord('Z')+1)]
diff --git a/Lib/fontTools/varLib/mutator.py b/Lib/fontTools/varLib/mutator.py
index 79a6f3d..de61236 100644
--- a/Lib/fontTools/varLib/mutator.py
+++ b/Lib/fontTools/varLib/mutator.py
@@ -9,7 +9,7 @@
 from fontTools.pens.boundsPen import BoundsPen
 from fontTools.ttLib import TTFont, newTable
 from fontTools.ttLib.tables import ttProgram
-from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
+from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates, flagOverlapSimple, OVERLAP_COMPOUND
 from fontTools.varLib import _GetCoordinates, _SetCoordinates
 from fontTools.varLib.models import (
 	supportScalar,
@@ -145,7 +145,7 @@
 			hmtx[gname] = tuple(entry)
 
 
-def instantiateVariableFont(varfont, location, inplace=False):
+def instantiateVariableFont(varfont, location, inplace=False, overlap=True):
 	""" Generate a static instance from a variable TTFont and a dictionary
 	defining the desired location along the variable font's axes.
 	The location values must be specified as user-space coordinates, e.g.:
@@ -154,6 +154,10 @@
 
 	By default, a new TTFont object is returned. If ``inplace`` is True, the
 	input varfont is modified and reduced to a static font.
+
+	When the overlap parameter is defined as True,
+	OVERLAP_SIMPLE and OVERLAP_COMPOUND bits are set to 1.  See
+	https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
 	"""
 	if not inplace:
 		# make a copy to leave input varfont unmodified
@@ -308,6 +312,15 @@
 				addidef = any(op.startswith("GETVARIATION") for op in instructions)
 				if addidef:
 					break
+		if overlap:
+			for glyph_name in glyf.keys():
+				glyph = glyf[glyph_name]
+				# Set OVERLAP_COMPOUND bit for compound glyphs
+				if glyph.isComposite():
+					glyph.components[0].flags |= OVERLAP_COMPOUND
+				# Set OVERLAP_SIMPLE bit for simple glyphs
+				elif glyph.numberOfContours > 0:
+					glyph.flags[0] |= flagOverlapSimple
 	if addidef:
 		log.info("Adding IDEF to fpgm table for GETVARIATION opcode")
 		asm = []
@@ -404,6 +417,12 @@
 		"-v", "--verbose", action="store_true", help="Run more verbosely.")
 	logging_group.add_argument(
 		"-q", "--quiet", action="store_true", help="Turn verbosity off.")
+	parser.add_argument(
+		"--no-overlap",
+		dest="overlap",
+		action="store_false",
+		help="Don't set OVERLAP_SIMPLE/OVERLAP_COMPOUND glyf flags."
+	)
 	options = parser.parse_args(args)
 
 	varfilename = options.input
@@ -428,7 +447,7 @@
 	log.info("Loading variable font")
 	varfont = TTFont(varfilename)
 
-	instantiateVariableFont(varfont, loc, inplace=True)
+	instantiateVariableFont(varfont, loc, inplace=True, overlap=options.overlap)
 
 	log.info("Saving instance font %s", outfile)
 	varfont.save(outfile)
diff --git a/Lib/fontTools/varLib/plot.py b/Lib/fontTools/varLib/plot.py
index b03744e..6b13416 100644
--- a/Lib/fontTools/varLib/plot.py
+++ b/Lib/fontTools/varLib/plot.py
@@ -21,34 +21,84 @@
 	       [b + (c - b) * i / count for i in range(count)] + \
 	       [c]
 
-def plotLocations(locations, axes, axis3D, **kwargs):
-	for loc,color in zip(locations, cycle(pyplot.cm.Set1.colors)):
-		axis3D.plot([loc.get(axes[0], 0)],
-			    [loc.get(axes[1], 0)],
-			    [1.],
-			    'o',
-			    color=color,
-			    **kwargs)
 
-def plotLocationsSurfaces(locations, fig, names=None, **kwargs):
+def _plotLocationsDots(locations, axes, subplot, **kwargs):
+	for loc, color in zip(locations, cycle(pyplot.cm.Set1.colors)):
+		if len(axes) == 1:
+			subplot.plot(
+				[loc.get(axes[0], 0)],
+				[1.],
+				'o',
+				color=color,
+				**kwargs
+			)
+		elif len(axes) == 2:
+			subplot.plot(
+				[loc.get(axes[0], 0)],
+				[loc.get(axes[1], 0)],
+				[1.],
+				'o',
+				color=color,
+				**kwargs
+			)
+		else:
+			raise AssertionError(len(axes))
 
-	assert len(locations[0].keys()) == 2
 
-	if names is None:
-		names = ['']
-
+def plotLocations(locations, fig, names=None, **kwargs):
 	n = len(locations)
 	cols = math.ceil(n**.5)
 	rows = math.ceil(n / cols)
 
+	if names is None:
+		names = [None] * len(locations)
+
 	model = VariationModel(locations)
 	names = [names[model.reverseMapping[i]] for i in range(len(names))]
 
-	ax1, ax2 = sorted(locations[0].keys())
-	for i, (support,color, name) in enumerate(zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names))):
+	axes = sorted(locations[0].keys())
+	if len(axes) == 1:
+		_plotLocations2D(
+			model, axes[0], fig, cols, rows, names=names, **kwargs
+		)
+	elif len(axes) == 2:
+		_plotLocations3D(
+			model, axes, fig, cols, rows, names=names, **kwargs
+		)
+	else:
+		raise ValueError("Only 1 or 2 axes are supported")
 
+
+def _plotLocations2D(model, axis, fig, cols, rows, names, **kwargs):
+	for i, (support, color, name) in enumerate(
+		zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names))
+	):
+		subplot = fig.add_subplot(rows, cols, i + 1)
+		if name is not None:
+			subplot.set_title(name)
+		subplot.set_xlabel(axis)
+		pyplot.xlim(-1.,+1.)
+
+		Xs = support.get(axis, (-1.,0.,+1.))
+		X, Y = [], []
+		for x in stops(Xs):
+			y = supportScalar({axis:x}, support)
+			X.append(x)
+			Y.append(y)
+		subplot.plot(X, Y, color=color, **kwargs)
+
+		_plotLocationsDots(model.locations, [axis], subplot)
+
+
+def _plotLocations3D(model, axes, fig, rows, cols, names, **kwargs):
+	ax1, ax2 = axes
+
+	for i, (support, color, name) in enumerate(
+		zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names))
+	):
 		axis3D = fig.add_subplot(rows, cols, i + 1, projection='3d')
-		axis3D.set_title(name)
+		if name is not None:
+			axis3D.set_title(name)
 		axis3D.set_xlabel(ax1)
 		axis3D.set_ylabel(ax2)
 		pyplot.xlim(-1.,+1.)
@@ -73,14 +123,14 @@
 				Z.append(z)
 			axis3D.plot(X, Y, Z, color=color, **kwargs)
 
-		plotLocations(model.locations, [ax1, ax2], axis3D)
+		_plotLocationsDots(model.locations, [ax1, ax2], axis3D)
 
 
 def plotDocument(doc, fig, **kwargs):
 	doc.normalize()
 	locations = [s.location for s in doc.sources]
 	names = [s.name for s in doc.sources]
-	plotLocationsSurfaces(locations, fig, names, **kwargs)
+	plotLocations(locations, fig, names, **kwargs)
 
 
 def main(args=None):
@@ -101,6 +151,7 @@
 		sys.exit(1)
 
 	fig = pyplot.figure()
+	fig.set_tight_layout(True)
 
 	if len(args) == 1 and args[0].endswith('.designspace'):
 		doc = DesignSpaceDocument()
@@ -109,7 +160,7 @@
 	else:
 		axes = [chr(c) for c in range(ord('A'), ord('Z')+1)]
 		locs = [dict(zip(axes, (float(v) for v in s.split(',')))) for s in args]
-		plotLocationsSurfaces(locs, fig)
+		plotLocations(locs, fig)
 
 	pyplot.show()
 
diff --git a/Lib/fonttools.egg-info/PKG-INFO b/Lib/fonttools.egg-info/PKG-INFO
index fc57c8d..d57498f 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.38.0
+Version: 3.39.0
 Summary: Tools to manipulate font files
 Home-page: http://github.com/fonttools/fonttools
 Author: Just van Rossum
@@ -8,8 +8,7 @@
 Maintainer: Behdad Esfahbod
 Maintainer-email: behdad@behdad.org
 License: MIT
-Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage Status|
-        |PyPI| |Gitter Chat|
+Description: |Travis Build Status| |Appveyor Build status| |Coverage Status| |PyPI| |Gitter Chat|
         
         What is this?
         ~~~~~~~~~~~~~
@@ -147,12 +146,10 @@
         Other Tools
         ~~~~~~~~~~~
         
-        Commands for inspecting, merging and subsetting fonts are also
-        available:
+        Commands for merging and subsetting fonts are also available:
         
         .. code:: sh
         
-            pyftinspect
             pyftmerge
             pyftsubset
         
@@ -311,16 +308,6 @@
           * `reportlab <https://pypi.python.org/pypi/reportlab>`__: Python toolkit
             for generating PDFs and graphics.
         
-        - ``Lib/fontTools/inspect.py``
-        
-          A GUI font inspector, requires one of the following packages:
-        
-          * `PyGTK <https://pypi.python.org/pypi/PyGTK>`__: Python bindings for
-            GTK  2.x (only works with Python 2).
-          * `PyGObject <https://wiki.gnome.org/action/show/Projects/PyGObject>`__ :
-            Python bindings for GTK 3.x and gobject-introspection libraries (also
-            compatible with Python 3).
-        
         Testing
         ~~~~~~~
         
@@ -417,8 +404,6 @@
            :target: https://travis-ci.org/fonttools/fonttools
         .. |Appveyor Build status| image:: https://ci.appveyor.com/api/projects/status/0f7fmee9as744sl7/branch/master?svg=true
            :target: https://ci.appveyor.com/project/fonttools/fonttools/branch/master
-        .. |Health| image:: https://landscape.io/github/behdad/fonttools/master/landscape.svg?style=flat
-           :target: https://landscape.io/github/behdad/fonttools/master
         .. |Coverage Status| image:: https://codecov.io/gh/fonttools/fonttools/branch/master/graph/badge.svg
            :target: https://codecov.io/gh/fonttools/fonttools
         .. |PyPI| image:: https://img.shields.io/pypi/v/fonttools.svg
@@ -430,6 +415,35 @@
         Changelog
         ~~~~~~~~~
         
+        3.39.0 (released 2019-03-19)
+        ----------------------------
+        
+        - [ttLib/glyf] Raise more specific error when encountering recursive
+          component references (#1545, #1546).
+        - [Doc/designspaceLib] Defined new ``public.skipExportGlyphs`` lib key (#1534,
+          unified-font-object/ufo-spec#84).
+        - [varLib] Use ``vmtx`` to compute vertical phantom points; or ``hhea.ascent``
+          and ``head.unitsPerEM`` if ``vmtx`` is missing (#1528).
+        - [gvar/cvar] Sort XML element's min/value/max attributes in TupleVariation
+          toXML to improve readability of TTX dump (#1527).
+        - [varLib.plot] Added support for 2D plots with only 1 variation axis (#1522).
+        - [designspaceLib] Use axes maps when normalizing locations in
+          DesignSpaceDocument (#1226, #1521), and when finding default source (#1535).
+        - [mutator] Set ``OVERLAP_SIMPLE`` and ``OVERLAP_COMPOUND`` glyf flags by
+          default in ``instantiateVariableFont``. Added ``--no-overlap`` cli option
+          to disable this (#1518).
+        - [subset] Fixed subsetting ``VVAR`` table (#1516, #1517).  
+          Fixed subsetting an ``HVAR`` table that has an ``AdvanceWidthMap`` when the
+          option ``--retain-gids`` is used.
+        - [feaLib] Added ``forceChained`` in MultipleSubstStatement (#1511).  
+          Fixed double indentation of ``subtable`` statement (#1512).  
+          Added support for ``subtable`` statement in more places than just PairPos
+          lookups (#1520).  
+          Handle lookupflag 0 and lookupflag without a value (#1540).
+        - [varLib] In ``load_designspace``, provide a default English name for the
+          ``ital`` axis tag.
+        - Remove pyftinspect because it is unmaintained and bitrotted.
+        
         3.38.0 (released 2019-02-18)
         ----------------------------
         
@@ -1670,13 +1684,13 @@
 Classifier: Topic :: Text Processing :: Fonts
 Classifier: Topic :: Multimedia :: Graphics
 Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
-Provides-Extra: woff
-Provides-Extra: type1
-Provides-Extra: interpolatable
 Provides-Extra: symfont
-Provides-Extra: graphite
-Provides-Extra: all
-Provides-Extra: ufo
-Provides-Extra: unicode
+Provides-Extra: type1
 Provides-Extra: lxml
+Provides-Extra: ufo
+Provides-Extra: interpolatable
+Provides-Extra: all
+Provides-Extra: woff
 Provides-Extra: plot
+Provides-Extra: unicode
+Provides-Extra: graphite
diff --git a/Lib/fonttools.egg-info/SOURCES.txt b/Lib/fonttools.egg-info/SOURCES.txt
index b07edae..3bb752f 100644
--- a/Lib/fonttools.egg-info/SOURCES.txt
+++ b/Lib/fonttools.egg-info/SOURCES.txt
@@ -29,7 +29,6 @@
 Doc/source/encodings.rst
 Doc/source/feaLib.rst
 Doc/source/index.rst
-Doc/source/inspect.rst
 Doc/source/merge.rst
 Doc/source/subset.rst
 Doc/source/t1Lib.rst
@@ -88,7 +87,6 @@
 Lib/fontTools/afmLib.py
 Lib/fontTools/agl.py
 Lib/fontTools/fontBuilder.py
-Lib/fontTools/inspect.py
 Lib/fontTools/merge.py
 Lib/fontTools/ttx.py
 Lib/fontTools/unicode.py
@@ -348,8 +346,14 @@
 Tests/feaLib/error_test.py
 Tests/feaLib/lexer_test.py
 Tests/feaLib/parser_test.py
+Tests/feaLib/data/AlternateSubtable.fea
+Tests/feaLib/data/AlternateSubtable.ttx
 Tests/feaLib/data/Attach.fea
 Tests/feaLib/data/Attach.ttx
+Tests/feaLib/data/ChainPosSubtable.fea
+Tests/feaLib/data/ChainPosSubtable.ttx
+Tests/feaLib/data/ChainSubstSubtable.fea
+Tests/feaLib/data/ChainSubstSubtable.ttx
 Tests/feaLib/data/GPOS_1.fea
 Tests/feaLib/data/GPOS_1.ttx
 Tests/feaLib/data/GPOS_1_zero.fea
@@ -382,8 +386,14 @@
 Tests/feaLib/data/LigatureCaretByIndex.ttx
 Tests/feaLib/data/LigatureCaretByPos.fea
 Tests/feaLib/data/LigatureCaretByPos.ttx
+Tests/feaLib/data/LigatureSubtable.fea
+Tests/feaLib/data/LigatureSubtable.ttx
+Tests/feaLib/data/MultipleSubstSubtable.fea
+Tests/feaLib/data/MultipleSubstSubtable.ttx
 Tests/feaLib/data/PairPosSubtable.fea
 Tests/feaLib/data/PairPosSubtable.ttx
+Tests/feaLib/data/SingleSubstSubtable.fea
+Tests/feaLib/data/SingleSubstSubtable.ttx
 Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.fea
 Tests/feaLib/data/ZeroValue_ChainSinglePos_horizontal.ttx
 Tests/feaLib/data/ZeroValue_ChainSinglePos_vertical.fea
diff --git a/Lib/fonttools.egg-info/entry_points.txt b/Lib/fonttools.egg-info/entry_points.txt
index 6d70774..2c235cf 100644
--- a/Lib/fonttools.egg-info/entry_points.txt
+++ b/Lib/fonttools.egg-info/entry_points.txt
@@ -1,6 +1,5 @@
 [console_scripts]
 fonttools = fontTools.__main__:main
-pyftinspect = fontTools.inspect:main
 pyftmerge = fontTools.merge:main
 pyftsubset = fontTools.subset:main
 ttx = fontTools.ttx:main
diff --git a/METADATA b/METADATA
index e3f3b93..ea7bfaa 100644
--- a/METADATA
+++ b/METADATA
@@ -7,12 +7,12 @@
   }
   url {
     type: ARCHIVE
-    value: "https://github.com/fonttools/fonttools/releases/download/3.38.0/fonttools-3.38.0.zip"
+    value: "https://github.com/fonttools/fonttools/releases/download/3.39.0/fonttools-3.39.0.zip"
   }
-  version: "3.38.0"
+  version: "3.39.0"
   last_upgrade_date {
     year: 2019
-    month: 2
+    month: 3
     day: 19
   }
 }
diff --git a/NEWS.rst b/NEWS.rst
index b3b0adb..c48ed88 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,3 +1,32 @@
+3.39.0 (released 2019-03-19)
+----------------------------
+
+- [ttLib/glyf] Raise more specific error when encountering recursive
+  component references (#1545, #1546).
+- [Doc/designspaceLib] Defined new ``public.skipExportGlyphs`` lib key (#1534,
+  unified-font-object/ufo-spec#84).
+- [varLib] Use ``vmtx`` to compute vertical phantom points; or ``hhea.ascent``
+  and ``head.unitsPerEM`` if ``vmtx`` is missing (#1528).
+- [gvar/cvar] Sort XML element's min/value/max attributes in TupleVariation
+  toXML to improve readability of TTX dump (#1527).
+- [varLib.plot] Added support for 2D plots with only 1 variation axis (#1522).
+- [designspaceLib] Use axes maps when normalizing locations in
+  DesignSpaceDocument (#1226, #1521), and when finding default source (#1535).
+- [mutator] Set ``OVERLAP_SIMPLE`` and ``OVERLAP_COMPOUND`` glyf flags by
+  default in ``instantiateVariableFont``. Added ``--no-overlap`` cli option
+  to disable this (#1518).
+- [subset] Fixed subsetting ``VVAR`` table (#1516, #1517).  
+  Fixed subsetting an ``HVAR`` table that has an ``AdvanceWidthMap`` when the
+  option ``--retain-gids`` is used.
+- [feaLib] Added ``forceChained`` in MultipleSubstStatement (#1511).  
+  Fixed double indentation of ``subtable`` statement (#1512).  
+  Added support for ``subtable`` statement in more places than just PairPos
+  lookups (#1520).  
+  Handle lookupflag 0 and lookupflag without a value (#1540).
+- [varLib] In ``load_designspace``, provide a default English name for the
+  ``ital`` axis tag.
+- Remove pyftinspect because it is unmaintained and bitrotted.
+
 3.38.0 (released 2019-02-18)
 ----------------------------
 
diff --git a/PKG-INFO b/PKG-INFO
index fc57c8d..d57498f 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: fonttools
-Version: 3.38.0
+Version: 3.39.0
 Summary: Tools to manipulate font files
 Home-page: http://github.com/fonttools/fonttools
 Author: Just van Rossum
@@ -8,8 +8,7 @@
 Maintainer: Behdad Esfahbod
 Maintainer-email: behdad@behdad.org
 License: MIT
-Description: |Travis Build Status| |Appveyor Build status| |Health| |Coverage Status|
-        |PyPI| |Gitter Chat|
+Description: |Travis Build Status| |Appveyor Build status| |Coverage Status| |PyPI| |Gitter Chat|
         
         What is this?
         ~~~~~~~~~~~~~
@@ -147,12 +146,10 @@
         Other Tools
         ~~~~~~~~~~~
         
-        Commands for inspecting, merging and subsetting fonts are also
-        available:
+        Commands for merging and subsetting fonts are also available:
         
         .. code:: sh
         
-            pyftinspect
             pyftmerge
             pyftsubset
         
@@ -311,16 +308,6 @@
           * `reportlab <https://pypi.python.org/pypi/reportlab>`__: Python toolkit
             for generating PDFs and graphics.
         
-        - ``Lib/fontTools/inspect.py``
-        
-          A GUI font inspector, requires one of the following packages:
-        
-          * `PyGTK <https://pypi.python.org/pypi/PyGTK>`__: Python bindings for
-            GTK  2.x (only works with Python 2).
-          * `PyGObject <https://wiki.gnome.org/action/show/Projects/PyGObject>`__ :
-            Python bindings for GTK 3.x and gobject-introspection libraries (also
-            compatible with Python 3).
-        
         Testing
         ~~~~~~~
         
@@ -417,8 +404,6 @@
            :target: https://travis-ci.org/fonttools/fonttools
         .. |Appveyor Build status| image:: https://ci.appveyor.com/api/projects/status/0f7fmee9as744sl7/branch/master?svg=true
            :target: https://ci.appveyor.com/project/fonttools/fonttools/branch/master
-        .. |Health| image:: https://landscape.io/github/behdad/fonttools/master/landscape.svg?style=flat
-           :target: https://landscape.io/github/behdad/fonttools/master
         .. |Coverage Status| image:: https://codecov.io/gh/fonttools/fonttools/branch/master/graph/badge.svg
            :target: https://codecov.io/gh/fonttools/fonttools
         .. |PyPI| image:: https://img.shields.io/pypi/v/fonttools.svg
@@ -430,6 +415,35 @@
         Changelog
         ~~~~~~~~~
         
+        3.39.0 (released 2019-03-19)
+        ----------------------------
+        
+        - [ttLib/glyf] Raise more specific error when encountering recursive
+          component references (#1545, #1546).
+        - [Doc/designspaceLib] Defined new ``public.skipExportGlyphs`` lib key (#1534,
+          unified-font-object/ufo-spec#84).
+        - [varLib] Use ``vmtx`` to compute vertical phantom points; or ``hhea.ascent``
+          and ``head.unitsPerEM`` if ``vmtx`` is missing (#1528).
+        - [gvar/cvar] Sort XML element's min/value/max attributes in TupleVariation
+          toXML to improve readability of TTX dump (#1527).
+        - [varLib.plot] Added support for 2D plots with only 1 variation axis (#1522).
+        - [designspaceLib] Use axes maps when normalizing locations in
+          DesignSpaceDocument (#1226, #1521), and when finding default source (#1535).
+        - [mutator] Set ``OVERLAP_SIMPLE`` and ``OVERLAP_COMPOUND`` glyf flags by
+          default in ``instantiateVariableFont``. Added ``--no-overlap`` cli option
+          to disable this (#1518).
+        - [subset] Fixed subsetting ``VVAR`` table (#1516, #1517).  
+          Fixed subsetting an ``HVAR`` table that has an ``AdvanceWidthMap`` when the
+          option ``--retain-gids`` is used.
+        - [feaLib] Added ``forceChained`` in MultipleSubstStatement (#1511).  
+          Fixed double indentation of ``subtable`` statement (#1512).  
+          Added support for ``subtable`` statement in more places than just PairPos
+          lookups (#1520).  
+          Handle lookupflag 0 and lookupflag without a value (#1540).
+        - [varLib] In ``load_designspace``, provide a default English name for the
+          ``ital`` axis tag.
+        - Remove pyftinspect because it is unmaintained and bitrotted.
+        
         3.38.0 (released 2019-02-18)
         ----------------------------
         
@@ -1670,13 +1684,13 @@
 Classifier: Topic :: Text Processing :: Fonts
 Classifier: Topic :: Multimedia :: Graphics
 Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
-Provides-Extra: woff
-Provides-Extra: type1
-Provides-Extra: interpolatable
 Provides-Extra: symfont
-Provides-Extra: graphite
-Provides-Extra: all
-Provides-Extra: ufo
-Provides-Extra: unicode
+Provides-Extra: type1
 Provides-Extra: lxml
+Provides-Extra: ufo
+Provides-Extra: interpolatable
+Provides-Extra: all
+Provides-Extra: woff
 Provides-Extra: plot
+Provides-Extra: unicode
+Provides-Extra: graphite
diff --git a/README.rst b/README.rst
index f36084a..14e7c4c 100644
--- a/README.rst
+++ b/README.rst
@@ -1,5 +1,4 @@
-|Travis Build Status| |Appveyor Build status| |Health| |Coverage Status|
-|PyPI| |Gitter Chat|
+|Travis Build Status| |Appveyor Build status| |Coverage Status| |PyPI| |Gitter Chat|
 
 What is this?
 ~~~~~~~~~~~~~
@@ -137,12 +136,10 @@
 Other Tools
 ~~~~~~~~~~~
 
-Commands for inspecting, merging and subsetting fonts are also
-available:
+Commands for merging and subsetting fonts are also available:
 
 .. code:: sh
 
-    pyftinspect
     pyftmerge
     pyftsubset
 
@@ -301,16 +298,6 @@
   * `reportlab <https://pypi.python.org/pypi/reportlab>`__: Python toolkit
     for generating PDFs and graphics.
 
-- ``Lib/fontTools/inspect.py``
-
-  A GUI font inspector, requires one of the following packages:
-
-  * `PyGTK <https://pypi.python.org/pypi/PyGTK>`__: Python bindings for
-    GTK  2.x (only works with Python 2).
-  * `PyGObject <https://wiki.gnome.org/action/show/Projects/PyGObject>`__ :
-    Python bindings for GTK 3.x and gobject-introspection libraries (also
-    compatible with Python 3).
-
 Testing
 ~~~~~~~
 
@@ -407,8 +394,6 @@
    :target: https://travis-ci.org/fonttools/fonttools
 .. |Appveyor Build status| image:: https://ci.appveyor.com/api/projects/status/0f7fmee9as744sl7/branch/master?svg=true
    :target: https://ci.appveyor.com/project/fonttools/fonttools/branch/master
-.. |Health| image:: https://landscape.io/github/behdad/fonttools/master/landscape.svg?style=flat
-   :target: https://landscape.io/github/behdad/fonttools/master
 .. |Coverage Status| image:: https://codecov.io/gh/fonttools/fonttools/branch/master/graph/badge.svg
    :target: https://codecov.io/gh/fonttools/fonttools
 .. |PyPI| image:: https://img.shields.io/pypi/v/fonttools.svg
diff --git a/Snippets/svg2glif.py b/Snippets/svg2glif.py
index 2dd6402..aed3100 100755
--- a/Snippets/svg2glif.py
+++ b/Snippets/svg2glif.py
@@ -8,8 +8,8 @@
 from fontTools.misc.py23 import SimpleNamespace
 from fontTools.svgLib import SVGPath
 
-from ufoLib.pointPen import SegmentToPointPen
-from ufoLib.glifLib import writeGlyphToString
+from fontTools.pens.pointPen import SegmentToPointPen
+from fontTools.ufoLib.glifLib import writeGlyphToString
 
 
 __all__ = ["svg2glif"]
diff --git a/Tests/designspaceLib/data/test.designspace b/Tests/designspaceLib/data/test.designspace
index 9f97ab5..901a0ee 100644
--- a/Tests/designspaceLib/data/test.designspace
+++ b/Tests/designspaceLib/data/test.designspace
@@ -5,9 +5,10 @@
       <labelname xml:lang="en">Wéíght</labelname>
       <labelname xml:lang="fa-IR">قطر</labelname>
     </axis>
-    <axis tag="wdth" name="width" minimum="0" maximum="1000" default="20" hidden="1">
+    <axis tag="wdth" name="width" minimum="0" maximum="1000" default="15" hidden="1">
       <labelname xml:lang="fr">Chasse</labelname>
       <map input="0" output="10"/>
+      <map input="15" output="20"/>
       <map input="401" output="66"/>
       <map input="1000" output="990"/>
     </axis>
diff --git a/Tests/designspaceLib/designspace_test.py b/Tests/designspaceLib/designspace_test.py
index 2044f00..2d01c65 100644
--- a/Tests/designspaceLib/designspace_test.py
+++ b/Tests/designspaceLib/designspace_test.py
@@ -66,10 +66,10 @@
     a2 = AxisDescriptor()
     a2.minimum = 0
     a2.maximum = 1000
-    a2.default = 20
+    a2.default = 15
     a2.name = "width"
     a2.tag = "wdth"
-    a2.map = [(0.0, 10.0), (401.0, 66.0), (1000.0, 990.0)]
+    a2.map = [(0.0, 10.0), (15.0, 20.0), (401.0, 66.0), (1000.0, 990.0)]
     a2.hidden = True
     a2.labelNames[u'fr'] = u"Chasse"
     doc.addAxis(a2)
@@ -619,7 +619,7 @@
     for axis in doc.axes:
         r.append((axis.name, axis.map))
     r.sort()
-    assert r == [('ddd', [(0, 0.1), (300, 0.5), (600, 0.5), (1000, 0.9)])]
+    assert r == [('ddd', [(0, 0.0), (300, 0.5), (600, 0.5), (1000, 1.0)])]
 
 def test_axisMapping():
     # note: because designspance lib does not do any actual
@@ -638,7 +638,7 @@
     for axis in doc.axes:
         r.append((axis.name, axis.map))
     r.sort()
-    assert r == [('ddd', [(0, 0.1), (300, 0.5), (600, 0.5), (1000, 0.9)])]
+    assert r == [('ddd', [(0, 0.0), (300, 0.5), (600, 0.5), (1000, 1.0)])]
 
 def test_rulesConditions(tmpdir):
     # tests of rules, conditionsets and conditions
@@ -847,3 +847,61 @@
     doc = DesignSpaceDocument()
     doc.write(dest)
     assert dest.exists()
+
+
+def test_findDefault_axis_mapping():
+    designspace_string = """\
+<?xml version='1.0' encoding='UTF-8'?>
+<designspace format="4.0">
+  <axes>
+    <axis tag="wght" name="Weight" minimum="100" maximum="800" default="400">
+      <map input="100" output="20"/>
+      <map input="300" output="40"/>
+      <map input="400" output="80"/>
+      <map input="700" output="126"/>
+      <map input="800" output="170"/>
+    </axis>
+    <axis tag="ital" name="Italic" minimum="0" maximum="1" default="1"/>
+  </axes>
+  <sources>
+    <source filename="Font-Light.ufo">
+      <location>
+        <dimension name="Weight" xvalue="20"/>
+        <dimension name="Italic" xvalue="0"/>
+      </location>
+    </source>
+    <source filename="Font-Regular.ufo">
+      <location>
+        <dimension name="Weight" xvalue="80"/>
+        <dimension name="Italic" xvalue="0"/>
+      </location>
+    </source>
+    <source filename="Font-Bold.ufo">
+      <location>
+        <dimension name="Weight" xvalue="170"/>
+        <dimension name="Italic" xvalue="0"/>
+      </location>
+    </source>
+    <source filename="Font-LightItalic.ufo">
+      <location>
+        <dimension name="Weight" xvalue="20"/>
+        <dimension name="Italic" xvalue="1"/>
+      </location>
+    </source>
+    <source filename="Font-Italic.ufo">
+      <location>
+        <dimension name="Weight" xvalue="80"/>
+        <dimension name="Italic" xvalue="1"/>
+      </location>
+    </source>
+    <source filename="Font-BoldItalic.ufo">
+      <location>
+        <dimension name="Weight" xvalue="170"/>
+        <dimension name="Italic" xvalue="1"/>
+      </location>
+    </source>
+  </sources>
+</designspace>
+    """
+    designspace = DesignSpaceDocument.fromstring(designspace_string)
+    assert designspace.findDefault().filename == "Font-Italic.ufo"
diff --git a/Tests/feaLib/builder_test.py b/Tests/feaLib/builder_test.py
index fc1141a..eb80078 100644
--- a/Tests/feaLib/builder_test.py
+++ b/Tests/feaLib/builder_test.py
@@ -70,7 +70,8 @@
         ZeroValue_SinglePos_horizontal ZeroValue_SinglePos_vertical
         ZeroValue_PairPos_horizontal ZeroValue_PairPos_vertical
         ZeroValue_ChainSinglePos_horizontal ZeroValue_ChainSinglePos_vertical
-        PairPosSubtable
+        PairPosSubtable ChainSubstSubtable ChainPosSubtable LigatureSubtable
+        AlternateSubtable MultipleSubstSubtable SingleSubstSubtable
     """.split()
 
     def __init__(self, methodName):
@@ -512,17 +513,20 @@
         addOpenTypeFeatures(font, tree)
         assert "GSUB" in font
 
+    @unittest.skipIf(sys.version_info[0:2] < (3, 4),
+                     "assertLogs() was introduced in 3.4")
     def test_unsupported_subtable_break(self):
-        self.assertRaisesRegex(
-            FeatureLibError,
-            'explicit "subtable" statement is intended for .* class kerning',
-            self.build,
-            "feature liga {"
-            "    sub f f by f_f;"
-            "    subtable;"
-            "    sub f i by f_i;"
-            "} liga;"
-        )
+        with self.assertLogs(level='WARNING') as logs:
+            self.build(
+                "feature test {"
+                "    pos a 10;"
+                "    subtable;"
+                "    pos b 10;"
+                "} test;"
+            )
+        self.assertEqual(logs.output,
+                ['WARNING:fontTools.feaLib.builder:<features>:1:32: '
+                 'unsupported "subtable" statement for lookup type'])
 
     def test_skip_featureNames_if_no_name_table(self):
         features = (
diff --git a/Tests/feaLib/data/AlternateSubtable.fea b/Tests/feaLib/data/AlternateSubtable.fea
new file mode 100644
index 0000000..c4bce3c
--- /dev/null
+++ b/Tests/feaLib/data/AlternateSubtable.fea
@@ -0,0 +1,5 @@
+feature test {
+    sub A from [A.alt1 A.alt2];
+    subtable;
+    sub B from [B.alt1 B.alt2];
+} test;
diff --git a/Tests/feaLib/data/AlternateSubtable.ttx b/Tests/feaLib/data/AlternateSubtable.ttx
new file mode 100644
index 0000000..e8119ba
--- /dev/null
+++ b/Tests/feaLib/data/AlternateSubtable.ttx
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="3"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <AlternateSubst index="0">
+          <AlternateSet glyph="A">
+            <Alternate glyph="A.alt1"/>
+            <Alternate glyph="A.alt2"/>
+          </AlternateSet>
+        </AlternateSubst>
+        <AlternateSubst index="1">
+          <AlternateSet glyph="B">
+            <Alternate glyph="B.alt1"/>
+            <Alternate glyph="B.alt2"/>
+          </AlternateSet>
+        </AlternateSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ChainPosSubtable.fea b/Tests/feaLib/data/ChainPosSubtable.fea
new file mode 100644
index 0000000..4650622
--- /dev/null
+++ b/Tests/feaLib/data/ChainPosSubtable.fea
@@ -0,0 +1,7 @@
+feature test {
+    pos X [A-B]' -40 B' -40 A' -40 Y;
+    subtable;
+    pos X A' -111 Y;
+    subtable;
+    pos X B' -40 A' -111 [A-C]' -40 Y;
+} test;
diff --git a/Tests/feaLib/data/ChainPosSubtable.ttx b/Tests/feaLib/data/ChainPosSubtable.ttx
new file mode 100644
index 0000000..a780ccb
--- /dev/null
+++ b/Tests/feaLib/data/ChainPosSubtable.ttx
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GPOS>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="8"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=3 -->
+        <ChainContextPos index="0" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="X"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0">
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="B"/>
+          </InputCoverage>
+          <InputCoverage index="2">
+            <Glyph value="A"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="Y"/>
+          </LookAheadCoverage>
+          <!-- PosCount=3 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="2">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="1"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+        <ChainContextPos index="1" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="X"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="A"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="Y"/>
+          </LookAheadCoverage>
+          <!-- PosCount=1 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="2"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+        <ChainContextPos index="2" Format="3">
+          <!-- BacktrackGlyphCount=1 -->
+          <BacktrackCoverage index="0">
+            <Glyph value="X"/>
+          </BacktrackCoverage>
+          <!-- InputGlyphCount=3 -->
+          <InputCoverage index="0">
+            <Glyph value="B"/>
+          </InputCoverage>
+          <InputCoverage index="1">
+            <Glyph value="A"/>
+          </InputCoverage>
+          <InputCoverage index="2">
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+            <Glyph value="C"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=1 -->
+          <LookAheadCoverage index="0">
+            <Glyph value="Y"/>
+          </LookAheadCoverage>
+          <!-- PosCount=3 -->
+          <PosLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="3"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="1">
+            <SequenceIndex value="1"/>
+            <LookupListIndex value="3"/>
+          </PosLookupRecord>
+          <PosLookupRecord index="2">
+            <SequenceIndex value="2"/>
+            <LookupListIndex value="4"/>
+          </PosLookupRecord>
+        </ChainContextPos>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="-40"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="-111"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="2">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <!-- ValueCount=2 -->
+          <Value index="0" XAdvance="-111"/>
+          <Value index="1" XAdvance="-40"/>
+        </SinglePos>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SinglePos index="0" Format="1">
+          <Coverage>
+            <Glyph value="A"/>
+            <Glyph value="B"/>
+            <Glyph value="C"/>
+          </Coverage>
+          <ValueFormat value="4"/>
+          <Value XAdvance="-40"/>
+        </SinglePos>
+      </Lookup>
+    </LookupList>
+  </GPOS>
+
+</ttFont>
diff --git a/Tests/feaLib/data/ChainSubstSubtable.fea b/Tests/feaLib/data/ChainSubstSubtable.fea
new file mode 100644
index 0000000..b6e959a
--- /dev/null
+++ b/Tests/feaLib/data/ChainSubstSubtable.fea
@@ -0,0 +1,9 @@
+feature test {
+    sub G' by G.swash;
+    subtable;
+    sub H' by H.swash;
+    subtable;
+    sub G' by g;
+    subtable;
+    sub H' by H.swash;
+} test;
diff --git a/Tests/feaLib/data/ChainSubstSubtable.ttx b/Tests/feaLib/data/ChainSubstSubtable.ttx
new file mode 100644
index 0000000..f7a09c7
--- /dev/null
+++ b/Tests/feaLib/data/ChainSubstSubtable.ttx
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=5 -->
+      <Lookup index="0">
+        <LookupType value="6"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=4 -->
+        <ChainContextSubst index="0" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="G"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="1"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="1" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="H"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="2"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="2" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="G"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="3"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+        <ChainContextSubst index="3" Format="3">
+          <!-- BacktrackGlyphCount=0 -->
+          <!-- InputGlyphCount=1 -->
+          <InputCoverage index="0">
+            <Glyph value="H"/>
+          </InputCoverage>
+          <!-- LookAheadGlyphCount=0 -->
+          <!-- SubstCount=1 -->
+          <SubstLookupRecord index="0">
+            <SequenceIndex value="0"/>
+            <LookupListIndex value="4"/>
+          </SubstLookupRecord>
+        </ChainContextSubst>
+      </Lookup>
+      <Lookup index="1">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="G" out="G.swash"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="2">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="H" out="H.swash"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="3">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="G" out="g"/>
+        </SingleSubst>
+      </Lookup>
+      <Lookup index="4">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=1 -->
+        <SingleSubst index="0">
+          <Substitution in="H" out="H.swash"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/GPOS_1_zero.fea b/Tests/feaLib/data/GPOS_1_zero.fea
index bfb51db..2e738e2 100644
--- a/Tests/feaLib/data/GPOS_1_zero.fea
+++ b/Tests/feaLib/data/GPOS_1_zero.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/471
+# https://github.com/fonttools/fonttools/issues/471
 feature test {
     pos zero 0;
     pos four 500;
diff --git a/Tests/feaLib/data/GPOS_2.fea b/Tests/feaLib/data/GPOS_2.fea
index b1f26d1..2948148 100644
--- a/Tests/feaLib/data/GPOS_2.fea
+++ b/Tests/feaLib/data/GPOS_2.fea
@@ -1,7 +1,7 @@
 languagesystem DFLT dflt;
 
 # Mixes kerning between single glyphs, and class-based kerning.
-# https://github.com/behdad/fonttools/issues/456
+# https://github.com/fonttools/fonttools/issues/456
 lookup MixedKerning {
     pos v v 14;
     pos [D O Q] [T V W] -26;
diff --git a/Tests/feaLib/data/LigatureSubtable.fea b/Tests/feaLib/data/LigatureSubtable.fea
new file mode 100644
index 0000000..aa4ec26
--- /dev/null
+++ b/Tests/feaLib/data/LigatureSubtable.fea
@@ -0,0 +1,5 @@
+feature test {
+    sub f f by f_f;
+    subtable;
+    sub f i by f_i;
+} test;
diff --git a/Tests/feaLib/data/LigatureSubtable.ttx b/Tests/feaLib/data/LigatureSubtable.ttx
new file mode 100644
index 0000000..e13451a
--- /dev/null
+++ b/Tests/feaLib/data/LigatureSubtable.ttx
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="4"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <LigatureSubst index="0">
+          <LigatureSet glyph="f">
+            <Ligature components="f" glyph="f_f"/>
+          </LigatureSet>
+        </LigatureSubst>
+        <LigatureSubst index="1">
+          <LigatureSet glyph="f">
+            <Ligature components="i" glyph="f_i"/>
+          </LigatureSet>
+        </LigatureSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/MultipleSubstSubtable.fea b/Tests/feaLib/data/MultipleSubstSubtable.fea
new file mode 100644
index 0000000..4964037
--- /dev/null
+++ b/Tests/feaLib/data/MultipleSubstSubtable.fea
@@ -0,0 +1,5 @@
+feature test {
+    sub c_t by c t;
+    subtable;
+    sub f_i by f i;
+} test;
diff --git a/Tests/feaLib/data/MultipleSubstSubtable.ttx b/Tests/feaLib/data/MultipleSubstSubtable.ttx
new file mode 100644
index 0000000..6b3afd4
--- /dev/null
+++ b/Tests/feaLib/data/MultipleSubstSubtable.ttx
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="2"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <MultipleSubst index="0">
+          <Substitution in="c_t" out="c,t"/>
+        </MultipleSubst>
+        <MultipleSubst index="1">
+          <Substitution in="f_i" out="f,i"/>
+        </MultipleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/SingleSubstSubtable.fea b/Tests/feaLib/data/SingleSubstSubtable.fea
new file mode 100644
index 0000000..f5a6da3
--- /dev/null
+++ b/Tests/feaLib/data/SingleSubstSubtable.fea
@@ -0,0 +1,5 @@
+feature test {
+    sub a by b;
+    subtable;
+    sub c by d;
+} test;
diff --git a/Tests/feaLib/data/SingleSubstSubtable.ttx b/Tests/feaLib/data/SingleSubstSubtable.ttx
new file mode 100644
index 0000000..5030b73
--- /dev/null
+++ b/Tests/feaLib/data/SingleSubstSubtable.ttx
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont>
+
+  <GSUB>
+    <Version value="0x00010000"/>
+    <ScriptList>
+      <!-- ScriptCount=1 -->
+      <ScriptRecord index="0">
+        <ScriptTag value="DFLT"/>
+        <Script>
+          <DefaultLangSys>
+            <ReqFeatureIndex value="65535"/>
+            <!-- FeatureCount=1 -->
+            <FeatureIndex index="0" value="0"/>
+          </DefaultLangSys>
+          <!-- LangSysCount=0 -->
+        </Script>
+      </ScriptRecord>
+    </ScriptList>
+    <FeatureList>
+      <!-- FeatureCount=1 -->
+      <FeatureRecord index="0">
+        <FeatureTag value="test"/>
+        <Feature>
+          <!-- LookupCount=1 -->
+          <LookupListIndex index="0" value="0"/>
+        </Feature>
+      </FeatureRecord>
+    </FeatureList>
+    <LookupList>
+      <!-- LookupCount=1 -->
+      <Lookup index="0">
+        <LookupType value="1"/>
+        <LookupFlag value="0"/>
+        <!-- SubTableCount=2 -->
+        <SingleSubst index="0">
+          <Substitution in="a" out="b"/>
+        </SingleSubst>
+        <SingleSubst index="1">
+          <Substitution in="c" out="d"/>
+        </SingleSubst>
+      </Lookup>
+    </LookupList>
+  </GSUB>
+
+</ttFont>
diff --git a/Tests/feaLib/data/bug453.fea b/Tests/feaLib/data/bug453.fea
index fc5072e..486632e 100644
--- a/Tests/feaLib/data/bug453.fea
+++ b/Tests/feaLib/data/bug453.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/453
+# https://github.com/fonttools/fonttools/issues/453
 feature mark {
     lookup mark1 {
         markClass [acute] <anchor 150 -10> @TOP_MARKS;
diff --git a/Tests/feaLib/data/bug463.fea b/Tests/feaLib/data/bug463.fea
index e7e21af..4b1910c 100644
--- a/Tests/feaLib/data/bug463.fea
+++ b/Tests/feaLib/data/bug463.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/463
+# https://github.com/fonttools/fonttools/issues/463
 feature ordn {
     @DIGIT = [zero one two three four five six seven eight nine];
     sub @DIGIT [A a]' by ordfeminine;
diff --git a/Tests/feaLib/data/bug501.fea b/Tests/feaLib/data/bug501.fea
index 58a4081..06d91dd 100644
--- a/Tests/feaLib/data/bug501.fea
+++ b/Tests/feaLib/data/bug501.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/501
+# https://github.com/fonttools/fonttools/issues/501
 languagesystem DFLT dflt;
 feature test {
     lookup L {
diff --git a/Tests/feaLib/data/bug502.fea b/Tests/feaLib/data/bug502.fea
index b8130c4..3fcb94e 100644
--- a/Tests/feaLib/data/bug502.fea
+++ b/Tests/feaLib/data/bug502.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/502
+# https://github.com/fonttools/fonttools/issues/502
 feature aalt {
     sub A by A.alt1;
     sub Eng by Eng.alt1;
diff --git a/Tests/feaLib/data/bug504.fea b/Tests/feaLib/data/bug504.fea
index 64d05ba..e6920b2 100644
--- a/Tests/feaLib/data/bug504.fea
+++ b/Tests/feaLib/data/bug504.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/504
+# https://github.com/fonttools/fonttools/issues/504
 
 feature test {
     sub [a b c d] by [T E S T];
diff --git a/Tests/feaLib/data/bug505.fea b/Tests/feaLib/data/bug505.fea
index 016ed1f..742ea11 100644
--- a/Tests/feaLib/data/bug505.fea
+++ b/Tests/feaLib/data/bug505.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/505
+# https://github.com/fonttools/fonttools/issues/505
 
 languagesystem armn dflt;
 languagesystem avst dflt;
diff --git a/Tests/feaLib/data/bug506.fea b/Tests/feaLib/data/bug506.fea
index 62e4bbb..2e1e97d 100644
--- a/Tests/feaLib/data/bug506.fea
+++ b/Tests/feaLib/data/bug506.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/506
+# https://github.com/fonttools/fonttools/issues/506
 feature test {
     sub f' i' by f_i;
 } test;
diff --git a/Tests/feaLib/data/bug568.fea b/Tests/feaLib/data/bug568.fea
index 3e879b1..d9f56f9 100644
--- a/Tests/feaLib/data/bug568.fea
+++ b/Tests/feaLib/data/bug568.fea
@@ -1,4 +1,4 @@
-# https://github.com/behdad/fonttools/issues/568
+# https://github.com/fonttools/fonttools/issues/568
 
 feature tst1 {
   script latn;
diff --git a/Tests/feaLib/parser_test.py b/Tests/feaLib/parser_test.py
index bf26023..98ac101 100644
--- a/Tests/feaLib/parser_test.py
+++ b/Tests/feaLib/parser_test.py
@@ -408,7 +408,7 @@
         self.assertEqual(smcp.statements[0].glyphSet(), ("a", "b", "s"))
 
     def test_glyphclass_scoping_bug496(self):
-        # https://github.com/behdad/fonttools/issues/496
+        # https://github.com/fonttools/fonttools/issues/496
         f1, f2 = self.parse(
             "feature F1 { lookup L { @GLYPHCLASS = [A B C];} L; } F1;"
             "feature F2 { sub @GLYPHCLASS by D; } F2;"
@@ -677,6 +677,7 @@
         self.assertEqual(flag.value, 9)
         self.assertIsNone(flag.markAttachment)
         self.assertIsNone(flag.markFilteringSet)
+        self.assertEqual(flag.asFea(), "lookupflag RightToLeft IgnoreMarks;")
 
     def test_lookupflag_format_A_MarkAttachmentType(self):
         flag = self.parse_lookupflag_(
@@ -688,6 +689,8 @@
         self.assertEqual(flag.markAttachment.glyphSet(),
                          ("acute", "grave", "macron"))
         self.assertIsNone(flag.markFilteringSet)
+        self.assertEqual(flag.asFea(),
+            "lookupflag RightToLeft MarkAttachmentType @TOP_MARKS;")
 
     def test_lookupflag_format_A_UseMarkFilteringSet(self):
         flag = self.parse_lookupflag_(
@@ -699,6 +702,8 @@
         self.assertIsInstance(flag.markFilteringSet, ast.GlyphClassName)
         self.assertEqual(flag.markFilteringSet.glyphSet(),
                          ("cedilla", "ogonek"))
+        self.assertEqual(flag.asFea(),
+            "lookupflag IgnoreLigatures UseMarkFilteringSet @BOTTOM_MARKS;")
 
     def test_lookupflag_format_B(self):
         flag = self.parse_lookupflag_("lookupflag 7;")
@@ -706,6 +711,23 @@
         self.assertEqual(flag.value, 7)
         self.assertIsNone(flag.markAttachment)
         self.assertIsNone(flag.markFilteringSet)
+        self.assertEqual(flag.asFea(),
+            "lookupflag RightToLeft IgnoreBaseGlyphs IgnoreLigatures;")
+
+    def test_lookupflag_format_B_zero(self):
+        flag = self.parse_lookupflag_("lookupflag 0;")
+        self.assertIsInstance(flag, ast.LookupFlagStatement)
+        self.assertEqual(flag.value, 0)
+        self.assertIsNone(flag.markAttachment)
+        self.assertIsNone(flag.markFilteringSet)
+        self.assertEqual(flag.asFea(), "lookupflag 0;")
+
+    def test_lookupflag_no_value(self):
+        self.assertRaisesRegex(
+            FeatureLibError,
+            'lookupflag must have a value',
+            self.parse,
+            "feature test {lookupflag;} test;")
 
     def test_lookupflag_repeated(self):
         self.assertRaisesRegex(
@@ -1289,6 +1311,14 @@
         self.assertEqual(sub.glyph, "f_f_i")
         self.assertEqual(sub.replacement, ("f", "f", "i"))
 
+    def test_substitute_multiple_force_chained(self):
+        doc = self.parse("lookup L {sub f_f_i' by f f i;} L;")
+        sub = doc.statements[0].statements[0]
+        self.assertIsInstance(sub, ast.MultipleSubstStatement)
+        self.assertEqual(sub.glyph, "f_f_i")
+        self.assertEqual(sub.replacement, ("f", "f", "i"))
+        self.assertEqual(sub.asFea(), "sub f_f_i' by f f i;")
+
     def test_substitute_multiple_by_mutliple(self):
         self.assertRaisesRegex(
             FeatureLibError,
diff --git a/Tests/misc/xmlWriter_test.py b/Tests/misc/xmlWriter_test.py
index 0930e1c..ac8a789 100644
--- a/Tests/misc/xmlWriter_test.py
+++ b/Tests/misc/xmlWriter_test.py
@@ -26,19 +26,19 @@
 				 writer.file.getvalue())
 
 	def test_encoding_utf8(self):
-		# https://github.com/behdad/fonttools/issues/246
+		# https://github.com/fonttools/fonttools/issues/246
 		writer = XMLWriter(BytesIO(), encoding="utf8")
 		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
 				 writer.file.getvalue())
 
 	def test_encoding_UTF_8(self):
-		# https://github.com/behdad/fonttools/issues/246
+		# https://github.com/fonttools/fonttools/issues/246
 		writer = XMLWriter(BytesIO(), encoding="UTF-8")
 		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
 				 writer.file.getvalue())
 
 	def test_encoding_UTF8(self):
-		# https://github.com/behdad/fonttools/issues/246
+		# https://github.com/fonttools/fonttools/issues/246
 		writer = XMLWriter(BytesIO(), encoding="UTF8")
 		self.assertEqual(b'<?xml version="1.0" encoding="UTF-8"?>' + linesep,
 				 writer.file.getvalue())
diff --git a/Tests/subset/subset_test.py b/Tests/subset/subset_test.py
index d78c496..2fc3b8a 100644
--- a/Tests/subset/subset_test.py
+++ b/Tests/subset/subset_test.py
@@ -298,7 +298,7 @@
         self.expect_ttx(subsetfont, self.getpath("expect_prop_1.ttx"), ["prop"])
 
     def test_options(self):
-        # https://github.com/behdad/fonttools/issues/413
+        # https://github.com/fonttools/fonttools/issues/413
         opt1 = subset.Options()
         self.assertTrue('Xyz-' not in opt1.layout_features)
         opt2 = subset.Options()
diff --git a/Tests/ttLib/data/TestOTF-Regular.otx b/Tests/ttLib/data/TestOTF-Regular.otx
index 573fe24..92e0b2f 100644
--- a/Tests/ttLib/data/TestOTF-Regular.otx
+++ b/Tests/ttLib/data/TestOTF-Regular.otx
@@ -142,13 +142,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
     <namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
       Test TTF
@@ -184,13 +184,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
   </name>
 
diff --git a/Tests/ttLib/data/TestTTF-Regular.ttx b/Tests/ttLib/data/TestTTF-Regular.ttx
index 8ffcefe..1f1dd2b 100644
--- a/Tests/ttLib/data/TestTTF-Regular.ttx
+++ b/Tests/ttLib/data/TestTTF-Regular.ttx
@@ -462,13 +462,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
     <namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
       Test TTF
@@ -504,13 +504,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
   </name>
 
diff --git a/Tests/ttLib/tables/TupleVariation_test.py b/Tests/ttLib/tables/TupleVariation_test.py
index 6986d5b..aab4cba 100644
--- a/Tests/ttLib/tables/TupleVariation_test.py
+++ b/Tests/ttLib/tables/TupleVariation_test.py
@@ -107,7 +107,7 @@
 		self.assertIn("bad delta format", [r.msg for r in captor.records])
 		self.assertEqual([
 			'<tuple>',
-			  '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
+			  '<coord axis="wdth" min="0.3" value="0.4" max="0.5"/>',
 			  '<!-- bad delta #0 -->',
 			'</tuple>',
 		], TupleVariationTest.xml_lines(writer))
@@ -118,7 +118,7 @@
 		g.toXML(writer, ["wdth", "wght", "opsz"])
 		self.assertEqual([
 			'<tuple>',
-			  '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
+			  '<coord axis="wdth" min="0.3" value="0.4" max="0.5"/>',
 			  '<coord axis="wght" value="1.0"/>',
 			  '<coord axis="opsz" value="-0.7"/>',
 			  '<delta cvt="0" value="42"/>',
@@ -134,7 +134,7 @@
 		g.toXML(writer, ["wdth", "wght", "opsz"])
 		self.assertEqual([
 			'<tuple>',
-			  '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
+			  '<coord axis="wdth" min="0.3" value="0.4" max="0.5"/>',
 			  '<coord axis="wght" value="1.0"/>',
 			  '<coord axis="opsz" value="-0.7"/>',
 			  '<delta pt="0" x="9" y="8"/>',
@@ -370,7 +370,7 @@
 		self.assertEqual(({"wght": -1.0, "wdth": 0.5}, 6), decompileCoord(["wght", "wdth"], data, 2))
 
 	def test_decompileCoord_roundTrip(self):
-		# Make sure we are not affected by https://github.com/behdad/fonttools/issues/286
+		# Make sure we are not affected by https://github.com/fonttools/fonttools/issues/286
 		data = deHexStr("7F B9 80 35")
 		values, _ = TupleVariation.decompileCoord_(["wght", "wdth"], data, 0)
 		axisValues = {axis:(val, val, val) for axis, val in  values.items()}
diff --git a/Tests/ttLib/tables/_c_m_a_p_test.py b/Tests/ttLib/tables/_c_m_a_p_test.py
index 306d048..233cd14 100644
--- a/Tests/ttLib/tables/_c_m_a_p_test.py
+++ b/Tests/ttLib/tables/_c_m_a_p_test.py
@@ -51,6 +51,47 @@
 		self.assertEqual(subtable.getEncoding("ascii"), "ascii")
 		self.assertEqual(subtable.getEncoding(default="xyz"), "xyz")
 
+	def test_compile_2(self):
+		subtable = self.makeSubtable(2, 1, 2, 0)
+		subtable.cmap = {c: "cid%05d" % c for c in range(32, 8192)}
+		font = ttLib.TTFont()
+		font.setGlyphOrder([".notdef"] + list(subtable.cmap.values()))
+		data = subtable.compile(font)
+
+		subtable2 = CmapSubtable.newSubtable(2)
+		subtable2.decompile(data, font)
+		self.assertEqual(subtable2.cmap, subtable.cmap)
+
+	def test_compile_2_rebuild_rev_glyph_order(self):
+		for fmt in [2, 4, 12]:
+			subtable = self.makeSubtable(fmt, 1, 2, 0)
+			subtable.cmap = {c: "cid%05d" % c for c in range(32, 8192)}
+			font = ttLib.TTFont()
+			font.setGlyphOrder([".notdef"] + list(subtable.cmap.values()))
+			font._reverseGlyphOrderDict = {}  # force first KeyError branch in subtable.compile()
+			data = subtable.compile(font)
+			subtable2 = CmapSubtable.newSubtable(fmt)
+			subtable2.decompile(data, font)
+			self.assertEqual(subtable2.cmap, subtable.cmap, str(fmt))
+
+	def test_compile_2_gids(self):
+		for fmt in [2, 4, 12]:
+			subtable = self.makeSubtable(fmt, 1, 3, 0)
+			subtable.cmap = {0x0041:'gid001', 0x0042:'gid002'}
+			font = ttLib.TTFont()
+			font.setGlyphOrder([".notdef"])
+			data = subtable.compile(font)
+
+	def test_compile_decompile_4_empty(self):
+		subtable = self.makeSubtable(4, 3, 1, 0)
+		subtable.cmap = {}
+		font = ttLib.TTFont()
+		font.setGlyphOrder([])
+		data = subtable.compile(font)
+		subtable2 = CmapSubtable.newSubtable(4)
+		subtable2.decompile(data, font)
+		self.assertEqual(subtable2.cmap, {})
+
 	def test_decompile_4(self):
 		subtable = CmapSubtable.newSubtable(4)
 		font = ttLib.TTFont()
diff --git a/Tests/ttLib/tables/_g_l_y_f_test.py b/Tests/ttLib/tables/_g_l_y_f_test.py
index d078692..00c74bc 100644
--- a/Tests/ttLib/tables/_g_l_y_f_test.py
+++ b/Tests/ttLib/tables/_g_l_y_f_test.py
@@ -1,7 +1,8 @@
 from __future__ import print_function, division, absolute_import
 from fontTools.misc.py23 import *
 from fontTools.misc.fixedTools import otRound
-from fontTools.ttLib import TTFont, newTable
+from fontTools.pens.ttGlyphPen import TTGlyphPen
+from fontTools.ttLib import TTFont, newTable, TTLibError
 from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
 import sys
 import array
@@ -180,6 +181,13 @@
 
 class glyfTableTest(unittest.TestCase):
 
+    def __init__(self, methodName):
+        unittest.TestCase.__init__(self, methodName)
+        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
+        # and fires deprecation warnings if a program uses the old name.
+        if not hasattr(self, "assertRaisesRegex"):
+            self.assertRaisesRegex = self.assertRaisesRegexp
+
     @classmethod
     def setUpClass(cls):
         with open(GLYF_BIN, 'rb') as f:
@@ -215,6 +223,23 @@
         glyfData = glyfTable.compile(font)
         self.assertEqual(glyfData, self.glyfData)
 
+    def test_recursiveComponent(self):
+        glyphSet = {}
+        pen_dummy = TTGlyphPen(glyphSet)
+        glyph_dummy = pen_dummy.glyph()
+        glyphSet["A"] = glyph_dummy
+        glyphSet["B"] = glyph_dummy
+        pen_A = TTGlyphPen(glyphSet)
+        pen_A.addComponent("B", (1, 0, 0, 1, 0, 0))
+        pen_B = TTGlyphPen(glyphSet)
+        pen_B.addComponent("A", (1, 0, 0, 1, 0, 0))
+        glyph_A = pen_A.glyph()
+        glyph_B = pen_B.glyph()
+        glyphSet["A"] = glyph_A
+        glyphSet["B"] = glyph_B
+        with self.assertRaisesRegex(TTLibError, "glyph '.' contains a recursive component reference"):
+            glyph_A.getCoordinates(glyphSet)
+
 
 if __name__ == "__main__":
     import sys
diff --git a/Tests/ttLib/tables/_g_v_a_r_test.py b/Tests/ttLib/tables/_g_v_a_r_test.py
index 64adbb6..ebae580 100644
--- a/Tests/ttLib/tables/_g_v_a_r_test.py
+++ b/Tests/ttLib/tables/_g_v_a_r_test.py
@@ -93,7 +93,7 @@
     '</glyphVariations>',
     '<glyphVariations glyph="I">',
     '  <tuple>',
-    '    <coord axis="wght" max="1.0" min="0.0" value="0.5"/>',
+    '    <coord axis="wght" min="0.0" value="0.5" max="1.0"/>',
     '    <delta pt="0" x="3" y="3"/>',
     '    <delta pt="1" x="1" y="1"/>',
     '    <delta pt="2" x="4" y="4"/>',
diff --git a/Tests/ttLib/tables/_n_a_m_e_test.py b/Tests/ttLib/tables/_n_a_m_e_test.py
index fde14bc..136b63f 100644
--- a/Tests/ttLib/tables/_n_a_m_e_test.py
+++ b/Tests/ttLib/tables/_n_a_m_e_test.py
@@ -171,7 +171,7 @@
 		captor.assertRegex("cannot store language la into 'ltag' table")
 
 	def test_decompile_badOffset(self):
-                # https://github.com/behdad/fonttools/issues/525
+                # https://github.com/fonttools/fonttools/issues/525
 		table = table__n_a_m_e()
 		badRecord = {
 			"platformID": 1,
diff --git a/Tests/ttLib/tables/otTables_test.py b/Tests/ttLib/tables/otTables_test.py
index 5a771e6..f39e27c 100644
--- a/Tests/ttLib/tables/otTables_test.py
+++ b/Tests/ttLib/tables/otTables_test.py
@@ -166,7 +166,7 @@
                          {'c_t': ['c', 't'], 'f_f_i': ['f', 'f', 'i']})
 
     def test_fromXML_oldFormat_bug385(self):
-        # https://github.com/behdad/fonttools/issues/385
+        # https://github.com/fonttools/fonttools/issues/385
         table = otTables.MultipleSubst()
         table.Format = 1
         for name, attrs, content in parseXML(
diff --git a/Tests/ttx/data/TestOTF.ttx b/Tests/ttx/data/TestOTF.ttx
index 852dacf..96f1844 100644
--- a/Tests/ttx/data/TestOTF.ttx
+++ b/Tests/ttx/data/TestOTF.ttx
@@ -142,13 +142,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
     <namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
       Test TTF
@@ -184,13 +184,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
   </name>
 
diff --git a/Tests/ttx/data/TestTTF.ttx b/Tests/ttx/data/TestTTF.ttx
index c283a29..66caf6c 100644
--- a/Tests/ttx/data/TestTTF.ttx
+++ b/Tests/ttx/data/TestTTF.ttx
@@ -462,13 +462,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="1" platEncID="0" langID="0x0" unicode="True">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
     <namerecord nameID="18" platformID="1" platEncID="0" langID="0x0" unicode="True">
       Test TTF
@@ -504,13 +504,13 @@
       FontTools
     </namerecord>
     <namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools
+      https://github.com/fonttools/fonttools
     </namerecord>
     <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
-      https://github.com/behdad/fonttools/blob/master/LICENSE.txt
+      https://github.com/fonttools/fonttools/blob/master/LICENSE
     </namerecord>
   </name>
 
diff --git a/Tests/varLib/data/test_results/Build.ttx b/Tests/varLib/data/test_results/Build.ttx
index 5d665e3..1bc1880 100644
--- a/Tests/varLib/data/test_results/Build.ttx
+++ b/Tests/varLib/data/test_results/Build.ttx
@@ -281,7 +281,7 @@
         <delta pt="15" x="0" y="0"/>
         <delta pt="16" x="0" y="0"/>
         <delta pt="17" x="0" y="0"/>
-        <delta pt="18" x="0" y="7"/>
+        <delta pt="18" x="0" y="0"/>
         <delta pt="19" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -304,7 +304,7 @@
         <delta pt="15" x="0" y="0"/>
         <delta pt="16" x="0" y="0"/>
         <delta pt="17" x="0" y="0"/>
-        <delta pt="18" x="0" y="-18"/>
+        <delta pt="18" x="0" y="0"/>
         <delta pt="19" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -424,7 +424,7 @@
         <delta pt="23" x="-2" y="0"/>
         <delta pt="24" x="0" y="0"/>
         <delta pt="25" x="-10" y="0"/>
-        <delta pt="26" x="0" y="9"/>
+        <delta pt="26" x="0" y="0"/>
         <delta pt="27" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -455,7 +455,7 @@
         <delta pt="23" x="12" y="0"/>
         <delta pt="24" x="0" y="0"/>
         <delta pt="25" x="17" y="0"/>
-        <delta pt="26" x="0" y="-23"/>
+        <delta pt="26" x="0" y="0"/>
         <delta pt="27" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -620,7 +620,7 @@
         <delta pt="60" x="33" y="-1"/>
         <delta pt="61" x="0" y="0"/>
         <delta pt="62" x="-3" y="0"/>
-        <delta pt="63" x="0" y="-4"/>
+        <delta pt="63" x="0" y="0"/>
         <delta pt="64" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -688,8 +688,8 @@
         <delta pt="60" x="-25" y="-13"/>
         <delta pt="61" x="0" y="0"/>
         <delta pt="62" x="32" y="0"/>
-        <delta pt="63" x="0" y="16"/>
-        <delta pt="64" x="0" y="3"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
       </tuple>
       <tuple>
         <coord axis="cntr" value="1.0"/>
@@ -965,7 +965,7 @@
         <delta pt="61" x="-15" y="0"/>
         <delta pt="62" x="0" y="0"/>
         <delta pt="63" x="-7" y="0"/>
-        <delta pt="64" x="0" y="12"/>
+        <delta pt="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1034,7 +1034,7 @@
         <delta pt="61" x="39" y="0"/>
         <delta pt="62" x="0" y="0"/>
         <delta pt="63" x="63" y="0"/>
-        <delta pt="64" x="0" y="-19"/>
+        <delta pt="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1314,7 +1314,7 @@
         <delta pt="61" x="-15" y="0"/>
         <delta pt="62" x="0" y="0"/>
         <delta pt="63" x="-7" y="0"/>
-        <delta pt="64" x="0" y="12"/>
+        <delta pt="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1383,7 +1383,7 @@
         <delta pt="61" x="49" y="0"/>
         <delta pt="62" x="0" y="0"/>
         <delta pt="63" x="63" y="0"/>
-        <delta pt="64" x="0" y="-19"/>
+        <delta pt="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
diff --git a/Tests/varLib/data/test_results/BuildMain.ttx b/Tests/varLib/data/test_results/BuildMain.ttx
index 84a7573..6875c71 100644
--- a/Tests/varLib/data/test_results/BuildMain.ttx
+++ b/Tests/varLib/data/test_results/BuildMain.ttx
@@ -927,7 +927,7 @@
         <delta pt="15" x="0" y="0"/>
         <delta pt="16" x="0" y="0"/>
         <delta pt="17" x="0" y="0"/>
-        <delta pt="18" x="0" y="7"/>
+        <delta pt="18" x="0" y="0"/>
         <delta pt="19" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -950,7 +950,7 @@
         <delta pt="15" x="0" y="0"/>
         <delta pt="16" x="0" y="0"/>
         <delta pt="17" x="0" y="0"/>
-        <delta pt="18" x="0" y="-18"/>
+        <delta pt="18" x="0" y="0"/>
         <delta pt="19" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1070,7 +1070,7 @@
         <delta pt="23" x="-2" y="0"/>
         <delta pt="24" x="0" y="0"/>
         <delta pt="25" x="-10" y="0"/>
-        <delta pt="26" x="0" y="9"/>
+        <delta pt="26" x="0" y="0"/>
         <delta pt="27" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1101,7 +1101,7 @@
         <delta pt="23" x="12" y="0"/>
         <delta pt="24" x="0" y="0"/>
         <delta pt="25" x="17" y="0"/>
-        <delta pt="26" x="0" y="-23"/>
+        <delta pt="26" x="0" y="0"/>
         <delta pt="27" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1266,7 +1266,7 @@
         <delta pt="60" x="33" y="-1"/>
         <delta pt="61" x="0" y="0"/>
         <delta pt="62" x="-3" y="0"/>
-        <delta pt="63" x="0" y="-4"/>
+        <delta pt="63" x="0" y="0"/>
         <delta pt="64" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1334,8 +1334,8 @@
         <delta pt="60" x="-25" y="-13"/>
         <delta pt="61" x="0" y="0"/>
         <delta pt="62" x="32" y="0"/>
-        <delta pt="63" x="0" y="16"/>
-        <delta pt="64" x="0" y="3"/>
+        <delta pt="63" x="0" y="0"/>
+        <delta pt="64" x="0" y="0"/>
       </tuple>
       <tuple>
         <coord axis="cntr" value="1.0"/>
@@ -1611,7 +1611,7 @@
         <delta pt="61" x="-15" y="0"/>
         <delta pt="62" x="0" y="0"/>
         <delta pt="63" x="-7" y="0"/>
-        <delta pt="64" x="0" y="12"/>
+        <delta pt="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1680,7 +1680,7 @@
         <delta pt="61" x="39" y="0"/>
         <delta pt="62" x="0" y="0"/>
         <delta pt="63" x="63" y="0"/>
-        <delta pt="64" x="0" y="-19"/>
+        <delta pt="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -1960,7 +1960,7 @@
         <delta pt="61" x="-15" y="0"/>
         <delta pt="62" x="0" y="0"/>
         <delta pt="63" x="-7" y="0"/>
-        <delta pt="64" x="0" y="12"/>
+        <delta pt="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
@@ -2029,7 +2029,7 @@
         <delta pt="61" x="49" y="0"/>
         <delta pt="62" x="0" y="0"/>
         <delta pt="63" x="63" y="0"/>
-        <delta pt="64" x="0" y="-19"/>
+        <delta pt="64" x="0" y="0"/>
         <delta pt="65" x="0" y="0"/>
       </tuple>
       <tuple>
diff --git a/Tests/varLib/data/test_results/Mutator.ttx b/Tests/varLib/data/test_results/Mutator.ttx
index a106d21..fac0997 100644
--- a/Tests/varLib/data/test_results/Mutator.ttx
+++ b/Tests/varLib/data/test_results/Mutator.ttx
@@ -154,7 +154,7 @@
 
     <TTGlyph name=".notdef" xMin="80" yMin="0" xMax="560" yMax="666">
       <contour>
-        <pt x="83" y="0" on="1"/>
+        <pt x="83" y="0" on="1" overlap="1"/>
         <pt x="503" y="666" on="1"/>
         <pt x="557" y="666" on="1"/>
         <pt x="137" y="0" on="1"/>
@@ -190,7 +190,7 @@
 
     <TTGlyph name="uni0024" xMin="51" yMin="-115" xMax="474" yMax="746">
       <contour>
-        <pt x="251" y="31" on="1"/>
+        <pt x="251" y="31" on="1" overlap="1"/>
         <pt x="309" y="31" on="0"/>
         <pt x="379" y="92" on="0"/>
         <pt x="379" y="144" on="1"/>
@@ -262,7 +262,7 @@
 
     <TTGlyph name="uni0024.nostroke" xMin="51" yMin="-115" xMax="474" yMax="746">
       <contour>
-        <pt x="251" y="31" on="1"/>
+        <pt x="251" y="31" on="1" overlap="1"/>
         <pt x="308" y="31" on="0"/>
         <pt x="377" y="90" on="0"/>
         <pt x="377" y="142" on="1"/>
@@ -334,7 +334,7 @@
 
     <TTGlyph name="uni0041" xMin="7" yMin="0" xMax="656" yMax="670">
       <contour>
-        <pt x="7" y="0" on="1"/>
+        <pt x="7" y="0" on="1" overlap="1"/>
         <pt x="7" y="38" on="1"/>
         <pt x="104" y="53" on="1"/>
         <pt x="124" y="53" on="1"/>
@@ -370,7 +370,7 @@
 
     <TTGlyph name="uni0061" xMin="42" yMin="-14" xMax="511" yMax="490">
       <contour>
-        <pt x="42" y="110" on="1"/>
+        <pt x="42" y="110" on="1" overlap="1"/>
         <pt x="42" y="157" on="0"/>
         <pt x="110" y="229" on="0"/>
         <pt x="214" y="265" on="1"/>
diff --git a/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx b/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx
index a20f0e4..28e0766 100755
--- a/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx
+++ b/Tests/varLib/data/test_results/Mutator_Getvar-instance.ttx
@@ -168,7 +168,7 @@
 
     <TTGlyph name="a" xMin="38" yMin="-12" xMax="388" yMax="468">
       <contour>
-        <pt x="312" y="0" on="1"/>
+        <pt x="312" y="0" on="1" overlap="1"/>
         <pt x="312" y="64" on="1"/>
         <pt x="244" y="-12" on="1"/>
         <pt x="180" y="-12" on="1"/>
@@ -200,7 +200,7 @@
 
     <TTGlyph name="b" xMin="76" yMin="-12" xMax="426" yMax="628">
       <contour>
-        <pt x="218" y="468" on="1"/>
+        <pt x="218" y="468" on="1" overlap="1"/>
         <pt x="284" y="468" on="1"/>
         <pt x="426" y="316" on="1"/>
         <pt x="426" y="140" on="1"/>
@@ -229,7 +229,7 @@
     <TTGlyph name="nonmarkingreturn"/><!-- contains no outline data -->
 
     <TTGlyph name="q" xMin="38" yMin="-172" xMax="388" yMax="468">
-      <component glyphName="b" x="464" y="456" scale="-0.99994" flags="0x4"/>
+      <component glyphName="b" x="464" y="456" scale="-0.99994" flags="0x404"/>
     </TTGlyph>
 
     <TTGlyph name="space"/><!-- contains no outline data -->
diff --git a/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx b/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
index 1800479..1f7b651 100755
--- a/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
+++ b/Tests/varLib/data/test_results/Mutator_IUP-instance.ttx
@@ -157,7 +157,7 @@
 
     <TTGlyph name="a" xMin="38" yMin="-12" xMax="388" yMax="468">
       <contour>
-        <pt x="312" y="0" on="1"/>
+        <pt x="312" y="0" on="1" overlap="1"/>
         <pt x="312" y="64" on="1"/>
         <pt x="244" y="-12" on="1"/>
         <pt x="180" y="-12" on="1"/>
@@ -185,7 +185,7 @@
 
     <TTGlyph name="b" xMin="76" yMin="-12" xMax="426" yMax="628">
       <contour>
-        <pt x="218" y="468" on="1"/>
+        <pt x="218" y="468" on="1" overlap="1"/>
         <pt x="284" y="468" on="1"/>
         <pt x="426" y="316" on="1"/>
         <pt x="426" y="140" on="1"/>
@@ -214,7 +214,7 @@
     <TTGlyph name="nonmarkingreturn"/><!-- contains no outline data -->
 
     <TTGlyph name="q" xMin="38" yMin="-172" xMax="388" yMax="468">
-      <component glyphName="b" x="464" y="456" scale="-0.99994" flags="0x4"/>
+      <component glyphName="b" x="464" y="456" scale="-0.99994" flags="0x404"/>
     </TTGlyph>
 
     <TTGlyph name="space"/><!-- contains no outline data -->
diff --git a/Tests/varLib/data/test_results/SparseMasters.ttx b/Tests/varLib/data/test_results/SparseMasters.ttx
index 99a80bf..06e58c9 100644
--- a/Tests/varLib/data/test_results/SparseMasters.ttx
+++ b/Tests/varLib/data/test_results/SparseMasters.ttx
@@ -574,7 +574,7 @@
     </glyphVariations>
     <glyphVariations glyph="e">
       <tuple>
-        <coord axis="wght" max="1.0" min="0.0" value="0.36365"/>
+        <coord axis="wght" min="0.0" value="0.36365" max="1.0"/>
         <delta pt="0" x="-1" y="-25"/>
         <delta pt="1" x="0" y="0"/>
         <delta pt="2" x="-84" y="5"/>
@@ -594,7 +594,7 @@
         <delta pt="16" x="0" y="0"/>
       </tuple>
       <tuple>
-        <coord axis="wght" max="1.0" min="0.36365" value="1.0"/>
+        <coord axis="wght" min="0.36365" value="1.0" max="1.0"/>
         <delta pt="0" x="70" y="1"/>
         <delta pt="1" x="70" y="1"/>
         <delta pt="2" x="-76" y="1"/>
@@ -610,7 +610,7 @@
         <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="15" x="0" y="0"/>
         <delta pt="16" x="0" y="0"/>
       </tuple>
     </glyphVariations>
@@ -632,7 +632,7 @@
         <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"/>
+        <delta pt="15" x="0" y="0"/>
       </tuple>
     </glyphVariations>
     <glyphVariations glyph="dotabovecomb">
@@ -644,15 +644,14 @@
         <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"/>
+        <delta pt="6" x="0" y="0"/>
+        <delta pt="7" x="0" y="0"/>
       </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>
diff --git a/Tests/varLib/models_test.py b/Tests/varLib/models_test.py
index c5c2a9a..ae67e0e 100644
--- a/Tests/varLib/models_test.py
+++ b/Tests/varLib/models_test.py
@@ -2,6 +2,7 @@
 from fontTools.misc.py23 import *
 from fontTools.varLib.models import (
     normalizeLocation, supportScalar, VariationModel)
+import pytest
 
 
 def test_normalizeLocation():
@@ -35,65 +36,121 @@
     assert supportScalar({'wght':2.5}, {'wght':(0,2,4)}) == 0.75
 
 
-def test_VariationModel():
-    locations = [
-        {'wght':100},
-        {'wght':-100},
-        {'wght':-180},
-        {'wdth':+.3},
-        {'wght':+120,'wdth':.3},
-        {'wght':+120,'wdth':.2},
-        {},
-        {'wght':+180,'wdth':.3},
-        {'wght':+180},
-    ]
-    model = VariationModel(locations, axisOrder=['wght'])
+class VariationModelTest(object):
 
-    assert model.locations == [
-        {},
-        {'wght': -100},
-        {'wght': -180},
-        {'wght': 100},
-        {'wght': 180},
-        {'wdth': 0.3},
-        {'wdth': 0.3, 'wght': 180},
-        {'wdth': 0.3, 'wght': 120},
-        {'wdth': 0.2, 'wght': 120}]
+    @pytest.mark.parametrize(
+        "locations, axisOrder, sortedLocs, supports, deltaWeights",
+        [
+            (
+                [
+                    {'wght': 0.55, 'wdth': 0.0},
+                    {'wght': -0.55, 'wdth': 0.0},
+                    {'wght': -1.0, 'wdth': 0.0},
+                    {'wght': 0.0, 'wdth': 1.0},
+                    {'wght': 0.66, 'wdth': 1.0},
+                    {'wght': 0.66, 'wdth': 0.66},
+                    {'wght': 0.0, 'wdth': 0.0},
+                    {'wght': 1.0, 'wdth': 1.0},
+                    {'wght': 1.0, 'wdth': 0.0},
+                ],
+                ["wght"],
+                [
+                    {},
+                    {'wght': -0.55},
+                    {'wght': -1.0},
+                    {'wght': 0.55},
+                    {'wght': 1.0},
+                    {'wdth': 1.0},
+                    {'wdth': 1.0, 'wght': 1.0},
+                    {'wdth': 1.0, 'wght': 0.66},
+                    {'wdth': 0.66, 'wght': 0.66}
+                ],
+                [
+                    {},
+                    {'wght': (-1.0, -0.55, 0)},
+                    {'wght': (-1.0, -1.0, -0.55)},
+                    {'wght': (0, 0.55, 1.0)},
+                    {'wght': (0.55, 1.0, 1.0)},
+                    {'wdth': (0, 1.0, 1.0)},
+                    {'wdth': (0, 1.0, 1.0), 'wght': (0, 1.0, 1.0)},
+                    {'wdth': (0, 1.0, 1.0), 'wght': (0, 0.66, 1.0)},
+                    {'wdth': (0, 0.66, 1.0), 'wght': (0, 0.66, 1.0)}
+                ],
+                [
+                    {},
+                    {0: 1.0},
+                    {0: 1.0},
+                    {0: 1.0},
+                    {0: 1.0},
+                    {0: 1.0},
+                    {0: 1.0,
+                     4: 1.0,
+                     5: 1.0},
+                    {0: 1.0,
+                     3: 0.7555555555555555,
+                     4: 0.24444444444444444,
+                     5: 1.0,
+                     6: 0.66},
+                    {0: 1.0,
+                     3: 0.7555555555555555,
+                     4: 0.24444444444444444,
+                     5: 0.66,
+                     6: 0.43560000000000006,
+                     7: 0.66}
+                ]
+            ),
+            (
+                [
+                    {},
+                    {'bar': 0.5},
+                    {'bar': 1.0},
+                    {'foo': 1.0},
+                    {'bar': 0.5, 'foo': 1.0},
+                    {'bar': 1.0, 'foo': 1.0},
+                ],
+                None,
+                [
+                    {},
+                    {'bar': 0.5},
+                    {'bar': 1.0},
+                    {'foo': 1.0},
+                    {'bar': 0.5, 'foo': 1.0},
+                    {'bar': 1.0, 'foo': 1.0},
+                ],
+                [
+                    {},
+                    {'bar': (0, 0.5, 1.0)},
+                    {'bar': (0.5, 1.0, 1.0)},
+                    {'foo': (0, 1.0, 1.0)},
+                    {'bar': (0, 0.5, 1.0), 'foo': (0, 1.0, 1.0)},
+                    {'bar': (0.5, 1.0, 1.0), 'foo': (0, 1.0, 1.0)},
+                ],
+                [
+                    {},
+                    {0: 1.0},
+                    {0: 1.0},
+                    {0: 1.0},
+                    {0: 1.0, 1: 1.0, 3: 1.0},
+                    {0: 1.0, 2: 1.0, 3: 1.0},
+                ],
+            )
+        ]
+    )
+    def test_init(
+        self, locations, axisOrder, sortedLocs, supports, deltaWeights
+    ):
+        model = VariationModel(locations, axisOrder=axisOrder)
 
-    assert model.deltaWeights == [
-        {},
-        {0: 1.0},
-        {0: 1.0},
-        {0: 1.0},
-        {0: 1.0},
-        {0: 1.0},
-        {0: 1.0, 4: 1.0, 5: 1.0},
-        {0: 1.0, 3: 0.75, 4: 0.25, 5: 1.0, 6: 0.6666666666666666},
-        {0: 1.0,
-         3: 0.75,
-         4: 0.25,
-         5: 0.6666666666666667,
-         6: 0.4444444444444445,
-         7: 0.6666666666666667}]
+        assert model.locations == sortedLocs
+        assert model.supports == supports
+        assert model.deltaWeights == deltaWeights
 
-def test_VariationModel():
-    locations = [
-        {},
-        {'bar': 0.5},
-        {'bar': 1.0},
-        {'foo': 1.0},
-        {'bar': 0.5, 'foo': 1.0},
-        {'bar': 1.0, 'foo': 1.0},
-    ]
-    model = VariationModel(locations)
-
-    assert model.locations == locations
-
-    assert model.supports == [
-        {},
-        {'bar': (0, 0.5, 1.0)},
-        {'bar': (0.5, 1.0, 1.0)},
-        {'foo': (0, 1.0, 1.0)},
-        {'bar': (0, 0.5, 1.0), 'foo': (0, 1.0, 1.0)},
-        {'bar': (0.5, 1.0, 1.0), 'foo': (0, 1.0, 1.0)},
-    ]
+    def test_init_duplicate_locations(self):
+        with pytest.raises(ValueError, match="locations must be unique"):
+            VariationModel(
+                [
+                    {"foo": 0.0, "bar": 0.0},
+                    {"foo": 1.0, "bar": 1.0},
+                    {"bar": 1.0, "foo": 1.0},
+                ]
+            )
diff --git a/requirements.txt b/requirements.txt
index acfd0fd..8532edd 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,6 +4,6 @@
 brotlipy==0.7.0; platform_python_implementation == "PyPy"
 unicodedata2==11.0.0; python_version < '3.7' and platform_python_implementation != "PyPy"
 scipy==1.2.1; platform_python_implementation != "PyPy"
-munkres==1.0.12; platform_python_implementation == "PyPy"
+munkres==1.0.12; platform_python_implementation == "PyPy"  # pyup: ignore
 zopfli==0.1.6
-fs==2.3.1
+fs==2.4.4
diff --git a/setup.cfg b/setup.cfg
index 9fe169e..a3bcdd1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 3.38.0
+current_version = 3.39.0
 commit = True
 tag = False
 tag_name = {new_version}
diff --git a/setup.py b/setup.py
index 273c585..0b244a4 100755
--- a/setup.py
+++ b/setup.py
@@ -352,7 +352,7 @@
 
 setup(
 	name="fonttools",
-	version="3.38.0",
+	version="3.39.0",
 	description="Tools to manipulate font files",
 	author="Just van Rossum",
 	author_email="just@letterror.com",
@@ -374,7 +374,6 @@
 			"ttx = fontTools.ttx:main",
 			"pyftsubset = fontTools.subset:main",
 			"pyftmerge = fontTools.merge:main",
-			"pyftinspect = fontTools.inspect:main"
 		]
 	},
 	cmdclass={